简体   繁体   中英

connectionstyle arc3 (FancyArrowPatch) and path CURVE3 - two quadratic Bezier curves, different outcomes?

I started from a similar problem as mentioned here: Draw rounded fancyarrowpatch with midpoint arrow in matplotlib

Just that I would like to add a label to an arrow, ideally in variable places. I tried to reconstruct coordinates with the path vertices, but the labels still were all over the place. Then, I tried to follow the description of the connectionstyle arc3

( https://matplotlib.org/api/_as_gen/matplotlib.patches.ConnectionStyle.html?highlight=connectionstyle )

class Arc3(rad=0.0)

Creates a simple quadratic bezier curve between two points. The curve is created so that the middle control point (C1) is located at the same distance from the start (C0) and end points(C2) and the distance of the C1 to the line connecting C0-C2 is rad times the distance of C0-C2.

I reconstructed this and noticed that the Curve drawn by FancyArrowPatch is NOT the same as a quadratic Bezier curve with the specified points.

Minimal example:

import numpy
import matplotlib
from matplotlib import pyplot as plt
import matplotlib.path as mpath
import matplotlib.patches as mpatches
from matplotlib.patches import FancyArrowPatch, Circle

fig = plt.figure(frameon=False)
ax = fig.add_subplot(111, frame_on=False)

rad=0.3
shrink=0.3

# Setting up circles as start and end points
size = 0.1
n1 = Circle([0,0], radius=size, alpha=0., fill=False, linewidth=0.1)
n2 = Circle([1,1], radius=size, alpha=0., fill=False, linewidth=0.1)
ax.add_patch(n1)
ax.add_patch(n2)

# Create a fancy arrow between start and end point
e = FancyArrowPatch(n1.center, n2.center,
    connectionstyle='arc3,rad=%s' % rad,
    arrowstyle='simple',
    clip_on=False,
    linewidth=2.,
    shrinkA=shrink,shrinkB=shrink
    )

ax.add_patch(e)

# Start point
vs=numpy.asarray(n1.center)
# End point
ve=numpy.asarray(n2.center)
# Connection vector start->end
vD=ve-vs
# Perpendicular vector to vD
vp=numpy.asarray([vD[1],-vD[0]]) 
# Control point: same distance from start and end point, and rad*|vD| from the connection line between start and end
vc=vs+0.5*vD+rad*vp 

Path=mpath.Path
pp1 = mpatches.PathPatch(
    Path([vs, vc, ve],
     [Path.MOVETO, Path.CURVE3, Path.CURVE3]), color="red", transform=ax.transData,fc='None')

ax.add_patch(pp1)

# Putting labels on quadratic Bezier curve at various points
# Uses start point, control point and end point from above
for tt in numpy.arange(0,1.1,0.1):
    vl=((1-tt)**2)*vs+(2*(1-tt)*tt)*vc+(tt**2)*ve
    string = str(tt)
    ax.text(vl[0], vl[1], string,
        fontsize=10,bbox={'alpha':0.5, 'pad':2},
        verticalalignment='center',
        horizontalalignment='center')

plt.show()

Which gives

https://i.imgur.com/6XD8txW.png

Black line: FancyArrowPatch (don't know why there is no arrowhead)

Red line: PathPatch

Blue labels: Points on quadratic Bezier Curve between (0,0) and (1,1) with control point at same distance from start and end, and rad*(distance startpoint to endpoint) from the connection line between start and end. (as described in connectionstyle arc3).

(Unfortunately I can't just give FancyArrowPatch the constructed path, because I need its shrink/patch options.)

So the question is how I reliably put a label on an arrow, and how FancyArrowPatch is actually constructing its path.

(I know I can have the vertices returned, but I use identical code for different arrow lengths (only start and end point differ), and these paths can look very different from each other).

The curve you create as Path lives in data space. The FancyArrowPatch curve that is automatically generated lives in display space. This means that they will be equal only if the distance between points in data space is propotional to the distance between points in display space. This is in general only the case when the axes aspect ratio is equal.

This is somehow similar to the more intuitive (inverse) case of a circle defined in data coordinates. In such case a documentation would say something like "draws a circle, ie a line which has the same distance to a given center point everywhere". Yet, if you plot a circle it would rather look like an ellipse, unless you set the aspect ratio of the axes to be 1.

Indeed, when setting ax.set_aspect("equal") , in the case above you'd get

在此处输入图片说明

Since the Arc3 itself will not know anything about the coordinate system it will be used in, the Arc3 documentation is completely correct.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM