How to plot a contour with variable line color in Gnuplot?

Viewed 221

I want to plot a single contour with variable line colour. I have defined the colour at each (x,y) point explicitly in a data file as follows

  -0.4000  -0.4000   7.6807  253    0    0
  -0.3933  -0.4000   7.6907  253    10   0
  -0.3867  -0.4000   7.7009  153    25   45
  -0.3800  -0.4000   7.7112   33    25   90
  -0.3733  -0.4000   7.7215  200   130   40
  -0.3667  -0.4000   7.7320   23    11  100
   .
   .
   .

Above, the first three columns correspond to the x, y and z values, and the last three columns are the respective values for red, green and blue colours, each varying between [0:255].

Using the following code, I can get the right colour projection for the surface, but the line colour of the contour is wrong and does not vary at all.

set pm3d at s
set contour base
set cntrparam levels discrete 7.245
rgb(r,g,b) = int(r)*65536 + int(g)*256 + int(b)
splot 'data' u 1:2:3:(rgb($4,$5,$6)) w pm3d lw 2 lc rgb variable 

Does anyone know how to fix this issue? Any help is much appreciated.

Update: In case it helps, here is the output I get from the above script and the related data file. As you can see, the isocontour does not follow the colour pattern projected onto the surface.

enter image description here

2 Answers

This would be my suggestion on the simplfied assumption that there are only 3 colors (red, green, blue) depending only on x and y and constant colors within 60 degree sectors. Let me know if this assumption is not correct.

Script: (tested with gnuplot 5.0.0 and 5.4.1)

### variable color contour line
reset session

FILE = "SO73015796.dat"

set contour
set cntrparam levels discrete 7.245
rgb(r,g,b) = int(r)*65536 + int(g)*256 + int(b)

set table $Contour
    splot FILE u 1:2:3
unset table

set xyplane relative 0
unset contour
unset colorbox

set angle degrees 
myColor(x,y) = (a=atan2(y,x), abs(sin(a))<sin(30) ? 0x00ff00 : \
                abs(sin(a-60))<sin(30) ? 0xff0000 : 0x0000ff)
set key noautotitle

splot FILE u 1:2:3:(rgb($4,$5,$6)) w pm3d lc rgb var, \
      $Contour u 1:2:(6.5):(myColor($1,$2)) index 1 w l lw 2 lc rgb var
### end of script

Result:

enter image description here

Addition:

The following is a solution which doesn't make simplified assumptions, but takes the color from the original data. Well, a bit slow and inefficient. Maybe somebody has ideas to speed this up (maybe with specialized external tools).

  • plot the contour lines into a new datablock
  • find the closest point (in x,y) from 3D data and take its color (in principle, you could also interpolate the color between closest points)

In the example below you have 2 contour lines and about 700 contour points and about 15000 3D-points in your file. Hence, you need to read the 3D-file about 700 times from disk. Therefore, I thought it might be faster if you first load the file 1:1 into a datablock into memory, but I am not sure if it is really faster. On my old laptop the creation of the graph takes about 1-2 minutes.

Script: (works for gnuplot>=5.2.0)

### variable color contour line
reset session

FILE = "SO73015796.dat"

FileToDatablock(f,d) = GPVAL_SYSNAME[1:7] eq "Windows" ? \
                       sprintf('< echo   %s ^<^<EOD  & type "%s"',d,f) : \
                       sprintf('< echo "\%s   <<EOD" & cat  "%s"',d,f)     # Linux/MacOS

load FileToDatablock(FILE,'$Data')

set contour
set cntrparam levels discrete 7.25, 7.35
rgb(r,g,b) = int(r)*0x10000 + int(g)*0x100 + int(b)

# plot grid and contour data into datablock
set table $Temp
    splot $Data u 1:2:3     
unset table
unset contour

# get only contour lines into a new datablock
stats $Temp u 0 nooutput   # get number of blocks
N = STATS_blocks
set table $CONTOURS
    splot $Temp u 1:2:3 index 1:N-1   # all blocks except first (index 0-based) into datablock
unset table

# loop all contour points and take the color from closest 3D point
Dist(x0,y0,x1,y1) = sqrt((x1-x0)**2 + (y1-y0)**2)   # distance between to points
set print $ColoredContours
    do for [i=1:|$CONTOURS|] {
        if (strlen($CONTOURS[i])==0 || $CONTOURS[i][1:1] eq '#') {
            print $CONTOURS[i]
        }
        else {
            x0 = real(word($CONTOURS[i],1))
            y0 = real(word($CONTOURS[i],2))
            dmin = NaN
            stats $Data u (d=Dist(x0,y0,$1,$2),d<dmin || dmin!=dmin ? \
                (dmin=d, color=rgb($4,$5,$6)) : 0) nooutput
            print sprintf("%g %g 0x%06x", x0, y0, color)
        }
    }
set print

set xyplane relative 0
unset colorbox
set grid x,y
set key noautotitle
set zrange [zMin=6.0:]

splot $Data u 1:2:3:(rgb($4,$5,$6)) index 0 w pm3d lc rgb var, \
      $ColoredContours u 1:2:(zMin):3 w l lc rgb var
### end of script

Result:

enter image description here

In order to get contour with variable linecolors you need to include the palette command in the splot.

set pm3d at s
set palette rgbformulae 33,13,10
set contour
set cntrparam levels 50
set zrange [:8]
#rgb(r,g,b) = int(r)*65536 + int(g)*256 + int(b)
splot 'data' u 1:2:3 w l ls 7 palette notitle 

Contour with variable linecolors

However, based on the output it looks like your data doesn't vary much at the center to have variable linecolors. You can also play around with set contrparam levels to change the intensity of the contours.

Related