The following code works. The problem is I dont know exactly why it works. The code draws a circle patch (using PathPatch) with a triangle cutout from the centre. My guess is that the inner triangle is cut out because it is drawn clockwise while the outer circle is drawn counter-clockwise. The triangle would not get cutout if the direction were not reversed. I have not found anything in the documentation about the rule employed. So why does this work?
from matplotlib import pyplot
from matplotlib.path import Path
from matplotlib.patches import PathPatch
import numpy
#
# draw a triangle within a circle using PathPatch
#
phi = numpy.linspace(0, 2*numpy.pi, 100)
circle = 4 * numpy.exp(1j * phi)
circleV = [[p.real, p.imag] for p in circle]
phi = numpy.linspace(0, 2*numpy.pi, 4)
triangle = 2 * numpy.exp(1j * phi)
triangleV = [[p.real, p.imag] for p in triangle]
circleC = [Path.LINETO for p in circleV]
circleC[0] = Path.MOVETO
triangleC = [Path.LINETO for p in triangleV]
triangleC[0] = Path.MOVETO
vertices = []
vertices.extend(circleV)
vertices.extend(triangleV[::-1])
codes = []
codes.extend(circleC)
codes.extend(triangleC)
path = Path(vertices, codes)
patch = PathPatch(path, facecolor="#aa6677")
fig, ax = pyplot.subplots()
ax.add_patch(patch)
ax.set_xlim([-5, 5])
ax.set_ylim([-5, 5])
ax.set_aspect(1.0)
pyplot.show()
Now as a more complicated example, a pentagon is cutout from the main circle and small circles are also cutout from the main circle that partially intersect the pentagon. If the small circles are drawn clockwise, they are filled where they intersect the pentagon and not filled where they do not. This sort of agrees with the rule above. However if they are drawn counter-clockwise, they are completely filled, which does not agree with the rule above.
from matplotlib import pyplot
from matplotlib.path import Path
from matplotlib.patches import PathPatch
import numpy
clockwise_inner_circles = True
#
# draw a pentagon within a circle using PathPatch
#
phi = numpy.linspace(0, 2*numpy.pi, 100)
circle = 4 * numpy.exp(1j * phi)
circleV = [[p.real, p.imag] for p in circle]
phi = numpy.linspace(0, 2*numpy.pi, 6)
triangle = 2 * numpy.exp(1j * phi)
triangleV = [[p.real, p.imag] for p in triangle]
circleC = [Path.LINETO for p in circleV]
circleC[0] = Path.MOVETO
triangleC = [Path.LINETO for p in triangleV]
triangleC[0] = Path.MOVETO
vertices = []
vertices.extend(circleV)
vertices.extend(triangleV[::-1])
codes = []
codes.extend(circleC)
codes.extend(triangleC)
#
# draw circles in a circular pattern
#
phi = numpy.linspace(0, 2*numpy.pi, 100)
for theta in 2*numpy.pi*numpy.arange(5)/5:
circle = 2*numpy.exp(1j*theta) + 0.5*numpy.exp(1j*phi)
circleV = [[p.real, p.imag] for p in circle]
circleC = [Path.LINETO for p in circleV]
circleC[0] = Path.MOVETO
if clockwise_inner_circles:
vertices.extend(circleV[::-1])
else:
vertices.extend(circleV[::1])
codes.extend(circleC)
path = Path(vertices, codes)
patch = PathPatch(path, facecolor="#aa6677")
fig, ax = pyplot.subplots()
ax.add_patch(patch)
ax.set_xlim([-5, 5])
ax.set_ylim([-5, 5])
ax.set_aspect(1.0)
pyplot.show()
Based on your first example, I have added more comments, which should be clear. The key for PathPath is to get vertices and codes. More details about vertices and codes can be found here.
Although this is pretty old question, but I hope my answer here can be useful for people who need it.
from matplotlib import pyplot
from matplotlib.path import Path
from matplotlib.patches import PathPatch
import numpy
# ##########################################
# draw a triangle within a circle using PathPatch
# ##########################################
phi = numpy.linspace(0, 2*numpy.pi, 100)
circle = 4 * numpy.exp(1j * phi)
## generate circle vertices --> "circleV"
circleV = [[p.real, p.imag] for p in circle]
phi = numpy.linspace(0, 2*numpy.pi, 4)
triangle = 2 * numpy.exp(1j * phi)
## generate triangle vertices --> "triangleV"
triangleV = [[p.real, p.imag] for p in triangle]
# generate codes for patch, "C" means "codes"
# codes for circle
circleC = [Path.LINETO for p in circleV]
circleC[0] = Path.MOVETO
# codes for triangle
triangleC = [Path.LINETO for p in triangleV]
triangleC[0] = Path.MOVETO
# combine vertices
vertices = []
vertices.extend(circleV)
vertices.extend(triangleV[::-1])
# combine codes
codes = []
codes.extend(circleC)
codes.extend(triangleC)
# create Path object from vertices and codes
path = Path(vertices, codes)
# create patch from path
patch = PathPatch(path, facecolor="#aa6677")
# plot fig and add patch
fig, ax = pyplot.subplots()
ax.add_patch(patch)
ax.set_xlim([-5, 5])
ax.set_ylim([-5, 5])
ax.set_aspect(1.0)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With