anyplotlib 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- anyplotlib/__init__.py +53 -0
- anyplotlib/_base_plot.py +229 -0
- anyplotlib/_electron.py +74 -0
- anyplotlib/_repr_utils.py +345 -0
- anyplotlib/_utils.py +194 -0
- anyplotlib/axes/__init__.py +6 -0
- anyplotlib/axes/_axes.py +510 -0
- anyplotlib/axes/_inset_axes.py +126 -0
- anyplotlib/callbacks.py +328 -0
- anyplotlib/embed.py +194 -0
- anyplotlib/figure/__init__.py +7 -0
- anyplotlib/figure/_figure.py +633 -0
- anyplotlib/figure/_gridspec.py +99 -0
- anyplotlib/figure/_subplots.py +100 -0
- anyplotlib/figure_esm.js +6011 -0
- anyplotlib/markers.py +704 -0
- anyplotlib/plot1d/__init__.py +6 -0
- anyplotlib/plot1d/_plot1d.py +1376 -0
- anyplotlib/plot1d/_plotbar.py +436 -0
- anyplotlib/plot2d/__init__.py +5 -0
- anyplotlib/plot2d/_plot2d.py +726 -0
- anyplotlib/plot2d/_plotmesh.py +116 -0
- anyplotlib/plot3d/__init__.py +4 -0
- anyplotlib/plot3d/_plot3d.py +524 -0
- anyplotlib/plotxy/__init__.py +4 -0
- anyplotlib/plotxy/_plotxy.py +273 -0
- anyplotlib/sphinx_anywidget/__init__.py +177 -0
- anyplotlib/sphinx_anywidget/_directive.py +245 -0
- anyplotlib/sphinx_anywidget/_repr_utils.py +298 -0
- anyplotlib/sphinx_anywidget/_scraper.py +390 -0
- anyplotlib/sphinx_anywidget/_wheel_builder.py +84 -0
- anyplotlib/sphinx_anywidget/static/anywidget_bridge.js +1099 -0
- anyplotlib/sphinx_anywidget/static/anywidget_overlay.css +100 -0
- anyplotlib/widgets/__init__.py +18 -0
- anyplotlib/widgets/_base.py +218 -0
- anyplotlib/widgets/_widgets1d.py +109 -0
- anyplotlib/widgets/_widgets2d.py +141 -0
- anyplotlib/widgets/_widgets3d.py +50 -0
- anyplotlib-0.1.0.dist-info/METADATA +160 -0
- anyplotlib-0.1.0.dist-info/RECORD +42 -0
- anyplotlib-0.1.0.dist-info/WHEEL +4 -0
- anyplotlib-0.1.0.dist-info/licenses/LICENSE +21 -0
anyplotlib/_utils.py
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"""
|
|
2
|
+
_utils.py
|
|
3
|
+
=========
|
|
4
|
+
Shared low-level utilities used across plot subpackages.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
_LINESTYLE_ALIASES: dict[str, str] = {
|
|
12
|
+
"-": "solid",
|
|
13
|
+
"--": "dashed",
|
|
14
|
+
":": "dotted",
|
|
15
|
+
"-.": "dashdot",
|
|
16
|
+
"solid": "solid",
|
|
17
|
+
"dashed": "dashed",
|
|
18
|
+
"dotted": "dotted",
|
|
19
|
+
"dashdot": "dashdot",
|
|
20
|
+
"step-mid": "step-mid",
|
|
21
|
+
"steps-mid": "step-mid",
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _arr_to_b64(arr: np.ndarray, dtype) -> str:
|
|
26
|
+
"""Encode a NumPy array as base-64 (little-endian raw bytes).
|
|
27
|
+
|
|
28
|
+
Uses little-endian byte order so the result is compatible with
|
|
29
|
+
JavaScript's ``Float64Array`` / ``Float32Array`` / ``Int32Array``
|
|
30
|
+
on all modern platforms (x86, ARM).
|
|
31
|
+
"""
|
|
32
|
+
import base64
|
|
33
|
+
le_dtype = np.dtype(dtype).newbyteorder("<")
|
|
34
|
+
return base64.b64encode(np.asarray(arr).astype(le_dtype).tobytes()).decode("ascii")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _norm_linestyle(ls: str) -> str:
|
|
38
|
+
"""Normalise a linestyle name or shorthand to its canonical form.
|
|
39
|
+
|
|
40
|
+
Accepted values
|
|
41
|
+
---------------
|
|
42
|
+
``"solid"`` / ``"-"``, ``"dashed"`` / ``"--"``,
|
|
43
|
+
``"dotted"`` / ``":"``, ``"dashdot"`` / ``"-."``.
|
|
44
|
+
|
|
45
|
+
Raises
|
|
46
|
+
------
|
|
47
|
+
ValueError
|
|
48
|
+
If *ls* is not a recognised name or shorthand.
|
|
49
|
+
"""
|
|
50
|
+
canonical = _LINESTYLE_ALIASES.get(ls)
|
|
51
|
+
if canonical is None:
|
|
52
|
+
raise ValueError(
|
|
53
|
+
f"Unknown linestyle {ls!r}. Expected one of: "
|
|
54
|
+
"'solid', 'dashed', 'dotted', 'dashdot', 'step-mid' (alias: 'steps-mid') "
|
|
55
|
+
"or shorthands '-', '--', ':', '-.'."
|
|
56
|
+
)
|
|
57
|
+
return canonical
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _to_rgba_u8(data: np.ndarray) -> np.ndarray:
|
|
61
|
+
"""Convert an (H, W, 3|4) colour array to uint8 RGBA.
|
|
62
|
+
|
|
63
|
+
Floats are interpreted as 0–1 (scaled ×255) when the max is ≤ 1,
|
|
64
|
+
otherwise as 0–255 and clipped. A missing alpha channel becomes 255.
|
|
65
|
+
"""
|
|
66
|
+
data = np.asarray(data)
|
|
67
|
+
if data.ndim != 3 or data.shape[2] not in (3, 4):
|
|
68
|
+
raise ValueError(f"expected (H, W, 3|4) colour array, got {data.shape}")
|
|
69
|
+
if data.dtype != np.uint8:
|
|
70
|
+
arr = data.astype(np.float64)
|
|
71
|
+
if arr.max() <= 1.0:
|
|
72
|
+
arr = arr * 255.0
|
|
73
|
+
data = np.clip(arr, 0, 255).astype(np.uint8)
|
|
74
|
+
if data.shape[2] == 3:
|
|
75
|
+
rgba = np.empty((*data.shape[:2], 4), dtype=np.uint8)
|
|
76
|
+
rgba[..., :3] = data
|
|
77
|
+
rgba[..., 3] = 255
|
|
78
|
+
return rgba
|
|
79
|
+
return np.ascontiguousarray(data)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _normalize_image(data: np.ndarray):
|
|
83
|
+
"""Normalise data to uint8, returning (img_u8, vmin, vmax)."""
|
|
84
|
+
img = data.astype(np.float64, copy=False)
|
|
85
|
+
vmin = float(np.nanmin(img))
|
|
86
|
+
vmax = float(np.nanmax(img))
|
|
87
|
+
if vmax > vmin:
|
|
88
|
+
buf = np.empty_like(img)
|
|
89
|
+
np.subtract(img, vmin, out=buf)
|
|
90
|
+
np.divide(buf, vmax - vmin, out=buf)
|
|
91
|
+
np.multiply(buf, 255.0, out=buf)
|
|
92
|
+
img_u8 = buf.astype(np.uint8)
|
|
93
|
+
else:
|
|
94
|
+
img_u8 = np.zeros(data.shape, dtype=np.uint8)
|
|
95
|
+
return img_u8, vmin, vmax
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
# Mapping from common matplotlib colormap names to their nearest colorcet
|
|
99
|
+
# equivalents so callers can keep using familiar names without any matplotlib
|
|
100
|
+
# dependency.
|
|
101
|
+
_CMAP_ALIASES: dict[str, str] = {
|
|
102
|
+
"viridis": "bmy", # blue→magenta→yellow, perceptually uniform
|
|
103
|
+
"plasma": "fire", # warm sequential (dark→bright)
|
|
104
|
+
"inferno": "kb", # dark→blue→white
|
|
105
|
+
"magma": "kbc", # dark→blue→cyan sequential
|
|
106
|
+
"cividis": "bgy", # accessible, blue→green→yellow sequential
|
|
107
|
+
"hot": "fire",
|
|
108
|
+
"afmhot": "fire",
|
|
109
|
+
"jet": "rainbow4",
|
|
110
|
+
"hsv": "rainbow4",
|
|
111
|
+
"nipy_spectral": "rainbow4",
|
|
112
|
+
"RdBu": "coolwarm",
|
|
113
|
+
"bwr": "cwr", # blue→white→red diverging
|
|
114
|
+
"seismic": "coolwarm",
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _build_colormap_lut(name: str) -> list:
|
|
119
|
+
"""Return a 256-entry ``[[r, g, b], ...]`` LUT for the named colormap.
|
|
120
|
+
|
|
121
|
+
Priority order:
|
|
122
|
+
|
|
123
|
+
1. **colorcet** — preferred; common matplotlib names are remapped via
|
|
124
|
+
:data:`_CMAP_ALIASES` so callers can use ``"viridis"`` etc.
|
|
125
|
+
2. **matplotlib** — fallback when colorcet is not installed (e.g. in
|
|
126
|
+
Pyodide before micropip finishes, or in minimal test environments).
|
|
127
|
+
3. **Built-in gray ramp** — final fallback for unknown names.
|
|
128
|
+
"""
|
|
129
|
+
# ── 1. Try colorcet ───────────────────────────────────────────────────
|
|
130
|
+
try:
|
|
131
|
+
import colorcet as cc
|
|
132
|
+
resolved = _CMAP_ALIASES.get(name, name)
|
|
133
|
+
palette = cc.palette.get(resolved)
|
|
134
|
+
if palette is not None:
|
|
135
|
+
n = len(palette)
|
|
136
|
+
lut: list = []
|
|
137
|
+
for i in range(256):
|
|
138
|
+
h = palette[int(round(i * (n - 1) / 255))].lstrip("#")
|
|
139
|
+
lut.append([int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16)])
|
|
140
|
+
return lut
|
|
141
|
+
except Exception:
|
|
142
|
+
pass
|
|
143
|
+
|
|
144
|
+
# ── 2. Try matplotlib ─────────────────────────────────────────────────
|
|
145
|
+
try:
|
|
146
|
+
import matplotlib
|
|
147
|
+
import numpy as _np
|
|
148
|
+
cmap = matplotlib.colormaps[name]
|
|
149
|
+
rgba = cmap(_np.linspace(0, 1, 256))
|
|
150
|
+
return [[int(r * 255), int(g * 255), int(b * 255)]
|
|
151
|
+
for r, g, b, _ in rgba]
|
|
152
|
+
except Exception:
|
|
153
|
+
pass
|
|
154
|
+
|
|
155
|
+
# ── 3. Gray ramp fallback ─────────────────────────────────────────────
|
|
156
|
+
return [[v, v, v] for v in range(256)]
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _resample_mesh(data: np.ndarray, x_edges, y_edges) -> np.ndarray:
|
|
160
|
+
"""Resample a mesh to a regular pixel grid via nearest-neighbour lookup.
|
|
161
|
+
|
|
162
|
+
For uniform edges this is an identity operation. For non-uniform edges
|
|
163
|
+
(e.g. log-spaced) it maps each uniform output pixel to the nearest input
|
|
164
|
+
cell, producing a visually correct linear-axis image.
|
|
165
|
+
|
|
166
|
+
Parameters
|
|
167
|
+
----------
|
|
168
|
+
data : ndarray, shape (M, N) — one value per mesh cell.
|
|
169
|
+
x_edges : array-like, length N+1 — column edge coordinates.
|
|
170
|
+
y_edges : array-like, length M+1 — row edge coordinates.
|
|
171
|
+
|
|
172
|
+
Returns
|
|
173
|
+
-------
|
|
174
|
+
ndarray, shape (M, N)
|
|
175
|
+
"""
|
|
176
|
+
rows, cols = data.shape
|
|
177
|
+
x_edges = np.asarray(x_edges, dtype=float)
|
|
178
|
+
y_edges = np.asarray(y_edges, dtype=float)
|
|
179
|
+
|
|
180
|
+
# Cell centres
|
|
181
|
+
x_c = (x_edges[:-1] + x_edges[1:]) / 2.0
|
|
182
|
+
y_c = (y_edges[:-1] + y_edges[1:]) / 2.0
|
|
183
|
+
|
|
184
|
+
# Uniform sample points (same count as original cells)
|
|
185
|
+
x_samp = np.linspace(x_c[0], x_c[-1], cols)
|
|
186
|
+
y_samp = np.linspace(y_c[0], y_c[-1], rows)
|
|
187
|
+
|
|
188
|
+
# Nearest-neighbour cell lookup via edge-sorted searchsorted
|
|
189
|
+
xi = np.searchsorted(x_edges, x_samp) - 1
|
|
190
|
+
xi = np.clip(xi, 0, cols - 1)
|
|
191
|
+
yi = np.searchsorted(y_edges, y_samp) - 1
|
|
192
|
+
yi = np.clip(yi, 0, rows - 1)
|
|
193
|
+
|
|
194
|
+
return data[np.ix_(yi, xi)]
|