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

时间:2016-05-29 16:50:24

标签: python matplotlib

在matplotlib中绘制一个循环回指向其原点的箭头的正确方法是什么?我试过了:

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()

这并没有给出正确的结果:

arrow

connectionstyle参数很难从此页面(http://matplotlib.org/users/annotations_guide.html)开始。

我正在寻找类似thisthis的内容:

loopy arrow

更新:链接到的答案未显示如何与plt.annotate具有我想要使用的其他功能。使用$\circlearrowleft$标记的提议不是真正的解决方案。

6 个答案:

答案 0 :(得分:7)

我发现只能使用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()

enter image description here

答案 1 :(得分:5)

创建易于修改的循环箭头似乎最简单的方法是使用patches。我已粘贴代码以执行此操作。更改变量部分中的变量,事物应该全部旋转并缩放。您可以使用创建箭头的补丁来制作不同的形状,但我怀疑这个三角形是最简单的。

%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()    

enter image description here

答案 2 :(得分:3)

试试这个:

{{1}}

Control

答案 3 :(得分:3)

我的建议只使用了绘图命令

import org.hibernate.boot.model.naming.Identifier;

import java.util.ArrayList;
import java.util.List;
import java.util.zip.CRC32;

/**
* Based on:
*  Doctrine\DBAL\Schema\Table
*  Doctrine\DBAL\Schema\AbstractAsset
*/
public class DoctrineNamingHelper {

    public static final DoctrineNamingHelper INSTANCE = new DoctrineNamingHelper();
    private static Integer MAX_LENGTH=63;

    /*
        FK:
            $name = $this->_generateIdentifierName(
                array_merge((array) $this->getName(), $constraint->getLocalColumns()), "fk", $this->_getMaxIdentifierLength()
            );


        IDX:
            $indexName = $this->_generateIdentifierName(
                array_merge(array($this->getName()), $constraint->getColumns()),
                "idx",
                $this->_getMaxIdentifierLength()
            );

            $indexName = $this->_generateIdentifierName(
                array_merge(array($this->getName()), $columnNames), "idx", $this->_getMaxIdentifierLength()
            );

        UNIQ:
            $indexName = $this->_generateIdentifierName(
                array_merge(array($this->getName()), $columnNames), "uniq", $this->_getMaxIdentifierLength()
            );
    */

    public String generateHashedFkName( String prefix, Identifier tableName, Identifier referencedTableName, List<Identifier> columnNames){
        List<Identifier> parts = new ArrayList<>();

        parts.add(tableName);
        parts.addAll(columnNames);

        return makeHash(prefix, parts, MAX_LENGTH) ;
    }

    public String generateHashedConstraintName(String prefix, Identifier tableName, List<Identifier> columnNames) {
        List<Identifier> parts = new ArrayList<>();

        parts.add(tableName);
        parts.addAll(columnNames);

        return makeHash(prefix,parts, MAX_LENGTH) ;
    }

    private String makeHash(String prefix, List<Identifier> parts, Integer maxSize){

        // Based on Doctrine\DBAL\Schema\AbstractAsset::_generateIdentifierName()

        String hash = prefix;

        for(Identifier part: parts)
        {
            CRC32 crc32=new CRC32();
            crc32.update(part.getText().getBytes());

            String hexed=Integer.toHexString((int)crc32.getValue());

            hash += hexed;
        }

        if (hash.length()>maxSize) {
            hash = hash.substring(0, maxSize);
        }

        return hash;
    }

}

测试它:

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)

enter image description here

答案 4 :(得分:1)

另一种可能性是使用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}

这是结果: enter image description here

在matplotlib中有一个pgf / tikz后端,你可以生成你的matplotlib输出到pdflatex或lualatex可以处理的tikz代码。 所以这种方式,我认为,你可以无缝地插入looparrow数字 你的matplotlib图。 见ex: http://matplotlib.org/users/whats_new.html#pgf-tikz-backend

答案 5 :(得分:0)

如果你想要一个光滑的圆弧而不是一个完整的圆圈,那么@ Aguy的答案很有用。在Aguy的回答中,箭头是逐行绘制的,但是可以使用FancyArrowPatch。这给出了一个完整的箭头,这可能更合适。下面给出了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)

测试它:

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)