From 226b2f254675c1d664cbd439bba5a79824401e1e Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Sun, 26 Jan 2025 12:55:49 +0100 Subject: [PATCH 1/2] Add new method Colormap.with_alpha() We currently do not have a way of adjusting only the transparency of the mapped colors of the artist, e.g. the fill in contourf(). The Artist alpha parameter also affects lines and hatches. The simplest solution is to support creating semi-transparent colormaps, which can then be used via `contourf(..., cmap=plt.colormaps['jet'].with_alpha(0.5))`. See also https://github.com/matplotlib/matplotlib/pull/29044#issuecomment-2614320129. --- doc/users/next_whats_new/colormap_with_alpha | 5 +++++ lib/matplotlib/colors.py | 20 ++++++++++++++++++++ lib/matplotlib/colors.pyi | 1 + lib/matplotlib/tests/test_colors.py | 13 ++++++++++++- 4 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 doc/users/next_whats_new/colormap_with_alpha diff --git a/doc/users/next_whats_new/colormap_with_alpha b/doc/users/next_whats_new/colormap_with_alpha new file mode 100644 index 000000000000..7bc9a84618a2 --- /dev/null +++ b/doc/users/next_whats_new/colormap_with_alpha @@ -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. diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index d8954c8e50db..d8f6a237c853 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -943,6 +943,26 @@ def _set_extremes(self): 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 + return new_cm + def _init(self): """Generate the lookup table, ``self._lut``.""" raise NotImplementedError("Abstract class only") diff --git a/lib/matplotlib/colors.pyi b/lib/matplotlib/colors.pyi index 9fb758cb146c..3e761c949068 100644 --- a/lib/matplotlib/colors.pyi +++ b/lib/matplotlib/colors.pyi @@ -111,6 +111,7 @@ class Colormap: under: ColorType | None = ..., over: ColorType | None = ... ) -> Colormap: ... + def with_alpha(self, alpha: float) -> Colormap: ... def is_gray(self) -> bool: ... def resampled(self, lutsize: int) -> Colormap: ... def reversed(self, name: str | None = ...) -> Colormap: ... diff --git a/lib/matplotlib/tests/test_colors.py b/lib/matplotlib/tests/test_colors.py index 465d9d294a09..8df11ae4dd41 100644 --- a/lib/matplotlib/tests/test_colors.py +++ b/lib/matplotlib/tests/test_colors.py @@ -20,7 +20,7 @@ import matplotlib.scale as mscale from matplotlib.rcsetup import cycler from matplotlib.testing.decorators import image_comparison, check_figures_equal -from matplotlib.colors import is_color_like, to_rgba_array +from matplotlib.colors import is_color_like, to_rgba_array, ListedColormap @pytest.mark.parametrize('N, result', [ @@ -248,6 +248,17 @@ def test_LinearSegmentedColormap_from_list_bad_under_over(): assert mcolors.same_color(cmap.get_over(), "y") +def test_colormap_with_alpha(): + cmap = ListedColormap(["red", "green", ("blue", 0.8)]) + cmap2 = cmap.with_alpha(0.5) + # color is the same: + vals = [0, 0.5, 1] # numeric positions that map to the listed colors + assert_array_equal(cmap(vals)[:, :2], cmap2(vals)[:, :2]) + # alpha of cmap2 is changed: + assert_array_equal(cmap(vals)[:, 3], [1, 1, 0.8]) + assert_array_equal(cmap2(vals)[:, 3], [0.5, 0.5, 0.5]) + + def test_BoundaryNorm(): """ GitHub issue #1258: interpolation was failing with numpy From 1639a25be6246f0ee69360313d3afaf5a1a9e42d Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Wed, 29 Jan 2025 09:16:08 +0100 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: Elliott Sales de Andrade --- lib/matplotlib/colors.py | 3 +-- lib/matplotlib/tests/test_colors.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index d8f6a237c853..b8a7bf404064 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -953,8 +953,7 @@ def with_alpha(self, alpha): 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)}") + 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() diff --git a/lib/matplotlib/tests/test_colors.py b/lib/matplotlib/tests/test_colors.py index 8df11ae4dd41..bb90806f38ee 100644 --- a/lib/matplotlib/tests/test_colors.py +++ b/lib/matplotlib/tests/test_colors.py @@ -253,7 +253,7 @@ def test_colormap_with_alpha(): cmap2 = cmap.with_alpha(0.5) # color is the same: vals = [0, 0.5, 1] # numeric positions that map to the listed colors - assert_array_equal(cmap(vals)[:, :2], cmap2(vals)[:, :2]) + assert_array_equal(cmap(vals)[:, :3], cmap2(vals)[:, :3]) # alpha of cmap2 is changed: assert_array_equal(cmap(vals)[:, 3], [1, 1, 0.8]) assert_array_equal(cmap2(vals)[:, 3], [0.5, 0.5, 0.5])