Reputation: 822
I would like to plot a curve with a variable line width. I normally would do the following if I wanted to use points instead of a line:
gnuplot> plot 'curve.dat' u ($1):($2):($1) ps var
where curve.dat is filled with:
0 0
1 1
2 4
3 9
4 16
5 25
and so on. Now if I try something similar for the line width:
gnuplot> plot 'curve.dat' u ($1):($2):($1) lw var
I get the error message:
undefined variable: var
Or is this something that cannot be done with gnuplot?
Upvotes: 4
Views: 1468
Reputation: 26088
So far, there is no variable linewidth in gnuplot.
Furthermore, if your data has N
points you will have N-1
lines, although, you give N
linewidths in your data. Which linewidth of which data point should be skipped? The first one or the last one?
If you draw tapered lines you don't have this problem. You can use filledcurves for this, but as @Ilya Zakharevich mentioned, @Gavin Portwood's solution is actually plotting a line height, not a line width.
The solution below is using filledcurves as well, but plotting real linewidths. For this, you have to go to pixel coordinates, do some calculations and go back to axis coordinates (will not yet work for log scale). gnuplot can easily fill curves between two y-values with identical x-values, which, however, would not help here. But gnuplot can also fill closed curves. Hence, the script is creating a closed curve by creating the "right" outline of the path and the reversed "left" outline of the tapered "line".
Edit: complete revision
x + j*y
is used where j = sqrt(-1)
is the imaginary unit. This shortens the script a bit.Script: (works with gnuplot>=5.2.0, Sept. 2017)
### variable linewidth / tapered lines plot
reset session
$Data <<EOD
1 10 1.0
9 20 25.0
2 30 15.0
4 20 5.0
7 40 5.0
3 70 1.0
2 50 25.0
4 40 3.0
7 70 30.0
9 40 80.0
EOD
# plot data to get GPVAL_... values and number of rows
plot $Data u (r=$0+1,$1):2
j = {0, 1} # imaginary unit
array P[r]
array LW[r]
stats $Data u (P[$0+1]=$1+j*$2, LW[$0+1]=$3) nooutput # put data into arrays
# axes to pixel conversion and vice versa
Factor = GPVAL_VERSION==5.2 && int(GPVAL_PATCHLEVEL)<=7 ? \
GPVAL_TERM eq "wxt" ? 20 : GPVAL_TERM eq "qt" ? 10 : 1 : 1
aMin = GPVAL_X_MIN + j*GPVAL_Y_MIN
pMin = GPVAL_TERM_XMIN + j*GPVAL_TERM_YMIN
R = Factor*((GPVAL_X_MAX-real(aMin))/(GPVAL_TERM_XMAX-real(pMin)) + \
j*(GPVAL_Y_MAX-imag(aMin))/(GPVAL_TERM_YMAX-imag(pMin)))
a2p(a) = (real(a)-real(aMin))/real(R) + real(pMin) + j*((imag(a)-imag(aMin))/imag(R) + imag(pMin))
p2a(p) = (real(p)-real(pMin))*real(R) + real(aMin) + j*((imag(p)-imag(pMin))*imag(R) + imag(aMin))
# various functions in pixel coordinates
dp(i) = a2p(P[i+1])-a2p(P[i]) # distance between two points
ap(i) = atan2(imag(dp(i)),real(dp(i))) + acos(0) # perpendicular angle (independent of setting degrees or radians)
wp(i,k) = LW[k]*0.5*(cos(ap(i)) + j*sin(ap(i))) # offset for outline
array OUTL[4*|P|-4]
do for [i=1:2*|P|-2] {
m0 = (i-1)/2 + 1 # integer division!
n0 = i/2 + 1 #
m1 = |P| - m0
n1 = |P| - n0 + 1
OUTL[i] = p2a(a2p(P[n0]) - wp(m0,n0))
OUTL[i+2*|P|-2] = p2a(a2p(P[n1]) + wp(m1,n1))
}
set style fill noborder
set key noautotitle
set grid x,y
plot OUTL u (real(OUTL[$0+1])):(imag(OUTL[$0+1])) w filledcurves lc "web-green"
### end of script
Comments:
qt
terminal the result looks different, which is not the intended way, but I guess the reason is the way how an area is filled by the graphics library. The script would need some adaptions.pngcairo
terminal: since you have to plot twice (the first time to get the GPVAL_...
variables) you also have to set the filename twice, i.e. something like this:reset session
...
set term pngcairo
set output "SO52250137.png"
plot $Data u (r=$0+1,$1):2
set output "SO52250137.png"
...
plot OUTL u (real(OUTL[$0+1])):(imag(OUTL[$0+1])) w filledcurves lc "web-green"
set output
### end of script
Result: (from wxt
and pngcairo
terminal)
(output from qt
terminal)
Upvotes: 1
Reputation: 1419
This addresses a problem with Gavin’s answer: his method creates a certain line height (as opposed to line width). I found a different way, which works with widths.
The defects in my solution: one needs to know the “coordinate space aspect ratio” in advance, and there are artifacts at joins.
Upvotes: 1
Reputation: 1217
You're right that linewidth
doesn't accept var
like pointsize
does. But you can have a similar effect by using filledcurves
:
WIDTH_FACTOR=20
plot 'curve.dat' u ($1):($2+$1/WIDTH_FACTOR):($2-$1/WIDTH_FACTOR) w filledcurves
Upvotes: 3