简体   繁体   English

如何制作在matplotlib中循环的箭头?

[英]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()    

在此输入图像描述

Try this: 试试这个:

import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.set_xlim(1,3) 
ax.set_ylim(1,3)
ax.plot([2.5],[2.5],marker=r'$\circlearrowleft$',ms=100)
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}

This is the result: 这是结果: 在此输入图像描述

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.

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