I'm trying to use gnuplot 6.0 patchlevel 0 (as provided by Chocolatey under MS Windows 10) to draw some arrows behind a series of plots, but the results are inconsistent and baffling.
Here is some sample data that reproduces the behavior, saved as question-data.csv
:
X,Y,Group
0,3,A
1,7,A
2,3,A
3,7,B
4,3,B
5,7,B
6,3,C
7,7,C
8,3,C
This script uses plot
to draw the graphs, and does what I mean.
dataFile = "question-data.csv"
set datafile separator ','
set yrange [0:10]
# Filter by group.
yornothing(l,lc,y) = ((l eq lc)?y:NaN)
# Has to be done before plot!
set arrow 1 from graph 0, first 4 to graph 1, first 4 nohead back lc rgb "#C0C000" lw 2
set arrow 2 from graph 0, first 5 to graph 1, first 5 nohead back lc rgb "#00FF00" lw 2
set arrow 3 from graph 0, first 6 to graph 1, first 6 nohead back lc rgb "#C0C000" lw 2
plot dataFile every ::1 using 1:(yornothing(strcol(3),"A",$2)) with filledcurves above lc rgb "#0000FF" notitle, \
dataFile every ::1 using 1:(yornothing(strcol(3),"B",$2)) with filledcurves above lc rgb "#FFA500" notitle, \
dataFile every ::1 using 1:(yornothing(strcol(3),"C",$2)) with filledcurves above lc rgb "#0000FF" notitle
It took a while to realize that arrows have to be drawn before any plots; until then, they silently failed. I was unable to find where that was documented.
Here's a script that tries to use multiplot
to draw the graphs, but the arrows end up in front of the first two plots, and only end up behind the third one:
dataFile = "question-data.csv"
set datafile separator ','
set yrange [0:10]
# Filter by group.
yornothing(l,lc,y) = ((l eq lc)?y:NaN)
# Has to be done before plot!
set arrow 1 from graph 0, first 4 to graph 1, first 4 nohead back lc rgb "#C0C000" lw 2
set arrow 2 from graph 0, first 5 to graph 1, first 5 nohead back lc rgb "#00FF00" lw 2
set arrow 3 from graph 0, first 6 to graph 1, first 6 nohead back lc rgb "#C0C000" lw 2
set multiplot
plot dataFile every ::1 using 1:(yornothing(strcol(3),"A",$2)) with filledcurves above lc rgb "#0000FF" notitle
plot dataFile every ::1 using 1:(yornothing(strcol(3),"B",$2)) with filledcurves above lc rgb "#FFA500" notitle
plot dataFile every ::1 using 1:(yornothing(strcol(3),"C",$2)) with filledcurves above lc rgb "#0000FF" notitle
unset multiplot
If I add a bogus 4th plot to the multiplot
, the arrows now mysteriously draw in front of all graphs:
dataFile = "question-data.csv"
set datafile separator ','
set yrange [0:10]
# Filter by group.
yornothing(l,lc,y) = ((l eq lc)?y:NaN)
# Has to be done before plot!
set arrow 1 from graph 0, first 4 to graph 1, first 4 nohead back lc rgb "#C0C000" lw 2
set arrow 2 from graph 0, first 5 to graph 1, first 5 nohead back lc rgb "#00FF00" lw 2
set arrow 3 from graph 0, first 6 to graph 1, first 6 nohead back lc rgb "#C0C000" lw 2
set multiplot
plot dataFile every ::1 using 1:(yornothing(strcol(3),"A",$2)) with filledcurves above lc rgb "#0000FF" notitle
plot dataFile every ::1 using 1:(yornothing(strcol(3),"B",$2)) with filledcurves above lc rgb "#FFA500" notitle
plot dataFile every ::1 using 1:(yornothing(strcol(3),"C",$2)) with filledcurves above lc rgb "#0000FF" notitle
# Empty plot, to change nature of problem
plot dataFile every ::1 using 1:(yornothing(strcol(3),"D",$2)) with filledcurves above lc rgb "#0000FF" notitle
unset multiplot
Is this a bug, or my misunderstanding of multiplot? gnuplot is reasonably complex, and I have only scratched its surface.
I think this is a misunderstanding of multiplot. "set multiplot" allows you to issue multiple plot commands that draw onto the same canvas without erasing the canvas in between plots. Each of the plot
commands in the multiplot sequence is drawn in full using all of the settings that are in effect at the time of that plot
command, including all of the objects, arrows, labels, etc that are defined. So your three arrows are drawn all over again for each of the three plot
commands inside the multiplot
, and each time they are drawn they necessarily are drawn on top of the previous content of the canvas (since it isn't erased in between).
As to your first remark that "arrows have to be drawn before any plots", that may indicate a separate misunderstanding. The set arrow
command does not by itself drawn an arrow. It only defines an arrow that will be drawn as part of all subsequent plot
commands. This is the same as for the commands set label
, set object
, and so on that define additional elements that will be included in all subsequent plots.
One more thing that may be relevant. When you define your arrows you can specify whether they appear behind or in front of the curves drawn by plot with filledcurves
. The back
keyword in your set arrow
commands tells the program that the arrows should be in back of the curves; front
would instead draw them in front. However as explained above, in the case of multiplots the front
elements of a component plot
command are nevertheless overwritten by everything in the subsequent plot
commands.
gnuplot> help layer
A gnuplot plot is built up by drawing its various components in a fixed order.
This order can be modified by assigning some components to a specific layer
using the keywords `behind`, `back`, or `front`. For example, to replace the
background color of the plot area you could define a colored rectangle with the
attribute `behind`.
set object 1 rectangle from graph 0,0 to graph 1,1 fc rgb "gray" behind
The order of drawing is
behind
back
the plot itself
the plot legend (`key`)
front
Within each layer elements are drawn in the order
grid, axis, and border elements
pixmaps in numerical order
objects (rectangles, circles, ellipses, polygons) in numerical order
labels in numerical order
arrows in numerical order
In the case of multiple plots on a single page (multiplot mode) this order
applies separately to each component plot, not to the multiplot as a whole.
Illustration
Here is a slight modification of your multiplot example that makes it easier to see what is happening. It shifts the origin after each invocation of plot
inside the multiplot so that redrawn elements are slightly displaced from their previous locations. This makes it clear that the arrows are being drawn multiple times. Arrow colors are changed to make them easier to distinguish from the filled curves.
# Has to be done before plot!
set arrow 1 from graph 0, first 4 to graph 1, first 4 nohead back lc rgb "yellow" lw 2
set arrow 2 from graph 0, first 5 to graph 1, first 5 nohead back lc rgb "brown" lw 2
set arrow 3 from graph 0, first 6 to graph 1, first 6 nohead back lc rgb "dark-green" lw 2
set obj rect from graph 0,0 to graph 1,1 fc "gray" fs transparent solid 0.25
set multiplot
plot dataFile every ::1 using 1:(yornothing(strcol(3),"A",$2)) with filledcurves above lc rgb "#0000FF" notitle
set origin 0.05, 0.05
plot dataFile every ::1 using 1:(yornothing(strcol(3),"B",$2)) with filledcurves above lc rgb "#FFA500" notitle
set origin 0.1, 0.1
plot dataFile every ::1 using 1:(yornothing(strcol(3),"C",$2)) with filledcurves above lc rgb "#0000FF" notitle
unset multiplot