@@ -84,7 +84,8 @@ class Path(object):
84
84
85
85
code_type = np .uint8
86
86
87
- def __init__ (self , vertices , codes = None , _interpolation_steps = 1 , closed = False ):
87
+ def __init__ (self , vertices , codes = None , _interpolation_steps = 1 , closed = False ,
88
+ readonly = False ):
88
89
"""
89
90
Create a new path with the given vertices and codes.
90
91
@@ -109,6 +110,8 @@ def __init__(self, vertices, codes=None, _interpolation_steps=1, closed=False):
109
110
such as Polar, that this path should be linearly interpolated
110
111
immediately before drawing. This attribute is primarily an
111
112
implementation detail and is not intended for public use.
113
+
114
+ *readonly*, when True, makes the path immutable.
112
115
"""
113
116
if ma .isMaskedArray (vertices ):
114
117
vertices = vertices .astype (np .float_ ).filled (np .nan )
@@ -130,14 +133,117 @@ def __init__(self, vertices, codes=None, _interpolation_steps=1, closed=False):
130
133
assert vertices .ndim == 2
131
134
assert vertices .shape [1 ] == 2
132
135
133
- self .should_simplify = (rcParams ['path.simplify' ] and
134
- (len (vertices ) >= 128 and
135
- (codes is None or np .all (codes <= Path .LINETO ))))
136
- self .simplify_threshold = rcParams ['path.simplify_threshold' ]
137
- self .has_nonfinite = not np .isfinite (vertices ).all ()
138
- self .codes = codes
139
- self .vertices = vertices
136
+ self ._vertices = vertices
137
+ self ._codes = codes
140
138
self ._interpolation_steps = _interpolation_steps
139
+ self ._update_values ()
140
+
141
+ if readonly :
142
+ self ._vertices .flags .writeable = False
143
+ if self ._codes is not None :
144
+ self ._codes .flags .writeable = False
145
+ self ._readonly = True
146
+ else :
147
+ self ._readonly = False
148
+
149
+ def _update_values (self ):
150
+ self ._should_simplify = (
151
+ rcParams ['path.simplify' ] and
152
+ (len (self ._vertices ) >= 128 and
153
+ (self ._codes is None or np .all (self ._codes <= Path .LINETO ))))
154
+ self ._simplify_threshold = rcParams ['path.simplify_threshold' ]
155
+ self ._has_nonfinite = not np .isfinite (self ._vertices ).all ()
156
+
157
+ @property
158
+ def vertices (self ):
159
+ """
160
+ The list of vertices in the `Path` as an Nx2 numpy array.
161
+ """
162
+ return self ._vertices
163
+
164
+ @vertices .setter
165
+ def vertices (self , vertices ):
166
+ if self ._readonly :
167
+ raise AttributeError ("Can't set vertices on a readonly Path" )
168
+ self ._vertices = vertices
169
+ self ._update_values ()
170
+
171
+ @property
172
+ def codes (self ):
173
+ """
174
+ The list of codes in the `Path` as a 1-D numpy array. Each
175
+ code is one of `STOP`, `MOVETO`, `LINETO`, `CURVE3`, `CURVE4`
176
+ or `CLOSEPOLY`. For codes that correspond to more than one
177
+ vertex (`CURVE3` and `CURVE4`), that code will be repeated so
178
+ that the length of `self.vertices` and `self.codes` is always
179
+ the same.
180
+ """
181
+ return self ._codes
182
+
183
+ @codes .setter
184
+ def codes (self , codes ):
185
+ if self ._readonly :
186
+ raise AttributeError ("Can't set codes on a readonly Path" )
187
+ self ._codes = codes
188
+ self ._update_values ()
189
+
190
+ @property
191
+ def simplify_threshold (self ):
192
+ """
193
+ The fraction of a pixel difference below which vertices will
194
+ be simplified out.
195
+ """
196
+ return self ._simplify_threshold
197
+
198
+ @simplify_threshold .setter
199
+ def simplify_threshold (self , threshold ):
200
+ self ._simplify_threshold = threshold
201
+
202
+ @property
203
+ def has_nonfinite (self ):
204
+ """
205
+ `True` if the vertices array has nonfinite values.
206
+ """
207
+ return self ._has_nonfinite
208
+
209
+ @property
210
+ def should_simplify (self ):
211
+ """
212
+ `True` if the vertices array should be simplified.
213
+ """
214
+ return self ._should_simplify
215
+
216
+ @should_simplify .setter
217
+ def should_simplify (self , should_simplify ):
218
+ self ._should_simplify = should_simplify
219
+
220
+ @property
221
+ def readonly (self ):
222
+ """
223
+ `True` if the `Path` is read-only.
224
+ """
225
+ return self ._readonly
226
+
227
+ def __copy__ (self ):
228
+ """
229
+ Returns a shallow copy of the `Path`, which will share the
230
+ vertices and codes with the source `Path`.
231
+ """
232
+ import copy
233
+ return copy .copy (self )
234
+
235
+ copy = __copy__
236
+
237
+ def __deepcopy__ (self ):
238
+ """
239
+ Returns a deepcopy of the `Path`. The `Path` will not be
240
+ readonly, even if the source `Path` is.
241
+ """
242
+ return self .__class__ (
243
+ self .vertices .copy (), self .codes .copy (),
244
+ _interpolation_steps = self ._interpolation_steps )
245
+
246
+ deepcopy = __deepcopy__
141
247
142
248
@classmethod
143
249
def make_compound_path_from_polys (cls , XY ):
@@ -420,7 +526,8 @@ def unit_rectangle(cls):
420
526
if cls ._unit_rectangle is None :
421
527
cls ._unit_rectangle = \
422
528
cls ([[0.0 , 0.0 ], [1.0 , 0.0 ], [1.0 , 1.0 ], [0.0 , 1.0 ], [0.0 , 0.0 ]],
423
- [cls .MOVETO , cls .LINETO , cls .LINETO , cls .LINETO , cls .CLOSEPOLY ])
529
+ [cls .MOVETO , cls .LINETO , cls .LINETO , cls .LINETO , cls .CLOSEPOLY ],
530
+ readonly = True )
424
531
return cls ._unit_rectangle
425
532
426
533
_unit_regular_polygons = WeakValueDictionary ()
@@ -447,7 +554,7 @@ def unit_regular_polygon(cls, numVertices):
447
554
codes [0 ] = cls .MOVETO
448
555
codes [1 :- 1 ] = cls .LINETO
449
556
codes [- 1 ] = cls .CLOSEPOLY
450
- path = cls (verts , codes )
557
+ path = cls (verts , codes , readonly = True )
451
558
if numVertices <= 16 :
452
559
cls ._unit_regular_polygons [numVertices ] = path
453
560
return path
@@ -478,7 +585,7 @@ def unit_regular_star(cls, numVertices, innerCircle=0.5):
478
585
codes [0 ] = cls .MOVETO
479
586
codes [1 :- 1 ] = cls .LINETO
480
587
codes [- 1 ] = cls .CLOSEPOLY
481
- path = cls (verts , codes )
588
+ path = cls (verts , codes , readonly = True )
482
589
if numVertices <= 16 :
483
590
cls ._unit_regular_polygons [(numVertices , innerCircle )] = path
484
591
return path
@@ -552,7 +659,7 @@ def unit_circle(cls):
552
659
codes [0 ] = cls .MOVETO
553
660
codes [- 1 ] = cls .CLOSEPOLY
554
661
555
- cls ._unit_circle = cls (vertices , codes )
662
+ cls ._unit_circle = cls (vertices , codes , readonly = True )
556
663
return cls ._unit_circle
557
664
558
665
_unit_circle_righthalf = None
@@ -600,7 +707,7 @@ def unit_circle_righthalf(cls):
600
707
codes [0 ] = cls .MOVETO
601
708
codes [- 1 ] = cls .CLOSEPOLY
602
709
603
- cls ._unit_circle_righthalf = cls (vertices , codes )
710
+ cls ._unit_circle_righthalf = cls (vertices , codes , readonly = True )
604
711
return cls ._unit_circle_righthalf
605
712
606
713
@classmethod
@@ -679,7 +786,7 @@ def arc(cls, theta1, theta2, n=None, is_wedge=False):
679
786
vertices [vertex_offset + 2 :end :3 , 0 ] = xB
680
787
vertices [vertex_offset + 2 :end :3 , 1 ] = yB
681
788
682
- return cls (vertices , codes )
789
+ return cls (vertices , codes , readonly = True )
683
790
684
791
@classmethod
685
792
def wedge (cls , theta1 , theta2 , n = None ):
0 commit comments