1616"""
1717
1818from collections .abc import MutableMapping
19+ import functools
1920
2021import numpy as np
2122from numpy import ma
2223
2324import matplotlib as mpl
24- from matplotlib import _api , colors , cbook
25+ from matplotlib import _api , colors , cbook , scale
2526from matplotlib ._cm import datad
2627from matplotlib ._cm_listed import cmaps as cmaps_listed
2728
@@ -235,6 +236,36 @@ def unregister_cmap(name):
235236 return _cmap_registry .pop (name )
236237
237238
239+ @functools .lru_cache (None )
240+ def _auto_norm_from_scale (scale_cls ):
241+ """
242+ Automatically generate a norm class from *scale_cls*.
243+
244+ This differs from `.colors.make_norm_from_scale` in the following points:
245+
246+ - This function is not a class decorator, but directly returns a norm class
247+ (as if decorating `.Normalize`).
248+ - The scale is automatically constructed with ``nonpositive="mask"``, if it
249+ supports such a parameter, to work around the difference in defaults
250+ between standard scales (which use "clip") and norms (which use "mask").
251+ - The returned norm class is memoized and reused for later calls. (This
252+ would be desirable in `.colors.make_norm_from_scale` as well, but
253+ because that function sometimes gets called with `functools.partial`
254+ instances which always compare unequal, a simple ``@lru_cache`` would be
255+ insufficient.)
256+ """
257+ try :
258+ norm = colors .make_norm_from_scale (
259+ functools .partial (scale_cls , nonpositive = "mask" ))(
260+ colors .Normalize )()
261+ except TypeError :
262+ norm = colors .make_norm_from_scale (scale_cls )(
263+ colors .Normalize )()
264+ norm_cls = type (norm )
265+ norm_cls .__name__ = f"{ scale_cls .__name__ } Norm"
266+ return norm_cls
267+
268+
238269class ScalarMappable :
239270 """
240271 A mixin class to map scalar data to RGBA.
@@ -245,12 +276,13 @@ class ScalarMappable:
245276
246277 def __init__ (self , norm = None , cmap = None ):
247278 """
248-
249279 Parameters
250280 ----------
251- norm : `matplotlib.colors. Normalize` (or subclass thereof)
281+ norm : `. Normalize` (or subclass thereof) or str or None
252282 The normalizing object which scales data, typically into the
253283 interval ``[0, 1]``.
284+ If a `str`, a `.Normalize` subclass is dynamically generated based
285+ on the scale with the corresponding name.
254286 If *None*, *norm* defaults to a *colors.Normalize* object which
255287 initializes its scaling based on the first data processed.
256288 cmap : str or `~matplotlib.colors.Colormap`
@@ -277,11 +309,11 @@ def _scale_norm(self, norm, vmin, vmax):
277309 """
278310 if vmin is not None or vmax is not None :
279311 self .set_clim (vmin , vmax )
280- if norm is not None :
312+ if isinstance ( norm , colors . Normalize ) :
281313 raise ValueError (
282- "Passing parameters norm and vmin/vmax simultaneously is "
283- "not supported. Please pass vmin/vmax directly to the "
284- "norm when creating it." )
314+ "Passing a Normalize instance simultaneously with "
315+ "vmin/vmax is not supported. Please pass vmin/vmax "
316+ "directly to the norm when creating it." )
285317
286318 # always resolve the autoscaling so we have concrete limits
287319 # rather than deferring to draw time.
@@ -454,18 +486,22 @@ def set_norm(self, norm):
454486
455487 Parameters
456488 ----------
457- norm : `.Normalize` or None
489+ norm : `.Normalize` or str or None
458490
459491 Notes
460492 -----
461493 If there are any colorbars using the mappable for this norm, setting
462494 the norm of the mappable will reset the norm, locator, and formatters
463495 on the colorbar to default.
464496 """
465- _api .check_isinstance ((colors .Normalize , None ), norm = norm )
497+ _api .check_isinstance ((colors .Normalize , str , None ), norm = norm )
466498 in_init = self .norm is None
467499 if norm is None :
468500 norm = colors .Normalize ()
501+ elif isinstance (norm , str ):
502+ # case-insensitive, consistently with scale_factory.
503+ scale_cls = scale ._scale_mapping [norm .lower ()]
504+ norm = _auto_norm_from_scale (scale_cls )()
469505 self .norm = norm
470506 if not in_init :
471507 self .changed () # Things are not set up properly yet.
0 commit comments