@@ -84,7 +84,8 @@ class Path(object):
8484
8585 code_type = np .uint8
8686
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 ):
8889 """
8990 Create a new path with the given vertices and codes.
9091
@@ -109,6 +110,8 @@ def __init__(self, vertices, codes=None, _interpolation_steps=1, closed=False):
109110 such as Polar, that this path should be linearly interpolated
110111 immediately before drawing. This attribute is primarily an
111112 implementation detail and is not intended for public use.
113+
114+ *readonly*, when True, makes the path immutable.
112115 """
113116 if ma .isMaskedArray (vertices ):
114117 vertices = vertices .astype (np .float_ ).filled (np .nan )
@@ -130,14 +133,117 @@ def __init__(self, vertices, codes=None, _interpolation_steps=1, closed=False):
130133 assert vertices .ndim == 2
131134 assert vertices .shape [1 ] == 2
132135
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
140138 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__
141247
142248 @classmethod
143249 def make_compound_path_from_polys (cls , XY ):
@@ -420,7 +526,8 @@ def unit_rectangle(cls):
420526 if cls ._unit_rectangle is None :
421527 cls ._unit_rectangle = \
422528 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 )
424531 return cls ._unit_rectangle
425532
426533 _unit_regular_polygons = WeakValueDictionary ()
@@ -447,7 +554,7 @@ def unit_regular_polygon(cls, numVertices):
447554 codes [0 ] = cls .MOVETO
448555 codes [1 :- 1 ] = cls .LINETO
449556 codes [- 1 ] = cls .CLOSEPOLY
450- path = cls (verts , codes )
557+ path = cls (verts , codes , readonly = True )
451558 if numVertices <= 16 :
452559 cls ._unit_regular_polygons [numVertices ] = path
453560 return path
@@ -478,7 +585,7 @@ def unit_regular_star(cls, numVertices, innerCircle=0.5):
478585 codes [0 ] = cls .MOVETO
479586 codes [1 :- 1 ] = cls .LINETO
480587 codes [- 1 ] = cls .CLOSEPOLY
481- path = cls (verts , codes )
588+ path = cls (verts , codes , readonly = True )
482589 if numVertices <= 16 :
483590 cls ._unit_regular_polygons [(numVertices , innerCircle )] = path
484591 return path
@@ -552,7 +659,7 @@ def unit_circle(cls):
552659 codes [0 ] = cls .MOVETO
553660 codes [- 1 ] = cls .CLOSEPOLY
554661
555- cls ._unit_circle = cls (vertices , codes )
662+ cls ._unit_circle = cls (vertices , codes , readonly = True )
556663 return cls ._unit_circle
557664
558665 _unit_circle_righthalf = None
@@ -600,7 +707,7 @@ def unit_circle_righthalf(cls):
600707 codes [0 ] = cls .MOVETO
601708 codes [- 1 ] = cls .CLOSEPOLY
602709
603- cls ._unit_circle_righthalf = cls (vertices , codes )
710+ cls ._unit_circle_righthalf = cls (vertices , codes , readonly = True )
604711 return cls ._unit_circle_righthalf
605712
606713 @classmethod
@@ -679,7 +786,7 @@ def arc(cls, theta1, theta2, n=None, is_wedge=False):
679786 vertices [vertex_offset + 2 :end :3 , 0 ] = xB
680787 vertices [vertex_offset + 2 :end :3 , 1 ] = yB
681788
682- return cls (vertices , codes )
789+ return cls (vertices , codes , readonly = True )
683790
684791 @classmethod
685792 def wedge (cls , theta1 , theta2 , n = None ):
0 commit comments