2015-06-04 3 views
4

В Matplotlib, я хотел бы нарисовать заполненную дугу, которая выглядит следующим образом:Как нарисовать заполненную дугу в Matplotlib

Filled Arc Example

следующие результаты кода в незаполненной линии дуги:

import matplotlib.patches as mpatches 
import matplotlib.pyplot as plt 

fg, ax = plt.subplots(1, 1) 

pac = mpatches.Arc([0, -2.5], 5, 5, angle=0, theta1=45, theta2=135) 
ax.add_patch(pac) 

ax.axis([-2, 2, -2, 2]) 
ax.set_aspect("equal") 
fg.canvas.draw() 

documentation говорит, что заполненные дуги невозможны. Что было бы лучшим способом привлечь его?

ответ

3

@jeanrjc's solution почти достанется вам, но добавляет совершенно ненужный белый треугольник, который также спрячет другие объекты (см. рисунок ниже, версия 1).

Это более простой подход, который только добавляет многоугольник дуги:

В основном мы создаем ряд точек (points) по краю окружности (от theta1 до theta2). Этого уже достаточно, так как мы можем установить флаг close в конструкторе Polygon, который добавит линию от последней к первой точке (создавая замкнутую дугу).

import matplotlib.patches as mpatches 
import matplotlib.pyplot as plt 
import numpy as np 

def arc_patch(center, radius, theta1, theta2, ax=None, resolution=50, **kwargs): 
    # make sure ax is not empty 
    if ax is None: 
     ax = plt.gca() 
    # generate the points 
    theta = np.linspace(np.radians(theta1), np.radians(theta2), resolution) 
    points = np.vstack((radius*np.cos(theta) + center[0], 
         radius*np.sin(theta) + center[1])) 
    # build the polygon and add it to the axes 
    poly = mpatches.Polygon(points.T, closed=True, **kwargs) 
    ax.add_patch(poly) 
    return poly 

А потом применить его:

fig, ax = plt.subplots(1,2) 

# @jeanrjc solution, which might hide other objects in your plot 
ax[0].plot([-1,1],[1,-1], 'r', zorder = -10) 
filled_arc((0.,0.3), 1, 90, 180, ax[0], 'blue') 
ax[0].set_title('version 1') 

# simpler approach, which really is just the arc 
ax[1].plot([-1,1],[1,-1], 'r', zorder = -10) 
arc_patch((0.,0.3), 1, 90, 180, ax=ax[1], fill=True, color='blue') 
ax[1].set_title('version 2') 

# axis settings 
for a in ax: 
    a.set_aspect('equal') 
    a.set_xlim(-1.5, 1.5) 
    a.set_ylim(-1.5, 1.5) 

plt.show() 

Результат (версия 2):

enter image description here

+0

Просто любопытно, почему downvote ?! Как это не касается вопроса? – hitzg

+0

Ну, я не спустил вниз. Я очень доволен, что вам удастся улучшить свое решение, хотя, если бы вы могли подробно рассказать о том, как вы заполняете «очки», было бы еще лучше. Кстати, если вы отклонили мой ответ, я не вижу причин. – jrjc

+0

Нет, я не сделал нисходящего творения, на самом деле я собираюсь его продвигать. Ваше решение тоже приятно, мне нравится, что вы смогли использовать существующую функциональность (клин). Был кто-то еще на нисходящем волнении (без видимой причины). Что касается населения «точек», вы правы, я сделаю эту часть проще. – hitzg

4

Вы можете использовать fill_between для достижения этой

import matplotlib.patches as mpatches 
import matplotlib.pyplot as plt 
import numpy as np 

fg, ax = plt.subplots(1, 1) 

r=2. 
yoff=-1 
x=np.arange(-1.,1.05,0.05) 
y=np.sqrt(r-x**2)+yoff 

ax.fill_between(x,y,0) 

ax.axis([-2, 2, -2, 2]) 
ax.set_aspect("equal") 
fg.canvas.draw() 

Поиграйте с г и Yoff, чтобы переместить дугу

enter image description here

EDIT:

ОК, так что вы хотите чтобы иметь возможность отображать произвольные углы? Вам просто нужно найти уравнение аккорда, а не использовать плоскую линию, как показано выше. Вот функция, чтобы сделать только что:

import matplotlib.patches as mpatches 
import matplotlib.pyplot as plt 
import numpy as np 

fg, ax = plt.subplots(1, 1) 

col='rgbkmcyk' 

