Skip to content

[ENH]: fill_between extended to 3D #28225

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

Merged
merged 2 commits into from
Jun 17, 2024

Conversation

scottshambaugh
Copy link
Contributor

@scottshambaugh scottshambaugh commented May 14, 2024

PR summary

Closes #28142

This extends the 2D fill_between method to 3D plots, and IMO works quite nicely. I’m finding it really powerful to generate surfaces easily. Inspired by @artmenlope's solution here: https://github.com/artmenlope/matplotlib-fill_between-in-3D/blob/master/FillBetween3d.py

Here's the what's new image and fillbetween3d gallery example:
whatsnew

Note that I moved the poly3d example https://matplotlib.org/3.9.0/gallery/mplot3d/polys3d.html to a new example fillunder3d, to use this new method. Then I made a new example for poly3d since adding 3D collections wasn't shown anywhere else afaik.

New fillunder3d example:
fillbetween3d

New poly3d example:
whatsnewpoly

PR checklist

@scottshambaugh
Copy link
Contributor Author

scottshambaugh commented May 14, 2024

The white seams between the patches seems to be an instance of #1188, which I don't see a bug report for in 3D rendering. Is rather annoying but doesn't seem like there are easy fixes.

@scottshambaugh scottshambaugh added this to the v3.10.0 milestone May 14, 2024
@github-actions github-actions bot added the Documentation: API files in lib/ and doc/api label May 14, 2024
@scottshambaugh
Copy link
Contributor Author

Note that I decided to not implement the "mode 2" idea in https://github.com/artmenlope/matplotlib-fill_between-in-3D/blob/master/FillBetween3d.py, which concatenates all the points and plots them as one polygon. I think the only benefit is getting rid of the white seams between the patches in the filled flat curves example, but it is degenerate in the general case, and this use case can be handled by users defining their own polygons such as in the new poly3d example.

For example, Mode 1 / Mode 2:
image

@github-actions github-actions bot added the Documentation: plot types files in galleries/plot_types label May 17, 2024
@scottshambaugh
Copy link
Contributor Author

scottshambaugh commented May 17, 2024

Here's the new plot types thumbnail showing a double helix:

image

@scottshambaugh scottshambaugh force-pushed the 3d_fill_between branch 2 times, most recently from 779b177 to 76eff4a Compare May 17, 2024 14:29
Copy link
Member

@timhoffm timhoffm left a comment

Choose a reason for hiding this comment

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

Only doc suggestions.

Copy link
Contributor

@greglucas greglucas left a comment

Choose a reason for hiding this comment

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

Requesting changes just so someone doesn't merge without my comment being addressed.

I think "mode 1" vs "mode 2" should be investigated here, and I'd like to see the new test case with the "mode 2" version as well to see if that applies the edge color to only the rim/boundary of the single fill_between region. Naively thinking about it that would be my preference for how to draw these, but I also haven't given it a ton of thought.

@scottshambaugh
Copy link
Contributor Author

scottshambaugh commented Jun 14, 2024

@greglucas I've added the "mode 2" single_polygon flag, and the fillunder3d example renders nicely as single polygons without artifacts now. Test also added for that.
image

@scottshambaugh scottshambaugh force-pushed the 3d_fill_between branch 3 times, most recently from 9c89b67 to ad23551 Compare June 14, 2024 14:29
@greglucas greglucas dismissed their stale review June 14, 2024 20:17

comments have been addressed

@greglucas greglucas requested a review from timhoffm June 14, 2024 20:17
@greglucas
Copy link
Contributor

Re-requesting @timhoffm's review because it is quite a bit different now with a new keyword argument now and he might have opinions on that from the API standpoint.

Copy link
Member

@timhoffm timhoffm left a comment

Choose a reason for hiding this comment

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

Please change single_polygon to mode. I have no clear preference on its default.

@scottshambaugh
Copy link
Contributor Author

Changed the single_polygon flag to a mode kwarg, and fixed the where behavior for the 'polygon' mode.

@scottshambaugh
Copy link
Contributor Author

scottshambaugh commented Jun 16, 2024

We could try to get smart about 'auto' selecting 'polygon' when all the points lie on a 2D plane, but I'm not sure a robust way to do that. A simple case I think would be if one of (x1 == x2), (y1 == y2), (z1 == z2) is True? Trying to think through potential edge cases there.

