Matplotlib patch with holes

2024/9/20 0:53:10

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.MOVETOvertices = []
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 numpyclockwise_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.MOVETOvertices = []
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.MOVETOif 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()
Answer

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)
https://en.xdnf.cn/q/72388.html

Related Q&A

Convert sha256 digest to UUID in python

Given a sha256 hash of a str in python: import hashlibhash = hashlib.sha256(foobar.encode(utf-8))How can the hash be converted to a UUID? Note: there will obviously be a many-to-one mapping of hexdige…

Drag and Drop QLabels with PyQt5

Im trying to drag and drop a Qlabel on another Qlabel with PyQt5:from PyQt5.QtWidgets import QApplication, QWidget, QToolTip, QPushButton, QMessageBox, QHBoxLayout, QVBoxLayout, QGridLayout,QFrame, QCo…

replace block within {{ super() }}

I have a base template which includes a block for the default <head> content. Within the head block, theres a block for the <title>.For example, in the base file I would have:<head>{%…

Change Timezone for Date object Python

Hello I am using Pythonanywhere and when I call from datetime import *print date.today().dayIt is printing a different day than the day it is where I live (Austin, Texas). I figured it is because there…

Multiprocessing Pool - how to cancel all running processes if one returns the desired result?

Given the following Python code: import multiprocessingdef unique(somelist):return len(set(somelist)) == len(somelist)if __name__ == __main__:somelist = [[1,2,3,4,5,6,7,8,9,10,11,12,13,2], [1,2,3,4,5],…

Pandas: Product of specific columns

Finding the product of all columns in a dataframe is easy:df[Product] = df.product(axis=1)How can I specify which column names (not column numbers) to include in the product operation?From the help pa…

instagram.bind.InstagramClientError: Unable to parse response, not valid JSON

Made myself a simple Instagram client to make authenticated requests to their API. However, running it keeps throwing the following errorTraceback (most recent call last):File "request-ig-data.py&…

Using jinja to send data to Javascript

I have Python code, in which Im using jinja to send data to a template in Flask. I can access the code just find in HTML, but when I try displaying the data in Javascript, it doesnt work. For example, …

celery: Substantial drift from

I have quite a problem with celery on my distribted system. I have couple of machines among different localizations and Ive got a lot of warnings in my log files like:"Substantial drift from celer…

jinja2 link to static files

I am trying to understand how to create a link to static files in jinja2.Everything I look up relates to Flask whereas I am using just webapp2 at this stage.My main.py file looks as follows:import os i…