1
1
"""
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`.
3
13
"""
4
14
5
15
from __future__ import print_function
@@ -53,9 +63,9 @@ class Path(object):
53
63
54
64
Users of Path objects should not access the vertices and codes
55
65
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
59
69
:meth:`iter_segments`.
60
70
61
71
.. note::
@@ -73,12 +83,16 @@ class Path(object):
73
83
LINETO = 2 # 1 vertex
74
84
CURVE3 = 3 # 2 vertices
75
85
CURVE4 = 4 # 3 vertices
76
- CLOSEPOLY = 0x4f # 1 vertex
86
+ CLOSEPOLY = 79 # 1 vertex
77
87
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 }
82
96
83
97
code_type = np .uint8
84
98
@@ -87,29 +101,31 @@ def __init__(self, vertices, codes=None, _interpolation_steps=1, closed=False,
87
101
"""
88
102
Create a new path with the given vertices and codes.
89
103
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.
113
129
"""
114
130
if ma .isMaskedArray (vertices ):
115
131
vertices = vertices .astype (np .float_ ).filled (np .nan )
@@ -144,6 +160,37 @@ def __init__(self, vertices, codes=None, _interpolation_steps=1, closed=False,
144
160
else :
145
161
self ._readonly = False
146
162
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
+
147
194
def _update_values (self ):
148
195
self ._should_simplify = (
149
196
rcParams ['path.simplify' ] and
@@ -246,7 +293,7 @@ def __deepcopy__(self):
246
293
@classmethod
247
294
def make_compound_path_from_polys (cls , XY ):
248
295
"""
249
- (static method) Make a compound path object to draw a number
296
+ Make a compound path object to draw a number
250
297
of polygons with equal numbers of sides XY is a (numpolys x
251
298
numsides x 2) numpy array of vertices. Return object is a
252
299
:class:`Path`
@@ -273,10 +320,7 @@ def make_compound_path_from_polys(cls, XY):
273
320
274
321
@classmethod
275
322
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."""
280
324
lengths = [len (x ) for x in args ]
281
325
total_length = sum (lengths )
282
326
@@ -313,64 +357,87 @@ def iter_segments(self, transform=None, remove_nans=True, clip=None,
313
357
Additionally, this method can provide a number of standard
314
358
cleanups and conversions to the path.
315
359
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 ):
347
393
return
348
394
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 ]
356
403
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
362
407
363
408
i = 0
364
409
while i < len_vertices :
365
410
code = codes [i ]
366
411
if code == STOP :
367
412
return
368
413
else :
369
- num_vertices = NUM_VERTICES [ int ( code ) & 0xf ]
414
+ num_vertices = NUM_VERTICES_FOR_CODE [ code ]
370
415
curr_vertices = vertices [i :i + num_vertices ].flatten ()
371
416
yield curr_vertices , code
372
417
i += num_vertices
373
418
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
+
374
441
def transformed (self , transform ):
375
442
"""
376
443
Return a transformed copy of the path.
@@ -519,7 +586,7 @@ def to_polygons(self, transform=None, width=0, height=0):
519
586
@classmethod
520
587
def unit_rectangle (cls ):
521
588
"""
522
- (staticmethod) Returns a :class:`Path` of the unit rectangle
589
+ Return a :class:`Path` instance of the unit rectangle
523
590
from (0, 0) to (1, 1).
524
591
"""
525
592
if cls ._unit_rectangle is None :
@@ -534,7 +601,7 @@ def unit_rectangle(cls):
534
601
@classmethod
535
602
def unit_regular_polygon (cls , numVertices ):
536
603
"""
537
- (staticmethod) Returns a :class:`Path` for a unit regular
604
+ Return a :class:`Path` instance for a unit regular
538
605
polygon with the given *numVertices* and radius of 1.0,
539
606
centered at (0, 0).
540
607
"""
@@ -563,7 +630,7 @@ def unit_regular_polygon(cls, numVertices):
563
630
@classmethod
564
631
def unit_regular_star (cls , numVertices , innerCircle = 0.5 ):
565
632
"""
566
- (staticmethod) Returns a :class:`Path` for a unit regular star
633
+ Return a :class:`Path` for a unit regular star
567
634
with the given numVertices and radius of 1.0, centered at (0,
568
635
0).
569
636
"""
@@ -592,7 +659,7 @@ def unit_regular_star(cls, numVertices, innerCircle=0.5):
592
659
@classmethod
593
660
def unit_regular_asterisk (cls , numVertices ):
594
661
"""
595
- (staticmethod) Returns a :class:`Path` for a unit regular
662
+ Return a :class:`Path` for a unit regular
596
663
asterisk with the given numVertices and radius of 1.0,
597
664
centered at (0, 0).
598
665
"""
@@ -603,7 +670,7 @@ def unit_regular_asterisk(cls, numVertices):
603
670
@classmethod
604
671
def unit_circle (cls ):
605
672
"""
606
- (staticmethod) Returns a :class:`Path` of the unit circle.
673
+ Return a :class:`Path` of the unit circle.
607
674
The circle is approximated using cubic Bezier curves. This
608
675
uses 8 splines around the circle using the approach presented
609
676
here:
@@ -666,7 +733,7 @@ def unit_circle(cls):
666
733
@classmethod
667
734
def unit_circle_righthalf (cls ):
668
735
"""
669
- (staticmethod) Returns a :class:`Path` of the right half
736
+ Return a :class:`Path` of the right half
670
737
of a unit circle. The circle is approximated using cubic Bezier
671
738
curves. This uses 4 splines around the circle using the approach
672
739
presented here:
@@ -712,7 +779,7 @@ def unit_circle_righthalf(cls):
712
779
@classmethod
713
780
def arc (cls , theta1 , theta2 , n = None , is_wedge = False ):
714
781
"""
715
- (staticmethod) Returns an arc on the unit circle from angle
782
+ Return an arc on the unit circle from angle
716
783
*theta1* to angle *theta2* (in degrees).
717
784
718
785
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):
790
857
@classmethod
791
858
def wedge (cls , theta1 , theta2 , n = None ):
792
859
"""
793
- (staticmethod) Returns a wedge of the unit circle from angle
860
+ Return a wedge of the unit circle from angle
794
861
*theta1* to angle *theta2* (in degrees).
795
862
796
863
If *n* is provided, it is the number of spline segments to make.
0 commit comments