Заливка между дуговыми заплатами - Matplotlib - PullRequest
1 голос
/ 07 октября 2019

У меня есть ellipse, который я хочу solid fill с цветами в разных разделах. Для этого я использую arcs патчей. В настоящее время я рисую несколько arcs и использую zorder для перекрытия соответствующих arcs.

Основные проблемы заключаются в том, что я не могу полностью заполнить arc патчи, и они не аккуратно заполняют ellipse. Было бы здорово, если бы области не перекрывались, поэтому я мог быиспользуйте параметры прозрачности.

import matplotlib.pyplot as plt
import matplotlib as mpl
import math

fig, ax = plt.subplots(figsize = (8,5))
ax.grid(False)

ax.set_xlim(-85,85)
ax.set_ylim(-70,70)

Arc1_xy = -68, 0
Arc2_xy = -48,0
Arc3_xy = 15, 0

Arc4_xy = 68, 0
Arc5_xy = 48,0
Arc6_xy = -15, 0

E_xy = 0,0

angle = math.degrees(math.acos(2/9.15))

Arc1 = mpl.patches.Arc(Arc1_xy, 75, 92, angle = 360, theta2 = angle, theta1 = 360-angle, color = 'w', lw = 1, alpha = 1, hatch = 'oooooo', zorder = 3)
Arc2 = mpl.patches.Arc(Arc2_xy, 64, 94, angle = 180, theta2 = angle, theta1 = 360-angle, color = 'w', lw = 1, alpha = 1, hatch = 'oooooo', zorder = 3)
Arc3 = mpl.patches.Arc(Arc3_xy, 190, 132, angle = -180, theta2 = angle, theta1 = 360-angle, color = 'w', lw = 1, alpha = 1, hatch = 'oooooo', zorder = 1)

Arc4 = mpl.patches.Arc(Arc4_xy, 75, 92, angle = 180, theta2 = angle, theta1 = 360-angle, color = 'w', lw = 1, alpha = 1, hatch = 'oooooo', zorder = 3)
Arc5 = mpl.patches.Arc(Arc5_xy, 64, 94, angle = 0, theta2 = angle, theta1 = 0-angle, color = 'w', lw = 1, alpha = 1, hatch = 'oooooo', zorder = 3)
Arc6 = mpl.patches.Arc(Arc6_xy, 190, 132, angle = 360, theta2 = angle, theta1 = 360-angle, color = 'w', lw = 1, alpha = 1, hatch = 'oooooo', zorder = 1)

Ellipse = mpl.patches.Ellipse(E_xy, 160, 130, lw = 1, color = 'white', alpha = 1, fill = False)

ax.add_patch(Arc1)
ax.add_patch(Arc2)
ax.add_patch(Arc3)
ax.add_patch(Arc4)
ax.add_patch(Arc5)
ax.add_patch(Arc6)

ax.add_patch(Ellipse)

Arc1.set_color('green')
Arc2.set_color('green')
Arc3.set_color('blue')  
Arc4.set_color('red')
Arc5.set_color('red')
Arc6.set_color('purple')   

Основные проблемы: я хочу сплошное заполнение разноцветных секций внутри ellipse. В идеале разделы не должны перекрываться, поэтому я мог бы использовать alpha. Также arcs не вписывается в ellipse.

1 Ответ

1 голос
/ 09 октября 2019

Ваш вопрос не очень понятен. Вот обновленная версия вашего кода:

# EDITED, see below

Arc4 заполняет другую сторону эллипса. Это то, что вы ожидали?

Я также добавил пример использования карты цветов. Существует широкий диапазон цветовых карт , многие из которых варьируются от 0 до 255 (до вас нормализуется до 0,0-1,0).

Другое решение может состоять в том, чтобы рисовать перекрывающиеся эллипсы и использовать clip_path атрибут, чтобы показать только необходимую часть.


Редактировать после комментария

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

import matplotlib.pyplot as plt
import matplotlib as mpl

def main():
    fig, ax = plt.subplots(figsize = (8,5))
    ax.grid(False)
    ax.set_xlim(-80,80)
    ax.set_ylim(-70,70)

    Arc1_xy = -68, 0
    Arc3_xy = 15, 0
    E_xy = 0,0

    # Use a predefined colormap
    colormap = plt.cm.get_cmap("Set1")

    # Draw multiple ellipses with different colors and hatching style. All are perfectly superposed
    ellipse = mpl.patches.Ellipse( # Base one, with big black line for reference, in background (zorder=0)
        E_xy, 160, 130,
        lw = 2, color = 'k', fill=False, zorder=0)
    area1 = mpl.patches.Ellipse( # A second one, above the base one, using a color from colormap
        E_xy, 160, 130,
        lw=1,  # Just to highlight that we perfectly fit the base ellipse
        color = colormap(0), hatch='o', fill=False, zorder=1)
    area2 = mpl.patches.Ellipse( # Third one, above the others
        E_xy, 160, 130,
        lw=1,  # Just to highlight that we perfectly fit the base ellipse
        color = colormap(1), hatch='..', fill=False, zorder=2)
    # Add more if you want more "sub-areas" in your base ellipse

    # Define some clipping paths
    clip1 = mpl.patches.Ellipse(
        Arc1_xy, 75, 92,
        fill=False,
        ls=":"  # Just to highlight it but you should remove it
        # visible=False  # We do not need to display it, just to use it for clipping
    )
    clip2 = mpl.patches.Ellipse(
        Arc3_xy, 190, 132, angle = -180,
        fill=False,
        ls=":"  # Just to highlight it but you should remove it
        # visible=False  # We do not need to display it, just to use it for clipping
    )

    # Add all your components to your axe
    ax.add_patch(ellipse)
    ax.add_patch(area1)
    ax.add_patch(area2)
    ax.add_patch(clip1)
    ax.add_patch(clip2)

    # Clip the sub-areas with your clipping paths
    area1.set_clip_path(clip2)
    area2.set_clip_path(clip1)

    plt.show()

