Skip to content

Commit 45494d3

Browse files
committed
Creation of the Norm Protocol
1 parent 7c49d52 commit 45494d3

File tree

6 files changed

+166
-10
lines changed

6 files changed

+166
-10
lines changed

lib/matplotlib/colorizer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def norm(self):
9090

9191
@norm.setter
9292
def norm(self, norm):
93-
_api.check_isinstance((colors.Normalize, str, None), norm=norm)
93+
_api.check_isinstance((colors.Norm, str, None), norm=norm)
9494
if norm is None:
9595
norm = colors.Normalize()
9696
elif isinstance(norm, str):

lib/matplotlib/colorizer.pyi

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ class Colorizer:
1010
def __init__(
1111
self,
1212
cmap: str | colors.Colormap | None = ...,
13-
norm: str | colors.Normalize | None = ...,
13+
norm: str | colors.Norm | None = ...,
1414
) -> None: ...
1515
@property
16-
def norm(self) -> colors.Normalize: ...
16+
def norm(self) -> colors.Norm: ...
1717
@norm.setter
18-
def norm(self, norm: colors.Normalize | str | None) -> None: ...
18+
def norm(self, norm: colors.Norm | str | None) -> None: ...
1919
def to_rgba(
2020
self,
2121
x: np.ndarray,
@@ -63,18 +63,18 @@ class _ColorizerInterface:
6363
def get_cmap(self) -> colors.Colormap: ...
6464
def set_cmap(self, cmap: str | colors.Colormap) -> None: ...
6565
@property
66-
def norm(self) -> colors.Normalize: ...
66+
def norm(self) -> colors.Norm: ...
6767
@norm.setter
68-
def norm(self, norm: colors.Normalize | str | None) -> None: ...
69-
def set_norm(self, norm: colors.Normalize | str | None) -> None: ...
68+
def norm(self, norm: colors.Norm | str | None) -> None: ...
69+
def set_norm(self, norm: colors.Norm | str | None) -> None: ...
7070
def autoscale(self) -> None: ...
7171
def autoscale_None(self) -> None: ...
7272

7373

7474
class _ScalarMappable(_ColorizerInterface):
7575
def __init__(
7676
self,
77-
norm: colors.Normalize | None = ...,
77+
norm: colors.Norm | None = ...,
7878
cmap: str | colors.Colormap | None = ...,
7979
*,
8080
colorizer: Colorizer | None = ...,

lib/matplotlib/colors.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141

4242
import base64
4343
from collections.abc import Sequence, Mapping
44+
from typing import Protocol, runtime_checkable
4445
import functools
4546
import importlib
4647
import inspect
@@ -2257,6 +2258,84 @@ def _init(self):
22572258
self._isinit = True
22582259

22592260

2261+
@runtime_checkable
2262+
class Norm(Protocol):
2263+
2264+
@property
2265+
def vmin(self):
2266+
"""Lower limit of the input data interval; maps to 0."""
2267+
...
2268+
2269+
2270+
@property
2271+
def vmax(self):
2272+
"""Upper limit of the input data interval; maps to 1."""
2273+
...
2274+
2275+
@property
2276+
def clip(self):
2277+
"""
2278+
Determines the behavior for mapping values outside the range ``[vmin, vmax]``.
2279+
2280+
See the *clip* parameter in `.Normalize`.
2281+
"""
2282+
...
2283+
2284+
def _changed(self):
2285+
"""
2286+
Call this whenever the norm is changed to notify all the
2287+
callback listeners to the 'changed' signal.
2288+
"""
2289+
...
2290+
2291+
2292+
def __call__(self, value, clip=None):
2293+
"""
2294+
Normalize the data and return the normalized data.
2295+
2296+
Parameters
2297+
----------
2298+
value
2299+
Data to normalize.
2300+
clip : bool, optional
2301+
See the description of the parameter *clip* in `.Normalize`.
2302+
2303+
If ``None``, defaults to ``self.clip`` (which defaults to
2304+
``False``).
2305+
2306+
Notes
2307+
-----
2308+
If not already initialized, ``self.vmin`` and ``self.vmax`` are
2309+
initialized using ``self.autoscale_None(value)``.
2310+
"""
2311+
...
2312+
2313+
def inverse(self, value):
2314+
"""
2315+
Maps the normalized value (i.e., index in the colormap) back to image
2316+
data value.
2317+
2318+
Parameters
2319+
----------
2320+
value
2321+
Normalized value.
2322+
"""
2323+
...
2324+
2325+
2326+
def autoscale(self, A):
2327+
"""Set *vmin*, *vmax* to min, max of *A*."""
2328+
...
2329+
2330+
def autoscale_None(self, A):
2331+
"""If *vmin* or *vmax* are not set, use the min/max of *A* to set them."""
2332+
...
2333+
2334+
def scaled(self):
2335+
"""Return whether *vmin* and *vmax* are both set."""
2336+
...
2337+
2338+
22602339
class Normalize:
22612340
"""
22622341
A class which, when called, maps values within the interval

lib/matplotlib/colors.pyi

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence
22
from matplotlib import cbook, scale
33
import re
44

5-
from typing import Any, Literal, overload
5+
from typing import Any, Literal, overload, Protocol, runtime_checkable
66
from .typing import ColorType
77

88
import numpy as np
@@ -249,6 +249,33 @@ class BivarColormapFromImage(BivarColormap):
249249
origin: Sequence[float] = ..., name: str = ...
250250
) -> None: ...
251251

252+
@runtime_checkable
253+
class Norm(Protocol):
254+
callbacks: cbook.CallbackRegistry
255+
@property
256+
def vmin(self) -> float | None: ...
257+
@property
258+
def vmax(self) -> float | None: ...
259+
@property
260+
def clip(self) -> bool: ...
261+
@clip.setter
262+
def clip(self, value: bool) -> None: ...
263+
@overload
264+
def __call__(self, value: float, clip: bool | None = ...) -> float: ...
265+
@overload
266+
def __call__(self, value: np.ndarray, clip: bool | None = ...) -> np.ma.MaskedArray: ...
267+
@overload
268+
def __call__(self, value: ArrayLike, clip: bool | None = ...) -> ArrayLike: ...
269+
@overload
270+
def inverse(self, value: float) -> float: ...
271+
@overload
272+
def inverse(self, value: np.ndarray) -> np.ma.MaskedArray: ...
273+
@overload
274+
def inverse(self, value: ArrayLike) -> ArrayLike: ...
275+
def autoscale(self, A: ArrayLike) -> None: ...
276+
def autoscale_None(self, A: ArrayLike) -> None: ...
277+
def scaled(self) -> bool: ...
278+
252279
class Normalize:
253280
callbacks: cbook.CallbackRegistry
254281
def __init__(

lib/matplotlib/tests/test_colors.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
import matplotlib.pyplot as plt
2020
import matplotlib.scale as mscale
2121
from matplotlib.rcsetup import cycler
22-
from matplotlib.testing.decorators import image_comparison, check_figures_equal
22+
from matplotlib.testing.decorators import (image_comparison, check_figures_equal,
23+
remove_ticks_and_titles)
2324
from matplotlib.colors import is_color_like, to_rgba_array, ListedColormap
2425

2526

@@ -1829,3 +1830,52 @@ def test_LinearSegmentedColormap_from_list_value_color_tuple():
18291830
cmap([value for value, _ in value_color_tuples]),
18301831
to_rgba_array([color for _, color in value_color_tuples]),
18311832
)
1833+
1834+
1835+
@image_comparison(['test_norm_protocol.png'])
1836+
def test_norm_protocol():
1837+
class CustomHalfNorm:
1838+
def __init__(self):
1839+
self.callbacks = mpl.cbook.CallbackRegistry(signals=["changed"])
1840+
1841+
@property
1842+
def vmin(self):
1843+
return 0
1844+
1845+
@property
1846+
def vmax(self):
1847+
return 1
1848+
1849+
@property
1850+
def clip(self):
1851+
return False
1852+
1853+
def _changed(self):
1854+
self.callbacks.process('changed')
1855+
1856+
def __call__(self, value, clip=None):
1857+
return value/2
1858+
1859+
def inverse(self, value):
1860+
return value
1861+
1862+
1863+
def autoscale(self, A):
1864+
pass
1865+
1866+
def autoscale_None(self, A):
1867+
pass
1868+
1869+
def scaled(self):
1870+
return True
1871+
1872+
fig, axes = plt.subplots(2,2)
1873+
1874+
r = np.linspace(-1, 3, 16*16).reshape((16,16))
1875+
norm = CustomHalfNorm()
1876+
colorizer = mpl.colorizer.Colorizer(cmap='viridis', norm=norm)
1877+
c = axes[0,0].imshow(r, colorizer=colorizer)
1878+
axes[0,1].pcolor(r, colorizer=colorizer)
1879+
axes[1,0].contour(r, colorizer=colorizer)
1880+
axes[1,1].contourf(r, colorizer=colorizer)
1881+
remove_ticks_and_titles(fig)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy