Skip to content

Commit

Permalink
Make ListedColormap.monochrome a property
Browse files Browse the repository at this point in the history
The calculated property replaces the attribute *monochome*, which was
manually set on `__init__`, but was not correctly set for all possible
inputs.

This property ensures consistency and simplifies initialization at the
cost of some computation overhead to determine whether the colormap is
monochrome.

The computation cost is bearable (even without caching), because it's
only used in `ContourSet._process_colors`.

It's a separate discussion whether we need this property on colormaps at
all (at least as public API). Usually, colormaps are not monochrome
and monochrome colormaps are a very special edge case used in contours
only. We may eventually deprecate it, but since it is currently public
API, let's leave it for now.

There's also a technical API incompatibility in that users cannot set
the attribute anymore, but I'd argue that that has never been intended
and there's no practical use-case, so I refrain from the extra hassle
of allowing setting this property.

Co-authored-by: Greg Lucas <greg.m.lucas@gmail.com>
  • Loading branch information
timhoffm and greglucas committed Nov 12, 2024
1 parent ca39d41 commit ce488f5
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 6 deletions.
20 changes: 15 additions & 5 deletions lib/matplotlib/colors.py
Original file line number Diff line number Diff line change
Expand Up @@ -1191,17 +1191,13 @@ class ListedColormap(Colormap):
the list will be extended by repetition.
"""
def __init__(self, colors, name='from_list', N=None):
self.monochrome = False # Are all colors identical? (for contour.py)
if N is None:
self.colors = colors
N = len(colors)
else:
if isinstance(colors, str):
self.colors = [colors] * N
self.monochrome = True
elif np.iterable(colors):
if len(colors) == 1:
self.monochrome = True
self.colors = list(
itertools.islice(itertools.cycle(colors), N))
else:
Expand All @@ -1211,7 +1207,6 @@ def __init__(self, colors, name='from_list', N=None):
pass
else:
self.colors = [gray] * N
self.monochrome = True
super().__init__(name, N)

def _init(self):
Expand All @@ -1220,6 +1215,21 @@ def _init(self):
self._isinit = True
self._set_extremes()

@property
def monochrome(self):
"""Return whether all colors in the colormap are identical."""
# Replacement for the attribute *monochrome*. This ensures a consistent
# response independent of the way the ListedColormap was created, which
# was not the case for the manually set attribute.
#
# TODO: It's a separate discussion whether we need this property on
# colormaps at all (at least as public API). It's a very special edge
# case and we only use it for contours internally.
if not self._isinit:
self._init()

return self.N <= 1 or np.all(self._lut[0] == self._lut[1:self.N])

def resampled(self, lutsize):
"""Return a new colormap with *lutsize* entries."""
colors = self(np.linspace(0, 1, lutsize))
Expand Down
3 changes: 2 additions & 1 deletion lib/matplotlib/colors.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,12 @@ class LinearSegmentedColormap(Colormap):
def reversed(self, name: str | None = ...) -> LinearSegmentedColormap: ...

class ListedColormap(Colormap):
monochrome: bool
colors: ArrayLike | ColorType
def __init__(
self, colors: ArrayLike | ColorType, name: str = ..., N: int | None = ...
) -> None: ...
@property
def monochrome(self) -> bool: ...
def resampled(self, lutsize: int) -> ListedColormap: ...
def reversed(self, name: str | None = ...) -> ListedColormap: ...

Expand Down
6 changes: 6 additions & 0 deletions lib/matplotlib/tests/test_colors.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ def test_resampled():
assert_array_almost_equal(lc(np.nan), lc3(np.nan))


def test_monochrome():
assert mcolors.ListedColormap(["red"]).monochrome
assert mcolors.ListedColormap(["red"] * 5).monochrome
assert not mcolors.ListedColormap(["red", "green"]).monochrome


def test_colormaps_get_cmap():
cr = mpl.colormaps

Expand Down

0 comments on commit ce488f5

Please sign in to comment.