TL;DR
Is there a way to align corners of different plots in a multiplot setup?
Is there a way to convert axis coordinates to screen coordinates?
Detailed explanation
I'm trying to layout a quite complicated canvas with gnuplot. To achieve what I want, I need to stack different plots on each other exactly.
I did something like :
reset;
set samples 20;
set xrange [-pi:pi]; set yrange [-2:1];
set xlabel "x"; set ylabel "y";
unset key
set multiplot;
plot cos(x) w lp;
plot 2*cos(x+pi) w lp;
unset multiplot;
However, some components like the label, tics and borders are drawn twice. Then, when I do this with the cairolatex terminal the result is not as clear as if I don't stack plots.
Thus, I would like to draw these components for the first plot and then unset labels, tics and borders for the subsequent ones as shown in the snippet below:
set multiplot;
plot cos(x) w lp;
set border ls 4; # Just to distinguish it
unset xlabel; unset ylabel;
unset tics;
plot 2*cos(x+pi) w lp;
unset multiplot;
This generate an image where the next plots are not aligned (on left and bottom) with the first one, as no space is taken by the labels and tics.
To address this issue, I need to set the origin and size of the next plots manually. The bottom left corners must be aligned.
From the gnuplot documentation, it is possible the get the screen coordinated of an (X,Y) axis coordinate using:
GRAPH_X = (X - GPVAL_X_MIN) / (GPVAL_X_MAX - GPVAL_X_MIN)
GRAPH_Y = (Y - GPVAL_Y_MIN) / (GPVAL_Y_MAX - GPVAL_Y_MIN)
SCREEN_X = GPVAL_TERM_XMIN + GRAPH_X * (GPVAL_TERM_XMAX - GPVAL_TERM_XMIN)
SCREEN_Y = GPVAL_TERM_YMIN + GRAPH_Y * (GPVAL_TERM_YMAX - GPVAL_TERM_YMIN)
FRAC_X = SCREEN_X * GPVAL_TERM_SCALE / GPVAL_TERM_XSIZE
FRAC_Y = SCREEN_Y * GPVAL_TERM_SCALE / GPVAL_TERM_YSIZE
As we are trying to align the bottom left corner, (X,Y) == (GPVAL_X_MIN, GPVAL_Y_MIN). Thus, GRAPH_* = 0 and SCREEN_(*) = GPVAL_TERM_\1MIN. Thus:
# Multiply by 1.0 to promote variables to floats
FRAC_X = 1.0 * GPVAL_TERM_XMIN * GPVAL_TERM_SCALE / GPVAL_TERM_XSIZE
FRAC_Y = 1.0 * GPVAL_TERM_YMIN * GPVAL_TERM_SCALE / GPVAL_TERM_YSIZE
Resulting snippet:
set multiplot;
plot cos(x) w lp;
FRAC_X = 1.0 * GPVAL_TERM_XMIN * GPVAL_TERM_SCALE / GPVAL_TERM_XSIZE;
FRAC_Y = 1.0 * GPVAL_TERM_YMIN * GPVAL_TERM_SCALE / GPVAL_TERM_YSIZE;
set origin FRAC_X, FRAC_Y;
set border ls 4; # Just to distinguish it
unset xlabel; unset ylabel;
unset tics;
plot 2*cos(x+pi) w lp;
unset multiplot;
But as you can see, resulting plot is not better...
By the way, I did not find how to define the size to align the top right corners.
Questions
What is wrong with the origin computation? How to align top right corner as well?
Kind regards,
Alexis.
You can fix the margins to keep the automatic size after the first plot as follows (see also https://stackoverflow.com/a/19132068/2604213):
fix_margins = 'set margins '.\
'screen GPVAL_TERM_SCALE * GPVAL_TERM_XMIN/(1.0*GPVAL_TERM_XSIZE), '.\
'screen GPVAL_TERM_SCALE * GPVAL_TERM_XMAX/(1.0*GPVAL_TERM_XSIZE), '.\
'screen GPVAL_TERM_SCALE * GPVAL_TERM_YMIN/(1.0*GPVAL_TERM_YSIZE), '.\
'screen GPVAL_TERM_SCALE * GPVAL_TERM_YMAX/(1.0*GPVAL_TERM_YSIZE)'
set samples 20
set xrange [-pi:pi]; set yrange [-2:1]
set xlabel "x"; set ylabel "y"
unset key
set multiplot
plot cos(x) w lp
eval(fix_margins)
unset tics; unset xlabel; unset ylabel
plot 2*cos(x+pi) w lp
unset multiplot
In order to achieve an exact overlap, I think that an option would be to adjust the margin manually:
set terminal pngcairo
set output 'fig.png'
eps_h = 0.1
eps_w = 0.1
set multiplot
unset key
set lmargin at screen 2*eps_w
set tmargin at screen 1 - eps_h
set rmargin at screen 1 - eps_w
set bmargin at screen eps_h
plot cos(x) w lp lc rgb 'dark-red'
set border ls 4;
unset xlabel
unset ylabel
unset tics
plot 2*cos(x+pi) w lp lc rgb 'royalblue'
which gives:
Related
I need help with my polar plot shown below.
The plot was generated using the same code as in GNUPlot - Plotting a data set in polar form (r, θ, T(r,θ)) to a contour/heat map.
The code plot the whole of quadrant 1, but I only want the plot from 0 to 60 degrees, for radius up to 3000.
Can anyone help to suggest which part of the code that I should change?
Or what software that I can use to clean the unnecessary areas?
Thank you in advance.
Apparently, you can limit the polar graph to a quadrant.
However, I haven't found out how to get an other section than a quadrant.
So, maybe you have to draw it "manually"?
Check the following example as starting point.
Script:
### show only a section of a polar graph
reset session
set angle degrees
set size ratio 1
set margins 0,0,0,0
set origin 0.05,0.1
set size 0.9,0.9
set border 1
unset ytics
set xtics nomirror
# create some test data
set print $Data
do for [i=1:100] { print sprintf("%g %g",0.6+0.3*cos(7.2*i),i*0.6) }
set print
# draw polar graph "manually"
rmax = 1
rstep = 0.2
rcount = int(real(rmax)/rstep)+1
set xrange [0:rmax]
set yrange [0:rmax]
set for [r=1:rcount] obj 1+r circ at 0,0 size r*rstep arc [0:60] fc rgb r<rcount?0x777777:0x000000 dt r==rcount?1:3
amax = 60
astep = 10
acount = amax/astep
astepMin = 1
set for [a=0:acount] arrow 1+a \
from 0,0 angle a*astep length rmax lc rgb a<acount?0x777777:0x000000 dt a==acount?1:3 nohead
set for [a=0:amax:astepMin] arrow 1000+a \
from rmax*cos(a), rmax*sin(a) angle 180+a length rmax/100. lc "black" nohead
roff = 1.03 # factor for label offset
set for [a=0:amax:astep] label 1+a at cos(a)*rmax*roff, sin(a)*rmax*roff sprintf("%g°",a) center
# conversion polar to cartesian
xPtC(colR,colA) = column(colR)*cos(column(colA))
yPtC(colR,colA) = column(colR)*sin(column(colA))
plot $Data u (xPtC(1,2)):(yPtC(1,2)) w l lw 2 lc "red" ti "Data"
### end of script
Result:
I have a polar plot in Gnuplot given by
set polar
set angles degrees
unset xtics
unset ytics
unset raxis
set ttics 0,30
set grid polar linestyle 1 dashtype 2 linecolor rgb 'grey'
set samples 200, 200
unset border
set xrange [-0.51:0.51]
set yrange [-0.51:0.51]
set size square
unset key
plot sin(2*t)*cos(2*t)
Which gives the following output:
However,
I want the rlabels (0, 0.1, 0.2, etc) at an angle of 45 degrees instead of them being placed on the x-axis, and
There should be no rtics
Any help will be appreciated.
In order to remove the rtics do set rtics scale 0.
You can set the starting angle for polar graph (check help theta), however, only to {right|top|left|bottom} and not to 45°, but this still wouldn't help you here.
So, probably you have to set the labels "manually". Maybe there is an automatic way which I am not aware of. Check the following example.
Code:
### set rtics on 45° "axis"
reset session
set polar
set angles degrees
unset xtics
unset ytics
unset raxis
set ttics 0,30
set grid polar linestyle 1 dashtype 2 linecolor rgb 'grey'
set samples 200, 200
unset border
set xrange [-0.51:0.51]
set yrange [-0.51:0.51]
set size square
unset key
set rtics scale 0
set format r ""
myRTics = "0.1 0.2 0.3 0.4 0.5"
do for [i=1:words(myRTics)] {
r = word(myRTics,i)
set label i sprintf("%s",r) at r*cos(45),r*sin(45) center
}
plot sin(2*t)*cos(2*t)
### end of code
Result:
I addition to #theozh's excellent answer, I realized I can generate the labels on the fly, by providing the start, end, and increment of the labels. So instead of
myRTics = "0.1 0.2 0.3 0.4 0.5"
do for [i=1:words(myRTics)] {
r = word(myRTics,i)
set label i sprintf("%s",r) at r*cos(45),r*sin(45) center
}
I will write
rangle = 45
rstart = 0.1 ; rend = 0.5 ; rinc = 0.1 ;
n = floor((rend-rstart)/rinc) + 1
do for [i=1:n] {
r = rstart + (i-1)*rinc
set label i sprintf("%g",r) at r*cos(rangle),r*sin(rangle) center
}
The rest of it will be as already answered by #theozh. The output doesn't change.
I am a bit surprised that Gnuplot doesn't have any inbuilt method to handle this.
I am trying to plot a simple linear surface of the equation x + y + 2z = 0. This is my file:
set xrange [-4:4]
set yrange [-4:4]
set zrange [-4:4]
set xlabel 'x'
set ylabel 'y'
set zlabel 'z'
set xyplane at -4.0
unset xzeroaxis
unset yzeroaxis
unset zzeroaxis
set border 1023-128
set xtics out nomirror
set ytics out nomirror
set ztics out
set xtics add ('' -4)
set label 1 "{/:Italic x} + {/:Italic y} + 2{/:Italic z} = 0" at 4,4.2,-2 font 'Times New Roman, 11'
set arrow 1 from 0,0,-4 to 0,0,4 filled
set arrow 2 from 0,-4,0 to 0,4,0 filled
set arrow 3 from -4,0,0 to 4,0,0 filled
set arrow 4 from 3.9,4.1,-2.1 to 3.6,3.6,-2.5
unset key
set pm3d lighting primary 0.5 specular 0.6
set style fill transparent solid 1 noborder
set palette defined (0 "cyan", 1 "green")
unset colorbox
set pm3d depthorder
splot -x/2-y/2 with pm3d
The result
I'm using arrows to show the axis, since they seem to be stuck to the xy plane, and moving it causes further issues with the tics and border. But now the problem is that they are completely occluded by the surface. Is there a setting which allows to appear in front when they "pierce" the surface? I'd like to make the surface semitransparent, but the problem is clearer with these settings.
I guess your original idea, i.e. "piercing" a 3D surface with an arrow or line does not work right away in gnuplot, because gnuplot will not calculate the piercing points automatically. Please correct me if I am wrong and let me know in case there might be a simple solution to this.
As you did in your simple case, you can just split the arrow at the origin, because you already know the piercing point. However, what do you do if the surface is irregular or has several piercing points?
Solution: take the effort to create a segmented 3D arrow and let gnuplot automatically show and hide the surfaces as needed. This is probably getting close to what you had in mind. However, this solution will show surprises when you want to change the color of the arrows. So, there is still room for improvement.
Code: (simple version with arrows just along x,y,z axes)
### arrows "piercing" a 3D surface
reset session
set view equal xyz
set xyplane relative 0.0
set xrange [-4:4]
set yrange [-4:4]
set zrange [-4:4]
# create 3D arrow
r = 0.01 # radius of arrow
rHead = 0.1 # radius of arrrowhead
n = 6 # corners of arrow
set print $myArrow
do for [h=-100:90] {
do for [a=360/n:360:360/n] {
print sprintf("%g %g %g",r*cos(a),r*sin(a), h/100.)
}
print ""
}
do for [h=90:100] {
do for [a=360/n:360:360/n] {
print sprintf("%g %g %g",rHead*(100-h)/10.*cos(a), \
rHead*(100-h)/10.*sin(a), h/100.)
}
print ""
}
set print
unset key
unset colorbox
set pm3d depthorder
set samples 100
set isosamples 100
set view 65,46,1.3
# function to demonstrate "piercing"
f(x,y) = (sin(x*3)/x + sin(y*3)/y - 3)/2
splot \
f(x,y) w pm3d, \
$myArrow u 1:2:($3*4):(0) w pm3d lc rgb var, \
$myArrow u 2:($3*4):1:(0) w pm3d lc rgb var, \
$myArrow u ($3*4):1:2:(0) w pm3d lc rgb var
### end of code
Result:
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
So basically you need to add the "front" attribute to the objects you want to appear in front of the plot.
I hope I don't get firebombed here, this is the first time I'm posting. Lol
So after a grueling simulation, I have this large data set of the form r, θ, and T(r,θ). I needed to plot a contour map for this data set. θ is in degrees.
So I ran to GNUPlot and I hoped that this will save me from my problems. I studied a bit about how it works, but I still cannot plot my 2D contour data.
I then researched about my problem then I saw this thread here in SE:
gnuplot 2D polar plot with heatmap from 3D dataset - possible?
I used both codes (that I assume to be working) in that post and NONE is working with my dataset. I hope someone can help me with my problem.
This is what I changed the code into:
reset
set terminal pngcairo size 800,800
set output '3d-polar.png'
set lmargin at screen 0.05
set rmargin at screen 0.85
set bmargin at screen 0.1
set tmargin at screen 0.9
set pm3d map
unset key
set multiplot
# plot the heatmap
set parametric
set isosamples 500
unset border
unset xtics
unset ytics
set angles degree
r = 0.05 # CHANGED THIS
set urange[0:r] # radius
set vrange[0:360] # angle
set xrange[-r:r]
set yrange[-r:r]
set colorbox user origin 0.9,0.1 size 0.03,0.8
splot 'data.dat' using 1:2:3 # CHANGED THIS
# now plot the polar grid only
set style line 11 lc rgb 'white' lw 2
set grid polar ls 11
set polar
set rrange[0:r]
unset raxis
set rtics format '' scale 0
unset parametric
set for [i=0:330:30] label at first (r+0.35)*cos(i), first (r+0.35)*sin(i)\
center sprintf('%d', i)
plot NaN w l
unset multiplot
So I had this little 'eureka' moment. Apparently I had to put dgrid3d and had to convert the coordinates from polar to cartesian.
Upon converting (and putting set dgrid3d) this ugly plot appeared:
reset
set terminal pngcairo size 800,800
set output '3d-polar.png'
set lmargin at screen 0.05
set rmargin at screen 0.85
set bmargin at screen 0.1
set tmargin at screen 0.9
set pm3d map
unset key
set multiplot
# plot the heatmap
set parametric
set isosamples 500
unset border
unset xtics
unset ytics
set angles degree
r = 0.05 # CHANGED THIS
set urange[0:r] # radius
set vrange[0:360] # angle
set xrange[-r:r]
set yrange[-r:r]
set colorbox user origin 0.9,0.1 size 0.03,0.8
set dgrid3d # ADDED THIS
splot 'data.dat' using 1:2:3 # CHANGED THIS
# now plot the polar grid only
set style line 11 lc rgb 'white' lw 2
set grid polar ls 11
set polar
set rrange[0:r]
unset raxis
set rtics format '' scale 0
unset parametric
set for [i=0:330:30] label at first (r+0.35)*cos(i), first (r+0.35)*sin(i)\
center sprintf('%d', i)
plot NaN w l
unset multiplot
The plot.
I'm at my limit here now. Three questions.
1. Am I correct to assume that I can use the mathematics in gnuplot to convert the coordinates from polar to cartesian? Or is there a way for me to use the polar coordinate data immediately?
2. How do I limit the 'colors' to the round polar plot?
3. What happened to the degree and radius readings? Where did they go?
Thanks!
I am looking for a way to position tic marks in gnuplot between the axis, but so far I only found solution to put them in or out:
set tics in
puts all tic marks inside of the canvas
set tics out
puts all tic marks outide of the canvas
All I want is to place tic marks on both sides of the axis, somethink like
--l--l--
Thanks for a hint!
As said in the comments, it seems not possible to place the tics on both sides of the axis. A workaround would be to plot the axis twice, or to draw the tics by hand with the set arrow.
Drawing tics by hand:
Consider the following settings:
Xmin = -4.0 # range in x
Xmax = 4.0
Ymin = -1.2 # range in y
Ymax = 1.2
NXtics = 8 # number of Xtics
NYtics = 4 # number of Ytics
epsX = 0.05 # length of Xtics
epsY = 0.03 # length of Ytics
dX = (Xmax-Xmin)/NXtics # distance between Xtics
dY = (Ymax-Ymin)/NYtics # distance between Ytics
Next, we draw the bottom, top, left, and right tics:
# xtics and x2tics
do for [i=0:NXtics] {
posX = Xmin+i*dX
set arrow from posX,Ymin-epsY to posX,Ymin+epsY nohead front # bottom
set arrow from posX,Ymax-epsY to posX,Ymax+epsY nohead front # top
}
# ytics and y2tics
do for [i=0:NYtics] {
posY = Ymin+i*dY
set arrow from Xmin-epsX,posY to Xmin+epsX,posY nohead front # left
set arrow from Xmax-epsX,posY to Xmax+epsX,posY nohead front # right
}
Since you are drawing the tics by hand, you will need to configure the axis numbers and ranges:
set xtics Xmin,dX,Xmax scale 0 offset 0,-epsY
set ytics Ymin,dY,Ymax scale 0 offset -epsX,0
set xrange [XMIN:XMAX]
set yrange [YMIN:YMAX]
Finally, your highly complicated plot:
plot sin(x)
Result:
This method also allows you to break the axis
Drawing axis twice:
This method is easier; but you need to set set margins of the canvas, and to use the multiplot mode:
set tmargin at screen 0.9 # top margin
set bmargin at screen 0.2 # bottom
set lmargin at screen 0.2 # left
set rmargin at screen 0.9 # right
set yrange [-1.2:1.2]
set multiplot
set tics scale 0.5 # scale size of the tics
plot 2 notitle # a plot outside the canvas, just to draw the axis
set tics out # tics outside
set format xy '' # delete the numbers
unset border # delete the border
plot sin(x) # your awesome plot
unset multiplot
The result is similar :)
Quick & dirty approach:
set multi
set tics scale 0.5
plot sin(x)/x
set tics out
replot
unset multi
Beware this overprints your graph with a second one. Should be OK for bitmap output, but don't do it if you have vector output (pdf, eps), especially if your graph is complicated or contains a lot of datapoints. It blows up the resulting file to twice its size.
Gnuplot at the moment (v 5.0pl1) has no option to place the tics centered on the axis. You'll have to use one of the workarounds shown here.