Skip to content

Separates edgecolor from hatchcolor #28104

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 19 commits into from
Dec 29, 2024
Merged

Conversation

Impaler343
Copy link
Contributor

@Impaler343 Impaler343 commented Apr 19, 2024

PR summary

Taking after @Vashesh08 's PR #26993
Should close #26074 and #7059
Implemented the fallback logic that @story645 mentioned in this comment
Cherry picked some initial commits from the Issue26074 branch written by Vashesh
Modified the hatchcolor setting to way similar to edgecolor setting.

  • Need to do some extensive testing on patches and collections
  • Need to write documentation for hatchcolor attribute, and also describe the behaviour change

PR checklist

@r3kste
Copy link
Contributor

r3kste commented Aug 11, 2024

There are two tests that are currently failing and need a rewrite.

  1. test_axes.py::test_contour_hatching:

    Previous Behavior

    In collections, hatchcolor is set along with edgecolor.

    In the cases where edgecolors 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, if edgecolors is not specified, hatchcolor uses mpl.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

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

    alpha works on hatchcolor when edgecolors is specified. Therefore, I believe that it should also work when edgecolors is not specified.

  2. test_backend_bases.py::test_uses_per_path This requires a small change in the parameters passed to _iter_collection, corresponding to hatchcolors.

    --- a/lib/matplotlib/tests/test_backend_bases.py
    +++ b/lib/matplotlib/tests/test_backend_bases.py
    @@ -35,7 +35,7 @@ def test_uses_per_path():
                rb._iter_collection(
                    gc, range(len(raw_paths)), offsets,
                    transforms.AffineDeltaTransform(master_transform),
    -                   facecolors, edgecolors, [], [], [False],
    +                   facecolors, edgecolors, [], [], [], [False],
                    [], 'screen')]
            uses = rb._iter_collection_uses_per_path(
                paths, all_transforms, offsets, facecolors, edgecolors)

@Impaler343
Copy link
Contributor Author

Pinging @story645 for review

@Impaler343 Impaler343 marked this pull request as ready for review August 11, 2024 05:45
@story645 story645 self-assigned this Aug 14, 2024
@story645
Copy link
Member

Sorry for delay, will try to review but please add some tests.

@story645
Copy link
Member

Also this should probably go in before #27937 b/c I agree that it'll probably clean that one up.

Copy link
Member

@story645 story645 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 might need some discussion on how to manage since the changes dive pretty deep into the internals, but definitely add some tests cause that could help you debug the segfault.

@tacaswell
Copy link
Member

For this change we need to make sure both:

  • Anyone calling the backend methods without the new input value needs to not be broken (so the new keyword needs to be last and optional)
  • Anyplace we call the backend methods internally we need to be forgiving of backends that have not adopted the new signature (we should have an example of using inspect.signature to manage this)

This will also need a lot of documentation (both notifying third-party backends the expected API is changing) and announcing the new feature to users (with lots of examples).

@r3kste
Copy link
Contributor

r3kste commented Aug 30, 2024

Could I also go ahead with rewriting the two tests that are failing?

@story645
Copy link
Member

story645 commented Aug 30, 2024

Could I also go ahead with rewriting the two tests that are failing?

why are they failing? never mind, went and looked. So for this test:

TypeError: RendererBase._iter_collection() missing 1 required positional argument: 'offset_position'

It shouldn't be failing. This is what @tacaswell was talking about that you want to make sure the code is backwards compatible.

The test contour_hatch also seems like it should still pass, is there a reason it shouldn't?

@Impaler343
Copy link
Contributor Author

Could I also go ahead with rewriting the two tests that are failing?

why are they failing?

The initial comment explains the reason

@story645
Copy link
Member

Sorry, I updated while you posted. For the tests:

However, if edgecolors is not specified, hatchcolor uses mpl.rcParams['hatch.color'] for the color, but it doesn't use the alpha value specified by the user.
In the above test, alpha is set to 0.5 and edgecolors is not specified. Ideally, the hatch should have an alpha value of 0.5, but it actually has the default alpha value of 1.0.

