Reputation: 373
How can I create hypertext-like vectors or arrows that only appear when the user places the mouse on or near the start/end points? For points it looks like this:
indices = "first second"
plot for [id in indices] "a_test.dat" index id u 1:2
replot for [i=1:words(indices)] "a_test.dat" index word( indices, i ) u 1:2:(sprintf("point = (%d, %d)", column(1), column(2))) w labels hypertext point pt 7 ps 2 lc rgb '#ff000000' notitle
File "a_test.dat":
# first
x1 y1
5 1
6 2
9 8
# second
x2 y2
4 5
8 2
2 7
Upvotes: 1
Views: 195
Reputation: 25684
I guess you can mimic a "hypertext arrow" with a while
loop and pause mouse
.
For vectors it is better to have start/endpoints in one line, but that is a different topic. I will think about an answer about your other question. Here, for simplicity, it is assumed that you have the start/endpoints already in one line.
the data is plotted once and then you enter a while
loop (check help while
) which you can stop by pressing ESC
.
when button 1 of the mouse is pressed, the data is plotted again, but additionally, the dataline with the minimal distance to the mouse position (i.e. variables MOUSE_X
and MOUSE_Y
) is plotted with vectors
. However, it seems you can't use MOUSE_X
and MOUSE_Y
in a function. So, well, then your have to assign it first to variables x0
and y0
.
you could probably tune the behaviour of the appearance of the arrow, i.e. whether it disappears after some time or if you move on, etc...)
Script: (tested with gnuplot 5.4.1 and wxt and qt terminals)
### show arrow on mouse button press (mimic hypertext arrow)
reset session
$Data <<EOD
# first # second
x1 y1 x2 y2
5 1 4 5
6 2 8 2
9 8 2 7
EOD
set offsets 1,1,1,1
set key out noautotitle
plot $Data u 1:2 w p pt 7 ps 2 lc "blue" ti "start point", \
'' u 3:4 w p pt 7 ps 2 lc "red" ti "end point"
dist(colX,colY) = sqrt((column(colX)-x0)**2 + (column(colY)-y0)**2)
continue = 1
while (continue) {
pause mouse button1,keypress
if (MOUSE_KEY == 27) { continue=0 } # press ESC to stop loop
else {
x0 = MOUSE_X
y0 = MOUSE_Y
plot dMin=NaN \
$Data u (d=dist(1,2), (dMin!=dMin || d<dMin)?(dMin=d,idxMin=$0):0,$1):2 skip 1 \
w p pt 7 ps 2 lc "blue" ti "start point", \
'' u (d=dist(3,4), d<dMin?(dMin=d,idxMin=$0):0,$3):4 w p pt 7 ps 2 lc "red" ti "end point", \
'' u 1:2:($3-$1):($4-$2) every ::idxMin::idxMin skip 1 w vec lw 2 lc black
}
}
### end of script
Screen capture: (from wxt terminal)
Addition: (keep and remove arrows)
Here is a version where you keep several arrows and you can remove them by clicking again close to their start/endpoint. You can remove all arrows at once if you do a right mouse click (on my system this is MOUSE_BUTTON==3
).
arrowIdxs
containing the selected arrows, i.e. line indicesinList()
which checks if an index is in the listupdateList()
which either adds a new index or removes an index if it was already in the list.I hope you can figure out yourself how the script works in detail.
Check help <keyword>
for the following used keywords: sprintf
, sum
, strstrt
, strlen
.
Script:
### show/hide arrows on mouse button click
reset session
$Data <<EOD
# first # second
x1 y1 x2 y2
5 1 4 5
6 2 8 2
9 8 2 7
EOD
set offsets 1,1,1,1
set key out noautotitle
plot $Data u 1:2 w p pt 7 ps 2 lc "blue" ti "start point", \
'' u 3:4 w p pt 7 ps 2 lc "red" ti "end point"
dist(colX,colY) = sqrt((column(colX)-x0)**2 + (column(colY)-y0)**2)
arrowIdxs = ''
inList(list,n) = (_s=sprintf("%d",n), int(sum[_i=1:words(list)] word(list,_i) eq _s))
updateList(list,n) = (_s=sprintf("%d",n), inList(list,n) ? \
list[1:strstrt(list,_s)-2].list[strstrt(list,_s)+strlen(_s):] : list.' '.sprintf("%d",n))
continue = 1
while (continue) {
pause mouse button1,button3,keypress
if (MOUSE_KEY == 27) { continue=0 } # press ESC to stop loop
else {
mb=MOUSE_BUTTON
if (mb==1) {
x0 = MOUSE_X
y0 = MOUSE_Y
plot dMin=NaN \
$Data u (d=dist(1,2), (dMin!=dMin || d<dMin)?(dMin=d,idxMin=$0):0,$1):2 skip 1 \
w p pt 7 ps 2 lc "blue" ti "start point", \
'' u (d=dist(3,4), d<dMin?(dMin=d,idxMin=$0):0,$3):4 w p pt 7 ps 2 lc "red" ti "end point", \
arrowIdxs = updateList(arrowIdxs,idxMin) \
'' u (inList(arrowIdxs,$0)? $1:NaN):2:($3-$1):($4-$2) skip 1 w vec lw 2 lc black
}
if (mb==3) {
arrowIdxs = ''
plot $Data u 1:2 w p pt 7 ps 2 lc "blue" ti "start point", \
'' u 3:4 w p pt 7 ps 2 lc "red" ti "end point"
}
}
}
### end of script
Screen capture: (from wxt terminal)
Upvotes: 2
Reputation: 15093
I think this is possible, almost. But you wouldn't use hypertext, you'd use the "toggle plot" functionality. I will show a complete script below, but here's the basic idea:
Draw a graph that contains (plot 1) the tail points (plot 2) the head points (plots 3 to N) a separate plot for each arrow. Because each arrow is in a separate plot it can be toggled on/off by clicking on its title. The toggle
commands in the script turn the arrows off to start with. Now we get to the tricky part. Instead of letting the title be placed automatically in the key box for the graph, we explicitly position each title on top of its tail point. Now you can click on the point and voila, the arrow appears.
Here is the script
set xrange [0:10]
set yrange [0:10]
set style arrow 1 head nofill lc "black"
# Here is the data
$ARROWS << EOF
x1 y1 x2 y2
5 1 4 5
6 2 8 2
9 8 2 7
EOF
#
# This is the basic plot, with everything visible.
# In an interactive terminal (qt/wxt/x11/windows/svg) you can toggle
# the arrows on and off by clicking on the corresponding title in the key.
#
set key samplen 0.01
plot $ARROWS using 1:2 with points pt 7 ps 2 title "Click me", \
$ARROWS using 3:4 with points pt 7 ps 2 notitle, \
for [i=1:*] $ARROWS every 1::i::i using 1:2:($3-$1):($4-$2) with vectors as 1 title " "
pause -1
#
# Now we get tricky by placing the arrow titles on top of the points
# rather than in the key, and we programmatically toggle off the arrows.
# You should be able to make the arrow appear by clicking on the point
# that corresponds to the arrow tail.
#
array tx[3]
array ty[3]
do for [i=1:3] {
tx[i] = int(word($ARROWS[i+1],1))
ty[i] = int(word($ARROWS[i+1],2))
}
plot $ARROWS using 1:2 with points pt 7 ps 2 title "Click me", \
$ARROWS using 3:4 with points pt 7 ps 2 notitle, \
for [i=1:3] $ARROWS every 1::i::i using 1:2:($3-$1):($4-$2) with vectors as 1 \
title " " at first tx[i], first ty[i]
do for [plot=3:5] {toggle plot}
pause -1
Here is a screen shot of the plot before the first click
And here is a screen show of the plot after clicking on one of the points
I say this is "almost" possible because I had to use a very clunky separate step to load the title coordinates into arrays. It would be much cleaner to generate them on the fly as the plot is drawn. I don't think that is possible currently, though maybe I am missing some clever trick.
Upvotes: 1
Reputation: 12255
Extending on my answer to your previous question, this is the sort of thing you can do with hypertext image:
This is a screen snapshot of an svg file with the (yellow) cursor hovering above the point (4,5). The svg viewer (in this case, my browser) has popped up the small image with the purple arrow from the file /tmp/fig1.png
on my system. It may not be exactly what you want, but it's the best I can do with gnuplot. Here's the code:
set term png size 400, 400 linewidth 3
unset key
stats "a_test.dat" nooutput
array xx[STATS_records]
array yy[STATS_records]
set xrange [STATS_min_x:STATS_max_x]
set yrange [STATS_min_y:STATS_max_y]
set offsets graph 0.05, graph 0.05, graph 0.05, graph 0.05
# save all data into two arrays
i = 1
fnset(x,y) = (xx[i]=x, yy[i]=y, i=i+1)
set table $dummy
plot "" using (fnset($1,$2)) with table
unset table
numi = int((i-1)/2)
#-----create fig*.png files
do for [i=1:numi] {
set output "/tmp/fig".i.".png"
plot for [j=i:i] $dummy using (xx[j]):(yy[j]):(xx[numi+j]-xx[j]):(yy[numi+j]-yy[j]) with vectors
set output
}
#-----2nd part
set term svg size 400, 400 mouse standalone linewidth 3
set output "aaa.svg"
indices = "first second"
count=0
fncount(x,y)=(count=(count==numi?1:count+1),count)
plot for [i=1:words(indices)] "a_test.dat" \
index word( indices, i ) \
u 1:2:(sprintf("image:/tmp/fig%d.png\nfig",fncount($1,$2) )) \
w labels hypertext point pt 7 ps 2 notitle
I won't explain the lines that are from the previous answer, such as
reading the data into the 2 arrays xx
and yy
. To keep the same scale
for all the graphs, it is explicitly set using set xrange ...
to min and
max calculated from the statistics command.
The first part of the code uses do for
to create files fig1.png
and so
on, each showing just one arrow, using one set of 4 data items from the arrays.
The second part selects the svg
terminal type, with option mouse
and
standalone
. This inserts the JavaScript needed to handle the mouse popup into
the svg output file. The plot
command is based on your example, with the 3rd column
of data being, for example, the string "image:/tmp/fig1.png\nfig" for the
first set of data. The function fncount()
just increments on each call,
from 1 to 3 (if you have 3 lines of data per block), then back to 1. This
re-uses the same 3 image files at the start and end data points.
Note that the svg viewer must be able to find the image files in the filesystem.
Upvotes: 0