Draw a bended arrow between two points in gnuplot - gnuplot
I am producing the figure below using the following gnuplot code. I want to draw a bended arrow from the point labeled l=0 to l=1 with head.
Code
reset session
# Ranges
set xrange [-1:6]
set yrange [-2:1]
# Term options
set terminal postscript eps
set termoption font "Times, 30"
# set termoption
set style line 1 lc rgb 'black' lw 3 lt 1 pt 7 ps 2
# Data points
$DATA<<EOD
0 0
1 0
2 0
3 0
4 0
5 0
6 0
EOD
set output "Anderson_lattice.eps"
# set arrow
set arrow 1 from -0.5, -1.5 to 5.5, -1.5 lc rgb 'black' lw 5
set arrow 2 from -0.5, -1.5 to -0.5, -0.5 lc rgb 'black' lw 5
set label 1 "{/Times-Italic=30 {/Symbol e}_{l}}" at -0.75, -0.3 tc rgb "black"
set arrow 3 from -0.25, -1.0 to 0.25, -1.0 ls 1 nohead
set arrow 5 from 1 - 0.25, -0.75 to 1 + 0.25, -0.75 ls 1 nohead
set arrow 6 from 2 - 0.25, -0.5 to 2 + 0.25, -0.5 ls 1 nohead
set arrow 7 from 3 - 0.25, -1.35 to 3 + 0.25, -1.35 ls 1 nohead
set arrow 8 from 4 - 0.25, -1.0 to 4 + 0.25, -1 ls 1 nohead
set arrow 9 from 5 - 0.25, -0.85 to 4 + 0.25, -0.85 ls 1 nohead
set arrow 10 from 6 - 0.25, -1.25 to 6 + 0.25, -1.25 ls 1 nohead
set label 2 "{/Times-Italic=30 sites}" at 5.5, -1.65 tc 'black'
set label 3 "{/Times-Italic=30 l=0}" at 2.7, -0.25 tc 'black'
set label 4 "{/Times-Italic=30 l=1}" at 1 + 2.7, -0.25 tc 'black'
unset xtics; unset ytics; unset border
plot $DATA using 1:2 with p ls 1 notitle
unset output
Result
How do I do that?
I'm not aware that gnuplot offers a feature for directly drawing a bent arrow.
Edit:
(I removed my initial approach since it has no advantage over using Cubic Bézier. And added some more flexibility to the second approach.)
I completely agree with #GRSousaJr that Cubic Bézier curves give much more flexibility in drawing bent arrows. At the same time you can also draw straight arrows.
Based on #GRSousaJr's approach, my suggestions would be the following:
instead of entering absolute values for the control points, I would prefer relative or absolute angles and relative distances. This has the advantage that you don't have to care about absolute numbers, especially when two arrows should have the same proportions but have different absolute start/endpoints.
All parameters for the arrows are in the datablock $myArrows.
Some explanations:
for the Cubic Bézier curves 4 points are used: p0,p1,p2,p3, where p0 and p3 are the start and end points, respectively. p1 and p2 are points which control the curvature. p0x, p0y, ... p3x, p3y are the x and y components, respectively.
in contrast to #GRSousaJr's solution the control points p1 and p2 are not given in absolute values but calculated from p0 and p3 and the angles a0 and a3 and the radii r0 and r3.
the angles of the arrow at the points p0 and p3 can be given absolute or relative to the direction of p0 to p3. The parameter e tells which end has relative angle and which end absolute angle. 0=angles at both ends relative, 1=start angle relative, end angle absolute, 2=start angle absolute, end angle relative, 3=angles at both ends absolute. For the relative angle you first need to calculate the angle between p0 and p3 (function AngleP0P3())
the distance of the control points p1 and p2 from the points p0 and p3 are given in relative values r0 and r3 with respect to the distance between p0 and p3. That's why there is the function Length(). 0.5 is a good value to start with.
note that the functions AngleP0P3(n) and Length(n) actually do not depend on n. That is just to shorten the code. These functions use the parameters p1x, ..., p3y, and when calling AngleP0P3(0) the function will take the current values of p1x, ..., p3y. This is shorter than e.g. Angle(p0x,p0y,p3x,p3y).
the function ArrowInit(i) is to collect or initialize the values for p1x, ..., p3y from the ith row of datablock $myArrows.
the line of the arrows are simply plotted in a for loop as parametric function in t with the range t[0:1]. For every i in the plot command ArrowInit(i) is called to get the corresponding parameters from the datablock $myArrows.
The angle of the arrow in point p3 is in the direction from p2 to p3, i.e. the tangent of the Bézier curve in point p3. However you don't want the line, but just the arrow. So far, I don't have a better approach than plotting a short vector from 99% of the arrow path to 100% of the arrow path.
Some comments on usage:
in order to "see" the correct angles you specify in $myArrows, your plot has to have the same aspect ratio as your x and y ranges. In the below examples it is x[0:20] and y[0:10], hence, set the aspect ratio of the graph to 0.5, i.e. at the beginning set size 0.5.
the direction of the arrow head is the tangent in point p3. If you have a strong curvature at p3, the arrow head might look "bad", although the arrow head is in the correct angle. In such cases, increase the length r3 a little.
You can also draw straight arrows, see Arrow1. Just set a0=0,a3=0 and e=0.
Tested with gnuplot 5.2.8
Code:
### workaround for bent arrows
reset session
set size ratio 0.5
# p0x p0y a0 r0 p3x p3y a3 r3 e color
$myArrows <<EOD
1 1.00 1.00 0 0.5 3.00 3.00 0 0.5 0 0xff0000
2 3.00 1.00 0 0.5 5.00 3.00 0 0.5 1 0x00c000
3 5.00 1.00 0 0.5 7.00 3.00 0 0.5 2 0x0000ff
4 7.00 1.00 0 0.5 9.00 3.00 0 0.5 3 0xff00ff
5 1.00 4.00 0 0.5 3.00 6.00 90 0.5 0 0xff0000
6 3.00 4.00 0 0.5 5.00 6.00 90 0.5 1 0x00c000
7 5.00 4.00 0 0.5 7.00 6.00 90 0.5 2 0x0000ff
8 7.00 4.00 0 0.5 9.00 6.00 90 0.5 3 0xff00ff
9 1.00 7.00 90 0.5 3.00 9.00 0 0.5 0 0xff0000
10 3.00 7.00 90 0.5 5.00 9.00 0 0.5 1 0x00c000
11 5.00 7.00 90 0.5 7.00 9.00 0 0.5 2 0x0000ff
12 7.00 7.00 90 0.5 9.00 9.00 0 0.5 3 0xff00ff
13 11.00 1.00 45 0.5 13.00 3.00 -45 0.5 0 0xff0000
14 13.00 1.00 45 0.5 15.00 3.00 -45 0.5 1 0x00c000
15 15.00 1.00 45 0.5 17.00 3.00 -45 0.5 2 0x0000ff
16 17.00 1.00 45 0.5 19.00 3.00 -45 0.5 3 0xff00ff
17 11.00 4.00 -45 0.5 13.00 6.00 -45 0.5 0 0xff0000
18 13.00 4.00 -45 0.5 15.00 6.00 -45 0.5 1 0x00c000
19 15.00 4.00 -45 0.5 17.00 6.00 -45 0.5 2 0x0000ff
20 17.00 4.00 -45 0.5 19.00 6.00 -45 0.5 3 0xff00ff
21 11.00 7.00 0 0.5 15.00 9.00 90 0.5 1 0x00c000
22 15.00 7.00 0 0.5 19.00 9.00 0 0.5 1 0x00c000
EOD
set angle degrees
# Angle between p0 and p3 (range: -90° <= angle < 270°), NaN if dx=dy=0
AngleP0P3(n) = (dy=p3y-p0y,dx=p3x-p0x)==0 ? (dy==0 ? NaN : sgn(dy)*90) : \
(dx<0 ? 180 : 0) + atan(dy/dx)
# Parameter e: determines which ends have relative or absolute angles
# 0: both ends relative
# 1: start relative, end absolute,
# 2: start absolute, end relative
# 3: both ends absolute
AngleAbs(i) = int(word($myArrows[i],10)) # to set all arrows equal, use: AngleAbs(i) = 0,1,2, or 3
Angle(i,p) = word($myArrows[i],p) + \
((p==4 && AngleAbs(i)&2) || (p==8 && AngleAbs(i)&1) ? 0 : AngleP0P3(0))
Length(n) = sqrt((p3x-p0x)**2 + (p3y-p0y)**2)
Color(i) = word($myArrows[i],11)
ArrowInit(i) = (p0x=word($myArrows[i],2),p0y=word($myArrows[i],3), \
p3x=word($myArrows[i],6),p3y=word($myArrows[i],7), \
p1x=p0x+Length(0)*word($myArrows[i],5)*cos(Angle(i,4)), \
p1y=p0y+Length(0)*word($myArrows[i],5)*sin(Angle(i,4)), \
p2x=p3x-Length(0)*word($myArrows[i],9)*cos(Angle(i,8)), \
p2y=p3y-Length(0)*word($myArrows[i],9)*sin(Angle(i,8)))
# Cubic Bézier curves function with t[0:1] as parameter
# p0: start point, p1: 1st control point, p2: 2nd control point, p3: endpoint
px(t) = (-p0x + 3*p1x - 3*p2x + p3x)*t**3 + (3*p0x - 6*p1x + 3*p2x)*t**2 + (-3*p0x + 3*p1x)*t + p0x
py(t) = (-p0y + 3*p1y - 3*p2y + p3y)*t**3 + (3*p0y - 6*p1y + 3*p2y)*t**2 + (-3*p0y + 3*p1y)*t + p0y
# set linestyles and arrowstyles
do for [i=1:|$myArrows|] {
set style line i lw 2 lc rgb Color(i)
set style arrow i head size 0.20,15,45 fixed filled ls i
}
set key out noautotitle below
set xrange [0:20]
set xtics 1
set format x ""
set grid xtics ls -1 lc rgb "gray"
set yrange [0:10]
set ytics 1
set format y ""
set grid ytics ls -1 lc rgb "gray"
plot for [i=1:|$myArrows|] [0:1] '+' u (ArrowInit(i),px($1)):(py($1)) w l ls i, \
for [i=1:|$myArrows|] [0:1] '+' u (ArrowInit(i),px(0.99)):(py(0.99)): \
(px(1)-px(0.99)):(py(1)-py(0.99)) every ::0::0 w vec as i, \
$myArrows u 2:3:1 w labels offset 0,-0.7, \
keyentry w l ls 1 ti "both ends relative angles", \
keyentry w l ls 2 ti "start relative, end absolute angle", \
keyentry w l ls 3 ti "start absolute, end relative angle", \
keyentry w l ls 4 ti "both ends absolute angles"
### end of code
exit
Result:
I created (at least in my mind) an "enhanced" version of #theozh's answer, which allows somewhat control over arrow form.
The idea is to use a Bézier curve to draw the bent arrow. The head is drawn as on #theozh's answer, i.e., using vectors. The initial (xi,yi) and final points (xy,yf), as well the control points (xc1,yc1 and xc2,yc2), are passed to a function using call command. The function creates a datafile using theid, like a tag on standard arrow, defined by user, and associates a variavel (e.g. BentArrow_id) name to such datafile. Each created datafile contain:
the control points
the datapoints to create the arrow, and
the datapoints do create head
as three indexable datablocks (0, 1 and 2, respectively), like this:
# Block index 0 (control points)
1.000000e+00 -1.250000e+00
1.250000e+00 0.000000e+00
2.800000e+00 -5.000000e-01
3.000000e+00 -7.500000e-01
# Block index 1 (arrow)
1.000000e+00 -1.250000e+00
1.016539e+00 -1.177084e+00
1.036070e+00 -1.108272e+00
1.058468e+00 -1.043468e+00
... ...
2.927437e+00 -6.862240e-01
2.949992e+00 -7.027320e-01
2.969690e+00 -7.189280e-01
2.986401e+00 -7.347160e-01
3.000000e+00 -7.500000e-01
# Block index 2 (head)
2.986401e+00 -7.347160e-01 1.359880e-02 -1.528400e-02
3.000000e+00 -7.500000e-01 0.000000e+00 0.000000e+00
To draw the bent arrow, the plot command must be composed by three parts:
plot \
...
BentArrow_id i 0 u 1:2 w lp ...,\
BentArrow_id i 1 u 1:2 w lines ...,\
BentArrow_id i 2 u 1:2:3:4 w vectors ...,\
...
Each part corresponds to a piece of arrow (the control points, the arrow itself, and the head, respectively).
To better show the script (called BentArrow.fct) working, consider the example.
reset
set terminal wxt size 500,500
set size ratio -1
set grid ls -1 lc "gray"
unset key
set tics out nomirror
set xrange [-0.25:9.25]
set yrange [-0.25:9.25]
set style arrow 1 head size 0.25,15,45 fixed filled lc "red"
BentArrow(id,xi,yi,x1,y1,x2,y2,xf,yf) = \
sprintf("call 'BentArrow.fct' '%g' '%f' '%f' '%f' '%f' '%f' '%f' '%f' '%f'", \
id, xi,yi, x1,y1, x2,y2, xf,yf )
# id, xi,yi , xc1,yc1, xc2,yc2, xf,yf
eval BentArrow(1, 1.0,1.0, 2.0,2.0, 3.0,0.0, 4.0,1.0)
eval BentArrow(2, 5.0,1.0, 6.0,0.0, 7.0,2.0, 8.0,1.0)
eval BentArrow(3, 1.0,4.0, 2.0,3.0, 3.0,3.0, 4.0,4.0)
eval BentArrow(4, 5.0,4.0, 6.0,5.0, 7.0,5.0, 8.0,4.0)
eval BentArrow(5, 1.0,7.0, 5.0,5.0, 0.0,5.0, 4.0,7.0)
eval BentArrow(6, 5.0,7.0, 5.0,9.0, 6.0,7.0, 8.0,7.0)
CtrlPoints = "w lp ls -1 pt 6 ps 1 pi -1"
StyleArrow = "w lines lc 'red' lw 2"
StyleHead = "w vec as 1"
plot \
BentArrow_1 i 0 u 1:2 #CtrlPoints ,\
BentArrow_1 i 1 u 1:2 #StyleArrow ,\
BentArrow_1 i 2 u 1:2:3:4 #StyleHead ,\
BentArrow_2 i 0 u 1:2 #CtrlPoints ,\
BentArrow_2 i 1 u 1:2 #StyleArrow ,\
BentArrow_2 i 2 u 1:2:3:4 #StyleHead ,\
BentArrow_3 i 0 u 1:2 #CtrlPoints ,\
BentArrow_3 i 1 u 1:2 #StyleArrow ,\
BentArrow_3 i 2 u 1:2:3:4 #StyleHead ,\
BentArrow_4 i 0 u 1:2 #CtrlPoints ,\
BentArrow_4 i 1 u 1:2 #StyleArrow ,\
BentArrow_4 i 2 u 1:2:3:4 #StyleHead ,\
BentArrow_5 i 0 u 1:2 #CtrlPoints ,\
BentArrow_5 i 1 u 1:2 #StyleArrow ,\
BentArrow_5 i 2 u 1:2:3:4 #StyleHead ,\
BentArrow_6 i 0 u 1:2 #CtrlPoints ,\
BentArrow_6 i 1 u 1:2 #StyleArrow ,\
BentArrow_6 i 2 u 1:2:3:4 #StyleHead
The results
Applying the script to your example, the result look like this
Of course the control points are useful just to defining each arrow.
The variable showCtrlPoints = "False" (default "True") is defined to allows hide the control points on final plot.
The script to last example is:
reset
# The data
$levels<<EOD
1 0.5 -1.25
2 0.5 -1.00
3 0.5 -0.75
4 0.5 -0.50
5 0.5 -0.25
6 0.5 -1.25
7 0.5 -1.00
8 0.5 -0.75
9 0.5 -0.50
10 0.5 -0.25
EOD
# Cubic Bézier function
BentArrow(id,xi,yi,x1,y1,x2,y2,xf,yf) = \
sprintf("call 'BentArrow.fct' '%g' '%f' '%f' '%f' '%f' '%f' '%f' '%f' '%f'", \
id, xi,yi, x1,y1, x2,y2, xf,yf )
# Arrow styles
set style arrow 1 head size 0.2,15,45 fixed filled lc "red"
set style arrow 2 head size 0.2,15,45 fixed filled lc "web-green"
set style arrow 3 head size 0.2,15,45 fixed filled lc "blue"
# To levels
set errorbars small
unset key
# Options to drawing the bent arrows
showCtrlPoints = "False"
ArrowPoints = 50
# Calling the function
eval BentArrow(1, 1.00,-1.25, 1.25, 0.00, 2.80,-0.50, 3.00,-0.75)
eval BentArrow(2, 8.00, 0.50, 8.00, 0.00, 5.00, 0.25, 5.00,-0.25)
eval BentArrow(3, 1.00, 0.50, 2.00,-0.25, 9.00, 0.50, 10.0,-0.25)
# Macros
Points = "w p ls -1 pt 7 ps 2"
Levels = "w xerrorbars ls -1 lw 2"
CtrlPoints = "w lp ls -1 pt 6 ps 1 pi -1"
StyleArrow = "w lines lw 2"
StyleHead = "w vectors"
# Allow to toggle between show/hide the control points
CP(n) = showCtrlPoints eq "True" ? n : NaN
plot \
$levels u 1:2 #Points ,\
"" u 1:3:(0.35) #Levels ,\
BentArrow_1 i 0 u 1:(CP($2)) #CtrlPoints ,\
BentArrow_1 i 1 u 1:2 #StyleArrow lc "red" ,\
BentArrow_1 i 2 u 1:2:3:4 #StyleHead as 1 ,\
BentArrow_2 i 0 u 1:(CP($2)) #CtrlPoints ,\
BentArrow_2 i 1 u 1:2 #StyleArrow lc "web-green" ,\
BentArrow_2 i 2 u 1:2:3:4 #StyleHead as 2 ,\
BentArrow_3 i 0 u 1:(CP($2)) #CtrlPoints ,\
BentArrow_3 i 1 u 1:2 #StyleArrow lc "blue" ,\
BentArrow_3 i 2 u 1:2:3:4 #StyleHead as 3
The BentArrow.fct file contain:
# Implements a bent arrow using Cubic Bézier curves (https://en.wikipedia.org/wiki/Bézier_curve)
#
# Usage: call 'BentArrow.fct' tag xi yi yc1 yc1 xc2 yc2 xf yf
# where
# xi,yi = start point
# xc1,yc1 = control point #1
# xc2,yc2 = control point #2
# xf,yf = final point
#
# The algorithm creates
# 1) a variable named BentArrow_id with 'id' as a integer number,
# defined by user like a standart arrow, and
# 2) a datafile (e.g BentArrow_id.bentarrow) containing
# i) the control points,
# ii) the datapoints to create the curve, and
# iii) the datapoints do ceate head
# as indexable datablocks (0, 1 and 2, respectively).
# The number of datapoint (samples) used on bent arrow construction
# are defined by 'ArrowPoints' (default 50)
# Receiving the arguments from 'call' command
tag = int(ARG1)
x_i = real(ARG2)
y_i = real(ARG3)
x_1 = real(ARG4)
y_1 = real(ARG5)
x_2 = real(ARG6)
y_2 = real(ARG7)
x_f = real(ARG8)
y_f = real(ARG9)
# Defining the variable to filename, based on 'tag', and creating the datafile
eval sprintf( "%s_%g = %s", 'BentArrow', tag, sprintf("'BentArrow_%g.bentarrow'", tag) )
# Checking if 'ArrowPoints' is defined
if ( !exists("ArrowPoints") ) {
ArrowPoints = 50
}
# Quadratic Bézier function
DrawArrow(t,p0,p1,p2,p3) = (1-t)**3*p0 + 3*(1-t)**2*t*p1 + 3*(1-t)*t**2*p2 + t**3*p3 # 0 <= t <= 1
# Creating the datafile containing the datapoints to bent arrow
set print sprintf('BentArrow_%g.bentarrow', tag)
# ----- ControlPoints -----------------------
print "# Block index 0 (control points)"
print sprintf("% e\t% e", x_i, y_i)
print sprintf("% e\t% e", x_1, y_1)
print ""
print sprintf("% e\t% e", x_2, y_2)
print sprintf("% e\t% e", x_f, y_f)
print ""
print ""
# ----- ArrowData -----------------------
print "# Block index 1 (arrow)"
do for [i=0:int(ArrowPoints):1] {
t = i/real(ArrowPoints)
print sprintf("% e\t% e", DrawArrow(t,x_i,x_1,x_2,x_f), DrawArrow(t,y_i,y_1,y_2,y_f))
}
print ""
print ""
# ----- ArrowHead -----------------------
print "# Block index 2 (head)"
do for [i=int(ArrowPoints)-1:int(ArrowPoints):1] {
t = i/real(ArrowPoints)
x_head = x_f - DrawArrow(t,x_i,x_1,x_2,x_f)
y_head = y_f - DrawArrow(t,y_i,y_1,y_2,y_f)
print sprintf("% e\t% e\t% e\t% e", DrawArrow(t,x_i,x_1,x_2,x_f), DrawArrow(t,y_i,y_1,y_2,y_f), x_head, y_head)
}
unset print
Improvements will be well received!
Related
Drawing a simple polygon using Gnuplot with "angle signs"
Disclaimer: I am new to Gnuplot, and I need to plot some "simple" things for my studies. I want to plot a part of a polygon with some names and vectors added. The picutre below was created with Euklid Dynageo, and I am now trying to create this with Gnuplot. The biggest problem I am facing right now is the labeling u,v,w and adding the angles to the plot. I think I would use vectors and lines. Do you now a 'simple' way to create this plot?
If you really need to use Gnuplot for this, you have to make it all manually by placing various objects, labels and arrows (keep in mind that complex plots will be cumbersome). A minimal example for two arrows and alpha_1, similar like in your example, could like like this: # two arrows: set arrow 1 from 0,0 to sqrt(2)/2,sqrt(2)/2 set arrow 2 from 0,0 to 1,0 # the alpha_1 symbol: set label 1 '{/Symbol a}_1' at 0.2,0.1 front # the filled yellow arc (from 0 to 45deg): set style fill solid 1.0 border set object 1 circle at 0,0 radius 0.2 arc[0:45] fc rgb "yellow" # proper ratio, range, and plot 'something': set xrange[-0.1:1.1] set yrange[-0.1:1.1] set size ratio 1 plot 1/0 Lookup the manual for possible object properties.
gnuplot is a very versatile plotting tool, but certainly not optimized for such tasks. For this type of drawing maybe Inkscape or others tools might be better choices, especially for interactive drawing by clicking, dragging and snapping. And as #JackGlasshard already mentioned, of course you can do such graphs with gnuplot which will be rather time consuming if you do it manually. Alternatively, you can facilitate the work if you use a "template". Creating such a template probably only makes sense if you need to create more than one drawing. Just for fun and feasibility, I tried to create such a template for general use to make such graphs easier. For the input data you need 3 datablocks (without headers) $Points: # no., x, y, label, xoff, yoff, pt, ps, color you define: point number, x-coordinate, y-coordinate, point label, x-offset, y-offset, pointtype, pointsize, point color $Vectors: # p1, p2, label, arrow, lw, dt, xoff, yoff, color you define: first point number, second point number, label, arrowsstyle (-1=backhead, 0=nohead, 1=head, 2=heads), linewidth, dashtype, x-offset, y-offset, vector color $Angles: # p1, p2, p3, r, label, aoff, roff, color you define: 1st point number, 2nd point number (=angle center point), 3rd point number, radius, label, angular label offset, radial label offset, color Now, you only have to change the data part of the script for your custom graph. In case you want to insert "invisible" vector starting or end points, simply set pointsize to 0 in $Points. The plotting of the variable arrowheads is a bit cumbersome because of the issue mentioned in this question. For sure, more features and more flexibility can be added to the template. No warranty that the script is free of bugs. There is certainly room for improvements. Update: (linewidth and dashtype added) Since there is no variable linewidth (lw var) and no variable dashtype (dt var) you have to plot each line separately in a for loop and every ::n::n. Furthermore, I noticed that the order of variable pointsize (ps var) and and variable pointtype (pt var) apparently has changed from gnuplot 5.2 to 5.4. Not sure whether this intentional or a "bug". The version below has the order for gnuplot 5.4. Script: ### drawing sketch with points, vectors and angles reset session # no., x, y, label, xoff, yoff, pt, ps, color $Points <<EOD 1 1 1 "" 0 0 5 1 0xff0000 2 10 2 V_{i-1} 0 0 5 1 0xff0000 3 15 10 V_i 0 0 5 1 0xff0000 4 4 12 X -1.5 0 5 1 0xff0000 5 14 16 V_{i+1} 0 0 5 1 0xff0000 6 5 20 "" 0 0 5 1 0xff0000 EOD # p1, p2, label, arrow, lw, dt, xoff, yoff, color # arrows: -1=backhead, 0=nohead, 1=head, 2=heads $Vectors <<EOD 1 2 "" 0 1.0 1 0 0 0xff0000 2 3 "" 0 2.0 1 0 0 0x000000 3 5 "" 0 1.5 3 0 0 0x000000 4 2 w 1 1.0 1 1.0 0 0x000000 4 3 v 1 1.0 1 0 0.7 0x0000ff 4 5 u 1 1.0 1 0 0.7 0x000000 5 6 "" 0 1.0 1 0 0 0x000000 EOD # p1, p2, p3, r, label, aoff, roff, color # p2=angle center point $Angles <<EOD 2 4 3 4.0 α_{i-1} 7.0 0.5 0xffcccc 3 4 5 4.5 α_i 3.0 0.7 0xffffcc EOD ### end of data input ### start of template # point/vector coordinates px(n,m) = real(word($Points[int(word($Vectors[n],m))],2)) # x coordinate of point n py(n,m) = real(word($Points[int(word($Vectors[n],m))],3)) # y coordinate of point n vxd(n) = px(n,2)-px(n,1) # vector delta x vyd(n) = py(n,2)-py(n,1) # vector delta y vxc(n) = (px(n,2)+px(n,1))/2. # vector center x vyc(n) = (py(n,2)+py(n,1))/2. # vector center y lw(n) = real(word($Vectors[n],5)) dt(n) = int(word($Vectors[n],6)) # angles as(n) = int(column(5)+2) # arrow style ax(n) = real(word($Points[int(word($Angles[int(column(0)+1)],n))],2)) # angle center x ay(n) = real(word($Points[int(word($Angles[int(column(0)+1)],n))],3)) # angle center y set angle degrees Angle(x0,y0,x1,y1) = (_dx=x1-x0, _dy=y1-y0, _L=sqrt(_dx**2 + _dy**2), _L==0 ? NaN : \ (_dy>=0 ? acos(_dx/_L) : -acos(_dx/_L) )) a1(m) = Angle(ax(2),ay(2),ax(1),ay(1)) # starting angle a2(m) = Angle(ax(2),ay(2),ax(3),ay(3)) # end angle set style arrow 1 backhead filled # -1 set style arrow 2 nohead filled # 0 set style arrow 3 head filled # 1 set style arrow 4 heads filled # 2 set size ratio -1 # ensure same x- and y-ratio set key noautotitle set xrange [0:22] set yrange [0:22] set mxtics 5 set mytics 5 set grid x,y,mx,my set style fill solid 0.5 border lc "black" plot $Angles u (ax(2)):(ay(2)):4:(a1(0)):(a2(0)):8 w circles lc rgb var, \ '' u (ax(2)+($4*0.5+$7)*cos(0.5*(a1(0)+a2(0))+$6)): \ (ay(2)+($4*0.5+$7)*sin(0.5*(a1(0)+a2(0))+$6)):5 w labels font "Times New Roman,13" center, \ for [i=1:|$Vectors|] $Vectors \ u (px(i,1)):(py(i,1)):(vxd(i)):($4==-1?vyd(i):NaN):9 every ::i-1::i-1 w vectors lc rgb var lw lw(i) dt dt(i) filled backhead, \ for [i=1:|$Vectors|] '' \ u (px(i,1)):(py(i,1)):(vxd(i)):($4== 0?vyd(i):NaN):9 every ::i-1::i-1 w vectors lc rgb var lw lw(i) dt dt(i) filled nohead, \ for [i=1:|$Vectors|] '' \ u (px(i,1)):(py(i,1)):(vxd(i)):($4== 1?vyd(i):NaN):9 every ::i-1::i-1 w vectors lc rgb var lw lw(i) dt dt(i) filled head, \ for [i=1:|$Vectors|] '' \ u (px(i,1)):(py(i,1)):(vxd(i)):($4== 2?vyd(i):NaN):9 every ::i-1::i-1 w vectors lc rgb var lw lw(i) dt dt(i) filled heads, \ for [i=1:|$Vectors|] '' \ u (vxc(i)+$7):(vyc(i)+$8):3:9 every ::i-1::i-1 w labels font ",12" tc rgb var, \ $Points u 2:3:7:8:9 w p ps var pt var lc rgb var, \ '' u ($2+$5):($3+$6):4:9 w labels tc rgb var font ",12" left offset 1,0 ### end of script Result:
Adding vertical marker lines in gnuplot for histogram
There are a few instructions to get vertical lines in gnuplot when plotting functions. Like using the set arrow function. I need this functionality for a histogram and it turns out the histogram has different position of 0.0 on X axis. In my case the X axis markers are just strings from the data file. When plotting the histogram it would be so nice to have the mean, +-3sigma and maybe the X=0 point marked by vertical lines from top to bottom of the plot in fat colored solid lines. My histogram code: set boxwidth 1.0 absolute set style line 1 lc rgb 'skyblue' set style fill solid border lt -1 set style data histogram set style histogram clustered gap 0.0 set xtics in rotate by 90 offset first +0.5,0 right set xlabel set ylabel 'Count' set terminal unknown plot 'histo.raw' using 3 set title 'data' set yrange [0:GPVAL_DATA_Y_MAX*1.2] set term X11 plot 'histo.raw' using 3:xtic(2) ls 1 title columnheader(1) set arrow 1 from 0.0,0.0 to 0.0,GPVAL_DATA_Y_MAX*1.2 nohead My data: "data" 0 "-INF -> -5.0" 0 0.00 1 " -5.0 -> -4.5" 0 0.00 2 " -4.5 -> -4.0" 2 0.03 3 " -4.0 -> -3.5" 4 0.06 4 " -3.5 -> -3.0" 3 0.05 5 " -3.0 -> -2.5" 5 0.08 6 " -2.5 -> -2.0" 19 0.30 7 " -2.0 -> -1.5" 49 0.78 8 " -1.5 -> -1.0" 193 3.07 9 " -1.0 -> -0.5" 527 8.39 10 " -0.5 -> +0.0" 1289 20.53 11 " +0.0 -> +0.5" 1878 29.90 12 " +0.5 -> +1.0" 1411 22.47 13 " +1.0 -> +1.5" 636 10.13 14 " +1.5 -> +2.0" 178 2.83 15 " +2.0 -> +2.5" 56 0.89 16 " +2.5 -> +3.0" 17 0.27 17 " +3.0 -> +3.5" 9 0.14 18 " +3.5 -> +4.0" 4 0.06 19 " +4.0 -> +4.5" 0 0.00 20 " +4.5 -> +5.0" 0 0.00 21 " +5.0 -> +INF" 0 0.00 The set arrow function puts the line in the wrong spot. set arrow 1 from 0.0,0.0 to 0.0,GPVAL_DATA_Y_MAX*1.2 nohead In this data mean= 0.2743 sigma= 0.7491 Thanks for your ideas. Gert
I found that the histogram bars are defining their own X coordinates counting left to right. As I have 22 data rows for 22 histogram bars adding 11.0 to the line position does the job. set boxwidth 1.0 absolute set style line 1 lc rgb 'skyblue' set style fill solid border lt -1 set style data histogram set style histogram clustered gap 0.0 set xtics in rotate by 90 offset first +0.5,0 right set xlabel set ylabel 'Count' set terminal unknown plot 'histo.raw' using 3 set title 'data' set yrange [0:GPVAL_DATA_Y_MAX*1.2] set terminal png size 1200,800 set output 'histo.png' mean= +0.2743 sdev= +0.7491 lboffs= 0.2 set arrow from 11.0 + mean,0.0 to 11.0 + mean ,GPVAL_DATA_Y_MAX*1.2 nohead lw 2 lc rgb "dark-green" set arrow from 11.0 + mean - 3 * sdev,0.0 to 11.0 + mean - 3 * sdev ,GPVAL_DATA_Y_MAX*1.2 nohead lw 2 lc rgb "red" set arrow from 11.0 + mean + 3 * sdev,0.0 to 11.0 + mean + 3 * sdev ,GPVAL_DATA_Y_MAX*1.2 nohead lw 2 lc rgb "red" set arrow from 11.0,0.0 to 11.0,GPVAL_DATA_Y_MAX*1.2 nohead lw 2 lc rgb "blue" set label "Mean" at 11.0 + mean + lboffs,GPVAL_DATA_Y_MAX*1.1 tc rgb "dark-green" set label "+3%" at 11.0 + mean + 3 * sdev + lboffs,GPVAL_DATA_Y_MAX*1.1 tc rgb "red" set label "-3%" at 11.0 + mean - 3 * sdev + lboffs,GPVAL_DATA_Y_MAX*1.1 tc rgb "red" plot 'histo.raw' using 3:xtic(2) ls 1 title columnheader(1) set output set term X11
Gnuplot different colors
I'm trying to color a plot and a fit in gnuplot in different colors, but it doesn't work: set ylabel "s in m" set xlabel "t in s" unset key set style line 1 lt 2 lc rgb "red" lw 3 set style line 2 lt 2 lc rgb "orange" lw 2 plot "-" with lines ls1 0 0 1 4.2 2 7.9 3 11.7 4 16.3 fit "-" with lines ls2 0 0 1 4.2 2 7.9 3 11.7 4 16.3 Does anybody have an idea what I am doing wrong?
There are several things you are doing wrong: The fit command is a bit different from the plot command. You must define a function like f(x) = a*x + b and fit this to your data. This calculates appropriate values for a and b. Afterwards you can plot the function. You must terminate the inline data with an e. To select a line style, use ls 1 (with the space before the number). So your script should look as follows: set ylabel "s in m" set xlabel "t in s" unset key set style line 1 lt 2 lc rgb "red" lw 3 set style line 2 lt 2 lc rgb "orange" lw 2 f(x) = a*x + b fit f(x) '-' via a,b 0 0 1 4.2 2 7.9 3 11.7 4 16.3 e plot f(x) with lines ls 2, "-" with points ls 1 0 0 1 4.2 2 7.9 3 11.7 4 16.3 e This plots your fit as a line, and the according data as points.
gnuplot - calculate distance between lines
Can gnuplot calculate the distance between two lines or maybe two points? I'm having a plot where two (main) lines are plotted. For the moment let's assume that the first line is always above the second one. Is there a way to calculate the distance from line 2 to line 1 at a given x-value? here is a picture of what my plot looks like and which distance I want to calculate: The vertical lines are just for style and have nothing to do with the actual plot, their data is stored in test.dat and test2.dat. My data-files of the lines look like this: line1 0 118.1 2.754 117.77 4.054 117.64 6.131 116.17 7.7 116.04 8.391 115.36 10.535 115.25 11.433 116.03 12.591 116.22 19.519 118.59 line2 19.4 118.51 15.2 116.56 10.9 115.94 10.35 114.93 9.05 114.92 8.3 115.9 5.9 116.19 4.2 116.62 2.2 117.66 -0.3 118.06 My plotting-code looks like this: set term wxt enhanced cd 'working directory' unset key set size 0.9,0.9 set origin 0.1,0.1 set title 'TITLE' unset border set label 21 " rotate by 45" at -3.0,0.0 rotate by 45 point ps 2 set xrange [0:19.519] set yrange [110:119] set xtics nomirror(0, 2.745, 4.054, 6.131, 7.7, 8.391, 10.535, 11.433, 12.591, 19.519) rotate by 90 offset 0,-0.1 right set ytics " ", 30000 plot "line1.dat" using ($1):($2):2 with labels offset 1, 1.8 rotate by 90, "line1.dat" using 1:2 with lines lt 1 lw 1 lc rgb '#000000', +112 lt 1 lw 1 lc rgb '#000000' , 'test.dat' with lines lt 1 lw 1 lc rgb '#000000', +110 lt 1 lw 1 lc rgb '#000000', 'line2.dat' with lines lt 0.5 lw 1 lc rgb '#000000', 'test2.dat' with lines lt 0.5 lw 1 lc rgb '#000000'
You can measure the distance manually. Move the mouse to the first point and type 'r'. Then as you move the mouse around, the x and y offsets, distance and angle are displayed. Type '5' to draw a line segment and to toggle between degrees and tangent display. Zooming in beforehand increases accuracy. By the way, typing 'h' in the plot window will display a list of keybindings to the console.
An answer to this "rather old" question still might be of interest to OP, if not, maybe to others. Yes, you can calculate and plot the difference of two lines. It requires some linear interpolation. Simply assign the desired x-value to the variable myX. Data: SO17717287_1.dat 0 118.1 2.754 117.77 4.054 117.64 6.131 116.17 7.7 116.04 8.391 115.36 10.535 115.25 11.433 116.03 12.591 116.22 19.519 118.59 SO17717287_2.dat 19.4 118.51 15.2 116.56 10.9 115.94 10.35 114.93 9.05 114.92 8.3 115.9 5.9 116.19 4.2 116.62 2.2 117.66 -0.3 118.06 Script: (works for gnuplot>=4.6.0) ### calculating and plotting a difference between two curves reset FILE1 = "SO17717287_1.dat" FILE2 = "SO17717287_2.dat" set border 1 unset key set origin 0.05,0.05 set size 0.9,0.8 set xrange [0:19.519] set xtics nomirror rotate by 90 offset 0,-0.1 right set yrange [110:119] unset ytics myX = 15.2 getYa(xi) = (x0=x1, x1=$1, y0=y1, y1=$2, x1==xi ? ya=y1 : (sgn(x0-xi)!=sgn(x1-xi)) ? ya=(y1-y0)/(x1-x0)*(xi-x0)+y0 : NaN) getYb(xi) = (x0=x1, x1=$1, y0=y1, y1=$2, x1==xi ? yb=y1 : (sgn(x0-xi)!=sgn(x1-xi)) ? yb=(y1-y0)/(x1-x0)*(xi-x0)+y0 : NaN) set samples 2 # set to minimal possible value for plotting '+' plot x1=y1=NaN FILE1 u 1:2:2:xtic(1) w labels offset 0,0.5 left rotate by 90, \ '' u 1:(getYa(myX),$2) w l lc rgb 'black', \ '' u 1:2:(0):(110-$2) w vec lt 0 nohead, \ +112 w l lc rgb 'black', \ x1=y1=NaN FILE2 u 1:(getYb(myX),$2) w l lt 0 lc rgb 'black', \ '+' u (myX):(ya):(0):(yb-ya) w vec heads lc rgb "red", \ '+' u (myX):(ya):(sprintf("%.3f",yb-ya)):xtic(sprintf("%g",myX)) w labels tc rgb "red" offset 0,1, \ '+' u (myX):(ya):(0):(110-ya) w vec nohead lt 0 lc rgb "red" ### end of script Result: (created with gnuplot 4.6.0)
merging multiple data files to plot in a single graph
extending the question I had asked earlier which can be found here, plotting multiple (x,y) co-ordinates in a single curve with gnuplot. I am trying to plot the bezier curves in gnuplot using 2 different files. Each (x,y) from the first file forms a bezier curve passing through the points from the second file. The first file has the co-ordinates like the following: x y 0.0 0.5 0.12 0.1 0.16 0.4 0.2 0.35 0.31 0.8 0.34 0.6 0.38 1.0 0.46 0.2 0.51 0.7 0.69 0.9 The second file has the co-ordinates as the following: x y 0.00 0.7 0.04 0.74073082208 0.08 0.85926917792 0.12 0.9 0.16 0.9 0.2 0.9 0.24 0.749720623086 0.28 0.874229601255 0.32 0.74073082208 0.36 0.8 0.4 0.721178508605 0.44 0.878821491395 0.48 0.761772990545 0.52 0.700774803388 0.56 0.723771273415 0.6 0.789508073675 0.64 0.864014272269 0.68 0.896743348931 Now, how do I merge these two files to plot a single graph. These two files don't have the same number of rows, but I guess that doesn't matter. The first curve would be between (x1,y1) and (x2,y2) from the first file which would continue till (x10,y10). In the curve between (x1,y1) and (x2,y2); the points (x1,y1), (x2,y2) and (x3,y3) from the second file lie. I followed this link http://t16web.lanl.gov/Kawano/gnuplot/datafile3-e.html to sort and concatenate the two files but got some weird lines which is totally wrong. These values should actually plot Bezier curves but not getting the graph. I have written the following gnuplot script to plot the concatenated data: set term x11 persist set title "Animation curves" set xlabel "Time (secs.)" set ylabel "Parameter" set x2label "Phoneme1" offset -35 set pointsize 2 set key off set style line 2 lt 0 lc 1 lw 2 set xrange [0.0:1.0] set yrange [0.0:1.3] plot [0.0:0.8] "< cat -n file1.dat" u 1:2 smooth csplines ls 1, "" u 1:(0.0):(0):(1.3) w vectors nohead ls 2, "" u ($1+0.005):(1):(sprintf("P %d", $0)) w labels, \ "file1.dat" u 1:2 with points, \ "file2.dat" u 1:2 with points, \ I got the following error: plot "< cat -n file1.dat" u 1:2 smooth csplines ls 1, "" u 1:(0.0):(0):(1.3) w vectors nohead ls 2, "" u ($1+0.005):(1):(sprintf("P %d", $0)) w labels, "file1.dat" u 1:2 with points, "file2.dat" u 1:2 with points, ^ "plot.gp", line 21: Cannot smooth: no data within fixed xrange!
The script below works on my machine. Maybe this is even what you are looking for... set term x11 persist set title "Animation curves" set xlabel "Time (secs.)" set ylabel "Parameter" set x2label "Phoneme1" offset -35 set pointsize 2 set key off set style line 2 lt 0 lc 1 lw 2 set xrange [0.0:1.0] set yrange [0.0:1.3] plot [0.0:0.8] "< cat file1.dat file2.dat | sort -n" u 1:2 smooth csplines ls 1, \ "" u 1:(0):(0):2 w vectors nohead ls 2, \ "" u ($1 + 0.005):(1):(sprintf("P %d", $0)) w labels, \ "file1.dat" u 1:2 w p, \ "file2.dat" u 1:2 w p