plotlive 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.
- plotlive/__init__.py +18 -0
- plotlive/_jupyter.py +127 -0
- plotlive/_parsers.py +88 -0
- plotlive/animation.py +279 -0
- plotlive/artists.py +207 -0
- plotlive/axes.py +634 -0
- plotlive/colors.py +324 -0
- plotlive/data/DejaVuSans-Bold.ttf +1 -0
- plotlive/data/DejaVuSans.ttf +1 -0
- plotlive/data/FreeSansBold.ttf +0 -0
- plotlive/drawing.py +168 -0
- plotlive/events.py +226 -0
- plotlive/figure.py +94 -0
- plotlive/fonts.py +73 -0
- plotlive/pyplot.py +333 -0
- plotlive/renderer.py +571 -0
- plotlive/ticks.py +112 -0
- plotlive/transform.py +205 -0
- plotlive-0.1.0.dist-info/METADATA +804 -0
- plotlive-0.1.0.dist-info/RECORD +21 -0
- plotlive-0.1.0.dist-info/WHEEL +4 -0
plotlive/artists.py
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import numpy as np
|
|
3
|
+
from .colors import to_rgba, get_cmap, DEFAULT_CYCLE
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Artist:
|
|
7
|
+
def __init__(self):
|
|
8
|
+
self.label: str = '_nolegend_'
|
|
9
|
+
self.visible: bool = True
|
|
10
|
+
self.alpha: float = 1.0
|
|
11
|
+
self.zorder: float = 1.0
|
|
12
|
+
|
|
13
|
+
def get_xdata(self) -> np.ndarray | None:
|
|
14
|
+
return None
|
|
15
|
+
|
|
16
|
+
def get_ydata(self) -> np.ndarray | None:
|
|
17
|
+
return None
|
|
18
|
+
|
|
19
|
+
def get_label(self) -> str:
|
|
20
|
+
return self.label
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Line2D(Artist):
|
|
24
|
+
def __init__(self, xdata: np.ndarray, ydata: np.ndarray, **kwargs):
|
|
25
|
+
super().__init__()
|
|
26
|
+
self.xdata = np.asarray(xdata, dtype=float)
|
|
27
|
+
self.ydata = np.asarray(ydata, dtype=float)
|
|
28
|
+
color = kwargs.get('color', kwargs.get('c', 'C0'))
|
|
29
|
+
self.color: tuple = to_rgba(color)
|
|
30
|
+
self.linewidth: float = float(kwargs.get('linewidth', kwargs.get('lw', 1.5)))
|
|
31
|
+
self.linestyle: str = kwargs.get('linestyle', kwargs.get('ls', '-'))
|
|
32
|
+
self.marker: str | None = kwargs.get('marker', None)
|
|
33
|
+
self.markersize: float = float(kwargs.get('markersize', kwargs.get('ms', 6.0)))
|
|
34
|
+
self.markerfacecolor = kwargs.get('markerfacecolor', None)
|
|
35
|
+
self.label = kwargs.get('label', '_nolegend_')
|
|
36
|
+
self.alpha = float(kwargs.get('alpha', 1.0))
|
|
37
|
+
|
|
38
|
+
def get_xdata(self) -> np.ndarray:
|
|
39
|
+
return self.xdata
|
|
40
|
+
|
|
41
|
+
def get_ydata(self) -> np.ndarray:
|
|
42
|
+
return self.ydata
|
|
43
|
+
|
|
44
|
+
def set_xdata(self, x) -> None:
|
|
45
|
+
self.xdata = np.asarray(x, dtype=float)
|
|
46
|
+
|
|
47
|
+
def set_ydata(self, y) -> None:
|
|
48
|
+
self.ydata = np.asarray(y, dtype=float)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class PathCollection(Artist):
|
|
52
|
+
"""Stores data for a scatter plot."""
|
|
53
|
+
|
|
54
|
+
def __init__(self, xdata: np.ndarray, ydata: np.ndarray, **kwargs):
|
|
55
|
+
super().__init__()
|
|
56
|
+
self.xdata = np.asarray(xdata, dtype=float).ravel()
|
|
57
|
+
self.ydata = np.asarray(ydata, dtype=float).ravel()
|
|
58
|
+
s = kwargs.get('s', 20.0)
|
|
59
|
+
self.sizes = np.broadcast_to(
|
|
60
|
+
np.atleast_1d(np.asarray(s, dtype=float)), self.xdata.shape
|
|
61
|
+
).copy()
|
|
62
|
+
self._c_raw = kwargs.get('c', kwargs.get('color', 'C0'))
|
|
63
|
+
self.cmap_name: str = kwargs.get('cmap', 'viridis')
|
|
64
|
+
self.vmin: float | None = kwargs.get('vmin', None)
|
|
65
|
+
self.vmax: float | None = kwargs.get('vmax', None)
|
|
66
|
+
self.marker: str = kwargs.get('marker', 'o')
|
|
67
|
+
self.linewidths: float = float(kwargs.get('linewidths', 0.0))
|
|
68
|
+
self.edgecolors = kwargs.get('edgecolors', 'none')
|
|
69
|
+
self.label = kwargs.get('label', '_nolegend_')
|
|
70
|
+
self.alpha = float(kwargs.get('alpha', 1.0))
|
|
71
|
+
self._colors_resolved: np.ndarray | None = None
|
|
72
|
+
|
|
73
|
+
def get_xdata(self) -> np.ndarray:
|
|
74
|
+
return self.xdata
|
|
75
|
+
|
|
76
|
+
def get_ydata(self) -> np.ndarray:
|
|
77
|
+
return self.ydata
|
|
78
|
+
|
|
79
|
+
def resolve_colors(self) -> np.ndarray:
|
|
80
|
+
"""Return (N, 4) uint8 RGBA array."""
|
|
81
|
+
if self._colors_resolved is not None:
|
|
82
|
+
return self._colors_resolved
|
|
83
|
+
|
|
84
|
+
c = self._c_raw
|
|
85
|
+
n = len(self.xdata)
|
|
86
|
+
result = np.zeros((n, 4), dtype=np.uint8)
|
|
87
|
+
|
|
88
|
+
# Try to interpret c as an array of floats (for cmap mapping)
|
|
89
|
+
try:
|
|
90
|
+
c_arr = np.asarray(c, dtype=float)
|
|
91
|
+
if c_arr.ndim == 1 and c_arr.shape[0] == n and n > 1:
|
|
92
|
+
# It's a per-point scalar for colormap
|
|
93
|
+
vmin = self.vmin if self.vmin is not None else float(c_arr.min())
|
|
94
|
+
vmax = self.vmax if self.vmax is not None else float(c_arr.max())
|
|
95
|
+
cmap = get_cmap(self.cmap_name)
|
|
96
|
+
span = vmax - vmin if vmax != vmin else 1.0
|
|
97
|
+
t = (c_arr - vmin) / span
|
|
98
|
+
rgba = cmap(t)
|
|
99
|
+
result[:] = rgba[:, :4]
|
|
100
|
+
self._colors_resolved = result
|
|
101
|
+
return result
|
|
102
|
+
except (TypeError, ValueError):
|
|
103
|
+
pass
|
|
104
|
+
|
|
105
|
+
# Single color broadcast
|
|
106
|
+
try:
|
|
107
|
+
rgba = to_rgba(c, self.alpha)
|
|
108
|
+
except Exception:
|
|
109
|
+
rgba = to_rgba(DEFAULT_CYCLE[0], self.alpha)
|
|
110
|
+
result[:] = rgba
|
|
111
|
+
self._colors_resolved = result
|
|
112
|
+
return result
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class Rectangle(Artist):
|
|
116
|
+
def __init__(self, x: float, y: float, width: float, height: float, **kwargs):
|
|
117
|
+
super().__init__()
|
|
118
|
+
self.x = float(x)
|
|
119
|
+
self.y = float(y)
|
|
120
|
+
self.width = float(width)
|
|
121
|
+
self.height = float(height)
|
|
122
|
+
color = kwargs.get('color', kwargs.get('facecolor', 'C0'))
|
|
123
|
+
self.facecolor: tuple = to_rgba(color, float(kwargs.get('alpha', 1.0)))
|
|
124
|
+
ec = kwargs.get('edgecolor', kwargs.get('ec', 'black'))
|
|
125
|
+
self.edgecolor: tuple = to_rgba(ec) if ec not in ('none', 'None', None) else (0, 0, 0, 0)
|
|
126
|
+
self.linewidth: float = float(kwargs.get('linewidth', kwargs.get('lw', 1.0)))
|
|
127
|
+
self.label = kwargs.get('label', '_nolegend_')
|
|
128
|
+
self.alpha = float(kwargs.get('alpha', 1.0))
|
|
129
|
+
self._horizontal: bool = kwargs.get('_horizontal', False)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class BarContainer:
|
|
133
|
+
def __init__(self, patches: list[Rectangle], label: str = ''):
|
|
134
|
+
self.patches = patches
|
|
135
|
+
self.label = label
|
|
136
|
+
|
|
137
|
+
def __iter__(self):
|
|
138
|
+
return iter(self.patches)
|
|
139
|
+
|
|
140
|
+
def __len__(self):
|
|
141
|
+
return len(self.patches)
|
|
142
|
+
|
|
143
|
+
def __getitem__(self, idx):
|
|
144
|
+
return self.patches[idx]
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class Polygon(Artist):
|
|
148
|
+
"""Filled polygon — used by fill_between, violinplot, pie, stackplot."""
|
|
149
|
+
|
|
150
|
+
def __init__(self, xy: np.ndarray, **kwargs):
|
|
151
|
+
super().__init__()
|
|
152
|
+
self.xy = np.asarray(xy, dtype=float) # (N, 2): columns [x, y]
|
|
153
|
+
color = kwargs.get('color', kwargs.get('facecolor', 'C0'))
|
|
154
|
+
alpha = float(kwargs.get('alpha', 0.5))
|
|
155
|
+
self.facecolor: tuple = to_rgba(color, alpha)
|
|
156
|
+
ec = kwargs.get('edgecolor', kwargs.get('ec', 'none'))
|
|
157
|
+
self.edgecolor: tuple = (to_rgba(ec)
|
|
158
|
+
if ec not in ('none', 'None', None)
|
|
159
|
+
else (0, 0, 0, 0))
|
|
160
|
+
self.linewidth: float = float(kwargs.get('linewidth', kwargs.get('lw', 1.0)))
|
|
161
|
+
self.label = kwargs.get('label', '_nolegend_')
|
|
162
|
+
self.alpha = alpha
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
class ErrorBar(Artist):
|
|
166
|
+
"""Central line + whiskers — used by errorbar()."""
|
|
167
|
+
|
|
168
|
+
def __init__(self, x, y, yerr=None, xerr=None, **kwargs):
|
|
169
|
+
super().__init__()
|
|
170
|
+
self.xdata = np.asarray(x, dtype=float).ravel()
|
|
171
|
+
self.ydata = np.asarray(y, dtype=float).ravel()
|
|
172
|
+
n = len(self.xdata)
|
|
173
|
+
|
|
174
|
+
def _norm(err):
|
|
175
|
+
if err is None:
|
|
176
|
+
return None
|
|
177
|
+
e = np.asarray(err, float)
|
|
178
|
+
if e.ndim <= 1:
|
|
179
|
+
e = np.stack([e * np.ones(n), e * np.ones(n)])
|
|
180
|
+
return np.broadcast_to(e, (2, n)).copy()
|
|
181
|
+
|
|
182
|
+
self.yerr = _norm(yerr)
|
|
183
|
+
self.xerr = _norm(xerr)
|
|
184
|
+
color = kwargs.get('color', kwargs.get('c', 'C0'))
|
|
185
|
+
self.color: tuple = to_rgba(color)
|
|
186
|
+
self.linewidth: float = float(kwargs.get('linewidth', kwargs.get('lw', 1.5)))
|
|
187
|
+
self.capsize: float = float(kwargs.get('capsize', 4))
|
|
188
|
+
self.marker: str = kwargs.get('marker', 'o')
|
|
189
|
+
self.markersize: float = float(kwargs.get('markersize', kwargs.get('ms', 5.0)))
|
|
190
|
+
self.linestyle: str = kwargs.get('linestyle', kwargs.get('ls', '-'))
|
|
191
|
+
self.label = kwargs.get('label', '_nolegend_')
|
|
192
|
+
self.alpha: float = float(kwargs.get('alpha', 1.0))
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
class AxesImage(Artist):
|
|
196
|
+
def __init__(self, data: np.ndarray, **kwargs):
|
|
197
|
+
super().__init__()
|
|
198
|
+
self.data = np.asarray(data)
|
|
199
|
+
self.cmap_name: str = kwargs.get('cmap', 'viridis')
|
|
200
|
+
self.vmin: float | None = kwargs.get('vmin', None)
|
|
201
|
+
self.vmax: float | None = kwargs.get('vmax', None)
|
|
202
|
+
self.origin: str = kwargs.get('origin', 'upper')
|
|
203
|
+
self.aspect: str = kwargs.get('aspect', 'equal')
|
|
204
|
+
self.extent: tuple | None = kwargs.get('extent', None)
|
|
205
|
+
self.label = kwargs.get('label', '_nolegend_')
|
|
206
|
+
self._surface_cache = None
|
|
207
|
+
self._cache_key = None
|