def filled_arc(center,r,theta1,theta2): 

    # Range of angles 
    phi=np.linspace(theta1,theta2,100) 

    # x values 
    x=center[0]+r*np.sin(np.radians(phi)) 

    # y values. need to correct for negative values in range theta=90--270 
    yy = np.sqrt(r-x**2) 
    yy = [-yy[i] if phi[i] > 90 and phi[i] < 270 else yy[i] for i in range(len(yy))] 

    y = center[1] + np.array(yy) 

    # Equation of the chord 
    m=(y[-1]-y[0])/(x[-1]-x[0]) 
    c=y[0]-m*x[0] 
    y2=m*x+c 

    # Plot the filled arc 
    ax.fill_between(x,y,y2,color=col[theta1/45]) 

# Lets plot a whole range of arcs 
for i in [0,45,90,135,180,225,270,315]: 
    filled_arc([0,0],1,i,i+45) 

ax.axis([-2, 2, -2, 2]) 
ax.set_aspect("equal") 
fg.savefig('filled_arc.png') 

И вот результат:

enter image description here

+0

Просто любопытно, почему downvote? Не отвечает ли это на вопрос? – tom

+1

Я думаю, это потому, что, хотя вам удается воссоздать форму, заданную (умным способом), она не позволяет выполнять заполненную дугу от 'theta1 = 0' до' theta2 = 45', например. Или, если бы вы могли, пожалуйста, опишите немного больше. – jrjc

+0

вы можете, вам просто нужно немного воображения. Я сейчас обновлю свой ответ. – tom

2

Вы можете нарисовать клин, а затем скрыть часть его с треугольником:

import matplotlib.patches as mpatches 
import matplotlib.pyplot as plt 
import numpy as np 

def filled_arc(center, radius, theta1, theta2, ax, color): 

    circ = mpatches.Wedge(center, radius, theta1, theta2, fill=True, color=color) 
    pt1 = (radius * (np.cos(theta1*np.pi/180.)) + center[0], 
      radius * (np.sin(theta1*np.pi/180.)) + center[1]) 
    pt2 = (radius * (np.cos(theta2*np.pi/180.)) + center[0], 
      radius * (np.sin(theta2*np.pi/180.)) + center[1]) 
    pt3 = center 
    pol = mpatches.Polygon([pt1, pt2, pt3], color=ax.get_axis_bgcolor(), 
          ec=ax.get_axis_bgcolor(), lw=2) 
    ax.add_patch(circ) 
    ax.add_patch(pol) 

, а затем вы можете позвонить по этому адресу:

fig, ax = plt.subplots(1,2) 
filled_arc((0,0), 1, 45, 135, ax[0], "blue") 
filled_arc((0,0), 1, 0, 40, ax[1], "blue") 

и вы получите:

filled_arc

или:

fig, ax = plt.subplots(1, 1) 
for i in range(0,360,45): 
    filled_arc((0,0), 1, i, i+45, ax, plt.cm.jet(i)) 

и вы получите:

enter image description here

НТН

+0

Пока это выполняется в этом базовом примере, это даст вам нежелательные результаты, если у вас есть другие художники на вашей фигуре, которые вы хотите быть ниже дуги (так как белый треугольник скрывает их тоже). – hitzg

+0

Мне нравится подход сочетания клина и треугольника. Как отметил @hitzg, могут возникнуть проблемы с закрытием других объектов. +1 для хороших фотографий – Dietrich

2

Вот простой обходной путь. Используйте аргумент штриховки в команде mpatches.Arc. Если вы повторяете символы с аргументом штриховки, это увеличивает плотность паттерна. Я нахожу, что если вы используете 6 тире, «-» или 6 точек, «.» (другие, вероятно, также работают), то он прочно заполняет дугу по желанию. Когда я запускаю этот

import matplotlib.patches as mpatches 
import matplotlib.pyplot as plt 

plt.axes() 
pac = mpatches.Arc([0, -2.5], 5, 5, 45, theta1=45, theta2=135, hatch = '......') 
plt.gca().add_patch(pac) 
pac.set_color('cyan') 
plt.axis('equal') 
plt.show() 

я получаю это:

Arc filled with dense dot hatch and rotated 45 degrees just for show

+0

Это решение зависит от разрешения изображения, при достаточном масштабировании всегда можно заметить точки. – heracho

+0

Если я не ошибаюсь, это единственный ответ в этой теме, который заполняет дугу эллипса, а не дугу окружности. – heracho

+0

Heracho, 1) Правда, к сожалению. Вот почему я представил его как «более простой обходной путь». Различные стили штриховки могут помочь сделать его более толстым. Досадно, что цвет края не соответствует псевдо-сплошному цвету, заполняющему форму. 2) Похоже, это решение по-прежнему остается единственным, что использует 'mpatches.Arc', поэтому, я полагаю, вы правы! – Quebert

Смежные вопросы