I think the user specified alpha should also be propagated to hatchcolors, w/ the same priority rules as color and edgecolor, which should then make the test pass.

This requires a small change in the parameters passed to _iter_collection, corresponding to hatchcolors.
these should be optional for backwards compatibility and therefore still pass

@tacaswell
Copy link
Member

It may be simpler to work out what the non-vectorized version of this API looks like (so do just the classes in pathes.py) first and then do the collection-based ones in a follow on PR.

Copy link
Member

@story645 story645 left a comment

Choose a reason for hiding this comment

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

couple of small comments but I'm really impressed w/ the work you've done here and how far this PR has come. 😄

Comment on lines +346 to +348
if self._edgecolor[3] == 0: # fully transparent
return colors.to_rgba(mpl.rcParams['patch.edgecolor'])
return self.get_edgecolor()
Copy link
Member

Choose a reason for hiding this comment

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

so just checking that this will get followed up and just used get_edgecolor? cause like I mentioned, I'm uncomfortable with using transparent edge as the test

Copy link
Member

@story645 story645 Dec 19, 2024

Choose a reason for hiding this comment

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

discussed this on the call today and is this being done to maintain backward compatibility if someone sets edgecolor to None and doesn't define hatchcolor or hatch.color to ensure that something gets drawn?

my thinking is that that should return a warning "Setting a hatch but hatch and edgecolor are none" but we shouldn't special case a fallback (that also may not work if patch.edgecolor is "none")

Copy link
Contributor

Choose a reason for hiding this comment

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

is this being done to maintain backward compatibility if someone sets edgecolor to None and doesn't define hatchcolor or hatch.color to ensure that something gets drawn?

Yes. This is for the final fallback where edgecolor is set to 'none', and hatchcolor is not set in which case patch.edgecolor rcParam is used and this condition is there to check whether edgecolor is None. Removing this condition causes test_artist::test_hatching to fail.

Copy link
Member

@story645 story645 Dec 22, 2024

Choose a reason for hiding this comment

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

Ok, so we should discuss if this behavior should be deprecated (b/c it's an iffy fallback to rely on facecolor.rcparam being set to not None) now that we allow an explicit hatchcolor, but I think it's okay to push that to a follow up to this PR.

Is part of the problem here eager resolution of edgeolor so you can't check that edgecolor is None (hasn't been set)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, as hatchcolor must depend on the current edgecolor now, we are essentially special casing for when the hatchcolor came through as black from the rcParam in the previous implementation. I believe we can't really check if the edgecolor is None in a better place
I am for hatching with the rcParam when no hatchcolor is specified as it not only maintains backward compatibility but seems more natural for a user to not care about the hatchcolor, but the next best thing would be to raise a warning as mentioned above

Copy link
Member

Choose a reason for hiding this comment

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

I'm gonna move the discussion of deprecating to a new issue, but my concern is that the fallback on edgecolor='None' means:

  1. if edgecolor='None' you'll backfill with 'patch.edgecolor' but that may also be None and this all happens silently
  2. edgecolor=('green', 0) will also trigger this path and the person may have wanted the 'green' and were gonna up the alpha

Copy link
Member

@story645 story645 left a comment

Choose a reason for hiding this comment

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

slight wording nits but otherwise I think this is good to go and a feature I wanted a few weeks back

Comment on lines +346 to +348
if self._edgecolor[3] == 0: # fully transparent
return colors.to_rgba(mpl.rcParams['patch.edgecolor'])
return self.get_edgecolor()
Copy link
Member

Choose a reason for hiding this comment

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

I'm gonna move the discussion of deprecating to a new issue, but my concern is that the fallback on edgecolor='None' means:

  1. if edgecolor='None' you'll backfill with 'patch.edgecolor' but that may also be None and this all happens silently
  2. edgecolor=('green', 0) will also trigger this path and the person may have wanted the 'green' and were gonna up the alpha

@timhoffm timhoffm merged commit c11175d into matplotlib:main Dec 29, 2024
37 of 39 checks passed
@timhoffm
Copy link
Member

Thanks @Impaler343 for going with us through the lengthy discussion and design process.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[ENH]: Different edgecolor and hatch color in bar plot
6 participants