Skip to content

Commit 97097cc

Browse files
committed
Early PathCollection support
1 parent 609fe49 commit 97097cc

File tree

2 files changed

+126
-2
lines changed

2 files changed

+126
-2
lines changed

data_prototype/wrappers.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from matplotlib.image import AxesImage as _AxesImage
1010
from matplotlib.patches import StepPatch as _StepPatch
1111
from matplotlib.text import Text as _Text
12-
from matplotlib.collections import LineCollection as _LineCollection
12+
from matplotlib.collections import LineCollection as _LineCollection, PathCollection as _PathCollection
1313
from matplotlib.artist import Artist as _Artist
1414

1515
from data_prototype.containers import DataContainer, _Transform
@@ -176,7 +176,7 @@ class LineWrapper(ProxyWrapper):
176176

177177
def __init__(self, data: DataContainer, nus=None, /, **kwargs):
178178
super().__init__(data, nus)
179-
self._wrapped_instance = self._wrapped_class([], [], **kwargs)
179+
self._wrapped_instance = self._wrapped_class(np.array([]), np.array([]), **kwargs)
180180

181181
@_stale_wrapper
182182
def draw(self, renderer):
@@ -188,6 +188,33 @@ def draw(self, renderer):
188188
def _update_wrapped(self, data):
189189
self._wrapped_instance.set_data(data["x"], data["y"])
190190

191+
class PathCollectionWrapper(ProxyWrapper):
192+
_wrapped_class = _PathCollection
193+
_privtized_methods = (
194+
"set_facecolors", "set_edgecolors", "set_offsets", "set_sizes", "set_paths",
195+
"get_facecolors", "get_edgecolors", "get_offsets", "get_sizes", "get_paths",
196+
)
197+
198+
def __init__(self, data: DataContainer, nus=None, /, **kwargs):
199+
super().__init__(data, nus)
200+
self._wrapped_instance = self._wrapped_class([], **kwargs)
201+
202+
@_stale_wrapper
203+
def draw(self, renderer):
204+
self._update_wrapped(
205+
self._query_and_transform(renderer, xunits=["x"], yunits=["y"]),
206+
)
207+
return self._wrapped_instance.draw(renderer)
208+
209+
def _update_wrapped(self, data):
210+
print(data)
211+
self._wrapped_instance.set_offsets(np.array([data["x"], data["y"]]).T)
212+
self._wrapped_instance.set_paths(data["paths"])
213+
self._wrapped_instance.set_facecolors(data["facecolors"])
214+
self._wrapped_instance.set_edgecolors(data["edgecolors"])
215+
self._wrapped_instance.set_sizes(data["sizes"])
216+
217+
191218

192219
class ImageWrapper(ProxyWrapper):
193220
_wrapped_class = _AxesImage

examples/lissajous.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
"""
2+
==========================
3+
An animated lissajous ball
4+
==========================
5+
6+
Inspired by https://twitter.com/_brohrer_/status/1584681864648065027
7+
8+
9+
"""
10+
import time
11+
from typing import Protocol, List, Dict, Tuple, Optional, Any, Union, Callable, MutableMapping
12+
from functools import partial
13+
14+
import numpy as np
15+
16+
import matplotlib.pyplot as plt
17+
import matplotlib.markers as mmarkers
18+
from matplotlib.animation import FuncAnimation
19+
20+
from data_prototype.containers import _Transform, Desc
21+
22+
from data_prototype.wrappers import PathCollectionWrapper, FormatedText
23+
24+
25+
class Lissajous:
26+
N = 1024
27+
# cycles per minutes
28+
scale = 2
29+
30+
def describe(self):
31+
return {
32+
"x": Desc([self.N], float),
33+
"y": Desc([self.N], float),
34+
"phase": Desc([], float),
35+
"time": Desc([], float),
36+
"sizes": Desc([], float),
37+
"paths": Desc([], float),
38+
"edgecolors": Desc([], str),
39+
"facecolors": Desc([self.N], str),
40+
}
41+
42+
def query(
43+
self,
44+
transform: _Transform,
45+
size: Tuple[int, int],
46+
) -> Tuple[Dict[str, Any], Union[str, int]]:
47+
def next_time():
48+
cur_time = time.time()
49+
cur_time = np.array([cur_time, cur_time-.1, cur_time-.2, cur_time-0.3])
50+
51+
phase = 15*np.pi * (self.scale * cur_time % 60) / 150
52+
marker_obj = mmarkers.MarkerStyle("o")
53+
return {
54+
"x": np.cos(5*phase),
55+
"y": np.sin(3*phase),
56+
"phase": phase[0],
57+
"sizes": np.array([.02]),
58+
"paths": [marker_obj.get_path().transformed(marker_obj.get_transform())],
59+
"edgecolors": "k",
60+
"facecolors": ["#4682b4ff", "#82b446aa", "#46b48288", "#8246b433"],
61+
"time": cur_time[0],
62+
}, hash(cur_time[0])
63+
64+
return next_time()
65+
66+
67+
def update(frame, art):
68+
return art
69+
70+
71+
sot_c = Lissajous()
72+
73+
fc = FormatedText(
74+
sot_c,
75+
"ϕ={phase:.2f} ".format,
76+
x=1,
77+
y=1,
78+
ha="right",
79+
)
80+
fig, ax = plt.subplots()
81+
ax.set_xlim(-1.1, 1.1)
82+
ax.set_ylim(-1.1, 1.1)
83+
lw = PathCollectionWrapper(sot_c, offset_transform=ax.transData)
84+
ax.add_artist(lw)
85+
ax.add_artist(fc)
86+
#ax.set_xticks([])
87+
#ax.set_yticks([])
88+
ax.set_aspect(1)
89+
ani = FuncAnimation(
90+
fig,
91+
partial(update, art=(lw, fc)),
92+
frames=60*15,
93+
interval=1000 / 100,
94+
# TODO: blitting does not work because wrappers do not inherent from Artist
95+
# blit=True,
96+
)
97+
plt.show()

0 commit comments

Comments
 (0)