Martin Wang
Martin Wang

Reputation: 1007

How to add consistent arrow for a set of curve with gnuplot

I want to add arrow for a series of curves in gnuplot. The question is how to put arrow in the center of a curve. My solution is to find line segment in the points sequence, and draw an arrow for this line segment. It works but the size of arrows are different.

I have write a code with python+gnuplot, it works but looks ugly

#!/bin/python

import numpy as np
import Gnuplot

def middleArrowCurve(g, x,y, percent, reverse=False):
    d = Gnuplot.Data(x,y,with_='l')
    index = int(len(x)*percent)
    fromx,tox = x[index],x[index+1]
    fromy,toy = y[index],y[index+1]
    g('set style arrow 1 front head filled size screen 0.03,15 lt 1 lw 1')
    if reverse:
        g('set arrow from ' + str(tox) + ',' + str(toy) + ' to ' + str(fromx) + ',' + str(fromy) + '  as 1')
    else :
        g('set arrow from ' + str(fromx) + ',' + str(fromy) + ' to ' + str(tox) + ',' + str(toy) + '  as 1')
    return d


def stableNode2():
    g = Gnuplot.Gnuplot(persist=1)
    g('set term png')
    g('set output "stableNode2.png"')
    g('unset key')
    g('unset tics')
    g('unset border')
    g('set xrange [-1:1]')
    g('set yrange [-1:1]')
    data = []
    reverse = False 
    for s in [-1,1]:
        x = np.linspace(s,0,20)
        data.append(middleArrowCurve(g,x,0.6*x**2,0.5,reverse))
        data.append(middleArrowCurve(g,x,-0.6*x**2,0.5,reverse))
        data.append(middleArrowCurve(g,x,2*x**2,0.5,reverse))
        data.append(middleArrowCurve(g,x,-2*x**2,0.5,reverse))
        data.append(middleArrowCurve(g,x,x*0,0.5,reverse))
        data.append(middleArrowCurve(g,x*0,x,0.5,reverse))

    g.plot(*data)

stableNode2()

enter image description here

Upvotes: 3

Views: 1538

Answers (2)

Martin Wang
Martin Wang

Reputation: 1007

Use set object poly can solve this.

#!/bin/python

import numpy as np
import Gnuplot

arrowCount = 0
def addArrow(g, x,y, dx, dy, width=0.01,angle=15):
    def normal(v):
        s = 1.0/np.sqrt(v[0]**2+v[1]**2)
        v[0] *= s
        v[1] *= s
    l = [dx,dy]
    n = [-dy,dx]
    normal(l)
    normal(n)
    length = width/np.tan(angle*np.pi/360)
    coords = []
    coords.append((x,y))
    coords.append((x+n[0]*width,y+n[1]*width))
    coords.append((x+l[0]*length,y+l[1]*length))
    coords.append((x-n[0]*width,y-n[1]*width))
    coords.append((x,y))
    coordsStr = "from " 
    coordsStr += " to ".join([str(c[0])+","+str(c[1]) for c in coords])
    global arrowCount
    arrowCount += 1
    return 'set object ' + str(arrowCount) + ' polygon ' + coordsStr + ';set object ' + str(arrowCount) + ' fc rgb "red" fs solid '

def middleArrowCurve(g, x,y, percent, reverse=False):
    d = Gnuplot.Data(x,y,with_='l')
    index = int(len(x)*percent)
    fromx,tox = x[index],x[index+1]
    fromy,toy = y[index],y[index+1]
    if reverse:
        return d,addArrow(g, tox,toy, -tox+fromx, -toy+fromy)
    else:
        return d,addArrow(g, fromx,fromy, tox-fromx, toy-fromy)

def stableNode2():
    g = Gnuplot.Gnuplot(persist=1)
    g('set term png')
    g('set output "stableNode2.png"')
    g('unset key')
    g('unset tics')
    g('unset border')
    g('set xrange [-1:1]')
    g('set yrange [-1:1]')
    data = []
    reverse = False 
    for s in [-1,1]:
        x = np.linspace(s,0,20)
        data.append(middleArrowCurve(g,x,0.6*x**2,0.5,reverse))
        data.append(middleArrowCurve(g,x,-0.6*x**2,0.5,reverse))
        data.append(middleArrowCurve(g,x,2*x**2,0.5,reverse))
        data.append(middleArrowCurve(g,x,-2*x**2,0.5,reverse))
        data.append(middleArrowCurve(g,x,x*0,0.5,reverse))
        data.append(middleArrowCurve(g,x*0,x,0.5,reverse))

    for a in data:
        g(a[1])
    g.plot(*[d[0] for d in data])

stableNode2()

The remaining problem is why the polygon arrows are covered by line plot? If I put polygon drawing after the line plot, they are just ignored.

enter image description here

Upvotes: 1

Christoph
Christoph

Reputation: 48390

As of version 4.6 gnuplot automatically scales the arrow head if the arrow length is less than twice the head's length. In version 5.0 (almost stable, 5.0RC2 is out) an additional arrow parameter fixed was introduced which prevents this scaling. So using the line

g('set style arrow 1 front head filled size screen 0.03,15 fixed lt 1 lw 1')

gives the result:

arrow with all the same length

In version 4.6 I don't know any other workaround than increasing the length of some arrows.

Upvotes: 3

Related Questions