flot chart ticks lines not uniform - flot

I am using flot charts to display ecg signals. The requirement is that the chart background should look exactly like the ecg graph paper.
All the inner grey lines should be lined at precise points to draw perfect squares on the ecg sheet. However, there are some lines which are not spaced evenly (they are closer to each other), making the ecg graph background incorrect.
Here is what my code does :
MARKINGS :
I have written a function to generate markings on x and y axis. Markings are the dark pink lines on the graph. These markings will be generated on x and y axis after 4 grey lines (which make up the smaller grey squares inside the bigger dark pink boxes). These seem to be drawn correctly.
TICKS:
I have overridden Flot charts native tick generator by writing two functions which generate ticks on x and y axis . On x axis, each 40 ms represents one tick (one grey line) and on y axis 0.1 millivolt represents one grey line. Even though the functions generate correct arrays with correct values for ticks, the tick spacing on flot chart is not even. Some tick lines are more closely spaced which is incorrect.
Not all ticks are drawn incorrectly.However, the ones that have irregular spacing are significant in number and are visible on more careful inspection of the graph. For a start, the 4 th column and row of the graph has unevenly spaced tick lines. (On browser outside of jsfiddle, this becomes 3rd row and 3rd column ). These uneven ticks repeat randomly throughout the graph.
My code is as shown below. It has also been uploaded at JSFiddle
Has anyone gone through similar problem with flot charts while drawing tick lines? Am I using ticks incorrectly? Appreciate your help on this.
JavaScript:
$(function() {
var d2=[];
// Make markings on x and y axis for drawing the more spaced grid lines
function markingsArray(axes) {
var markings = [];
for (var x = 200; x < axes.xaxis.max; x += 200)
markings.push({ xaxis: { from: x, to: x },color: "#EE1983" });
for (var y = -5; y < axes.yaxis.max; y += 0.5)
markings.push({ yaxis: { from: y, to: y },color: "#EE1983" });
return markings;
}
var options = {
series: {
shadowSize: 0, // Drawing is faster without shadows
lines: {
lineWidth: 1,
}
},
yaxis: {
ticks: function(axis){
var res = [];
var tickDrawCounter = 1;
var tickIncrement=0.1;
for(var i=-4.9;i<5;i+=tickIncrement){
if(tickDrawCounter<5){
res.push([parseFloat(i).toFixed(1),""]);
tickDrawCounter++;
}else{
tickDrawCounter=1;
}
}
return res;
},
min: -5,
max: 5
},
xaxis: {
ticks: function(axis) {
var res = [];
var tickDrawCounter = 1;
var tickIncrement=40;
for(var i=tickIncrement;i<8000;i+=tickIncrement){
if(tickDrawCounter<5){
res.push([parseFloat(i),""]);
tickDrawCounter++;
}else
tickDrawCounter=1;
}
return res;
},
min:0,
max:8000,
},
colors: ["#0000A0"], // color of the series plot
grid:{
markings: markingsArray,
backgroundColor:"pink",
markingsLineWidth:1,
}
}
$.plot("#placeholder",[ d2],options);
// Add the Flot version string to the footer
$("#footer").prepend("Flot " + $.plot.version + " – ");
}
);

