-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathpatches.py
202 lines (183 loc) · 6.54 KB
/
patches.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
import numpy as np
from .wrappers import ProxyWrapper, _stale_wrapper
from .containers import DataContainer
from matplotlib.patches import (
Patch as _Patch,
Annulus as _Annulus,
Ellipse as _Ellipse,
Circle as _Circle,
Arc as _Arc,
Rectangle as _Rectangle,
)
class PatchWrapper(ProxyWrapper):
_wrapped_class = _Patch
_privtized_methods = (
"get_edgecolor",
"get_facecolor",
"get_linewidth",
"get_linestyle",
"get_antialiased",
"get_hatch",
"get_fill",
"get_capstyle",
"get_joinstyle",
"get_path",
"set_edgecolor",
"set_facecolor",
"set_linewidth",
"set_linestyle",
"set_antialiased",
"set_hatch",
"set_fill",
"set_capstyle",
"set_joinstyle",
"set_path",
)
_xunits = ()
_yunits = ()
required_keys = {
"edgecolor",
"facecolor",
"linewidth",
"linestyle",
"antialiased",
"hatch",
"fill",
"capstyle",
"joinstyle",
}
def __init__(self, data: DataContainer, nus=None, /, **kwargs):
super().__init__(data, nus)
# FIXME this is a bit of an ugly hack, basically we want a null instance
# because all attributes are determined at draw time, but each class has different signatures
# The one used here is surprisingly common, but perhaps instantiation should be pushed to the subclasses
# Additionally, hasattr breaks for recursion depth which should be looked at
try:
self._wrapped_instance = self._wrapped_class((0, 0), 0, **kwargs)
except TypeError:
self._wrapped_instance = self._wrapped_class((0, 0), 1, 0, **kwargs)
@_stale_wrapper
def draw(self, renderer):
self._update_wrapped(self._query_and_transform(renderer, xunits=self._xunits, yunits=self._yunits))
return self._wrapped_instance.draw(renderer)
def _update_wrapped(self, data):
for k, v in data.items():
# linestyle and hatch do not work as arrays,
# but ArrayContainer requires arrays, so index into an array if needed
if k in ("linestyle", "hatch"):
if isinstance(v, np.ndarray):
v = v[0]
getattr(self._wrapped_instance, f"set_{k}")(v)
class RectangleWrapper(PatchWrapper):
_wrapped_class = _Rectangle
_privtized_methods = PatchWrapper._privtized_methods + (
"get_x",
"get_y",
"get_width",
"get_height",
"get_angle",
"get_rotation_point",
"set_x",
"set_y",
"set_width",
"set_height",
"set_angle",
"set_rotation_point",
)
_xunits = ("x", "width")
_yunits = ("y", "height")
required_keys = PatchWrapper.required_keys | {"x", "y", "width", "height", "angle", "rotation_point"}
def _update_wrapped(self, data):
for k, v in data.items():
# rotation_point is a property without a set_rotation_point method
if k == "rotation_point":
self._wrapped_instance.rotation_point = v
continue
# linestyle and hatch do not work as arrays,
# but ArrayContainer requires arrays, so index into an array if needed
elif k in ("linestyle", "hatch"):
if isinstance(v, np.ndarray):
v = v[0]
getattr(self._wrapped_instance, f"set_{k}")(v)
class AnnulusWrapper(PatchWrapper):
_wrapped_class = _Annulus
_privtized_methods = PatchWrapper._privtized_methods + (
"get_angle",
"get_center",
"get_radii",
"get_width",
"set_angle",
"set_center",
"set_radii",
"set_width",
# set_semi[major|minor] overlap with set_radii
)
# TODO: units, because "center" is one x and one y units
# Other things like semi-axis and width seem to _not_ be unit-ed, but maybe _could_ be
_xunits = ()
_yunits = ()
# Order is actually important... radii must come before width
required_keys = PatchWrapper.required_keys | {"center", "radii", "width", "angle"}
class EllipseWrapper(PatchWrapper):
_wrapped_class = _Ellipse
_privtized_methods = PatchWrapper._privtized_methods + (
"get_angle",
"get_center",
"get_width",
"get_height",
"set_angle",
"set_center",
"set_width",
"set_height",
)
# TODO: units, because "center" is one x and one y units
_xunits = ("width",)
_yunits = ("height",)
required_keys = PatchWrapper.required_keys | {"center", "width", "height", "angle"}
# While the actual patch inherits from ellipse, using it as a full ellipse doesn't make sense
# Therefore, the privitized methods, required keys, etc are not inheritied from EllipseWrapper
class CircleWrapper(PatchWrapper):
_wrapped_class = _Circle
_privtized_methods = PatchWrapper._privtized_methods + (
"get_radius",
"get_center",
"set_radius",
"set_center",
)
# TODO: units, because "center" is one x and one y units
# And "radius" is _both_ x and y units, so may not make sense unless units are the same
_xunits = ()
_yunits = ()
required_keys = PatchWrapper.required_keys | {"center", "radius"}
# Unlike Circle, Arc maintains width/height/etc from Ellipse
class ArcWrapper(EllipseWrapper):
_wrapped_class = _Arc
_privtized_methods = EllipseWrapper._privtized_methods + (
"get_radius",
"get_center",
"get_theta1",
"get_theta2",
"set_radius",
"set_center",
"set_theta1",
"set_theta2",
)
# TODO: units, because "center" is one x and one y units
# And theta1/2 could arguably pass through a units pipeline, but not the x/y one...
_xunits = ()
_yunits = ()
required_keys = EllipseWrapper.required_keys | {"theta1", "theta2"}
def _update_wrapped(self, data):
for k, v in data.items():
# theta[1,2] are properties without a set_rotation_point method
if k.startswith("theta"):
if isinstance(v, np.ndarray):
v = v[0]
setattr(self._wrapped_instance, k, v)
continue
# linestyle and hatch do not work as arrays,
# but ArrayContainer requires arrays, so index into an array if needed
elif k in ("linestyle", "hatch"):
if isinstance(v, np.ndarray):
v = v[0]
getattr(self._wrapped_instance, f"set_{k}")(v)