anyplotlib 0.1.0b1__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.0b1.dist-info/METADATA +160 -0
- anyplotlib-0.1.0b1.dist-info/RECORD +42 -0
- anyplotlib-0.1.0b1.dist-info/WHEEL +4 -0
- anyplotlib-0.1.0b1.dist-info/licenses/LICENSE +21 -0
anyplotlib/axes/_axes.py
ADDED
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
"""
|
|
2
|
+
axes/_axes.py
|
|
3
|
+
=============
|
|
4
|
+
Grid-cell container that owns a single plot panel.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
|
|
13
|
+
from anyplotlib.plot1d import Plot1D, PlotBar
|
|
14
|
+
from anyplotlib.plot2d import Plot2D, PlotMesh
|
|
15
|
+
from anyplotlib.plot3d import Plot3D
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from anyplotlib.figure import Figure
|
|
19
|
+
from anyplotlib.figure._gridspec import SubplotSpec
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Axes:
|
|
23
|
+
"""A single grid cell in a Figure.
|
|
24
|
+
|
|
25
|
+
Returned by Figure.add_subplot() and Figure.subplots().
|
|
26
|
+
Call .imshow() or .plot() to attach a data plot and get back
|
|
27
|
+
a Plot2D or Plot1D object.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, fig: "Figure", spec: "SubplotSpec"): # noqa: F821
|
|
31
|
+
self._fig = fig
|
|
32
|
+
self._spec = spec
|
|
33
|
+
self._plot: "Plot1D | Plot2D | None" = None
|
|
34
|
+
|
|
35
|
+
# ------------------------------------------------------------------
|
|
36
|
+
def imshow(self, data: np.ndarray,
|
|
37
|
+
axes: list | None = None,
|
|
38
|
+
units: str = "px",
|
|
39
|
+
cmap: str | None = None,
|
|
40
|
+
vmin: float | None = None,
|
|
41
|
+
vmax: float | None = None,
|
|
42
|
+
origin: str = "upper") -> "Plot2D":
|
|
43
|
+
"""Attach a 2-D image to this axes cell.
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
data : np.ndarray, shape (H, W) or (H, W, 3|4)
|
|
48
|
+
Image data. 2-D arrays are colormapped. ``(H, W, 3)`` /
|
|
49
|
+
``(H, W, 4)`` arrays render as true-colour RGB(A): uint8 values
|
|
50
|
+
are used directly; floats are interpreted as 0–1 (or 0–255 when
|
|
51
|
+
the max exceeds 1). ``cmap``/``vmin``/``vmax`` and the colorbar
|
|
52
|
+
do not apply to RGB images.
|
|
53
|
+
axes : [x_axis, y_axis], optional
|
|
54
|
+
Physical coordinate arrays for each axis.
|
|
55
|
+
units : str, optional
|
|
56
|
+
Axis units label. Default ``"px"``.
|
|
57
|
+
cmap : str, optional
|
|
58
|
+
Colormap name (e.g. ``"viridis"``, ``"inferno"``).
|
|
59
|
+
Defaults to ``"gray"``.
|
|
60
|
+
vmin, vmax : float, optional
|
|
61
|
+
Colormap clipping limits in data units. Values outside this
|
|
62
|
+
range are clamped to the colormap endpoints. Defaults to the
|
|
63
|
+
data min / max.
|
|
64
|
+
origin : ``"upper"`` | ``"lower"``, optional
|
|
65
|
+
Where row 0 of the array is placed. ``"upper"`` (default)
|
|
66
|
+
puts row 0 at the top, matching the usual image convention.
|
|
67
|
+
``"lower"`` puts row 0 at the bottom, matching the matplotlib
|
|
68
|
+
convention for matrices / scientific plots.
|
|
69
|
+
|
|
70
|
+
Returns
|
|
71
|
+
-------
|
|
72
|
+
Plot2D
|
|
73
|
+
"""
|
|
74
|
+
x_axis = axes[0] if axes and len(axes) > 0 else None
|
|
75
|
+
y_axis = axes[1] if axes and len(axes) > 1 else None
|
|
76
|
+
plot = Plot2D(data, x_axis=x_axis, y_axis=y_axis, units=units,
|
|
77
|
+
cmap=cmap, vmin=vmin, vmax=vmax, origin=origin)
|
|
78
|
+
self._attach(plot)
|
|
79
|
+
return plot
|
|
80
|
+
|
|
81
|
+
def pcolormesh(self, data: np.ndarray,
|
|
82
|
+
x_edges=None, y_edges=None,
|
|
83
|
+
units: str = "") -> "PlotMesh":
|
|
84
|
+
"""Attach a 2-D mesh to this axes cell using edge coordinates.
|
|
85
|
+
|
|
86
|
+
Follows the matplotlib pcolormesh convention: x_edges and y_edges
|
|
87
|
+
are the cell *edge* coordinates, so they have length N+1 and M+1
|
|
88
|
+
respectively for an (M, N) data array.
|
|
89
|
+
|
|
90
|
+
Parameters
|
|
91
|
+
----------
|
|
92
|
+
data : np.ndarray shape (M, N)
|
|
93
|
+
x_edges : array-like, length N+1, optional
|
|
94
|
+
Column edge coordinates. Defaults to ``np.arange(N+1)``.
|
|
95
|
+
y_edges : array-like, length M+1, optional
|
|
96
|
+
Row edge coordinates. Defaults to ``np.arange(M+1)``.
|
|
97
|
+
units : str, optional
|
|
98
|
+
|
|
99
|
+
Returns
|
|
100
|
+
-------
|
|
101
|
+
PlotMesh
|
|
102
|
+
"""
|
|
103
|
+
plot = PlotMesh(data, x_edges=x_edges, y_edges=y_edges, units=units)
|
|
104
|
+
self._attach(plot)
|
|
105
|
+
return plot
|
|
106
|
+
|
|
107
|
+
def plot_surface(self, X, Y, Z, *,
|
|
108
|
+
colormap: str = "viridis",
|
|
109
|
+
x_label: str = "x", y_label: str = "y", z_label: str = "z",
|
|
110
|
+
azimuth: float = -60.0, elevation: float = 30.0,
|
|
111
|
+
zoom: float = 1.0) -> "Plot3D":
|
|
112
|
+
"""Attach a 3-D surface to this axes cell.
|
|
113
|
+
|
|
114
|
+
Parameters
|
|
115
|
+
----------
|
|
116
|
+
X, Y, Z : array-like
|
|
117
|
+
2-D grid arrays of the same shape (e.g. from ``np.meshgrid``),
|
|
118
|
+
or 1-D centre arrays for X/Y with a 2-D Z.
|
|
119
|
+
colormap : str, optional Matplotlib colormap name. Default ``'viridis'``.
|
|
120
|
+
x_label, y_label, z_label : str, optional Axis labels.
|
|
121
|
+
azimuth, elevation : float, optional Initial camera angles in degrees.
|
|
122
|
+
zoom : float, optional Initial zoom factor.
|
|
123
|
+
|
|
124
|
+
Returns
|
|
125
|
+
-------
|
|
126
|
+
Plot3D
|
|
127
|
+
"""
|
|
128
|
+
plot = Plot3D("surface", X, Y, Z, colormap=colormap,
|
|
129
|
+
x_label=x_label, y_label=y_label, z_label=z_label,
|
|
130
|
+
azimuth=azimuth, elevation=elevation, zoom=zoom)
|
|
131
|
+
self._attach(plot)
|
|
132
|
+
return plot
|
|
133
|
+
|
|
134
|
+
def scatter3d(self, x, y, z, *,
|
|
135
|
+
color: str = "#4fc3f7",
|
|
136
|
+
colors=None,
|
|
137
|
+
point_size: float = 4.0,
|
|
138
|
+
x_label: str = "x", y_label: str = "y", z_label: str = "z",
|
|
139
|
+
azimuth: float = -60.0, elevation: float = 30.0,
|
|
140
|
+
zoom: float = 1.0,
|
|
141
|
+
bounds=None,
|
|
142
|
+
gpu: str | bool = "auto") -> "Plot3D":
|
|
143
|
+
"""Attach a 3-D scatter plot to this axes cell.
|
|
144
|
+
|
|
145
|
+
Parameters
|
|
146
|
+
----------
|
|
147
|
+
x, y, z : array-like, shape (N,) Point coordinates.
|
|
148
|
+
color : str, optional CSS colour for all points.
|
|
149
|
+
colors : list of "#rrggbb" or (N, 3) array, optional
|
|
150
|
+
Per-point colours (overrides *color*). Floats are 0–1.
|
|
151
|
+
point_size : float, optional Radius of each point in pixels.
|
|
152
|
+
x_label, y_label, z_label : str, optional Axis labels.
|
|
153
|
+
azimuth, elevation : float, optional Initial camera angles in degrees.
|
|
154
|
+
zoom : float, optional Initial zoom factor.
|
|
155
|
+
bounds : ((xmin, xmax), (ymin, ymax), (zmin, zmax)), optional
|
|
156
|
+
Fix the axes bounds instead of fitting them to the data — keeps
|
|
157
|
+
the origin and scale stable, e.g. ``((-1, 1),) * 3`` for unit
|
|
158
|
+
vectors on a sphere.
|
|
159
|
+
gpu : ``"auto"`` | bool, optional
|
|
160
|
+
WebGPU acceleration policy. ``"auto"`` (default) renders on the
|
|
161
|
+
GPU when available and the cloud exceeds ~20k points, else
|
|
162
|
+
Canvas2D; ``True`` always attempts GPU; ``False`` forces Canvas2D.
|
|
163
|
+
Falls back silently when WebGPU is unavailable — check
|
|
164
|
+
:attr:`Plot3D.gpu_active` for the actual path.
|
|
165
|
+
|
|
166
|
+
Returns
|
|
167
|
+
-------
|
|
168
|
+
Plot3D
|
|
169
|
+
"""
|
|
170
|
+
plot = Plot3D("scatter", x, y, z, color=color, colors=colors,
|
|
171
|
+
point_size=point_size,
|
|
172
|
+
x_label=x_label, y_label=y_label, z_label=z_label,
|
|
173
|
+
azimuth=azimuth, elevation=elevation, zoom=zoom,
|
|
174
|
+
bounds=bounds, gpu=gpu)
|
|
175
|
+
self._attach(plot)
|
|
176
|
+
return plot
|
|
177
|
+
|
|
178
|
+
def voxels(self, x, y, z, *,
|
|
179
|
+
colors=None,
|
|
180
|
+
color: str = "#4fc3f7",
|
|
181
|
+
size: float = 1.0,
|
|
182
|
+
alpha: float = 0.3,
|
|
183
|
+
x_label: str = "x", y_label: str = "y", z_label: str = "z",
|
|
184
|
+
azimuth: float = -60.0, elevation: float = 30.0,
|
|
185
|
+
zoom: float = 1.0,
|
|
186
|
+
bounds=None,
|
|
187
|
+
gpu: str | bool = "auto") -> "Plot3D":
|
|
188
|
+
"""Attach a 3-D voxel plot: shaded translucent cubes at the centres.
|
|
189
|
+
|
|
190
|
+
Designed for volumetric grain/label maps. Add draggable
|
|
191
|
+
:class:`~anyplotlib.PlaneWidget` slice selectors with
|
|
192
|
+
``plot.add_widget("plane", axis=..., position=...)`` — voxels lying
|
|
193
|
+
on a plane render at ``voxel_slice_alpha`` (more opaque) so the
|
|
194
|
+
selected slice pops out of the translucent volume.
|
|
195
|
+
|
|
196
|
+
**Large volumes** With WebGPU (``gpu="auto"``, the default, active
|
|
197
|
+
above ~8k cubes when a GPU is present) hundreds of thousands of
|
|
198
|
+
voxels render interactively via instancing. On the Canvas2D
|
|
199
|
+
fallback the budget is ~20k cubes (~3–6 µs each); a warning is
|
|
200
|
+
emitted above that *only when* ``gpu=False``. For volumes too large
|
|
201
|
+
even for the GPU (e.g. a 512 × 512 × 300 tomogram = 78M voxels),
|
|
202
|
+
downsample with stride slicing (``vol[::s, ::s, ::s]``) or draw only
|
|
203
|
+
grain-boundary voxels, and pair the 3-D overview with linked
|
|
204
|
+
full-resolution 2-D slice panels — the voxel grain explorer example
|
|
205
|
+
demonstrates this pattern.
|
|
206
|
+
|
|
207
|
+
Parameters
|
|
208
|
+
----------
|
|
209
|
+
x, y, z : array-like, shape (N,)
|
|
210
|
+
Voxel centre coordinates.
|
|
211
|
+
colors : list of "#rrggbb" or (N, 3) array, optional
|
|
212
|
+
Per-voxel colours (overrides *color*). Floats are 0–1.
|
|
213
|
+
color : str, optional Single CSS colour when *colors* is omitted.
|
|
214
|
+
size : float, optional Cube edge length in data units. Default 1.
|
|
215
|
+
alpha : float, optional
|
|
216
|
+
Base voxel opacity (0–1). Default 0.3. See also
|
|
217
|
+
:meth:`Plot3D.set_voxel_alpha`.
|
|
218
|
+
x_label, y_label, z_label : str, optional Axis labels.
|
|
219
|
+
azimuth, elevation : float, optional Initial camera angles in degrees.
|
|
220
|
+
zoom : float, optional Initial zoom factor.
|
|
221
|
+
bounds : ((xmin, xmax), (ymin, ymax), (zmin, zmax)), optional
|
|
222
|
+
Fix the axes bounds instead of fitting them to the data.
|
|
223
|
+
gpu : ``"auto"`` | bool, optional
|
|
224
|
+
WebGPU acceleration policy. ``"auto"`` (default) renders cubes
|
|
225
|
+
on the GPU when available and the set exceeds ~8k; ``True`` always
|
|
226
|
+
attempts GPU; ``False`` forces Canvas2D. Falls back silently when
|
|
227
|
+
WebGPU is unavailable — see :attr:`Plot3D.gpu_active`.
|
|
228
|
+
|
|
229
|
+
Returns
|
|
230
|
+
-------
|
|
231
|
+
Plot3D
|
|
232
|
+
"""
|
|
233
|
+
plot = Plot3D("voxels", x, y, z, color=color, colors=colors,
|
|
234
|
+
voxel_size=size, alpha=alpha,
|
|
235
|
+
x_label=x_label, y_label=y_label, z_label=z_label,
|
|
236
|
+
azimuth=azimuth, elevation=elevation, zoom=zoom,
|
|
237
|
+
bounds=bounds, gpu=gpu)
|
|
238
|
+
self._attach(plot)
|
|
239
|
+
return plot
|
|
240
|
+
|
|
241
|
+
def plot3d(self, x, y, z, *,
|
|
242
|
+
color: str = "#4fc3f7",
|
|
243
|
+
linewidth: float = 1.5,
|
|
244
|
+
x_label: str = "x", y_label: str = "y", z_label: str = "z",
|
|
245
|
+
azimuth: float = -60.0, elevation: float = 30.0,
|
|
246
|
+
zoom: float = 1.0) -> "Plot3D":
|
|
247
|
+
"""Attach a 3-D line plot to this axes cell.
|
|
248
|
+
|
|
249
|
+
Parameters
|
|
250
|
+
----------
|
|
251
|
+
x, y, z : array-like, shape (N,) Point coordinates along the line.
|
|
252
|
+
color : str, optional CSS colour.
|
|
253
|
+
linewidth : float, optional Stroke width in pixels.
|
|
254
|
+
x_label, y_label, z_label : str, optional Axis labels.
|
|
255
|
+
azimuth, elevation : float, optional Initial camera angles in degrees.
|
|
256
|
+
zoom : float, optional Initial zoom factor.
|
|
257
|
+
|
|
258
|
+
Returns
|
|
259
|
+
-------
|
|
260
|
+
Plot3D
|
|
261
|
+
"""
|
|
262
|
+
plot = Plot3D("line", x, y, z, color=color, linewidth=linewidth,
|
|
263
|
+
x_label=x_label, y_label=y_label, z_label=z_label,
|
|
264
|
+
azimuth=azimuth, elevation=elevation, zoom=zoom)
|
|
265
|
+
self._attach(plot)
|
|
266
|
+
return plot
|
|
267
|
+
|
|
268
|
+
def plot(self, data: np.ndarray,
|
|
269
|
+
axes: list | None = None,
|
|
270
|
+
units: str = "px",
|
|
271
|
+
y_units: str = "",
|
|
272
|
+
color: str = "#4fc3f7",
|
|
273
|
+
linewidth: float = 1.5,
|
|
274
|
+
linestyle: str = "solid",
|
|
275
|
+
ls: str | None = None,
|
|
276
|
+
alpha: float = 1.0,
|
|
277
|
+
marker: str = "none",
|
|
278
|
+
markersize: float = 4.0,
|
|
279
|
+
label: str = "",
|
|
280
|
+
yscale: str = "linear") -> "Plot1D":
|
|
281
|
+
"""Attach a 1-D line to this axes cell.
|
|
282
|
+
|
|
283
|
+
Parameters
|
|
284
|
+
----------
|
|
285
|
+
data : array-like, shape (N,)
|
|
286
|
+
Y values. Must be 1-D.
|
|
287
|
+
axes : list, optional
|
|
288
|
+
``[x_axis]`` — a one-element list containing the x-coordinates
|
|
289
|
+
(shape ``(N,)``). If omitted the x-axis defaults to
|
|
290
|
+
``0, 1, …, N-1``.
|
|
291
|
+
units : str, optional
|
|
292
|
+
Label for the x-axis (e.g. ``"eV"``, ``"s"``). Default
|
|
293
|
+
``"px"``.
|
|
294
|
+
y_units : str, optional
|
|
295
|
+
Label for the y-axis. Default ``""`` (no label).
|
|
296
|
+
color : str, optional
|
|
297
|
+
CSS colour string for the line (hex, ``rgb()``, named colour,
|
|
298
|
+
etc.). Default ``"#4fc3f7"``.
|
|
299
|
+
linewidth : float, optional
|
|
300
|
+
Stroke width in pixels. Default ``1.5``.
|
|
301
|
+
linestyle : str, optional
|
|
302
|
+
Dash pattern. Accepted values: ``"solid"`` (``"-"``),
|
|
303
|
+
``"dashed"`` (``"--"``), ``"dotted"`` (``":"``),
|
|
304
|
+
``"dashdot"`` (``"-."``) . Default ``"solid"``.
|
|
305
|
+
ls : str, optional
|
|
306
|
+
Short alias for *linestyle*. Takes precedence if both are given.
|
|
307
|
+
alpha : float, optional
|
|
308
|
+
Line opacity in the range 0–1. Default ``1.0`` (fully opaque).
|
|
309
|
+
marker : str, optional
|
|
310
|
+
Per-point marker symbol. Supported values: ``"o"`` (circle),
|
|
311
|
+
``"s"`` (square), ``"^"`` (triangle-up), ``"v"`` (triangle-down),
|
|
312
|
+
``"D"`` (diamond), ``"+"`` (plus), ``"x"`` (cross),
|
|
313
|
+
``"none"`` (no markers). Default ``"none"``.
|
|
314
|
+
markersize : float, optional
|
|
315
|
+
Marker radius / half-side in pixels. Default ``4.0``.
|
|
316
|
+
label : str, optional
|
|
317
|
+
Legend label. A legend is only drawn when at least one line has
|
|
318
|
+
a non-empty label. Default ``""`` (no legend entry).
|
|
319
|
+
|
|
320
|
+
Returns
|
|
321
|
+
-------
|
|
322
|
+
Plot1D
|
|
323
|
+
Live plot object. Call methods on it to update data, add
|
|
324
|
+
overlays, register callbacks, etc.
|
|
325
|
+
|
|
326
|
+
Examples
|
|
327
|
+
--------
|
|
328
|
+
Basic sine wave with a physical x-axis::
|
|
329
|
+
|
|
330
|
+
import numpy as np
|
|
331
|
+
import anyplotlib as apl
|
|
332
|
+
|
|
333
|
+
x = np.linspace(0, 4 * np.pi, 512)
|
|
334
|
+
fig, ax = apl.subplots(1, 1, figsize=(620, 320))
|
|
335
|
+
v = ax.plot(np.sin(x), axes=[x], units="rad",
|
|
336
|
+
color="#ff7043", linewidth=2, label="sin")
|
|
337
|
+
v # display in a Jupyter cell
|
|
338
|
+
|
|
339
|
+
Dashed line with semi-transparent markers::
|
|
340
|
+
|
|
341
|
+
v = ax.plot(data, linestyle="dashed", alpha=0.7,
|
|
342
|
+
marker="o", markersize=4)
|
|
343
|
+
|
|
344
|
+
Overlay a second curve with :meth:`Plot1D.add_line`::
|
|
345
|
+
|
|
346
|
+
v.add_line(np.cos(x), x_axis=x, color="#aed581", label="cos")
|
|
347
|
+
"""
|
|
348
|
+
x_axis = axes[0] if axes and len(axes) > 0 else None
|
|
349
|
+
plot = Plot1D(data, x_axis=x_axis, units=units, y_units=y_units,
|
|
350
|
+
color=color, linewidth=linewidth,
|
|
351
|
+
linestyle=ls if ls is not None else linestyle,
|
|
352
|
+
alpha=alpha, marker=marker, markersize=markersize,
|
|
353
|
+
label=label, yscale=yscale)
|
|
354
|
+
self._attach(plot)
|
|
355
|
+
return plot
|
|
356
|
+
|
|
357
|
+
def semilogy(self, data: np.ndarray,
|
|
358
|
+
axes: list | None = None, **kwargs) -> "Plot1D":
|
|
359
|
+
"""Attach a 1-D line with a logarithmic y-axis."""
|
|
360
|
+
kwargs.setdefault("yscale", "log")
|
|
361
|
+
return self.plot(data, axes=axes, **kwargs)
|
|
362
|
+
|
|
363
|
+
def axes2d(self, *, xlim=(0.0, 1.0), ylim=(0.0, 1.0), aspect=None,
|
|
364
|
+
units: str = "", y_units: str = "") -> "PlotXY": # noqa: F821
|
|
365
|
+
"""Attach a blank **data-coordinate 2-D axis** (`PlotXY`).
|
|
366
|
+
|
|
367
|
+
Unlike :meth:`plot` (a curve over a monotonic x-axis), this is a
|
|
368
|
+
coordinate canvas: set ``xlim`` / ``ylim`` (+ optional ``aspect="equal"``)
|
|
369
|
+
and draw :meth:`~anyplotlib.plotxy.PlotXY.scatter` / ``plot`` / ``fill`` /
|
|
370
|
+
``text`` as collection-style artists in data coordinates — matplotlib's
|
|
371
|
+
``transData`` + ``PathCollection`` model. Suits stereographic / IPF /
|
|
372
|
+
pole-figure style plots.
|
|
373
|
+
|
|
374
|
+
Examples
|
|
375
|
+
--------
|
|
376
|
+
>>> import anyplotlib as apl
|
|
377
|
+
>>> fig, ax = apl.subplots()
|
|
378
|
+
>>> xy = ax.axes2d(xlim=(-1, 1), ylim=(-1, 1), aspect="equal")
|
|
379
|
+
>>> xy.fill([0, 1, 0.5], [0, 0, 0.9], facecolor="#eee") # triangle
|
|
380
|
+
>>> xy.scatter([0.3], [0.3], c="#f00", s=10)
|
|
381
|
+
>>> xy.text(0.5, 0.95, r"$[111]$")
|
|
382
|
+
"""
|
|
383
|
+
from anyplotlib.plotxy import PlotXY
|
|
384
|
+
plot = PlotXY(xlim=xlim, ylim=ylim, aspect=aspect, units=units, y_units=y_units)
|
|
385
|
+
self._attach(plot)
|
|
386
|
+
return plot
|
|
387
|
+
|
|
388
|
+
def bar(self, x, height=None, width: float = 0.8, bottom: float = 0.0, *,
|
|
389
|
+
align: str = "center",
|
|
390
|
+
color: str = "#4fc3f7",
|
|
391
|
+
colors=None,
|
|
392
|
+
orient: str = "v",
|
|
393
|
+
log_scale: bool = False,
|
|
394
|
+
group_labels=None,
|
|
395
|
+
group_colors=None,
|
|
396
|
+
show_values: bool = False,
|
|
397
|
+
units: str = "",
|
|
398
|
+
y_units: str = "",
|
|
399
|
+
# ── legacy backward-compat kwargs ──────────────────────────────
|
|
400
|
+
x_labels=None,
|
|
401
|
+
x_centers=None,
|
|
402
|
+
bar_width=None,
|
|
403
|
+
baseline=None,
|
|
404
|
+
values=None) -> "PlotBar":
|
|
405
|
+
"""Attach a bar chart to this axes cell.
|
|
406
|
+
|
|
407
|
+
Signature mirrors ``matplotlib.pyplot.bar``::
|
|
408
|
+
|
|
409
|
+
ax.bar(x, height, width=0.8, bottom=0.0, ...)
|
|
410
|
+
|
|
411
|
+
Parameters
|
|
412
|
+
----------
|
|
413
|
+
x : array-like of str or numeric
|
|
414
|
+
Bar positions. Strings become category labels with auto-numeric
|
|
415
|
+
centres; numbers are used directly as bar centres.
|
|
416
|
+
height : array-like, shape ``(N,)`` or ``(N, G)``, optional
|
|
417
|
+
Bar heights. Pass a 2-D array to draw *G* grouped bars per
|
|
418
|
+
category. If omitted *x* is treated as the heights and positions
|
|
419
|
+
are generated automatically (backward-compatible call form).
|
|
420
|
+
width : float, optional
|
|
421
|
+
Bar width as a fraction of the category slot (0–1). Default ``0.8``.
|
|
422
|
+
bottom : float, optional
|
|
423
|
+
Value at which bars are rooted (baseline). Default ``0``.
|
|
424
|
+
align : ``"center"`` | ``"edge"``, optional
|
|
425
|
+
Alignment of the bar relative to its *x* position. Currently only
|
|
426
|
+
``"center"`` is rendered; stored for future use.
|
|
427
|
+
color : str, optional
|
|
428
|
+
Single CSS colour applied to every bar. Default ``"#4fc3f7"``.
|
|
429
|
+
colors : list of str, optional
|
|
430
|
+
Per-bar colour list (ungrouped) or ignored when *group_colors* is set.
|
|
431
|
+
orient : ``"v"`` | ``"h"``, optional
|
|
432
|
+
Vertical (default) or horizontal orientation.
|
|
433
|
+
log_scale : bool, optional
|
|
434
|
+
Use a logarithmic value axis. Non-positive values are clamped to
|
|
435
|
+
``1e-10`` for display. Default ``False``.
|
|
436
|
+
group_labels : list of str, optional
|
|
437
|
+
Legend labels for each group in a grouped bar chart.
|
|
438
|
+
group_colors : list of str, optional
|
|
439
|
+
CSS colours per group. Defaults to a built-in palette.
|
|
440
|
+
show_values : bool, optional
|
|
441
|
+
Draw the numeric value above / beside each bar.
|
|
442
|
+
units : str, optional
|
|
443
|
+
Label for the categorical axis.
|
|
444
|
+
y_units : str, optional
|
|
445
|
+
Label for the value axis.
|
|
446
|
+
|
|
447
|
+
Backward-compatible keyword aliases
|
|
448
|
+
------------------------------------
|
|
449
|
+
``values`` → ``height``
|
|
450
|
+
``x_centers`` → ``x``
|
|
451
|
+
``bar_width`` → ``width``
|
|
452
|
+
``baseline`` → ``bottom``
|
|
453
|
+
``x_labels`` → strings passed via ``x``
|
|
454
|
+
|
|
455
|
+
Returns
|
|
456
|
+
-------
|
|
457
|
+
PlotBar
|
|
458
|
+
"""
|
|
459
|
+
# ── legacy backward-compat resolution ─────────────────────────────
|
|
460
|
+
if height is None:
|
|
461
|
+
if values is not None:
|
|
462
|
+
height = values
|
|
463
|
+
else:
|
|
464
|
+
height = x
|
|
465
|
+
x = None
|
|
466
|
+
if baseline is not None:
|
|
467
|
+
bottom = baseline
|
|
468
|
+
if bar_width is not None:
|
|
469
|
+
width = bar_width
|
|
470
|
+
|
|
471
|
+
plot = PlotBar(x, height, width=width, bottom=bottom,
|
|
472
|
+
align=align, color=color, colors=colors,
|
|
473
|
+
orient=orient, log_scale=log_scale,
|
|
474
|
+
group_labels=group_labels, group_colors=group_colors,
|
|
475
|
+
show_values=show_values, units=units, y_units=y_units,
|
|
476
|
+
x_labels=x_labels, x_centers=x_centers)
|
|
477
|
+
self._attach(plot)
|
|
478
|
+
return plot
|
|
479
|
+
|
|
480
|
+
def _panel_id_from_spec(self) -> str:
|
|
481
|
+
"""Derive a deterministic, position-based panel ID from the SubplotSpec.
|
|
482
|
+
|
|
483
|
+
The ID is ``"p"`` followed by the first 7 hex characters of a SHA-256
|
|
484
|
+
hash of the row/col bounds, e.g. ``"p6a2f3b1"``. This is:
|
|
485
|
+
|
|
486
|
+
* **Deterministic** – the same SubplotSpec always produces the same ID
|
|
487
|
+
across Python processes and after code edits.
|
|
488
|
+
* **Starts with "p"** – satisfies the JS naming convention and makes it
|
|
489
|
+
easy to grep for panel traits (``panel_{id}_json``).
|
|
490
|
+
* **Short** – 8 characters total; safe to embed in CSS selectors.
|
|
491
|
+
"""
|
|
492
|
+
import hashlib as _hl
|
|
493
|
+
key = f"{self._spec.row_start},{self._spec.row_stop},{self._spec.col_start},{self._spec.col_stop}"
|
|
494
|
+
return "p" + _hl.sha256(key.encode()).hexdigest()[:7]
|
|
495
|
+
|
|
496
|
+
def _attach(self, plot: "Plot1D | Plot2D | PlotMesh | Plot3D | PlotBar") -> None:
|
|
497
|
+
"""Register a plot on this axes (replace any previous plot)."""
|
|
498
|
+
# Allocate a panel id if needed; reuse if replacing
|
|
499
|
+
if self._plot is not None:
|
|
500
|
+
panel_id = self._plot._id
|
|
501
|
+
else:
|
|
502
|
+
panel_id = self._panel_id_from_spec()
|
|
503
|
+
plot._id = panel_id
|
|
504
|
+
plot._fig = self._fig
|
|
505
|
+
self._plot = plot
|
|
506
|
+
self._fig._register_panel(self, plot)
|
|
507
|
+
|
|
508
|
+
def __repr__(self) -> str:
|
|
509
|
+
kind = type(self._plot).__name__ if self._plot else "empty"
|
|
510
|
+
return f"Axes(rows={self._spec.row_start}:{self._spec.row_stop}, cols={self._spec.col_start}:{self._spec.col_stop}, {kind})"
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""
|
|
2
|
+
axes/_inset_axes.py
|
|
3
|
+
===================
|
|
4
|
+
Floating overlay inset (not in the grid).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import uuid as _uuid
|
|
10
|
+
|
|
11
|
+
from anyplotlib.axes._axes import Axes
|
|
12
|
+
from anyplotlib.plot1d import PlotBar
|
|
13
|
+
from anyplotlib.plot2d import Plot2D, PlotMesh
|
|
14
|
+
from anyplotlib.plot3d import Plot3D
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _plot_kind(plot) -> str:
|
|
18
|
+
"""Return the JS panel-kind string for a plot object.
|
|
19
|
+
|
|
20
|
+
Used in ``Figure._push_layout()`` and ``InsetAxes.__repr__``.
|
|
21
|
+
"""
|
|
22
|
+
if isinstance(plot, Plot3D):
|
|
23
|
+
return "3d"
|
|
24
|
+
if isinstance(plot, (Plot2D, PlotMesh)):
|
|
25
|
+
return "2d"
|
|
26
|
+
if isinstance(plot, PlotBar):
|
|
27
|
+
return "bar"
|
|
28
|
+
return "1d"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
_VALID_CORNERS = ("top-right", "top-left", "bottom-right", "bottom-left")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class InsetAxes(Axes):
|
|
35
|
+
"""A floating inset sub-plot that overlays the main Figure grid.
|
|
36
|
+
|
|
37
|
+
Created via :meth:`Figure.add_inset`. Supports the same plot-factory
|
|
38
|
+
methods as :class:`Axes` (``imshow``, ``plot``, ``pcolormesh``, etc.).
|
|
39
|
+
|
|
40
|
+
The inset is positioned at a corner of the figure and can be minimized
|
|
41
|
+
(title bar only), maximized (expanded to fill ~72% of the figure), or
|
|
42
|
+
restored to its normal size.
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
fig : Figure
|
|
47
|
+
w_frac, h_frac : float
|
|
48
|
+
Width and height as fractions of the figure dimensions (0–1).
|
|
49
|
+
corner : str, optional
|
|
50
|
+
One of ``"top-right"``, ``"top-left"``, ``"bottom-right"``,
|
|
51
|
+
``"bottom-left"``. Default ``"top-right"``.
|
|
52
|
+
title : str, optional
|
|
53
|
+
Text shown in the inset title bar. Default ``""``.
|
|
54
|
+
|
|
55
|
+
Examples
|
|
56
|
+
--------
|
|
57
|
+
>>> fig, ax = apl.subplots(1, 1, figsize=(640, 480))
|
|
58
|
+
>>> ax.imshow(data)
|
|
59
|
+
>>> inset = fig.add_inset(0.3, 0.25, corner="top-right", title="Zoom")
|
|
60
|
+
>>> inset.imshow(data[64:128, 64:128])
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __init__(self, fig, w_frac: float, h_frac: float, *,
|
|
64
|
+
corner: str = "top-right", title: str = ""):
|
|
65
|
+
if corner not in _VALID_CORNERS:
|
|
66
|
+
raise ValueError(
|
|
67
|
+
f"corner must be one of {_VALID_CORNERS!r}, got {corner!r}"
|
|
68
|
+
)
|
|
69
|
+
# Pass a dummy SubplotSpec so Axes.__init__ doesn't fail — InsetAxes
|
|
70
|
+
# never occupies a grid cell, only overlays the figure.
|
|
71
|
+
from anyplotlib.figure._gridspec import SubplotSpec
|
|
72
|
+
super().__init__(fig, SubplotSpec(None, 0, 1, 0, 1))
|
|
73
|
+
self.w_frac = w_frac
|
|
74
|
+
self.h_frac = h_frac
|
|
75
|
+
self.corner = corner
|
|
76
|
+
self.title = title
|
|
77
|
+
self._inset_state: str = "normal"
|
|
78
|
+
|
|
79
|
+
# ── state API ─────────────────────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def inset_state(self) -> str:
|
|
83
|
+
"""Current state: ``"normal"``, ``"minimized"``, or ``"maximized"``."""
|
|
84
|
+
return self._inset_state
|
|
85
|
+
|
|
86
|
+
def minimize(self) -> None:
|
|
87
|
+
"""Collapse the inset to its title bar only (idempotent)."""
|
|
88
|
+
if self._inset_state == "minimized":
|
|
89
|
+
return
|
|
90
|
+
self._inset_state = "minimized"
|
|
91
|
+
self._fig._push_layout()
|
|
92
|
+
|
|
93
|
+
def maximize(self) -> None:
|
|
94
|
+
"""Expand the inset to ~72 % of the figure, centred (idempotent)."""
|
|
95
|
+
if self._inset_state == "maximized":
|
|
96
|
+
return
|
|
97
|
+
self._inset_state = "maximized"
|
|
98
|
+
self._fig._push_layout()
|
|
99
|
+
|
|
100
|
+
def restore(self) -> None:
|
|
101
|
+
"""Return the inset to its normal corner position (idempotent)."""
|
|
102
|
+
if self._inset_state == "normal":
|
|
103
|
+
return
|
|
104
|
+
self._inset_state = "normal"
|
|
105
|
+
self._fig._push_layout()
|
|
106
|
+
|
|
107
|
+
# ── internal ──────────────────────────────────────────────────────────
|
|
108
|
+
|
|
109
|
+
def _attach(self, plot) -> None:
|
|
110
|
+
"""Register the plot on this inset via Figure._register_inset."""
|
|
111
|
+
if self._plot is not None:
|
|
112
|
+
panel_id = self._plot._id
|
|
113
|
+
else:
|
|
114
|
+
panel_id = str(_uuid.uuid4())[:8]
|
|
115
|
+
plot._id = panel_id
|
|
116
|
+
plot._fig = self._fig
|
|
117
|
+
self._plot = plot
|
|
118
|
+
self._fig._register_inset(self, plot)
|
|
119
|
+
|
|
120
|
+
def __repr__(self) -> str:
|
|
121
|
+
kind = _plot_kind(self._plot) if self._plot else "empty"
|
|
122
|
+
return (
|
|
123
|
+
f"InsetAxes(corner={self.corner!r}, "
|
|
124
|
+
f"size=({self.w_frac:.2f}, {self.h_frac:.2f}), "
|
|
125
|
+
f"state={self._inset_state!r}, kind={kind!r})"
|
|
126
|
+
)
|