@scottshambaugh
Copy link
Contributor Author

scottshambaugh commented Jun 16, 2024

The more I think about it, the more I like the optimization to map auto to polygon when all the points are on an x, y, or z plane. It doesn't cover all the cases of points on an arbitrary plane, but it will cover what I think will be the most common ones, and the user can always choose the mode directly. Added that in.

@timhoffm timhoffm dismissed their stale review June 16, 2024 06:03

Handled

Copy link
Member

@timhoffm timhoffm left a comment

Choose a reason for hiding this comment

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

We may also want to think about shade. It's deactivated by default for Poly3dCollection, but activated by default for cases where the drawing is a geometrical object, e.g. in plot_surface() if it's a single color.

Your quad example (at least without edgecolor) could benefit from shading. Without having this thought through completely 'quad' likely wants it on by default, 'polygon' possibly wants it of - there's no shading within the single polygon, but OTOH one could make the polygon example single color and use depth shading to create visual distinction.

Please think about what's the best solution. The default may depend on mode. In any case, I think it's valuable to mention it in the docstring.

had_data = self.has_data()
x1, y1, z1, x2, y2, z2 = cbook._broadcast_with_masks(x1, y1, z1, x2, y2, z2)
if mode == 'auto':
if np.all(x1 == x2) or np.all(y1 == y2) or np.all(z1 == z2):
Copy link
Contributor

Choose a reason for hiding this comment

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

The more I think about it, the more I like the optimization to map auto to polygon when all the points are on an x, y, or z plane. It doesn't cover all the cases of points on an arbitrary plane, but it will cover what I think will be the most common ones, and the user can always choose the mode directly.

How hard would it be to generalize to "if all points lie in a plane"? You could construct a plane from the first 3 points and then calculate a dot product for all following to check distance to that plane and it wouldn't be too bad.

Copy link
Contributor Author

@scottshambaugh scottshambaugh Jun 17, 2024

Choose a reason for hiding this comment

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

@greglucas I added art3d._all_points_on_plane() to check for this, it's a little involved to catch the edge cases - what do you think?

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry for sending you down the rabbit hole 😂 . I think it is great! and I really appreciate that you checked the edge cases. Maybe it is a little involved, but seems worth it to me to generalize the statement to any plane. It is only a one-time performance hit when creating the collection initially and it is all numpy-vectorized so it isn't that terrible.

fill_between in plot types

Apply suggestions from code review

Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>

fill_between single_polygon flag

3D fill_between auto mode maps to polygon when all points lie on a x, y, or z plane

3D fill_between auto mode maps to polygon when all points lie on a x, y, or z plane

Code review comments

fill_between 3d shading

fill_between 3d shading
@scottshambaugh
Copy link
Contributor Author

@timhoffm I do like the addition of the shade parameter, and think it should be on by default for quad and off by default for polygon. AFAIK depthshading is for Patch3DCollection we don't have it built in for Poly3DCollections?

Here's the top example with shading and no transparency:
image

@scottshambaugh scottshambaugh force-pushed the 3d_fill_between branch 2 times, most recently from 731b26f to d84eef1 Compare June 17, 2024 03:32
Copy link
Contributor

@greglucas greglucas left a comment

Choose a reason for hiding this comment

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

I think this looks good now and there is nice auto-handling of all the various cases.

had_data = self.has_data()
x1, y1, z1, x2, y2, z2 = cbook._broadcast_with_masks(x1, y1, z1, x2, y2, z2)
if mode == 'auto':
if np.all(x1 == x2) or np.all(y1 == y2) or np.all(z1 == z2):
Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry for sending you down the rabbit hole 😂 . I think it is great! and I really appreciate that you checked the edge cases. Maybe it is a little involved, but seems worth it to me to generalize the statement to any plane. It is only a one-time performance hit when creating the collection initially and it is all numpy-vectorized so it isn't that terrible.

@scottshambaugh
Copy link
Contributor Author

CI failures are unrelated

@timhoffm timhoffm merged commit 167a26e into matplotlib:main Jun 17, 2024
36 of 42 checks passed
@timhoffm
Copy link
Member

Thanks @scottshambaugh. Great addition!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Documentation: API files in lib/ and doc/api Documentation: examples files in galleries/examples Documentation: plot types files in galleries/plot_types New feature topic: mplot3d
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[ENH]: Add fill between support for 3D plots
3 participants