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

Add hatchcolor parameter for Collections#29044

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 16 commits intomatplotlib:mainfromImpaler343:collections-hatchcolor
Mar 31, 2025

Conversation

Impaler343
Copy link
Contributor

@Impaler343Impaler343 commentedOct 30, 2024
edited
Loading

PR summary

Follow on PR to#28104 helping to fix issues#26074 and#7059
Addedhatchcolor parameter for Collections, to be able to separately controledgecolors andhatchcolors.

The fallback logic forhatchcolor is identical to the previous PR, where it follows this precedence order:

hatchcolor parameter ->hatch.color rcParam -> inherit fromedgecolors ifhatch.color isedge -> default topatch.edgecolor rcParam ifedgecolors is not specified.

  1. Ifhatchcolor parameter is specified, it will take precedence overhatch.color rcParam andedgecolors.
  2. Ifhatchcolor is not specified, it will try to usehatch.color rcParam. If this rcParam is a valid color, it will be used for the hatches.
  3. Ifhatch.color rcParam isedge, the hatches will inherit the color fromedgecolors if it is specified.
  4. Ifedgecolors is not specified, the hatches will default topatch.edgecolor rcParam.
  • Need to rewrite the failing test(s)
  • Need to write tests
  • What's New note
  • Example demos

PR checklist

story645 reacted with thumbs up emoji
@story645
Copy link
Member

Thanks for keeping at this, does#28104 need to be merged first?

@Impaler343
Copy link
ContributorAuthor

Yes, preferably, so that the changes for Patches can be accurately replicated for Collections

@r3kste
Copy link
Contributor

There is currently one test which is failing and might need a rewrite:lib/matplotlib/tests/test_axes.py::test_contour_hatching

Previous Behavior

In collections, hatchcolor is set along with edgecolor.

In the cases whereedgecolors is explicitly specified by the user,hatchcolor is set to the first color in edgecolors and it also uses the alpha value specified by the user. However, ifedgecolors is not specified,hatchcolor usesmpl.rcParams['hatch.color'] for the color, but it doesn't use the alpha value specified by the user.

New Behavior

The new implementation in this PR has hatchcolor separated from edgecolor and it uses the alpha value specified by the user, regardless of whether edgecolors is specified or not.

Reason for New Behavior

alpha works onhatchcolor whenedgecolors is specified. Therefore, I believe that it should also work whenedgecolors is not specified.

In the above test,alpha is set to 0.5. Ideally, the hatch should have an alpha value of 0.5, but it has the default alpha value of 1.0.

@Impaler343
Copy link
ContributorAuthor

pinging@story645 for review

@r3kste
Copy link
Contributor

The PDF test fortest_axes.py::test_contour_hatching isn't failing, so the baseline PDF is left untouched. I suspect that this is because alpha on hatches on PDF backend isn't completely implemented and it looks like#17049 aims to fix this.

Copy link
Member

@timhoffmtimhoffm left a comment
edited
Loading

Choose a reason for hiding this comment

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

It's somewhat unfortunate that we have to put the parameter at the end of each function signature and make it optional on all lower-level functions. But I suppose that is the price we have to pay to keep API backward compatibility. I would have to check how public all this is and whether we could eventually migrate to a cleaner API.

@r3kste
Copy link
Contributor

AFAICS you haven't modified the test. This would mean, whe have a change of default behavior. Can that be avoided?

I think that we can't avoid this change, because the default behavior is somewhat inconsistent, as explained in thiscomment.

(2) I suggest to modifying the test instead (likely by setting an explicit hatch color) so that the tests images do not need update. Generally, we try to use as few images as possible and changes them as a rarely as possible, because the images bloat the git repo.

I think it might be better to update the images, because it looks like the test was previously testing the incorrect behavior. I think that it is better to have the test images reflect the correct behavior, as this test covers the behavior of hatchcolors with alpha values more comprehensively than if we change the test to explicitly set the hatch color to black with alpha = 1.0.

When explicitly setting hatchcolor, I wasn't able to force alpha as 1.0 for hatchcolor without using_hatchcolors attribute.

diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.pyindex 38857e846c..5764d3fff6 100644--- a/lib/matplotlib/tests/test_axes.py+++ b/lib/matplotlib/tests/test_axes.py@@ -2651,6 +2651,9 @@ def test_contour_hatching():                 cmap=mpl.colormaps['gray'],                 extend='both', alpha=0.5)+    for c in ax.collections:+        c._hatchcolors = mpl.colors.to_rgba_array('black', 1.0)+  @image_comparison(     ['contour_colorbar'], style='mpl20',

Here is why I think that the test was previously testing the incorrect behavior:
For a stackplot, with alpha = 0.3

Previous BehaviorNew Behavior (PR)
edgecolor = 'black'prev_blacknew_black
edgecolor = Noneprev_nonenew_none

@story645
Copy link
Member

Do these changes need to be applied back to the changes in#28104 and should factoring out into a global context object + versioning be their own PR(s)?

@timhoffm
Copy link
Member

No,#28104 is not affected, because it's not changing the backend API.

story645 reacted with thumbs up emoji

@anntzer
Copy link
Contributor

I think that if we want to move this PR forward, the realistic short-term approach is to do API support sniffing via the empty call (and not even warn on third-parties for now, at least until we can agree on the long-term plan and likely also add support for multiple hatches).

r3kste, story645, and timhoffm reacted with thumbs up emoji

@anntzer
Copy link
Contributor

Thanks@Impaler343 for adding the API sniffing. Unfortunately this is not enough, because you choose to just not pass hatchcolors at all if they are unsupported. While the idea of just drawing single-color hatches on 3rd-party backends may be reasonable (I'm fine with that), it means that these backends will try to consult rcParams["hatch.color"] to select a color; and since#28104 they can see the new unnormalized value "edge", for which there was no support before, and they will crash there. At least this value should be normalized (e.g. to the first edgecolor, I guess); ideally if hatchcolors is not supported, the draw_path_collection call should be expanded as in ContourSet.draw to draw one path at a time.

r3kste and Impaler343 reacted with thumbs up emoji

@anntzer
Copy link
Contributor

This works now on mplcairo. Thanks for going through all the work!
I haven't carefully reviewed the implementation yet but the general approach looks fine to me. Perhaps add a note that the current new API is provisional, as I expect that it will change in the future at least to support multiple hatches as well? (I would rather only change things on mplcairo's side once multiple hatches are supported too.)

r3kste reacted with thumbs up emoji

@r3kste
Copy link
Contributor

@anntzer Are there any other changes expected? Or is this ready to be merged?

Copy link
Contributor

@anntzeranntzer left a comment

Choose a reason for hiding this comment

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

Just one last suggestion to improve on the future extensibility (approval is conditional on at least discussing it).
I still don't really like the API but it's also unrealistic to hope for a full rewrite here, so let's go with it.

@r3kste
Copy link
Contributor

I still don't really like the API but it's also unrealistic to hope for a full rewrite here, so let's go with it.

I agree that this API will need to be changed in the future.

@timhoffm any suggestions?

Copy link
Member

@dstansbydstansby left a comment

Choose a reason for hiding this comment

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

Thanks for this PR - it looks great overall, and the examples were super nice to understand the new feature.

There's a few minor points I think can be improved, and some questions I had - I've left my comments inline in the code.

"screen", hatchcolors=self.get_hatchcolor()
)
except TypeError:
hatchcolors_arg_supported = False
Copy link
Member

Choose a reason for hiding this comment

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

codecov is saying that these lines aren't covered (and several other lines in this file). Could you add some tests for these lines? They don't have to be figure tests, they can just test that the code works without erroring.

Copy link
Contributor

Choose a reason for hiding this comment

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

These lines are run only in the case of a third-party backend. However, I am not sure how to test this within the current testing framework of matplotlib, without a third-party backend.

Copy link
Member

Choose a reason for hiding this comment

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

You can make a mock third party backend to call the function - there are some examples of other mocks in the tests

r3kste reacted with thumbs up emoji
# The current new API of draw_path_collection() is provisional
# and will be changed in a future PR.

# Find whether renderer.draw_path_collection() takes hatchcolor parameter
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
# Find whether renderer.draw_path_collection() takes hatchcolor parameter
# Find whether renderer.draw_path_collection() takes hatchcolor parameter.
# Since third-party implementations of draw_path_collection() may not be
# introspectable, e.g. with inspect.signature, the only way is to try and
# call this with the hatchcolors parameter.

r3kste reacted with thumbs up emoji
"screen")
"screen", hatchcolors=self.get_hatchcolor()
)
except TypeError:
Copy link
Member

Choose a reason for hiding this comment

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

It's worth explicitly documenting this in the comment, see suggestion above.

r3kste reacted with thumbs up emoji
@@ -252,7 +258,7 @@ def draw_path_collection(self, gc, master_transform, paths, all_transforms,

def draw_quad_mesh(self, gc, master_transform, meshWidth, meshHeight,
coordinates, offsets, offsetTrans, facecolors,
antialiased, edgecolors):
antialiased, edgecolors, hatchcolors=None):
Copy link
Member

Choose a reason for hiding this comment

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

Is this a red herring? From what I understand from#29044 (comment), making hatchcolors optional (or even kw-only) does not buy us anything in terms of better API compatibility: This is reimplemented by backends. Some may have implemented ahatchcolors parameter, others may not. It is only called from our matplotlib code, so we anyway have to deal with it being available or not (and if it's available, we always want to pass hatchcolors). There shouldn't be any third-party/user code needing to call this function.

If that's correct, an additional positional parameter would have the same effect. And then I'd prefer that because it keeps the whole function consistent.

ping@anntzer Am I correct here?


