16
16
"""
17
17
18
18
from collections .abc import 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
@@ -235,6 +236,36 @@ def unregister_cmap(name):
235
236
return _cmap_registry .pop (name )
236
237
237
238
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
+
238
269
class ScalarMappable :
239
270
"""
240
271
A mixin class to map scalar data to RGBA.
@@ -245,12 +276,13 @@ class ScalarMappable:
245
276
246
277
def __init__ (self , norm = None , cmap = None ):
247
278
"""
248
-
249
279
Parameters
250
280
----------
251
- norm : `matplotlib.colors. Normalize` (or subclass thereof)
281
+ norm : `. Normalize` (or subclass thereof) or str or None
252
282
The normalizing object which scales data, typically into the
253
283
interval ``[0, 1]``.
284
+ If a `str`, a `.Normalize` subclass is dynamically generated based
285
+ on the scale with the corresponding name.
254
286
If *None*, *norm* defaults to a *colors.Normalize* object which
255
287
initializes its scaling based on the first data processed.
256
288
cmap : str or `~matplotlib.colors.Colormap`
@@ -277,11 +309,11 @@ def _scale_norm(self, norm, vmin, vmax):
277
309
"""
278
310
if vmin is not None or vmax is not None :
279
311
self .set_clim (vmin , vmax )
280
- if norm is not None :
312
+ if isinstance ( norm , colors . Normalize ) :
281
313
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." )
285
317
286
318
# always resolve the autoscaling so we have concrete limits
287
319
# rather than deferring to draw time.
@@ -454,18 +486,22 @@ def set_norm(self, norm):
454
486
455
487
Parameters
456
488
----------
457
- norm : `.Normalize` or None
489
+ norm : `.Normalize` or str or None
458
490
459
491
Notes
460
492
-----
461
493
If there are any colorbars using the mappable for this norm, setting
462
494
the norm of the mappable will reset the norm, locator, and formatters
463
495
on the colorbar to default.
464
496
"""
465
- _api .check_isinstance ((colors .Normalize , None ), norm = norm )
497
+ _api .check_isinstance ((colors .Normalize , str , None ), norm = norm )
466
498
in_init = self .norm is None
467
499
if norm is None :
468
500
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 )()
469
505
self .norm = norm
470
506
if not in_init :
471
507
self .changed () # Things are not set up properly yet.
0 commit comments