11"""
2- Contains a class for managing paths (polylines).
2+ A module for dealing with the polylines used throughout matplotlib.
3+
4+ The primary class for polyline handling in matplotlib is :class:`Path`.
5+ Almost all vector drawing makes use of Paths somewhere in the drawing
6+ pipeline.
7+
8+ Whilst a :class:`Path` instance itself cannot be drawn, there exists
9+ :class:`~matplotlib.artist.Artist` subclasses which can be used for
10+ convenient Path visualisation - the two most frequently used of these are
11+ :class:`~matplotlib.patches.PathPatch` and
12+ :class:`~matplotlib.collections.PathCollection`.
313"""
414
515from __future__ import print_function
@@ -53,9 +63,9 @@ class Path(object):
5363
5464 Users of Path objects should not access the vertices and codes
5565 arrays directly. Instead, they should use :meth:`iter_segments`
56- to get the vertex/code pairs. This is important, since many
57- :class:`Path` objects, as an optimization, do not store a *codes*
58- at all, but have a default one provided for them by
66+ or :meth:`cleaned` to get the vertex/code pairs. This is important,
67+ since many :class:`Path` objects, as an optimization, do not store a
68+ *codes* at all, but have a default one provided for them by
5969 :meth:`iter_segments`.
6070
6171 .. note::
@@ -73,12 +83,16 @@ class Path(object):
7383 LINETO = 2 # 1 vertex
7484 CURVE3 = 3 # 2 vertices
7585 CURVE4 = 4 # 3 vertices
76- CLOSEPOLY = 0x4f # 1 vertex
86+ CLOSEPOLY = 79 # 1 vertex
7787
78- NUM_VERTICES = [1 , 1 , 1 , 2 ,
79- 3 , 1 , 1 , 1 ,
80- 1 , 1 , 1 , 1 ,
81- 1 , 1 , 1 , 1 ]
88+ #: A dictionary mapping Path codes to the number of vertices that the
89+ #: code expects.
90+ NUM_VERTICES_FOR_CODE = {STOP : 1 ,
91+ MOVETO : 1 ,
92+ LINETO : 1 ,
93+ CURVE3 : 2 ,
94+ CURVE4 : 3 ,
95+ CLOSEPOLY : 1 }
8296
8397 code_type = np .uint8
8498
@@ -87,29 +101,31 @@ def __init__(self, vertices, codes=None, _interpolation_steps=1, closed=False,
87101 """
88102 Create a new path with the given vertices and codes.
89103
90- *vertices* is an Nx2 numpy float array, masked array or Python
91- sequence.
92-
93- *codes* is an N-length numpy array or Python sequence of type
94- :attr:`matplotlib.path.Path.code_type`.
95-
96- These two arrays must have the same length in the first
97- dimension.
98-
99- If *codes* is None, *vertices* will be treated as a series of
100- line segments.
101-
102- If *vertices* contains masked values, they will be converted
103- to NaNs which are then handled correctly by the Agg
104- PathIterator and other consumers of path data, such as
105- :meth:`iter_segments`.
106-
107- *interpolation_steps* is used as a hint to certain projections,
108- such as Polar, that this path should be linearly interpolated
109- immediately before drawing. This attribute is primarily an
110- implementation detail and is not intended for public use.
111-
112- *readonly*, when True, makes the path immutable.
104+ Parameters
105+ ----------
106+ vertices : array_like
107+ The ``(n, 2)`` float array, masked array or sequence of pairs
108+ representing the vertices of the path.
109+
110+ If *vertices* contains masked values, they will be converted
111+ to NaNs which are then handled correctly by the Agg
112+ PathIterator and other consumers of path data, such as
113+ :meth:`iter_segments`.
114+ codes : {None, array_like}, optional
115+ n-length array integers representing the codes of the path.
116+ If not None, codes must be the same length as vertices.
117+ If None, *vertices* will be treated as a series of line segments.
118+ _interpolation_steps : int, optional
119+ Used as a hint to certain projections, such as Polar, that this
120+ path should be linearly interpolated immediately before drawing.
121+ This attribute is primarily an implementation detail and is not
122+ intended for public use.
123+ closed : bool, optional
124+ If *codes* is None and closed is True, vertices will be treated as
125+ line segments of a closed polygon.
126+ readonly : bool, optional
127+ Makes the path behave in an immutable way and sets the vertices
128+ and codes as read-only arrays.
113129 """
114130 if ma .isMaskedArray (vertices ):
115131 vertices = vertices .astype (np .float_ ).filled (np .nan )
@@ -144,6 +160,37 @@ def __init__(self, vertices, codes=None, _interpolation_steps=1, closed=False,
144160 else :
145161 self ._readonly = False
146162
163+ @classmethod
164+ def _fast_from_codes_and_verts (cls , verts , codes , internals = None ):
165+ """
166+ Creates a Path instance without the expense of calling the constructor
167+
168+ Use this method at your own risk...
169+
170+ Parameters
171+ ----------
172+ verts : numpy array
173+ codes : numpy array (may not be None)
174+ internals : dict or None
175+ The attributes that the resulting path should have.
176+
177+ """
178+ internals = internals or {}
179+ pth = cls .__new__ (cls )
180+ pth ._vertices = verts
181+ pth ._codes = codes
182+ pth ._readonly = internals .pop ('_readonly' , False )
183+ pth ._should_simplify = internals .pop ('_should_simplify' , True )
184+ pth ._simplify_threshold = internals .pop ('_simplify_threshold' ,
185+ rcParams ['path.simplify_threshold' ])
186+ pth ._has_nonfinite = internals .pop ('_has_nonfinite' , False )
187+ pth ._interpolation_steps = internals .pop ('_interpolation_steps' , 1 )
188+ if internals :
189+ raise ValueError ('Unexpected internals provided to '
190+ '_fast_from_codes_and_verts: '
191+ '{0}' .format ('\n *' .join (internals .keys ())))
192+ return pth
193+
147194 def _update_values (self ):
148195 self ._should_simplify = (
149196 rcParams ['path.simplify' ] and
@@ -246,7 +293,7 @@ def __deepcopy__(self):
246293 @classmethod
247294 def make_compound_path_from_polys (cls , XY ):
248295 """
249- (static method) Make a compound path object to draw a number
296+ Make a compound path object to draw a number
250297 of polygons with equal numbers of sides XY is a (numpolys x
251298 numsides x 2) numpy array of vertices. Return object is a
252299 :class:`Path`
@@ -273,10 +320,7 @@ def make_compound_path_from_polys(cls, XY):
273320
274321 @classmethod
275322 def make_compound_path (cls , * args ):
276- """
277- (staticmethod) Make a compound path from a list of Path
278- objects.
279- """
323+ """Make a compound path from a list of Path objects."""
280324 lengths = [len (x ) for x in args ]
281325 total_length = sum (lengths )
282326
@@ -313,64 +357,87 @@ def iter_segments(self, transform=None, remove_nans=True, clip=None,
313357 Additionally, this method can provide a number of standard
314358 cleanups and conversions to the path.
315359
316- *transform*: if not None, the given affine transformation will
317- be applied to the path.
318-
319- *remove_nans*: if True, will remove all NaNs from the path and
320- insert MOVETO commands to skip over them.
321-
322- *clip*: if not None, must be a four-tuple (x1, y1, x2, y2)
323- defining a rectangle in which to clip the path.
324-
325- *snap*: if None, auto-snap to pixels, to reduce
326- fuzziness of rectilinear lines. If True, force snapping, and
327- if False, don't snap.
328-
329- *stroke_width*: the width of the stroke being drawn. Needed
330- as a hint for the snapping algorithm.
331-
332- *simplify*: if True, perform simplification, to remove
333- vertices that do not affect the appearance of the path. If
334- False, perform no simplification. If None, use the
335- should_simplify member variable.
336-
337- *curves*: If True, curve segments will be returned as curve
338- segments. If False, all curves will be converted to line
339- segments.
340-
341- *sketch*: If not None, must be a 3-tuple of the form
342- (scale, length, randomness), representing the sketch
343- parameters.
344- """
345- vertices = self .vertices
346- if not len (vertices ):
360+ Parameters
361+ ----------
362+ transform : None or :class:`~matplotlib.transforms.Transform` instance
363+ If not None, the given affine transformation will
364+ be applied to the path.
365+ remove_nans : {False, True}, optional
366+ If True, will remove all NaNs from the path and
367+ insert MOVETO commands to skip over them.
368+ clip : None or sequence, optional
369+ If not None, must be a four-tuple (x1, y1, x2, y2)
370+ defining a rectangle in which to clip the path.
371+ snap : None or bool, optional
372+ If None, auto-snap to pixels, to reduce
373+ fuzziness of rectilinear lines. If True, force snapping, and
374+ if False, don't snap.
375+ stroke_width : float, optional
376+ The width of the stroke being drawn. Needed
377+ as a hint for the snapping algorithm.
378+ simplify : None or bool, optional
379+ If True, perform simplification, to remove
380+ vertices that do not affect the appearance of the path. If
381+ False, perform no simplification. If None, use the
382+ should_simplify member variable.
383+ curves : {True, False}, optional
384+ If True, curve segments will be returned as curve
385+ segments. If False, all curves will be converted to line
386+ segments.
387+ sketch : None or sequence, optional
388+ If not None, must be a 3-tuple of the form
389+ (scale, length, randomness), representing the sketch
390+ parameters.
391+ """
392+ if not len (self ):
347393 return
348394
349- codes = self .codes
350-
351- NUM_VERTICES = self .NUM_VERTICES
352- MOVETO = self .MOVETO
353- LINETO = self .LINETO
354- CLOSEPOLY = self .CLOSEPOLY
355- STOP = self .STOP
395+ cleaned = self .cleaned (transform = transform ,
396+ remove_nans = remove_nans , clip = clip ,
397+ snap = snap , stroke_width = stroke_width ,
398+ simplify = simplify , curves = curves ,
399+ sketch = sketch )
400+ vertices = cleaned .vertices
401+ codes = cleaned .codes
402+ len_vertices = vertices .shape [0 ]
356403
357- vertices , codes = _path .cleanup_path (
358- self , transform , remove_nans , clip ,
359- snap , stroke_width , simplify , curves ,
360- sketch )
361- len_vertices = len (vertices )
404+ # Cache these object lookups for performance in the loop.
405+ NUM_VERTICES_FOR_CODE = self .NUM_VERTICES_FOR_CODE
406+ STOP = self .STOP
362407
363408 i = 0
364409 while i < len_vertices :
365410 code = codes [i ]
366411 if code == STOP :
367412 return
368413 else :
369- num_vertices = NUM_VERTICES [ int ( code ) & 0xf ]
414+ num_vertices = NUM_VERTICES_FOR_CODE [ code ]
370415 curr_vertices = vertices [i :i + num_vertices ].flatten ()
371416 yield curr_vertices , code
372417 i += num_vertices
373418
419+ def cleaned (self , transform = None , remove_nans = False , clip = None ,
420+ quantize = False , simplify = False , curves = False ,
421+ stroke_width = 1.0 , snap = False , sketch = None ):
422+ """
423+ Cleans up the path according to the parameters returning a new
424+ Path instance.
425+
426+ .. seealso::
427+
428+ See :meth:`iter_segments` for details of the keyword arguments.
429+
430+ Returns
431+ -------
432+ Path instance with cleaned up vertices and codes.
433+
434+ """
435+ vertices , codes = cleanup_path (self , transform ,
436+ remove_nans , clip ,
437+ snap , stroke_width ,
438+ simplify , curves , sketch )
439+ return Path ._fast_from_codes_and_verts (vertices , codes )
440+
374441 def transformed (self , transform ):
375442 """
376443 Return a transformed copy of the path.
@@ -519,7 +586,7 @@ def to_polygons(self, transform=None, width=0, height=0):
519586 @classmethod
520587 def unit_rectangle (cls ):
521588 """
522- (staticmethod) Returns a :class:`Path` of the unit rectangle
589+ Return a :class:`Path` instance of the unit rectangle
523590 from (0, 0) to (1, 1).
524591 """
525592 if cls ._unit_rectangle is None :
@@ -534,7 +601,7 @@ def unit_rectangle(cls):
534601 @classmethod
535602 def unit_regular_polygon (cls , numVertices ):
536603 """
537- (staticmethod) Returns a :class:`Path` for a unit regular
604+ Return a :class:`Path` instance for a unit regular
538605 polygon with the given *numVertices* and radius of 1.0,
539606 centered at (0, 0).
540607 """
@@ -563,7 +630,7 @@ def unit_regular_polygon(cls, numVertices):
563630 @classmethod
564631 def unit_regular_star (cls , numVertices , innerCircle = 0.5 ):
565632 """
566- (staticmethod) Returns a :class:`Path` for a unit regular star
633+ Return a :class:`Path` for a unit regular star
567634 with the given numVertices and radius of 1.0, centered at (0,
568635 0).
569636 """
@@ -592,7 +659,7 @@ def unit_regular_star(cls, numVertices, innerCircle=0.5):
592659 @classmethod
593660 def unit_regular_asterisk (cls , numVertices ):
594661 """
595- (staticmethod) Returns a :class:`Path` for a unit regular
662+ Return a :class:`Path` for a unit regular
596663 asterisk with the given numVertices and radius of 1.0,
597664 centered at (0, 0).
598665 """
@@ -603,7 +670,7 @@ def unit_regular_asterisk(cls, numVertices):
603670 @classmethod
604671 def unit_circle (cls ):
605672 """
606- (staticmethod) Returns a :class:`Path` of the unit circle.
673+ Return a :class:`Path` of the unit circle.
607674 The circle is approximated using cubic Bezier curves. This
608675 uses 8 splines around the circle using the approach presented
609676 here:
@@ -666,7 +733,7 @@ def unit_circle(cls):
666733 @classmethod
667734 def unit_circle_righthalf (cls ):
668735 """
669- (staticmethod) Returns a :class:`Path` of the right half
736+ Return a :class:`Path` of the right half
670737 of a unit circle. The circle is approximated using cubic Bezier
671738 curves. This uses 4 splines around the circle using the approach
672739 presented here:
@@ -712,7 +779,7 @@ def unit_circle_righthalf(cls):
712779 @classmethod
713780 def arc (cls , theta1 , theta2 , n = None , is_wedge = False ):
714781 """
715- (staticmethod) Returns an arc on the unit circle from angle
782+ Return an arc on the unit circle from angle
716783 *theta1* to angle *theta2* (in degrees).
717784
718785 If *n* is provided, it is the number of spline segments to make.
@@ -790,7 +857,7 @@ def arc(cls, theta1, theta2, n=None, is_wedge=False):
790857 @classmethod
791858 def wedge (cls , theta1 , theta2 , n = None ):
792859 """
793- (staticmethod) Returns a wedge of the unit circle from angle
860+ Return a wedge of the unit circle from angle
794861 *theta1* to angle *theta2* (in degrees).
795862
796863 If *n* is provided, it is the number of spline segments to make.
0 commit comments