Edit: There seem to be very few "real" usages ofdraw_path_collection (I've tried to filter outforks or copies of our files):
https://github.com/search?q=%2F%5C.draw_path_collection%5C%28%2F+language%3APython+NOT+is%3Afork+NOT+repo%3Amatplotlib%2Fmatplotlib+NOT+path%3A**%2Fbackend_bases.py+NOT+path%3A**%2Fcollections.py++NOT+path%3A**%2Fpatheffects.py+NOT+path%3A**%2Fbackend_agg.py+NOT+path%3A**%2Fbackend_svg.py+NOT+path%3A**%2Fbackend_pdf.py++NOT+path%3A**%2Fbackend_ps.py+NOT+path%3A**%2Fbackend_macosx.py++NOT+path%3A**%2Fleft.py+NOT+path%3A**%2Fright.py++NOT+path%3A**%2Fmerge.py++NOT+path%3A**%2Fbase.py+NOT+path%3A**%2Fsite-packages%2F**&type=code

I think making this optional is meaningful for a start because it will support some very rare use cases. But in terms of API consistency, I'd like to move this to a regular parameter in the future, i.e.

  • not make this kw-only right now
  • some time later deprecate calling this function withouthatchcolors

@anntzer@dstansby@r3kste Does that make sense?

@anntzer
Copy link
Contributor

re: making hatchcolors optional and API compatibility:
My point of view is that the renderer API (draw_foo) is public and it is legitimate for 3rd-parties to call it directly (e.g. they can create their own artist subclasses that implement their own draw() and call draw_foo() from there. However, I guess it could also be reasonable(?), and possibly helpful for extensibility, to declare that these methods arenot for 3rd-party direct use, and that third-party artistsmust go through a builtin artist's draw to invoke the renderer's drawing functions. But that's definitely not a policy we have stated so far.

@timhoffm
Copy link
Member

Thanks. I agreeoptional is set to begin with for compatibility.

Questions are:

  • Do we want/gain anything with keyword-only? - I tend towards no.
  • Do we want to evolve the API to non-optional through regular deprecation machinery? - I tend towards yes.

@anntzer
Copy link
Contributor

  • Do we want/gain anything with keyword-only? - I tend towards no.

It makes it easier to later include parametersbefore it, e.g.hatches is something that's clearly missing and would be expected to come first.

  • Do we want to evolve the API to non-optional through regular deprecation machinery? - I tend towards yes.

Let's not for now. As argued above I think the API introduced in this PR is actually not so great (but still the best that can be done without an in-depth reworking of draw_path_collection), so e.g. I'd rather not implement support for it at all in mplcairo (and instead rely on Matplotlib's fallback implementation) until a better API comes. Likewise I don't think we should encourage any third parties to implement it.

r3kste reacted with thumbs up emoji

@r3kster3ksteforce-pushed thecollections-hatchcolor branch from0c557e4 to2e4784bCompareMarch 28, 2025 15:55
Comment on lines 458 to 467
path_ids = renderer._iter_collection_raw_paths(
transform.frozen(), ipaths, self.get_transforms())
for xo, yo, path_id, gc0, rgbFace in renderer._iter_collection(
gc, list(path_ids), *args, hatchcolors=self.get_hatchcolor(),
):
path, transform = path_id
if xo != 0 or yo != 0:
transform = transform.frozen()
transform.translate(xo, yo)
renderer.draw_path(gc0, path, transform, rgbFace)
Copy link
Member

Choose a reason for hiding this comment

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

For better backward-compatibility/performance, would it be reasonable to split the "backend does not support hatchcolors" into

if no_hatches_needed:    renderer_draw_path_collection(...) # no hatch colors passedelse:    # unroll the collection to draw paths in a loop

I'm unclear how performance-critical this is and whether we can unversally force the collection unrolling on backends that do not support hatchcolors.

ping@anntzer

Copy link
Contributor

@anntzeranntzerMar 29, 2025
edited
Loading

Choose a reason for hiding this comment

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

Good catch. I agree that only going through the unrolled version when hatchcolors are actually passed would likely be much better performance-wise.

r3kste reacted with thumbs up emoji
@timhoffmtimhoffm added this to thev3.11.0 milestoneMar 31, 2025
@timhoffmtimhoffm merged commit65d2818 intomatplotlib:mainMar 31, 2025
39 of 42 checks passed
@timhoffm
Copy link
Member

Thanks@r3kste for going through all the nitty gritty details with us!

r3kste and story645 reacted with hooray emoji

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@story645story645story645 left review comments

@anntzeranntzeranntzer approved these changes

@dstansbydstansbydstansby left review comments

@r3kster3kster3kste left review comments

@timhoffmtimhoffmtimhoffm approved these changes

Assignees
No one assigned
Projects
None yet
Milestone
v3.11.0
Development

Successfully merging this pull request may close these issues.

6 participants
@Impaler343@story645@r3kste@timhoffm@anntzer@dstansby

[8]ページ先頭

©2009-2025 Movatter.jp