[英]how to make arrow that loops in matplotlib?
what is the right way to draw an arrow that loops back to point to its origin in matplotlib? 在matplotlib中绘制一个循环回指向其原点的箭头的正确方法是什么? i tried: 我试过了:
plt.figure()
plt.xlim([0, 1])
plt.ylim([0, 1])
plt.annotate("", xy=(0.6, 0.9),
xycoords="figure fraction",
xytext = (0.6, 0.8),
textcoords="figure fraction",
fontsize = 10, \
color = "k",
arrowprops=dict(edgecolor='black',
connectionstyle="angle,angleA=-180,angleB=45",
arrowstyle = '<|-',
facecolor="k",
linewidth=1,
shrinkA = 0,
shrinkB = 0))
plt.show()
this doesn't give the right result: 这没有给出正确的结果:
the connectionstyle
arguments are hard to follow from this page ( http://matplotlib.org/users/annotations_guide.html ). 这个页面很难理解connectionstyle
参数( http://matplotlib.org/users/annotations_guide.html )。
i'm looking for is something like this or this : 我正在寻找的是像这样或这样 :
update: the answer linked to does not show how do this with plt.annotate
which has other features i want to use. 更新:链接的答案没有显示如何使用plt.annotate
具有我想要使用的其他功能。 the proposal to use $\\circlearrowleft$
marker is not a real solution. 使用$\\circlearrowleft$
marker的提议不是真正的解决方案。
I find no way to make a loop using plt.annotate
only once, but using it four times works : 我发现没有办法只使用plt.annotate
进行一次循环,但使用它四次工作:
import matplotlib.pyplot as plt
fig,ax = plt.subplots()
# coordinates of the center of the loop
x_center = 0.5
y_center = 0.5
radius = 0.2
# linewidth of the arrow
linewidth = 1
ax.annotate("", (x_center + radius, y_center), (x_center, y_center + radius),
arrowprops=dict(arrowstyle="-",
shrinkA=10, # creates a gap between the start point and end point of the arrow
shrinkB=0,
linewidth=linewidth,
connectionstyle="angle,angleB=-90,angleA=180,rad=10"))
ax.annotate("", (x_center, y_center - radius), (x_center + radius, y_center),
arrowprops=dict(arrowstyle="-",
shrinkA=0,
shrinkB=0,
linewidth=linewidth,
connectionstyle="angle,angleB=180,angleA=-90,rad=10"))
ax.annotate("", (x_center - radius, y_center), (x_center, y_center - radius),
arrowprops=dict(arrowstyle="-",
shrinkA=0,
shrinkB=0,
linewidth=linewidth,
connectionstyle="angle,angleB=-90,angleA=180,rad=10"))
ax.annotate("", (x_center, y_center + radius), (x_center - radius, y_center),
arrowprops=dict(arrowstyle="-|>",
facecolor="k",
linewidth=linewidth,
shrinkA=0,
shrinkB=0,
connectionstyle="angle,angleB=180,angleA=-90,rad=10"))
plt.show()
It seems the easiest way to create an easily modifiable looping arrow is to use patches . 似乎创建一个易于修改的循环箭头的最简单方法是使用补丁 。 I've pasted code to do this below. 我已粘贴代码以执行此操作。 Change the variables in the variables section and things should all rotate and scale together. 更改变量部分中的变量,事物应该全部旋转并缩放。 You can play around with the patch that creates the arrow head to make a different shape though I suspect that this triangle will be the easiest one. 您可以使用创建箭头的补丁来制作不同的形状,但我怀疑这个三角形是最简单的。
%matplotlib inline
# from __future__ import division #Uncomment for python2.7
import matplotlib.pyplot as plt
from matplotlib.patches import Arc, RegularPolygon
import numpy as np
from numpy import radians as rad
fig = plt.figure(figsize=(9,9))
ax = plt.gca()
def drawCirc(ax,radius,centX,centY,angle_,theta2_,color_='black'):
#========Line
arc = Arc([centX,centY],radius,radius,angle=angle_,
theta1=0,theta2=theta2_,capstyle='round',linestyle='-',lw=10,color=color_)
ax.add_patch(arc)
#========Create the arrow head
endX=centX+(radius/2)*np.cos(rad(theta2_+angle_)) #Do trig to determine end position
endY=centY+(radius/2)*np.sin(rad(theta2_+angle_))
ax.add_patch( #Create triangle as arrow head
RegularPolygon(
(endX, endY), # (x,y)
3, # number of vertices
radius/9, # radius
rad(angle_+theta2_), # orientation
color=color_
)
)
ax.set_xlim([centX-radius,centY+radius]) and ax.set_ylim([centY-radius,centY+radius])
# Make sure you keep the axes scaled or else arrow will distort
drawCirc(ax,1,1,1,0,250)
drawCirc(ax,2,1,1,90,330,color_='blue')
plt.show()
My suggestion uses just the plot command 我的建议只使用plot命令
import matplotlib.pyplot as plt
import numpy as np
def circarrowdraw(x0, y0, radius=1, aspect=1, direction=270, closingangle=-330,
arrowheadrelativesize=0.3, arrowheadopenangle=30, *args):
"""
Circular arrow drawing. x0 and y0 are the anchor points.
direction gives the angle of the circle center relative to the anchor
in degrees. closingangle indicates how much of the circle is drawn
in degrees with positive being counterclockwise and negative being
clockwise. aspect is important to make the aspect of the arrow
fit the current figure.
"""
xc = x0 + radius * np.cos(direction * np.pi / 180)
yc = y0 + aspect * radius * np.sin(direction * np.pi / 180)
headcorrectionangle = 5
if closingangle < 0:
step = -1
else:
step = 1
x = [xc + radius * np.cos((ang + 180 + direction) * np.pi / 180)
for ang in np.arange(0, closingangle, step)]
y = [yc + aspect * radius * np.sin((ang + 180 + direction) * np.pi / 180)
for ang in np.arange(0, closingangle, step)]
plt.plot(x, y, *args)
xlast = x[-1]
ylast = y[-1]
l = radius * arrowheadrelativesize
headangle = (direction + closingangle + (90 - headcorrectionangle) *
np.sign(closingangle))
x = [xlast +
l * np.cos((headangle + arrowheadopenangle) * np.pi / 180),
xlast,
xlast +
l * np.cos((headangle - arrowheadopenangle) * np.pi / 180)]
y = [ylast +
aspect * l * np.sin((headangle + arrowheadopenangle) * np.pi / 180),
ylast,
ylast +
aspect * l * np.sin((headangle - arrowheadopenangle) * np.pi / 180)]
plt.plot(x, y, *args)
To test it: 测试它:
plt.figure()
plt.plot(np.arange(10)**2, 'b.')
bb = plt.gca().axis()
asp = (bb[3] - bb[2]) / (bb[1] - bb[0])
circarrowdraw(6, 36 , radius=0.4, aspect=asp, direction=90)
plt.grid()
plt.show()
Another possibility is to use tikz to generate the figure: 另一种可能性是使用tikz生成数字:
\documentclass {minimal}
\usepackage {tikz}
\begin{document}
\usetikzlibrary {arrows}
\begin {tikzpicture}[scale=1.8]
\draw[-angle 90, line width=5.0mm, rounded corners=20pt]
(0.25,0)-- (1.0, 0.0) -- (1.0, -3.0) -- (-3.0, -3.0) -- (-3.0, 0) --(-1,0);
\end{tikzpicture}
\end{document}
there is a pgf/tikz backend in matplotlib that you could generate your matplotlib output to tikz code that pdflatex or lualatex can process. 在matplotlib中有一个pgf / tikz后端,你可以生成你的matplotlib输出到pdflatex或lualatex可以处理的tikz代码。 So this way, I think, you could insert seamlessly the looparrow figure in your matplotlib figure. 所以这样,我想,你可以在你的matplotlib图中无缝插入looparrow图。 See for ex: http://matplotlib.org/users/whats_new.html#pgf-tikz-backend 参见例如: http : //matplotlib.org/users/whats_new.html#pgf-tikz-backend
@Aguy's answer is useful if you want a smooth arc instead of a complete circle. 如果你想要一个光滑的圆弧而不是一个完整的圆圈,@ Aguy的答案很有用。 In Aguy's answer an arrow head is drawn line by line, but instead a FancyArrowPatch can be used. 在Aguy的回答中,箭头是逐行绘制的,但是可以使用FancyArrowPatch。 This gives a full arrow head, which might be more suitable. 这给出了一个完整的箭头,这可能更合适。 Below gives the code with the FancyArrowPatch arrow head. 下面给出了FancyArrowPatch箭头的代码。
def circarrowdraw(x0, y0, radius=1, aspect=1, direction=270, closingangle=-330, rotate_head = 0.0, color='b', *args):
"""
Circular arrow drawing. x0 and y0 are the anchor points.
direction gives the angle of the circle center relative to the anchor
in degrees. closingangle indicates how much of the circle is drawn
in degrees with positive being counterclockwise and negative being
clockwise. aspect is important to make the aspect of the arrow
fit the current figure. rotate_head is used to rotate the arrow head
by increasing the y value of the arrow's tail coordinate.
"""
# Center of circle
xc = x0 + radius * np.cos(direction * np.pi / 180)
yc = y0 + aspect * radius * np.sin(direction * np.pi / 180)
# Draw circle
if closingangle < 0:
step = -1
else:
step = 1
x = [xc + radius * np.cos((ang + 180 + direction) * np.pi / 180)
for ang in np.arange(0, closingangle, step)]
y = [yc + aspect * radius * np.sin((ang + 180 + direction) * np.pi / 180)
for ang in np.arange(0, closingangle, step)]
plt.plot(x, y, *args, color=color)
# Draw arrow head
arc_arrow_head = patches.FancyArrowPatch((x[-1], y[-1] + rotate_head),
(x[0], y[0]),
arrowstyle="Simple,head_width=10,head_length=10,tail_width=0.01",
color = color,
zorder = 10)
plt.gca().add_patch(arc_arrow_head)
To test it: 测试它:
plt.plot([0, 0, 1, 1, 0], [0, 1, 1, 0, 0])
circarrowdraw(1.0, 1.0 , radius=0.1, aspect=0.3, direction=90, closingangle=-345, rotate_head = 0.003)
circarrowdraw(0.0, 1.0 , radius=0.1, aspect=1, direction=-90, closingangle=-345, rotate_head = 0.0)
circarrowdraw(0.0, 0.0 , radius=0.1, aspect=3.0, direction=90, closingangle=-345, rotate_head = 0.01)
circarrowdraw(1.0, 0.0 , radius=0.1, aspect=0.3, direction=-90, closingangle=-345)
plt.show()
Picture of image (I don't have a high enough reputation to embed the image in my answer) 图片的图片(我没有足够的声誉将图片嵌入到我的答案中)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.