if __name__ == '__main__':
    main()

Это нарисует вас:

  • Большая черная эллиптическая линия (базовый эллипс)
  • Подзона с двумя заштрихованными (одна с точками, одна с кругами) внутри ваш базовый эллипс
  • Тонкие пунктирные линии, очерчивающие ваши пути отсечения

Rendering of the above code

Вы говорите, что хотите «полностью заполнить эллипс. Не использовать патч». Для полной заливки просто поиграйте с аргументами fill и color ваших областей (и удалите hatch, если вы этого не хотите). Тем не менее, я не вижу, как вы можете достичь того, что вы хотите без «патча». Извините.

Надеюсь, что эта помощь.


Редактировать 2

После уточнений вот новая версия:

import math

import matplotlib.pyplot as plt
import matplotlib as mpl


def main():
    fig, ax = plt.subplots(figsize=(8, 5))
    ax.grid(False)
    ax.set_xlim(-85, 85)
    ax.set_ylim(-70, 70)

    Arc1_xy = -68, 0
    E_xy = 0, 0

    # Use a predefined colormap
    colormap = plt.cm.get_cmap("Set1")

    # Draw multiple ellipses with different colors and style. All are perfectly superposed
    ellipse = mpl.patches.Ellipse(  # Base one, with big black line for reference
        E_xy, 160, 130,
        lw=2, color='k', fill=False, zorder=100)
    # Ellipses for your sub-areas.
    # Add more if you want more areas
    # Apply the style of your areas here (colors, alpha, hatch, etc.)
    areas = [
        mpl.patches.Ellipse(
            E_xy, 160, 130,  # Perfectly fit your base ellipse
            color=colormap(i), fill=True, alpha=0.5,  # Add some style, fill, color, alpha
            zorder=i)
        for i in range(4)  # Here, we have 4 areas
    ]

    # Define some clipping paths
    # One for each area
    clips = [
        mpl.patches.Arc(  # One covering right half of your ellipse
            E_xy, 160, 130, theta1=-90, theta2=90,
            visible=False  # We do not need to display it, just to use it for clipping
        ),
        mpl.patches.Arc(  # One covering left half of your ellipse
            tuple([-x for x in E_xy]), 160, 130, theta1=90, theta2=-90,
            visible=False  # We do not need to display it, just to use it for clipping
        ),
        mpl.patches.Ellipse(  # A small area on the left
            Arc1_xy, 75, 92,
            visible=False  # We do not need to display it, just to use it for clipping
        ),
        mpl.patches.Ellipse(  # A small area on the right
            tuple([-x for x in Arc1_xy]), 75, 92,
            visible=False  # We do not need to display it, just to use it for clipping
        )
    ]

    # Add all your components to your axe
    ax.add_patch(ellipse)
    for area, clip in zip(areas, clips):
        ax.add_patch(area)
        ax.add_patch(clip)
        area.set_clip_path(clip)  # Use clipping paths to clip you areas

    plt.show()


if __name__ == '__main__':
    main()

И полученное изображение: Rendering of the above code

Как это работает:

  • путем рисования нескольких эллипсов, идеально наложенных на «базовый». Это гарантирует, что все ваши области идеально подходят внутри вашего базового эллипса (ни один из них не может охватить)
  • путем определения обтравочных контуров, которые будут обрезать / обрезать эллипсы, чтобы показать только их часть,Эти обтравочные контуры должны пересекать (или, по крайней мере, следовать) границы базового эллипса, чтобы он был заполнен (поэтому я меняю ваш Arc3, используя центр эллипса и размеры, иначе на границе было белое пространствовнутри эллипса, как вы можете видеть на первом рисунке).

Вы были не так далеко со своей последней попыткой. Чтобы стилизовать ваши области, вы должны применить стиль к вашим базовым эллипсам (перечисленным в списке areas в моем коде), а не к вашим путям отсечения (перечисленным в списке clips в моем коде). Оба списка areas и clips должны иметь одинаковую длину! Вы можете добавить больше областей и обтравочных контуров, если хотите, чтобы в ваших эллипсах было больше подобластей.

Кстати, я вижу ваш обновленный вопрос и ваши требования, чтобы области не перекрывались. Это потребует больше работы. Вы можете добиться этого, построив более сложный путь отсечения, используя модуль matplotlip.path.

...