-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Add new method Colormap.with_alpha() #29525
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
Tuning transparency of colormaps | ||
-------------------------------- | ||
The new method `.Colormap.with_alpha` allows to create a new colormap with the same | ||
color values but a new uniform alpha value. This is handy if you want to modify only | ||
the transparency of mapped colors for an Artist. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -943,6 +943,25 @@ | |
self._lut[self._i_over] = self._lut[self.N - 1] | ||
self._lut[self._i_bad] = self._rgba_bad | ||
|
||
def with_alpha(self, alpha): | ||
""" | ||
Return a copy of the colormap with a new uniform transparency. | ||
|
||
Parameters | ||
---------- | ||
alpha : float | ||
The alpha blending value, between 0 (transparent) and 1 (opaque). | ||
""" | ||
if not isinstance(alpha, Real): | ||
raise TypeError(f"'alpha' must be numeric or None, not {type(alpha)}") | ||
if not 0 <= alpha <= 1: | ||
ValueError("'alpha' must be between 0 and 1, inclusive") | ||
new_cm = self.copy() | ||
if not new_cm._isinit: | ||
new_cm._init() | ||
new_cm._lut[:, 3] = alpha | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This has the unintended(?) consequence of allowing an array as well. If that array is the wrong size, folks are going to perhaps get cryptic size mismatch errors? Did we want to give thought to handling that, or just wait and see if it's a problem in real life? I'm actually unsure of how a user would get how big the lookup table is. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I’m aware of this. I’ve intentionally specified this as float in the docstring and type annotation. Every other possibility accepted value is an implementation detail. I didn’t want to explicitly check for array and raise on that. I’m also unsure how helpful a per-element alpha is. I assume varying alpha in a color map is less used compared to making the whole mapping semi-transparent. Therefore, I’ve not included official varying alpha support for now. We can always later expand on it and also then decide whether the implementation just stays as is or whether we want additional safety checks and better error handling for arrays. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll chime in here and say that I sometimes do use varying alpha in colormaps. This is useful for hiding/washing-out less important features/regions. Making a linear ramp that goes from 0.2-> 1 or something like that along with the low-high values. You can do that here with: Example I added to mplcairo where the rendering of varying alpha between patches is better than with Agg: https://github.com/matplotlib/mplcairo/blob/247583de20b94d5c1a0d773e720d64e741b122df/examples/quadmesh.py#L22-L26 All this to say, I think the varying alpha is a useful feature, and making it easier would be nice! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you've already got a nice enough way of doing that here? I guess I'm thinking that it might be helpful to do as Jody mentioned and put some validation on alpha right away. Raise if it is not a float(s) between 0-1 or if it is of a different length than N. We also have the colorbar set_alpha method, so might be worth also thinking about how this will play with that (I think that colorbar.set_alpha() still wins on the colorbar, but the collections would be mapped with the cmap alpha here) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we please focus on getting this through quickly as a minimal public API, so that #29044 can move forward? I’m happy to discuss arrays, validation and Colorbar alpha in a follow-up. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure of the urgency here to avoid changing a visual test. We have a continual problem with the color/alpha/facecolor/edgecolor dance - maybe this is the solution, but it doesn't seem right. OTOH, adding more ways to sculpt colormaps seems like a net positive regardless of the current issue that spurred this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I don't understand the second half. If "this" means this PR, it's not supposed to solve any general problem with color/alpha/facecolor/edgecolor. This PR is a simple and straight forward extension of the Colormap functionality. It was motivated through a specific test in another PR that could better be written using this functionality. Not more not less. I've added a range check for the float. I don't oversee whether array alpha is a meaningful API, in particular in the context of LinearSegementedColormap, where the lookup-table is basically an implementation detail. Therefore I want to keep things simple here. |
||
return new_cm | ||
|
||
def _init(self): | ||
"""Generate the lookup table, ``self._lut``.""" | ||
raise NotImplementedError("Abstract class only") | ||
|
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is going to return
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
is someone inputs an array.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your observation on the error is correct, and to me this is intended. What's wrong with that? / What would you expect instead? I can't make sense of the suggestion, because it would raise on floats ("object of type 'float' has no len()").
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is cryptic if someone passes an
utterableiterable. You probably want something like:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally, we don't jump extra hoops to catch types that are not supported and give them a nicer error message.
But granted,
Artist.set_alpha
does something similar, so I've taken the same approach as there.