Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Handle MOVETO's, CLOSEPOLY's and empty paths in Path.interpolated#29919

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
timhoffm merged 3 commits intomatplotlib:mainfromrcomer:geo-contour
Apr 18, 2025

Conversation

rcomer
Copy link
Member

@rcomerrcomer commentedApr 15, 2025
edited
Loading

PR summary

Fixes#29917. The geo projection's path transform relies onPath.interpolated

deftransform_path_non_affine(self,path):
# docstring inherited
ipath=path.interpolated(self._resolution)
returnPath(self.transform(ipath.vertices),ipath.codes)

At v3.8ContourSet changed from having one path per contour to one path per level, and so its paths can now contain internalMOVETO's. We can make theinterpolated method handle those by just splitting the path up, applying itself to each sub-path, and re-joining the result. The example code provided in#29917 now gives

Figure_1

Forcontour the given example also had one or more empty paths which caused an exception in the interpolation. We can just pass those straight out again.

Figure_1

I welcome better wording for the docstring, though I do not think the original was correct since a unclosed path with N vertices would have become a path with(N-1) * steps + 1 vertices.

PR checklist

@@ -667,13 +667,27 @@ def intersects_bbox(self, bbox, filled=True):

def interpolated(self, steps):
"""
Return a new pathresampled to length N x *steps*.
Return a new pathwith each `LINETO` resampled to *steps* `LINETO`'s.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Suggested change
Returnanewpathwitheach`LINETO`resampledto*steps*`LINETO`'s.
Returnanewpathwitheachsegmentisdividedinto*steps*parts.

Note that "segment" is the term for one "step" (c.f.https://matplotlib.org/devdocs/api/path_api.html#matplotlib.path.Path.iter_segments)

Edit: I've just read one maybe "each LINETO step ..."


Codes other than `LINETO` are not handled correctly.
Codes other than `LINETO` and `MOVETO` are not handled correctly.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Not checked, but what about CLOSEPOLY? That should also be sub sampled.

Copy link
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

It can go wrong. Adapting the example fromhttps://matplotlib.org/stable/gallery/shapes_and_collections/compound_path.html

importmatplotlib.pyplotaspltfrommatplotlib.patchesimportPathPatchfrommatplotlib.pathimportPathcodes= [Path.MOVETO]+ [Path.LINETO]*2+ [Path.CLOSEPOLY]vertices= [(4,4), (5,5), (5,4), (0,0)]path=Path(vertices,codes)pathpatch=PathPatch(path,facecolor='none',edgecolor='blue',linewidth=3,label='original')pathpatch2=PathPatch(path.interpolated(2),facecolor='none',edgecolor='red',linewidth=3,linestyle='dashed',label='interpolated')fig,ax=plt.subplots()ax.add_patch(pathpatch)ax.add_patch(pathpatch2)ax.autoscale_view()ax.legend()plt.show()

image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

The vertex corresponding toCLOSEPOLY is not correctly ignored here; it should be interpolated from the previous point to the first point in the path (i.e., the most recentMOVETO or the first point in the whole thing.)

@rcomerrcomer changed the titleHandle MOVETO's in Path.interpolatedHandle MOVETO's and CLOSEPOLY's in Path.interpolatedApr 16, 2025
Comment on lines 691 to 692
if self.codes[-1] == self.CLOSEPOLY and not np.all(self.vertices[-1] ==
self.vertices[0]):
Copy link
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I am assuming that after splitting byMOVETO, anyCLOSEPOLY will be at the end of the path. Is that correct?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

It looks like_iter_connected_components only splits onMOVETO, so it wouldn't noticeCLOSEPOLY. I'm not sure what happens to aPath withCLOSEPOLY stuck in the middle.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

A CLOSEPOLY the middle can only happen if it was not followed by a MOVETO in the original path. But that means you closed a shape, and continue to draw from that position. People would likely just use LINETOs in such a case, But AFAIK there's nothing in the rules prohibitingLINETO, CLOSEPOLY, LINETO, and there could be rare cases where it makes sense/could happen.

That said, I haven't checked what we actually do here and whether that pattern renders reasonably.

Copy link
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Hmmmm

importmatplotlib.pyplotaspltfrommatplotlib.patchesimportPathPatchfrommatplotlib.pathimportPathcodes= [Path.MOVETO]+ ([Path.LINETO]*2+ [Path.CLOSEPOLY])*2vertices= [(4,4), (5,5), (5,4), (0,0), (3,3), (4,3), (0,0)]path=Path(vertices,codes)pathpatch=PathPatch(path,facecolor='none',edgecolor='blue',linewidth=3,label='original')pathpatch2=PathPatch(path.interpolated(2),facecolor='none',edgecolor='red',linewidth=3,linestyle='dashed',label='interpolated')fig,ax=plt.subplots()ax.add_patch(pathpatch)ax.add_patch(pathpatch2)ax.autoscale_view()ax.legend()plt.show()

Figure_1

It seems that the rendering at least handles internalCLOSEPOLY's. So probably we should handle them here too.

Copy link
MemberAuthor

@rcomerrcomerApr 17, 2025
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

With latest update

Figure_1

Edit: given the axes limits here go down beyond (0,0), I guess that whatever tellsautoscale the extent of the path also does not know to ignoreCLOSEPOLY.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Edit: given the axes limits here go down beyond (0,0), I guess that whatever tellsautoscale the extent of the path also does not know to ignoreCLOSEPOLY.

I'd say this is a separate topic. Without checking: I suspect that autoscale just takes all the points, no matter what the associated code is (likely also the control points for curve).

At least forCLOSEPOLY people can work around this by using existing coordinates.

Copy link
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I'd say this is a separate topic.

Sure, that was very much an aside - not proposing to investigate it here.

@rcomerrcomer added this to thev3.11.0 milestoneApr 16, 2025
Copy link
Contributor

@greglucasgreglucas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This seems like it could be considered a bug-fix and backported?

story645 reacted with thumbs up emoji


def test_interpolated_closepoly():
codes = [Path.MOVETO] + [Path.LINETO]*2 + [Path.CLOSEPOLY]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Test a compound path with multiple closepolys by doubling this again similar to your above test to make sure the implementation handles multiple MOVETOs and CLOSEPOLYS together?

@rcomer
Copy link
MemberAuthor

This seems like it could be considered a bug-fix and backported?

I could go either way. I figured it's a New Feature since we are making a public method do something it didn't before (and the docstring stated it didn't). OTOH this clearly fixes a bug for the geo projections, and I'll be astonished if anyone is relying on these codesnot being supported.

@rcomer
Copy link
MemberAuthor

I just realized that this does not in fact close#29917. I had not checked unfilled contours, but they are even more broken than filled contours.

@rcomerrcomer marked this pull request as draftApril 17, 2025 14:00
@rcomer
Copy link
MemberAuthor

rcomer commentedApr 17, 2025
edited
Loading

OK the problem with the unfilled contours is just that it has an empty path, so ended up at

  File "/[path-to-git-repo]/matplotlib/lib/matplotlib/cbook.py", line 914, in simple_linear_interpolation    fps = a.reshape((len(a), -1))          ^^^^^^^^^^^^^^^^^^^^^^^ValueError: cannot reshape array of size 0 into shape (0,newaxis)

This is an easy fix, though I am slightly worried that different input data could turn up different bugs.

@rcomerrcomer changed the titleHandle MOVETO's and CLOSEPOLY's in Path.interpolatedHandle MOVETO's and CLOSEPOLY's and empty paths in Path.interpolatedApr 17, 2025
@rcomerrcomer marked this pull request as ready for reviewApril 17, 2025 14:41
if self.codes is not None and self.CLOSEPOLY in self.codes and not np.all(
self.vertices[self.codes == self.CLOSEPOLY] == self.vertices[0]):
vertices = self.vertices.copy()
vertices[self.codes == self.CLOSEPOLY] = vertices[0]
Copy link
Member

@timhoffmtimhoffmApr 17, 2025
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I think this patch is not sufficient if you have a path with mixtures of CLOSEPOLY AND MOVETO. The MOVETO creates a new separate separate component with a starting point that is generally different fromvertices[0].

Maybe a variation of_iter_connected_components is the way to go?

Copy link
MemberAuthor

@rcomerrcomerApr 17, 2025
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

But if we got to here, we have no internalMOVETO's because we already split on them and called the function again on the subpath. I think this case is covered by the newtest_interpolated_moveto_closepoly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Yes, you are right.

@timhoffm
Copy link
Member

I don't have a strong opinion on 3.10.2 vs 3.11. Either is ok, and their releases will likely not be far apart.

@rcomer
Copy link
MemberAuthor

OK, I think there are two votes for v3.10.2 and none against, so I removed the whatsnew.

@rcomerrcomer changed the titleHandle MOVETO's and CLOSEPOLY's and empty paths in Path.interpolatedHandle MOVETO's, CLOSEPOLY's and empty paths in Path.interpolatedApr 18, 2025
@rcomerrcomer added the PR: bugfixPull requests that fix identified bugs labelApr 18, 2025
@timhoffmtimhoffm merged commit5ff6a93 intomatplotlib:mainApr 18, 2025
45 checks passed
meeseeksmachine pushed a commit to meeseeksmachine/matplotlib that referenced this pull requestApr 18, 2025
@rcomerrcomer deleted the geo-contour branchApril 18, 2025 11:58
oscargus added a commit that referenced this pull requestApr 18, 2025
…919-on-v3.10.xBackport PR#29919 on branch v3.10.x (Handle MOVETO's, CLOSEPOLY's and empty paths in Path.interpolated)
@ksundenksunden mentioned this pull requestMay 9, 2025
5 tasks
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@QuLogicQuLogicQuLogic left review comments

@oscargusoscargusoscargus left review comments

@timhoffmtimhoffmtimhoffm approved these changes

@greglucasgreglucasgreglucas approved these changes

Assignees
No one assigned
Labels
PR: bugfixPull requests that fix identified bugstopic: path handling
Projects
None yet
Milestone
v3.10.3
Development

Successfully merging this pull request may close these issues.

[Bug]: Contour plots using mollweide-projection
5 participants
@rcomer@timhoffm@QuLogic@oscargus@greglucas

[8]ページ先頭

©2009-2025 Movatter.jp