Awesome question.
Here's some of your ticks:
[1] -4.9 -4.8 -4.7 -4.6 -4.4 -4.3 -4.2 -4.1 -3.9 -3.8 -3.7 -3.6 -3.4 -3.3 -3.2
[16] -3.1 -2.9 -2.8 -2.7 -2.6 -2.4 -2.3 -2.2 -2.1 -1.9 -1.8 -1.7 -1.6 -1.4 -1.3
And here's the difference between each of your ticks:
[1] 0.1 0.1 0.1 0.2 0.1 0.1 0.1 0.2 0.1 0.1 0.1 0.2 0.1 0.1 0.1 0.2 0.1 0.1 0.1
[20] 0.2 0.1 0.1 0.1 0.2 0.1 0.1 0.1 0.2 0.1 0.1 0.1 0.2 0.1 0.1 0.1 0.2 0.1 0.1
That looks pretty good, nice and evenly spaced. Internally though flot has to convert these to canvas coordinates. Here they are:
[1] 479.16 474.32 469.48 464.64 454.96 450.12 445.28 440.44 430.76 425.92
[11] 421.08 416.24 406.56 401.72 396.88 392.04 382.36 377.52 372.68 367.84
And the diff:
[1] -4.84 -4.84 -4.84 -9.68 -4.84 -4.84 -4.84 -9.68 -4.84 -4.84 -4.84 -9.68
[13] -4.84 -4.84 -4.84 -9.68 -4.84 -4.84 -4.84 -9.68 -4.84 -4.84 -4.84 -9.68
Again that looks pretty damn good, still nicely spaced.
BUT we got a problem, flot can't draw a line at 479.16. It has to draw at pixel positions and those are integer values. So, what does flot do? It Math.floors them. Your "pixel positions" become:
[1] 479 474 469 464 454 450 445 440 430 425 421 416 406 401 396 392 382 377 372
[20] 367 358 353 348 343 333 329 324 319 309 304 300 295 285 280 275 271 261 256
And the diff:
[1] -5 -5 -5 -10 -4 -5 -5 -10 -5 -4 -5 -10 -5 -5 -4 -10 -5 -5 -5
[20] -9 -5 -5 -5 -10 -4 -5 -5 -10 -5 -4 -5 -10 -5 -5 -4 -10 -5 -5
[39] -5 -9 -5 -5 -5 -10 -4 -5 -5 -10 -5 -4 -5 -10 -5 -5 -4 -10 -5
Now we have a problem, no more even spacing.
So, my first instinct is to say, whoa, this is a bug in flot. But I can't see how flot can do the math any better (and still get the same efficiency).
So how do you fix it?
1.) Increase the size of the canvas. More spacing between the ticks and you won't be able to see a pixel off here or there.
2.) Work backworks. Start with pixel positions and calculate your axis positions from them. If I get some time tonight I'll try out that idea. It won't be pretty.
EDITS
Here's an attempt to evenly space ticks. It relies on drawing the plot twice since I need the initial bounds information to start the spacing (is there a margin, tick labels, etc...). It also required me to manually tweak the placeholder div height so all the ticks will fit:
ticks: function(axis){
var startTickPos = -4.9;
var endTickPos = 5;
var tickIncrement = 0.1;
var tickDrawCounter = 2;
if (plot) {
// this is the re-draw
var yaxis = plot.getAxes().yaxis;
// our first tick's pixel position
var currentTickPix = yaxis.p2c(yaxis.ticks[0].v);
var ticks = [[startTickPos,""]];
for(var i=startTickPos + tickIncrement;
i<endTickPos;
i+=tickIncrement){
// we are spacing each tick 5 pixels apart, adjust as needed for get a good plot height
currentTickPix -= 5;
if(tickDrawCounter<5){
// convert the pixel to tick position
ticks.push([yaxis.c2p(currentTickPix),""]);
tickDrawCounter += 1;
} else {
tickDrawCounter = 1;
}
}
return ticks;
} else {
// this is the first time through, we only need the initial tick's starting position
return [startTickPos];
}
},
Here's a fiddle. I remove the xaxis and markings to better visualize.
EDIT 2
So, it turns out the most important part here is the plot height. With your original code and a plot height of 510 pixels, the ticks space evenly all by themselves. Here's the diff:
[1] -5 -5 -5 -10 -5 -5 -5 -10 -5 -5 -5 -10 -5 -5 -5 -10 -5 -5 -5
[20] -10 -5 -5 -5 -10 -5 -5 -5 -10 -5 -5 -5 -10 -5 -5 -5 -10 -5 -5
[39] -5 -10 -5 -5 -5 -10 -5 -5 -5 -10 -5 -5 -5 -10 -5 -5 -5 -10 -5
[58] -5 -5 -10 -5 -5 -5 -10 -5 -5 -5 -10 -5 -5 -5 -10 -5 -5 -5 -10
[77] -5 -5 -5

Related

Draw a bended arrow between two points in 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!

Gnuplot filledcurves flip axes

There is a style to fill the space between two functions of x.
Examples of such plots can be found e.g. at http://gnuplot.sourceforge.net/demo/fillbetween.html
Is there any way to make similar plot, but with flipped x and y axes?
Here is the desired shape of a curve (without rotated/mirrored labels, titles and legends, of course)...
It could be done with closed contour (like last example here http://www.gnuplot.info/demo_svg_cvs/fillcrvs.html), but this would require reshuffling the data file. Any other options?
Thank you!
You can't do this directly. From help filledcurves:
The third variant fills the area between two curves sampled at the same set of
x coordinates. It requires three columns of input data (x, y1, y2).
I don't think you can specify (y, x1, x2) directly. As a workaround you can the area between the y axis and the larger function in some color, and then fill the area between the y axis and the smaller function in white:
x1(y) = cos(y)+1
x2(y) = cos(y)+2
xmax(y) = (x1(y) > x2(y) ? x1(y) : x2(y))
xmin(y) = (x1(y) < x2(y) ? x1(y) : x2(y))
plot '+' using (xmax($1)):1 with filledcurve y1, \
'+' using (xmin($1)):1 with filledcurve y1 fillcolor rgb "white"
This probably has to be tweaked a little if one or both of the two functions can be negative.
With gnuplot >=5.2 it could be tweaked even further because it allows arrays.
The following code shows a workaround how filled curves between vertically oriented curves can be realized. You can even use transparency. If you download the attached PNG you will notice that it actually has a transparent background. The basic idea behind this workaround is to make closed areas and fill them. For this, you need to reverse one border, concatenate the borders and plot them filled. Unfortunately, gnuplot has no function to reverse datapoints in a column, so you have to do it in a special procedure yourself.
The code:
### "Vertical" filledcurves
reset session
# create some dummy data
N = 50
set samples N
set xrange [-5:5]
set table $Data
plot '+' u (sin($1)):1:(rand(0)*0.3+1) with table
unset table
# put Borders into arrays
stats $Data nooutput
RowCount = STATS_records
array BorderX1[RowCount]
array BorderX2[RowCount]
array BorderY[RowCount]
set table $Dummy
plot $Data u (BorderX1[$0+1]=$1-$3):(BorderX2[$0+1]=$1+$3):(BorderY[$0+1]=$2) with table
unset table
# reverse BorderX2 and merge borders
set samples RowCount
set table $Border
plot '+' u (BorderX1[$0+1]):(BorderY[$0+1]) with table
plot '+' u (BorderX2[RowCount-$0]):(BorderY[RowCount-$0]) with table
unset table
# make the plot
set object 1 rect at 0,-3 size 10,0.5 fs solid 1.0 fc rgb "black" back
set yrange[-5:5]
plot \
$Border u 1:2 w filledcurves fc rgb "#AA00FF00" not,\
$Border u ($1*1.5):2 w filledcurves fc rgb "#AAFFFF00" not,\
$Data u ($1+2.5):2 w filledcurves y2 fc rgb "brown" not,\
$Data u 1:2 w l lw 8 lc rgb "blue" not,\
'+' u 1:(cos($1)-0.5):(cos($1)+0.5) w filledcurves lc rgb "grey" not,\
'+' u 1:(cos($1)):(1) w l lw 3 dt 2 lc rgb "white" not
### end of code
The result:
Update: These are two alternative and simpler approaches compared to my first answer. One of them works even with gnuplot 5.0.
The plotting style filledcurves (so far) can only fill between two y-curves with identical x-values. However, gnuplot can fill closed curves. So, make the curve closed. Like in my first answer, you can do this if you reverse one curve and add it to the other one.
The assumption for both scripts is that the data has a common y-column, i.e. is organized in 3 columns, e.g. here: y x1 x2
Data: SO50676753.dat (same as OP's data, from silver.dat in the gnuplot demo directory)
# y x1 x2
10 280 16.7332
20 191 13.8203
30 152 12.3288
40 150 12.2474
50 104 10.1980
60 77 8.7750
70 69 8.3066
80 60 7.7460
90 60 7.7460
100 51 7.1414
110 41 6.4031
120 34 5.8310
130 35 5.9161
140 34 5.8310
150 24 4.8990
160 24 4.8990
170 19 4.3589
180 21 4.5826
190 20 4.4721
200 18 4.2426
210 21 4.5826
220 15 3.8730
230 19 4.3589
240 12 3.4641
250 20 4.4721
260 20 4.4721
270 18 4.2426
280 18 4.2426
290 20 4.4721
300 12 3.4641
310 26 5.0990
320 17 4.1231
330 8 2.8284
340 6 2.4495
350 8 2.8284
360 10 3.1623
370 20 4.4721
380 14 3.7417
390 8 2.8284
400 10 3.1623
410 9 3.0000
420 8 2.8284
430 10 3.1623
440 13 3.6056
450 9 3.0000
460 5 2.2361
470 7 2.6458
480 11 3.3166
500 7 2.6458
510 9 3.0000
520 12 3.4641
530 4 2.0000
540 7 2.6458
550 10 3.1623
560 9 3.0000
580 8 2.8284
590 9 3.0000
600 5 2.2361
Script 1: (works with gnuplot>=5.0.0)
Here you assume that you have monotonic and unique y-values. With this you can use the option smooth unique (available at least in gnuplot 4.x versions) to reverse one curve. However, since this solution here uses datablocks and plotting style with table it requires at least gnuplot 5.0.0. Maybe with some workarounds and temporary files you can also get it to work with some 4.6 versions.
### fill between vertical curves
reset session
FILE = "SO50676753.dat"
set table $Temp
plot FILE u 1:2
plot FILE u (-$1):3 smooth unique
set table $Data
plot $Temp u 2:1 index 0 w table, \
'' u 2:(-$1) index 1 w table
unset table
set style fill solid 0.3
set grid x,y
plot $Data u 1:2 w filledcurves
### end of script
Script 2: (works with gnuplot>=5.2.0)
With this solution there are no special assumptions about the data, but since it uses indexing of datablocks it requires gnuplot>=5.2.0.
### fill between vertical curves
reset session
FILE = "SO50676753.dat"
set table $Temp1
plot FILE u 2:1 w table
set table $Temp2
plot FILE u 3:1 w table
unset table
set print $Data
do for [i=1:|$Temp1|] { print $Temp1[i] }
do for [i=|$Temp2|:1:-1] { print $Temp2[i] } # reverse data
set print
set style fill solid 0.3
set grid x,y
plot $Data u 1:2 w filledcurves
### end of script
Result: (same for both scripts):

Gnuplot negative x range and logscale

I am trying to plot some data with Gnuplot and would like to use a logscale on the x axis. This does not work, Gnuplot gives the error "x range must be greater than 0 for log scale". I saw examples that used a logscale with negative values on the y axis and tried to do it similar for the x axis, but it seems I can not get it to work. Initially I thought it would be the zero values, but even when I remove them, it does not work.
This is a little example:
stats 'stat_data1'
num1=STATS_records
stats 'stat_data2'
num2=STATS_records
set terminal pdf
set output "Cumulative_noise.pdf"
set autoscale yfix
set autoscale xfix
set key bottom right
set xlabel 'Noise in dB'
set ylabel 'Percent'
set xrange [0:110] reverse
set logscale x
set style increment user
set style line 2 lc rgb '#FF0000' lt 1 pt 1 ps 1
set style line 3 lc rgb '#008000' lt 2 pt 2 ps 2
set style line 4 lc rgb '#0000FF' lt 3 pt 3 ps 3
plot 0/0 notitle,\
'stat_data1' u (-$3) : ((100.0/num1)) title 'Node 1' smooth cumulative,\
'stat_data2' u (-$3) : ((100.0/num2)) title 'Node 2' smooth cumulative
And here some data. First file:
1437818411 -54 -95 85.2 0.0
1437818425 -54 -95 78.0 0.0
1437818440 -71 -95 38.7 0.0
1437818456 -70 -95 51.7 0.0
1437818471 -71 -95 42.0 0.0
Second file:
1437818545 -50 -95 43.7 100.0
1437818561 -51 -95 52.0 100.0
1437818576 -50 -94 79.4 0.10744142234781584
1437818592 -51 -94 16.6 0.308927509416507
1437818605 -49 -95 85.2 0.04368438558438699
I hope somebody has an idea as this would be very convenient. Thank you in advance!
The numbers given in the xrange settings are also subject to the actual axis transformations.
Removing the set xrange [0:110] fixes the error.

gnuplot - splot inline data and point labels

Hi Im using this data to get a 3d/2d like histogram.
I want to label each histogram column with a 'dx cx' label preferable on top of the column.
With my example the graph is drawing correctly, but there are no labels. if I'm using only the using 1:2:3:4 with labels offset 1 part, it shows the label with no lines. And it only shows the label to the space character, can I somehow escape the space?
Could anyone help please?
reset
unset key
set xrange [0:262.5]
set yrange [0:350]
set zrange [0:5]
set xtics 50
set ytics 50 offset .6,-.3
set ztics 1,1
set grid x y z back
set xyplane 0
set terminal pdf
set output "test.pdf"
splot '-' using 1:2:3 with lines, '' using 1:2:3:4 with labels offset 1
27.8409 350 0
27.8409 350 0.419595 d0 c3
31.8182 350 0.419595
31.8182 350 0
31.8182 350 0
31.8182 350 0.61032 d0 c4
35.7955 350 0.61032
35.7955 350 0
35.7955 350 0
35.7955 350 0.740013 d0 c5
39.7727 350 0.740013
39.7727 350 0
39.7727 350 0
39.7727 350 0.747642 d0 c6
43.75 350 0.747642
43.75 350 0
43.75 350 0
43.75 350 0.633207 d1 c1
47.7273 350 0.633207
47.7273 350 0
47.7273 350 0
47.7273 350 0.442482 d1 c2
51.7045 350 0.442482
51.7045 350 0
e
Your second plot is never done. The inline data is gone after the first part. You have to feed it twice (same dataset again after the "e") or in gp5 you can use a new form of inline data:
$data << EOD
1 2 3
2 3 4
3 4 5
EOD
splot $data, $data using 1:2:($2**2)
If the data comes in via stdin ("-"), you can use set table to plot it to a temporary file or to an inline data set set table $dat; plot "-"; unset table.

How to add a single color surface mesh below a transparent grid

gnuplot includes a bewildering number of options which I have not yet assimilated.
The code displays a single colored grid below a transparent plane. Light coloring of the lower grid does make it apparent it is below the plane. As the lower grid intersects
the plane on exactly two edges, the grid must be curved. Perhaps displaying
the grid with an interpolated mesh will make that clearer.
a) How to make a mesh for he lower grid?
b) Is there a better way to color with a single color, i.e. not use a palette?
# Try to add colour to mask
set terminal wxt size 600,600 enhanced font 'Verdana,10' persist
a=57344; b=-6976; c=1785856; d=-173228032
set cbrange[0:1]
set palette defined (0 'blue', 1 'light-blue')
set dgrid3d 5, 5
set style data lines
splot 'mask.dat' with pm3d, -(a*x+b*y+d)/c
reset
data file: mask.dat
0 0 97
0 256 94
0 512 91
0 768 88
0 1024 85
436 0 83
436 256 81
436 512 79
436 768 77
436 1024 75
872 0 69
872 256 68
872 512 67
872 768 66
872 1024 65
1308 0 55
1308 256 55
1308 512 55
1308 768 55
1308 1024 55
1744 0 41
1744 256 42
1744 512 43
1744 768 44
1744 1024 45
The coloring is fine like you did it, don't know a better method.
To plot a grid for the lower mesh, just redraw the data without pm3d:
reset
set terminal pngcairo size 600,600
set output 'test.png'
a=57344; b=-6976; c=1785856; d=-173228032
set cbrange[0:1]
set palette defined (0 'blue', 1 'light-blue')
set dgrid3d 5, 5
set style data lines
set ticslevel 0
unset colorbox
unset key
splot 'mask.dat' with pm3d, '' lc rgb 'blue', -(a*x+b*y+d)/c lt 1
Result (with 4.6.4):

Resources