matplotlib: Draw curved arrow that looks just like pyplot.arrow?

匿名 (未验证) 提交于 2019-12-03 01:22:02

问题:

I use pyplot.arrow do draw some straight arrows, e.g.,

import matplotlib.pyplot as plt import numpy as np  v={} for i in range (1,4):     v[i]=np.array([np.cos(-2*np.pi/3*i),np.sin(-2*np.pi/3*i)])  plt.arrow(.85*(.05*v[2]+.95*v[1])[0],.85*(.05*v[2]+.95*v[1])[1],.85*.9*(v[2]-v[1])[0],.85*.9*(v[2]-v[1])[1],width=0,head_width=.03,head_length=.045,length_includes_head=True,color="black") plt.arrow(.85*(.05*v[3]+.95*v[2])[0],.85*(.05*v[3]+.95*v[2])[1],.85*.9*(v[3]-v[2])[0],.85*.9*(v[3]-v[2])[1],width=0,head_width=.03,head_length=.045,length_includes_head=True,color="black") plt.arrow(.85*(.05*v[1]+.95*v[3])[0],.85*(.05*v[1]+.95*v[3])[1],.85*.9*(v[1]-v[3])[0],.85*.9*(v[1]-v[3])[1],width=0,head_width=.03,head_length=.045,length_includes_head=True,color="black")  plt.axes().set_xlim(-.5,1) plt.axes().set_ylim(-np.sqrt(3)/2,np.sqrt(3)/2) plt.axes().set_aspect(1) plt.show() 

Now I want to also draw some arrows that have circular curvature instead of being straight. I see that I can achieve this with pyplot.annotate() or patches.FancyArrowPatch with connectionstyle="arc3,rad=.5" or so.

But these arrows look completely different from the pyplot.arrows and do not fit with the rest of my figures. And I don't know how I could pass something like connectionstyle to pyplot.arrow. Is there a way to draw curved arrows that look exactly like those that I get from pyplot.arrow?

回答1:

You cannot plot curved arrows with pyplot.arrow. However, patches.FancyArrowPatch should offer all the options to get any arrow style you want, so the idea would be to use a FancyArrowPatch for the straight arrows as well, such that you can use the same style for all arrows.

import matplotlib.pyplot as plt import matplotlib.patches as patches  plt.axes().set_xlim(-.5,0.5) plt.axes().set_ylim(-0.9,0.7) plt.axes().set_aspect(1)  style="Simple,tail_width=0.5,head_width=4,head_length=8" kw = dict(arrowstyle=style, color="k")  a1 = patches.FancyArrowPatch((-0.4,-0.6), (0,0.6),**kw ) a2 = patches.FancyArrowPatch((0,0.6), (0.4,-0.6),**kw) a3 = patches.FancyArrowPatch((-0.4,-0.6), (0.4,-0.6),connectionstyle="arc3,rad=.5", **kw)  for a in [a1,a2,a3]:     plt.gca().add_patch(a) plt.show() 



回答2:

Here's what I ended up using; it's a bit of a hack and just draws straight arrow heads at the ends of an Arc:

import numpy as np import matplotlib.pyplot as plt from matplotlib.patches import Arc def circarrow(self,diameter,centX,centY,startangle,angle,**kwargs):     startarrow=kwargs.pop("startarrow",False)     endarrow=kwargs.pop("endarrow",False)      arc = Arc([centX,centY],diameter,diameter,angle=startangle,           theta1=np.rad2deg(kwargs.get("head_length",1.5*3*.001)) if startarrow else 0,theta2=angle-(np.rad2deg(kwargs.get("head_length",1.5*3*.001)) if endarrow else 0),linestyle="-",color=kwargs.get("color","black"))     self.axes().add_patch(arc)      if startarrow:         startX=diameter/2*np.cos(np.radians(startangle))         startY=diameter/2*np.sin(np.radians(startangle))         startDX=+.000001*diameter/2*np.sin(np.radians(startangle)+kwargs.get("head_length",1.5*3*.001))         startDY=-.000001*diameter/2*np.cos(np.radians(startangle)+kwargs.get("head_length",1.5*3*.001))         self.arrow(startX-startDX,startY-startDY,startDX,startDY,**kwargs)      if endarrow:         endX=diameter/2*np.cos(np.radians(startangle+angle))         endY=diameter/2*np.sin(np.radians(startangle+angle))         endDX=-.000001*diameter/2*np.sin(np.radians(startangle+angle)-kwargs.get("head_length",1.5*3*.001))         endDY=+.000001*diameter/2*np.cos(np.radians(startangle+angle)-kwargs.get("head_length",1.5*3*.001))         self.arrow(endX-endDX,endY-endDY,endDX,endDY,**kwargs)  import types plt.circarrow = types.MethodType(circarrow,plt) 

The function is called circarrow, and as arguments you pass the diameter, the two coordinates of the center, the angle at which the arc starts and the total angle the arc should pass over, as well as any parameters that are passed to pyplot arrow. To draw an arrowhead at the beginning of the arc you specify startarrow=True, while endarrow=True will enable an arrowhead at the end of the arc.

Here's an example image, where you can also see that the arrow style is consistent with the straight arrows:

plt.plot(0,0,"o",markersize=10,color="black",mfc="none")  plt.circarrow(.85,0,0,0.05*120,.9*120,startarrow=True,width=0,head_width=.03,head_length=.045,length_includes_head=True,color="black") plt.circarrow(.85,0,0,1.05*120,.9*120,startarrow=True,endarrow=True,width=0,head_width=.03,head_length=.045,length_includes_head=True,color="black") plt.arrow(-.2,-.33,.6,+.33,width=0,head_width=.03,head_length=.045,length_includes_head=True,color="black")  plt.axes().set_xlim(-.5,.5) plt.axes().set_ylim(-.5,.5) plt.axes().set_aspect(1) plt.show() 

example image



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!