-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
[Bug]: matplotlib.path.Path.to_polygons fails with TriContourSet paths #25114
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I'd recommended fixing this by setting the CLOSEPOLY codes in the C++ tricontour code somewhere near Line 745 in 5ec2bd2
|
Made some further tests with a simpler case, just generating a Path with one hole and performing path.to_polygons(). Same condition as paths returned by Apparently if the total number of vertexes defining the path is below 128 the import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
def path2polygons(path):
"""My solution to convert the matplotlib Path to a set of polygons."""
assert isinstance(path, mpl.path.Path), f"Type of input is {type(path)} instead of Path"
codesOld = path.codes[path.codes != mpl.path.Path.CLOSEPOLY]
vertsOld = path.vertices[path.codes != mpl.path.Path.CLOSEPOLY]
jj = np.append(np.flatnonzero(codesOld == path.MOVETO), [len(codesOld)])
verts, codes = [], []
for ji, je in zip(jj[:-1], jj[1:]):
codes.extend( list(codesOld[ji:je]) + [mpl.path.Path.CLOSEPOLY] )
verts.extend( list(vertsOld[ji:je]) + [vertsOld[ji]] )
return mpl.path.Path(verts, codes).to_polygons()
## WORKS
nout = 100
nint = 27
verts = list(zip(np.cos(2*np.pi*np.linspace(0, 1-1./nout, nout)), np.sin(2*np.pi*np.linspace(0, 1-1./nout, nout))))
codes = [mpl.path.Path.MOVETO] + [mpl.path.Path.LINETO]*(nout-1)
verts += list(zip(.2*np.cos(2*np.pi*np.linspace(0, 1-1./nint, nint))[::-1], .4*np.sin(2*np.pi*np.linspace(0, 1-1./nint, nint))[::-1]))
codes += [mpl.path.Path.MOVETO] + [mpl.path.Path.LINETO]*(nint-1)
print(len(verts))
path = mpl.path.Path(verts, codes)
patch = mpl.patches.PathPatch(path, facecolor='turquoise', alpha=.3)
plt.close('all')
plt.gca().add_patch(patch)
[plt.plot(*list(zip(*p)), c='orange', ls='-') for p in path.to_polygons()]
[plt.plot(*list(zip(*p)), c='red', ls='--') for p in path2polygons(path)]
plt.gca().set_title(f"vertices in path: {len(verts)}, verdict: OK")
plt.savefig("bug_path2polygons_127vertsOk.png")
plt.show()
## DOES NOT WORK
nout = 100
nint = 28
verts = list(zip(np.cos(2*np.pi*np.linspace(0, 1-1./nout, nout)), np.sin(2*np.pi*np.linspace(0, 1-1./nout, nout))))
codes = [mpl.path.Path.MOVETO] + [mpl.path.Path.LINETO]*(nout-1)
verts += list(zip(.2*np.cos(2*np.pi*np.linspace(0, 1-1./nint, nint))[::-1], .4*np.sin(2*np.pi*np.linspace(0, 1-1./nint, nint))[::-1]))
codes += [mpl.path.Path.MOVETO] + [mpl.path.Path.LINETO]*(nint-1)
print(len(verts))
path = mpl.path.Path(verts, codes)
patch = mpl.patches.PathPatch(path, facecolor='turquoise', alpha=.3)
plt.close('all')
plt.gca().add_patch(patch)
[plt.plot(*list(zip(*p)), c='orange', ls='-') for p in path.to_polygons()]
[plt.plot(*list(zip(*p)), c='red', ls='--') for p in path2polygons(path)]
plt.gca().set_title(f"vertices in path: {len(verts)}, verdict: FAILURE")
plt.savefig("bug_path2polygons_128vertsFail.png")
plt.show()
## WORKS
nout = 200
nint = 100
verts = list(zip(np.cos(2*np.pi*np.linspace(0, 1, nout)), np.sin(2*np.pi*np.linspace(0, 1, nout))))
codes = [mpl.path.Path.MOVETO] + [mpl.path.Path.LINETO]*(nout-2) + [mpl.path.Path.CLOSEPOLY]
verts += list(zip(.2*np.cos(2*np.pi*np.linspace(0, 1, nint))[::-1], .4*np.sin(2*np.pi*np.linspace(0, 1, nint))[::-1]))
codes += [mpl.path.Path.MOVETO] + [mpl.path.Path.LINETO]*(nint-2) + [mpl.path.Path.CLOSEPOLY]
print(len(verts))
path = mpl.path.Path(verts, codes)
patch = mpl.patches.PathPatch(path, facecolor='turquoise', alpha=.3)
plt.close('all')
plt.gca().add_patch(patch)
[plt.plot(*list(zip(*p)), c='orange', ls='-') for p in path.to_polygons()]
[plt.plot(*list(zip(*p)), c='red', ls='--') for p in path2polygons(path)]
plt.gca().set_title(f"ensuring CLOSEPOLY, vertices in path: {len(verts)}, verdict: OK")
plt.savefig("bug_path2polygons_300vertsOk.png")
plt.show() |
Thanks @cvr |
Bug summary
The Path objects produced by tricontourf to draw its contours lead to spurious polygons when running the respective
to_polygons
method.Code for reproduction
Actual outcome
Expected outcome
Additional information
The issue seems to be related to the Path object and its
to_polygons
method. More specifically the problem may happen when a Path contains multiple polylines but is defined without theCLOSEPOLY
codes ending each polyline. When producing contour plots the code can cope with this data without problems, but theto_polygons
method cannot.I have tested in both 3.4.1 and 3.0.2 versions and both have the same problem.
I have a fix where I defined a function
path2polygons
:but is not tested with other Path codes such as
CURVE3
andCURVE4
.Operating system
Debian 10
Matplotlib Version
3.4.1
Matplotlib Backend
TkAgg
Python version
3.7.3
Jupyter version
No response
Installation
pip
The text was updated successfully, but these errors were encountered: