@@ -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,77 @@ 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+ return self ._vertices
160+
161+ @vertices .setter
162+ def vertices (self , vertices ):
163+ if self ._readonly :
164+ raise AttributeError ("Can't set vertices on a readonly Path" )
165+ self ._vertices = vertices
166+ self ._update_values ()
167+
168+ @property
169+ def codes (self ):
170+ return self ._codes
171+
172+ @codes .setter
173+ def codes (self , codes ):
174+ if self ._readonly :
175+ raise AttributeError ("Can't set codes on a readonly Path" )
176+ self ._codes = codes
177+ self ._update_values ()
178+
179+ @property
180+ def simplify_threshold (self ):
181+ return self ._simplify_threshold
182+
183+ @property
184+ def has_nonfinite (self ):
185+ return self ._has_nonfinite
186+
187+ @property
188+ def should_simplify (self ):
189+ return self ._should_simplify
190+
191+ @property
192+ def readonly (self ):
193+ return self ._readonly
194+
195+ def __copy__ (self ):
196+ import copy
197+ return copy .copy (self )
198+
199+ copy = __copy__
200+
201+ def __deepcopy__ (self ):
202+ return self .__class__ (
203+ self .vertices .copy (), self .codes .copy (),
204+ _interpolation_steps = self ._interpolation_steps )
205+
206+ deepcopy = __deepcopy__
141207
142208 @classmethod
143209 def make_compound_path_from_polys (cls , XY ):
@@ -420,7 +486,8 @@ def unit_rectangle(cls):
420486 if cls ._unit_rectangle is None :
421487 cls ._unit_rectangle = \
422488 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 ])
489+ [cls .MOVETO , cls .LINETO , cls .LINETO , cls .LINETO , cls .CLOSEPOLY ],
490+ readonly = True )
424491 return cls ._unit_rectangle
425492
426493 _unit_regular_polygons = WeakValueDictionary ()
@@ -447,7 +514,7 @@ def unit_regular_polygon(cls, numVertices):
447514 codes [0 ] = cls .MOVETO
448515 codes [1 :- 1 ] = cls .LINETO
449516 codes [- 1 ] = cls .CLOSEPOLY
450- path = cls (verts , codes )
517+ path = cls (verts , codes , readonly = True )
451518 if numVertices <= 16 :
452519 cls ._unit_regular_polygons [numVertices ] = path
453520 return path
@@ -478,7 +545,7 @@ def unit_regular_star(cls, numVertices, innerCircle=0.5):
478545 codes [0 ] = cls .MOVETO
479546 codes [1 :- 1 ] = cls .LINETO
480547 codes [- 1 ] = cls .CLOSEPOLY
481- path = cls (verts , codes )
548+ path = cls (verts , codes , readonly = True )
482549 if numVertices <= 16 :
483550 cls ._unit_regular_polygons [(numVertices , innerCircle )] = path
484551 return path
@@ -552,7 +619,7 @@ def unit_circle(cls):
552619 codes [0 ] = cls .MOVETO
553620 codes [- 1 ] = cls .CLOSEPOLY
554621
555- cls ._unit_circle = cls (vertices , codes )
622+ cls ._unit_circle = cls (vertices , codes , readonly = True )
556623 return cls ._unit_circle
557624
558625 _unit_circle_righthalf = None
@@ -600,7 +667,7 @@ def unit_circle_righthalf(cls):
600667 codes [0 ] = cls .MOVETO
601668 codes [- 1 ] = cls .CLOSEPOLY
602669
603- cls ._unit_circle_righthalf = cls (vertices , codes )
670+ cls ._unit_circle_righthalf = cls (vertices , codes , readonly = True )
604671 return cls ._unit_circle_righthalf
605672
606673 @classmethod
@@ -679,7 +746,7 @@ def arc(cls, theta1, theta2, n=None, is_wedge=False):
679746 vertices [vertex_offset + 2 :end :3 , 0 ] = xB
680747 vertices [vertex_offset + 2 :end :3 , 1 ] = yB
681748
682- return cls (vertices , codes )
749+ return cls (vertices , codes , readonly = True )
683750
684751 @classmethod
685752 def wedge (cls , theta1 , theta2 , n = None ):
0 commit comments