16
16
"""
17
17
18
18
from collections .abc import Mapping , MutableMapping
19
+ import functools
19
20
20
21
import numpy as np
21
22
from numpy import ma
22
23
23
24
import matplotlib as mpl
24
- from matplotlib import _api , colors , cbook
25
+ from matplotlib import _api , colors , cbook , scale
25
26
from matplotlib ._cm import datad
26
27
from matplotlib ._cm_listed import cmaps as cmaps_listed
27
28
@@ -331,6 +332,35 @@ def unregister_cmap(name):
331
332
return _cmap_registry .pop (name )
332
333
333
334
335
+ @functools .lru_cache (None )
336
+ def _auto_norm_from_scale (scale_cls ):
337
+ """
338
+ Automatically generate a norm class from *scale_cls*.
339
+
340
+ This differs from `.colors.make_norm_from_scale` in the following points:
341
+
342
+ - This function is not a class decorator, but directly returns a norm class
343
+ (as if decorating `.Normalize`).
344
+ - The scale is automatically constructed with ``nonpositive="mask"``, if it
345
+ supports such a parameter, to work around the difference in defaults
346
+ between standard scales (which use "clip") and norms (which use "mask").
347
+ - The returned norm class is memoized and reused for later calls.
348
+ (`.colors.make_norm_from_scale` also memoizes, but we call it with a
349
+ `functools.partial` instances which always compare unequal, so the cache
350
+ doesn't get hit.)
351
+ """
352
+ # Actually try to construct an instance, to verify whether
353
+ # ``nonpositive="mask"`` is supported.
354
+ try :
355
+ norm = colors .make_norm_from_scale (
356
+ functools .partial (scale_cls , nonpositive = "mask" ))(
357
+ colors .Normalize )()
358
+ except TypeError :
359
+ norm = colors .make_norm_from_scale (scale_cls )(
360
+ colors .Normalize )()
361
+ return type (norm )
362
+
363
+
334
364
class ScalarMappable :
335
365
"""
336
366
A mixin class to map scalar data to RGBA.
@@ -341,12 +371,13 @@ class ScalarMappable:
341
371
342
372
def __init__ (self , norm = None , cmap = None ):
343
373
"""
344
-
345
374
Parameters
346
375
----------
347
- norm : `matplotlib.colors. Normalize` (or subclass thereof)
376
+ norm : `. Normalize` (or subclass thereof) or str or None
348
377
The normalizing object which scales data, typically into the
349
378
interval ``[0, 1]``.
379
+ If a `str`, a `.Normalize` subclass is dynamically generated based
380
+ on the scale with the corresponding name.
350
381
If *None*, *norm* defaults to a *colors.Normalize* object which
351
382
initializes its scaling based on the first data processed.
352
383
cmap : str or `~matplotlib.colors.Colormap`
@@ -376,11 +407,11 @@ def _scale_norm(self, norm, vmin, vmax):
376
407
"""
377
408
if vmin is not None or vmax is not None :
378
409
self .set_clim (vmin , vmax )
379
- if norm is not None :
410
+ if isinstance ( norm , colors . Normalize ) :
380
411
raise ValueError (
381
- "Passing parameters norm and vmin/vmax simultaneously is "
382
- "not supported. Please pass vmin/vmax directly to the "
383
- "norm when creating it." )
412
+ "Passing a Normalize instance simultaneously with "
413
+ "vmin/vmax is not supported. Please pass vmin/vmax "
414
+ "directly to the norm when creating it." )
384
415
385
416
# always resolve the autoscaling so we have concrete limits
386
417
# rather than deferring to draw time.
@@ -554,9 +585,13 @@ def norm(self):
554
585
555
586
@norm .setter
556
587
def norm (self , norm ):
557
- _api .check_isinstance ((colors .Normalize , None ), norm = norm )
588
+ _api .check_isinstance ((colors .Normalize , str , None ), norm = norm )
558
589
if norm is None :
559
590
norm = colors .Normalize ()
591
+ elif isinstance (norm , str ):
592
+ # case-insensitive, consistently with scale_factory.
593
+ scale_cls = scale ._scale_mapping [norm .lower ()]
594
+ norm = _auto_norm_from_scale (scale_cls )()
560
595
561
596
if norm is self .norm :
562
597
# We aren't updating anything
@@ -578,7 +613,7 @@ def set_norm(self, norm):
578
613
579
614
Parameters
580
615
----------
581
- norm : `.Normalize` or None
616
+ norm : `.Normalize` or str or None
582
617
583
618
Notes
584
619
-----
0 commit comments