Как сделать стрелку, которая петли в 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).

Я ищу что-то вроде this или this:

loopy arrow

update: ответ, связанный с не показывает, как это сделать с plt.annotate, у которого есть другие функции, которые я хочу использовать. предложение использовать маркер $\circlearrowleft$ не является реальным решением.

Ответ 1

Кажется, что самый простой способ создать легко изменяемую стрелку цикла - использовать 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()    

введите описание изображения здесь

Ответ 2

Я не нахожу никакого способа сделать цикл, используя 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()

введите описание изображения здесь

Ответ 3

Попробуйте следующее:

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

введите описание изображения здесь

Ответ 4

В моем предложении используется только команда 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)

Чтобы проверить это:

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

введите описание изображения здесь

Ответ 5

Другая возможность - использовать 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}

Это результат: введите описание изображения здесь

в matplotlib есть бэкэнд pgf/tikz, который вы можете сгенерировать свой вывод matplotlib для кода tikz, который может обрабатывать pdflatex или lualatex. Таким образом, таким образом, я думаю, вы можете легко вставить фигуру цикла в ваш рисунок matplotlib. См. Например: http://matplotlib.org/users/whats_new.html#pgf-tikz-backend