问题
I have an ellipse
that I want to solid fill
with colors in different sections. To achieve this I'm using arcs
patches. I'm currently drawing multiple arcs
and using zorder
to overlap the appropriate arcs
.
Main issues are that I cannot solid fill arc
patches and they are not neatly filling the ellipse
. It would be great if the areas didn't overlap either so I could use transparency parameters.
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')
The main issues are I want to solid fill the different colored sections within the ellipse
. Ideally the sections would not overlap so I could use alpha
. Also the arcs
don't neatly fit within the ellipse
.
回答1:
Your question is not very clear. Here is an updated version of your code:
# EDITED, see below
The Arc4
fills the other side of the ellipse. Is it what you expected?
I also added an example on how to use a colormap. There is wide range of colormaps, many of them ranging from 0 to 255 (up to you to normalize to 0.0-1.0).
Another solution could be to draw overlapping ellipses and use the clip_path attribute to only show the required part.
Edit after comment
To fit perfectly the ellipse you have to calculate you arcs to fit it, which is probably difficult (maybe some nice math tricks allow it, but it is out of my knowledge). So my best bet is to draw multiple ellipses of the same size and use clipping paths.
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()
This will draw you:
- A big black elliptic line (the base ellipse)
- Two hatched (one with dots, one with circles) sub-area inside your base ellipse
- Thin dotted lines, outlining your clipping paths
You say that you want to "solid fill the ellipse. Not use patch". To solid fill, just play with the fill
and color
arguments of your areas (and remove hatch
if you don't want it). However, I don't see how you can achieve what you want without "patch". Sorry.
Hope this help.
Edit 2
After clarifications here is a new version:
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()
And the resulting image:
How does it works is:
- by drawing multiple ellipses perfectly superposed to the "base" one. This make sure that all your areas are perfectly fitted inside your base ellipse (none of them can outreach)
- by defining clipping paths that will crop/clip the ellipses to just show part of them. These clipping paths have to cross (or at least follow) the base ellipse borders to make it filled (that's why I change your Arc3, using the ellipse center and dimensions, else there was a white space on the border, inside the ellipse, as you can see on the first figure).
You were not so far with your last try. To style your areas, you have to apply style to your base ellipses (listed in the areas
list in my code), not to your clipping paths (listed in the clips
list in my code). Both lists, areas
and clips
need to have the same length! Up to you to add more areas and more clipping paths if you want more sub-areas in your ellipses.
By the way, I see your updated question and your requirements to make the areas not overlapping. This will require more work. You can achieve it by building more complicated clipping path, using the matplotlip.path module.
来源:https://stackoverflow.com/questions/58263608/fill-between-arc-patches-matplotlib