Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Added the "cleared" method to Path, and updated the path module's documentation. #2011

Merged
merged 2 commits into from

2 participants

@pelson
Collaborator

No description provided.

lib/matplotlib/path.py
((94 lines not shown))
curr_vertices = vertices[i:i+num_vertices].flatten()
yield curr_vertices, code
i += num_vertices
+ def cleaned(self, transform=None, remove_nans=False, clip=None,
+ quantize=False, simplify=False, curves=False,
+ stroke_width=1.0, snap=False, sketch=None):
+ """
+ Cleans up the path according to the parameters and returns the
+ resulting vertices and codes.
+
+ .. seealso::
+
+ See :meth:`iter_segments` for details of the keyword arguments.
+
+ Returns:
+ A (vertices, codes) tuple, where vertices is an array of
+ coordinates, and codes is an array of path codes.
+
@mdboom Owner
mdboom added a note

Might as well do this in numpydoc format as well, while we're at it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/matplotlib/path.py
((104 lines not shown))
+
+ .. seealso::
+
+ See :meth:`iter_segments` for details of the keyword arguments.
+
+ Returns:
+ A (vertices, codes) tuple, where vertices is an array of
+ coordinates, and codes is an array of path codes.
+
+ """
+ vertices, codes = cleanup_path(self, transform,
+ remove_nans, clip,
+ snap, stroke_width,
+ simplify, curves, sketch)
+ # Remove the final vertex (with code 0)
+ return vertices[:-1, :], codes[:-1]
@mdboom Owner
mdboom added a note

Wouldn't it be better to return a new Path object with the vertices and codes in it?

@mdboom Owner
mdboom added a note

Also, why is it necessary to remove the final vertex -- all our code is supposedly robust against that anyway...

@pelson Collaborator
pelson added a note

The indexing shouldn't be there. In my case I'm calling cleaned in cartopy and operating on the codes and vertices and don't need the Path instance itself (which has a cost to construction). In this case, I'd rather keep the return value as (verts, codes) if that is agreeable?

@mdboom Owner
mdboom added a note

It just feels a little inconsistent with other methods which all return a Path object when appropriate. The main cost of construction is in the hasfinite and should_simplify checks. But those could probably be short-circuited somehow based on hasfinite on the input path, and remove_nans and clip.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@mdboom mdboom commented on the diff
lib/matplotlib/path.py
@@ -77,10 +88,14 @@ class Path(object):
CURVE4 = 4 # 3 vertices
CLOSEPOLY = 0x4f # 1 vertex
- NUM_VERTICES = [1, 1, 1, 2,
- 3, 1, 1, 1,
- 1, 1, 1, 1,
- 1, 1, 1, 1]
+ #: A dictionary mapping Path codes to the number of vertices that the
+ #: code expects.
+ NUM_VERTICES_FOR_CODE = {STOP: 1,
+ MOVETO: 1,
+ LINETO: 1,
+ CURVE3: 2,
+ CURVE4: 3,
+ CLOSEPOLY: 1}
@mdboom Owner
mdboom added a note

This is better as a list -- the lookups are much faster and in this case the speed really matters. Try outputting a large line to the PDF backend for an example.

As an alternative, we could include the old list-based one as a private variable (used internally by iter_segments) and include this dictionary for external use.

@pelson Collaborator
pelson added a note

I'm not sure I believe this:

In [5]: import random

In [9]: v_dict = {0: 1, 1: 2, 2:1}

In [4]: v_list = [1, 2, 1]

In [10]: %timeit v_dict[random.randint(0, 2)]
100000 loops, best of 3: 2.4 us per loop

In [11]: %timeit v_list[random.randint(0, 2)]
100000 loops, best of 3: 2.39 us per loop

In [12]: %timeit v_list[random.randint(0, 2)]
100000 loops, best of 3: 2.34 us per loop

In [13]: %timeit v_list[random.randint(0, 2)]
100000 loops, best of 3: 2.34 us per loop

In [14]: %timeit v_dict[random.randint(0, 2)]
100000 loops, best of 3: 2.39 us per loop

In [15]: %timeit v_dict[random.randint(0, 2)]
100000 loops, best of 3: 2.38 us per loop

In [16]: %timeit v_dict[random.randint(0, 2)]
100000 loops, best of 3: 2.39 us per loop

Both lookups are O(1) (according to http://wiki.python.org/moin/TimeComplexity). I'm happy enough to revert, but in terms of optimisation I'm really surprised this is noticeable...

@mdboom Owner
mdboom added a note

Interesting -- that code goes back a ways, so the earlier benchmarking was probably done on Python 2.4 or 2.5, which may behave differently. For dictionaries of this small size, I suppose it doesn't matter -- but note that that table has a worst case time for dictionary lookups of O(n), so it does grow as the dictionary grows.

You've convinced me this is probably ok (and obviously on legibility grounds it's no contest).

@pelson Collaborator
pelson added a note

but note that that table has a worst case time for dictionary lookups of O(n), so it does grow as the dictionary grows.

Yes - I'm not 100% but I think that is when you have hash collisions.

Ok, I'll keep the dictionary. Thanks for the update.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/matplotlib/path.py
((90 lines not shown))
return
else:
- num_vertices = NUM_VERTICES[int(code) & 0xf]
+ num_vertices = self.NUM_VERTICES_FOR_CODE[code]
@mdboom Owner
mdboom added a note

In addition to my comment above about this being a list, it should also remain a local variable, again for speed. I don't usually care so much, but this is the deep inner loop of a lot of the backends -- it's important that it be fast.

@pelson Collaborator
pelson added a note

Ah. Ok. This optimization I do believe :smile: - will do.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@pelson
Collaborator

Ok. Think this is now ready @mdboom.

@mdboom mdboom commented on the diff
lib/matplotlib/path.py
@@ -75,12 +86,16 @@ class Path(object):
LINETO = 2 # 1 vertex
CURVE3 = 3 # 2 vertices
CURVE4 = 4 # 3 vertices
- CLOSEPOLY = 0x4f # 1 vertex
+ CLOSEPOLY = 79 # 1 vertex
@mdboom Owner
mdboom added a note

Can we keep this as it was -- I know it doesn't really matter, but there are some tricks used to detect the end of paths that are made more obvious when you think of it in hex.

@pelson Collaborator
pelson added a note

but there are some tricks used to detect the end of paths that are made more obvious when you think of it in hex

For whom? I can see the benefit for developers, but not for everyday users. I'd be happy to add a comment here to jig the readers memory, if that would suffice?

@mdboom Owner
mdboom added a note

Ok -- probably simplest to leave it as a (base 10) number, then. A comment here doesn't make sense either.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@mdboom
Owner

I still think we should have cleaned return a Path instance -- we can bypass the constructor to make it nice and fast. It just seems unpythonic this way, to me at least.

@pelson
Collaborator

I still think we should have cleaned return a Path instance

The two cases I can envisage with cleared both just use the codes and verts. That said, I'm happy enough to compromise and have implemented a fast constructor for Path.

Was that the kind of thing you had in mind?

@mdboom
Owner

Yes -- that's basically what I had in mind. I think that's handy. There may be other cases in there where this should be used instead of the constructor, but that can wait for another PR.

@pelson
Collaborator

Should add ... here so that those things are set correctly in the result.

Nice. I've added a commit. I expect to have to rebase this PR after the deprecations PR gets merged.

@pelson
Collaborator

Other than that, I think this PR is good to go... Thanks for being patient with me ;)

No worries. Having your invaluable feedback on this module has been great.

I'll rebase now.

@pelson
Collaborator

Ok. Once travis does its thing, lets merge :smile:

@pelson
Collaborator

Other than the spurious test failiure from master, the tests are passing with this PR.

@mdboom mdboom merged commit 24a635f into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
9 doc/api/api_changes.rst
@@ -104,6 +104,15 @@ Changes in 1.3.x
existing text objects. This brings their behavior in line with most
other rcParams.
+* To support XKCD style plots, the :func:`matplotlib.path.cleanup_path`
+ method's signature was updated to require a sketch argument. Users of
+ :func:`matplotlib.path.cleanup_path` are encouraged to use the new
+ :meth:`~matplotlib.path.Path.cleaned` Path method.
+
+* The list at ``Path.NUM_VERTICES`` was replaced by a dictionary mapping
+ Path codes to the number of expected vertices at
+ :attr:`~matplotlib.path.Path.NUM_VERTICES_FOR_CODE`.
+
* Fixed a bug in setting the position for the right/top spine with data
position type. Previously, it would draw the right or top spine at
+1 data offset.
View
4 lib/matplotlib/cm.py
@@ -189,9 +189,9 @@ def __init__(self, norm=None, cmap=None):
norm = colors.Normalize()
self._A = None;
- #; The Normalization instance of this ScalarMappable.
+ #: The Normalization instance of this ScalarMappable.
self.norm = norm
- #; The Colormap instance of this ScalarMappable.
+ #: The Colormap instance of this ScalarMappable.
self.cmap = get_cmap(cmap)
self.colorbar = None
self.update_dict = {'array': False}
View
250 lib/matplotlib/path.py
@@ -1,5 +1,15 @@
"""
-Contains a class for managing paths (polylines).
+A module for dealing with the polylines used throughout matplotlib.
+
+The primary class for polyline handling in matplotlib is :class:`Path`.
+Almost all vector drawing makes use of Paths somewhere in the drawing
+pipeline.
+
+Whilst a :class:`Path` instance itself cannot be drawn, there exists
+:class:`~matplotlib.artist.Artist` subclasses which can be used for
+convenient Path visualisation - the two most frequently used of these are
+:class:`~matplotlib.patches.PathPatch` and
+:class:`~matplotlib.collections.PathCollection`.
"""
from __future__ import print_function
@@ -53,9 +63,9 @@ class Path(object):
Users of Path objects should not access the vertices and codes
arrays directly. Instead, they should use :meth:`iter_segments`
- to get the vertex/code pairs. This is important, since many
- :class:`Path` objects, as an optimization, do not store a *codes*
- at all, but have a default one provided for them by
+ or :meth:`cleaned` to get the vertex/code pairs. This is important,
+ since many :class:`Path` objects, as an optimization, do not store a
+ *codes* at all, but have a default one provided for them by
:meth:`iter_segments`.
.. note::
@@ -73,12 +83,16 @@ class Path(object):
LINETO = 2 # 1 vertex
CURVE3 = 3 # 2 vertices
CURVE4 = 4 # 3 vertices
- CLOSEPOLY = 0x4f # 1 vertex
+ CLOSEPOLY = 79 # 1 vertex
@mdboom Owner
mdboom added a note

Can we keep this as it was -- I know it doesn't really matter, but there are some tricks used to detect the end of paths that are made more obvious when you think of it in hex.

@pelson Collaborator
pelson added a note

but there are some tricks used to detect the end of paths that are made more obvious when you think of it in hex

For whom? I can see the benefit for developers, but not for everyday users. I'd be happy to add a comment here to jig the readers memory, if that would suffice?

@mdboom Owner
mdboom added a note

Ok -- probably simplest to leave it as a (base 10) number, then. A comment here doesn't make sense either.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
- NUM_VERTICES = [1, 1, 1, 2,
- 3, 1, 1, 1,
- 1, 1, 1, 1,
- 1, 1, 1, 1]
+ #: A dictionary mapping Path codes to the number of vertices that the
+ #: code expects.
+ NUM_VERTICES_FOR_CODE = {STOP: 1,
+ MOVETO: 1,
+ LINETO: 1,
+ CURVE3: 2,
+ CURVE4: 3,
+ CLOSEPOLY: 1}
@mdboom Owner
mdboom added a note

This is better as a list -- the lookups are much faster and in this case the speed really matters. Try outputting a large line to the PDF backend for an example.

As an alternative, we could include the old list-based one as a private variable (used internally by iter_segments) and include this dictionary for external use.

@pelson Collaborator
pelson added a note

I'm not sure I believe this:

In [5]: import random

In [9]: v_dict = {0: 1, 1: 2, 2:1}

In [4]: v_list = [1, 2, 1]

In [10]: %timeit v_dict[random.randint(0, 2)]
100000 loops, best of 3: 2.4 us per loop

In [11]: %timeit v_list[random.randint(0, 2)]
100000 loops, best of 3: 2.39 us per loop

In [12]: %timeit v_list[random.randint(0, 2)]
100000 loops, best of 3: 2.34 us per loop

In [13]: %timeit v_list[random.randint(0, 2)]
100000 loops, best of 3: 2.34 us per loop

In [14]: %timeit v_dict[random.randint(0, 2)]
100000 loops, best of 3: 2.39 us per loop

In [15]: %timeit v_dict[random.randint(0, 2)]
100000 loops, best of 3: 2.38 us per loop

In [16]: %timeit v_dict[random.randint(0, 2)]
100000 loops, best of 3: 2.39 us per loop

Both lookups are O(1) (according to http://wiki.python.org/moin/TimeComplexity). I'm happy enough to revert, but in terms of optimisation I'm really surprised this is noticeable...

@mdboom Owner
mdboom added a note

Interesting -- that code goes back a ways, so the earlier benchmarking was probably done on Python 2.4 or 2.5, which may behave differently. For dictionaries of this small size, I suppose it doesn't matter -- but note that that table has a worst case time for dictionary lookups of O(n), so it does grow as the dictionary grows.

You've convinced me this is probably ok (and obviously on legibility grounds it's no contest).

@pelson Collaborator
pelson added a note

but note that that table has a worst case time for dictionary lookups of O(n), so it does grow as the dictionary grows.

Yes - I'm not 100% but I think that is when you have hash collisions.

Ok, I'll keep the dictionary. Thanks for the update.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
code_type = np.uint8
@@ -87,29 +101,31 @@ def __init__(self, vertices, codes=None, _interpolation_steps=1, closed=False,
"""
Create a new path with the given vertices and codes.
- *vertices* is an Nx2 numpy float array, masked array or Python
- sequence.
-
- *codes* is an N-length numpy array or Python sequence of type
- :attr:`matplotlib.path.Path.code_type`.
-
- These two arrays must have the same length in the first
- dimension.
-
- If *codes* is None, *vertices* will be treated as a series of
- line segments.
-
- If *vertices* contains masked values, they will be converted
- to NaNs which are then handled correctly by the Agg
- PathIterator and other consumers of path data, such as
- :meth:`iter_segments`.
-
- *interpolation_steps* is used as a hint to certain projections,
- such as Polar, that this path should be linearly interpolated
- immediately before drawing. This attribute is primarily an
- implementation detail and is not intended for public use.
-
- *readonly*, when True, makes the path immutable.
+ Parameters
+ ----------
+ vertices : array_like
+ The ``(n, 2)`` float array, masked array or sequence of pairs
+ representing the vertices of the path.
+
+ If *vertices* contains masked values, they will be converted
+ to NaNs which are then handled correctly by the Agg
+ PathIterator and other consumers of path data, such as
+ :meth:`iter_segments`.
+ codes : {None, array_like}, optional
+ n-length array integers representing the codes of the path.
+ If not None, codes must be the same length as vertices.
+ If None, *vertices* will be treated as a series of line segments.
+ _interpolation_steps : int, optional
+ Used as a hint to certain projections, such as Polar, that this
+ path should be linearly interpolated immediately before drawing.
+ This attribute is primarily an implementation detail and is not
+ intended for public use.
+ closed : bool, optional
+ If *codes* is None and closed is True, vertices will be treated as
+ line segments of a closed polygon.
+ readonly : bool, optional
+ Makes the path behave in an immutable way and sets the vertices
+ and codes as read-only arrays.
"""
if ma.isMaskedArray(vertices):
vertices = vertices.astype(np.float_).filled(np.nan)
@@ -144,6 +160,38 @@ def __init__(self, vertices, codes=None, _interpolation_steps=1, closed=False,
else:
self._readonly = False
+ @classmethod
+ def _fast_from_codes_and_verts(cls, verts, codes, internals=None):
+ """
+ Creates a Path instance without the expense of calling the constructor
+
+ Parameters
+ ----------
+ verts : numpy array
+ codes : numpy array (may not be None)
+ internals : dict or None
+ The attributes that the resulting path should have.
+ Allowed keys are ``readonly``, ``should_simplify``,
+ ``simplify_threshold``, ``has_nonfinite`` and
+ ``interpolation_steps``.
+
+ """
+ internals = internals or {}
+ pth = cls.__new__(cls)
+ pth._vertices = verts
+ pth._codes = codes
+ pth._readonly = internals.pop('readonly', False)
+ pth.should_simplify = internals.pop('should_simplify', True)
+ pth.simplify_threshold = internals.pop('simplify_threshold',
+ rcParams['path.simplify_threshold'])
+ pth._has_nonfinite = internals.pop('has_nonfinite', False)
+ pth._interpolation_steps = internals.pop('interpolation_steps', 1)
+ if internals:
+ raise ValueError('Unexpected internals provided to '
+ '_fast_from_codes_and_verts: '
+ '{0}'.format('\n *'.join(internals.keys())))
+ return pth
+
def _update_values(self):
self._should_simplify = (
rcParams['path.simplify'] and
@@ -246,7 +294,7 @@ def __deepcopy__(self):
@classmethod
def make_compound_path_from_polys(cls, XY):
"""
- (static method) Make a compound path object to draw a number
+ Make a compound path object to draw a number
of polygons with equal numbers of sides XY is a (numpolys x
numsides x 2) numpy array of vertices. Return object is a
:class:`Path`
@@ -273,10 +321,7 @@ def make_compound_path_from_polys(cls, XY):
@classmethod
def make_compound_path(cls, *args):
- """
- (staticmethod) Make a compound path from a list of Path
- objects.
- """
+ """Make a compound path from a list of Path objects."""
lengths = [len(x) for x in args]
total_length = sum(lengths)
@@ -313,52 +358,53 @@ def iter_segments(self, transform=None, remove_nans=True, clip=None,
Additionally, this method can provide a number of standard
cleanups and conversions to the path.
- *transform*: if not None, the given affine transformation will
- be applied to the path.
-
- *remove_nans*: if True, will remove all NaNs from the path and
- insert MOVETO commands to skip over them.
-
- *clip*: if not None, must be a four-tuple (x1, y1, x2, y2)
- defining a rectangle in which to clip the path.
-
- *snap*: if None, auto-snap to pixels, to reduce
- fuzziness of rectilinear lines. If True, force snapping, and
- if False, don't snap.
-
- *stroke_width*: the width of the stroke being drawn. Needed
- as a hint for the snapping algorithm.
-
- *simplify*: if True, perform simplification, to remove
- vertices that do not affect the appearance of the path. If
- False, perform no simplification. If None, use the
- should_simplify member variable.
-
- *curves*: If True, curve segments will be returned as curve
- segments. If False, all curves will be converted to line
- segments.
-
- *sketch*: If not None, must be a 3-tuple of the form
- (scale, length, randomness), representing the sketch
- parameters.
- """
- vertices = self.vertices
- if not len(vertices):
+ Parameters
+ ----------
+ transform : None or :class:`~matplotlib.transforms.Transform` instance
+ If not None, the given affine transformation will
+ be applied to the path.
+ remove_nans : {False, True}, optional
+ If True, will remove all NaNs from the path and
+ insert MOVETO commands to skip over them.
+ clip : None or sequence, optional
+ If not None, must be a four-tuple (x1, y1, x2, y2)
+ defining a rectangle in which to clip the path.
+ snap : None or bool, optional
+ If None, auto-snap to pixels, to reduce
+ fuzziness of rectilinear lines. If True, force snapping, and
+ if False, don't snap.
+ stroke_width : float, optional
+ The width of the stroke being drawn. Needed
+ as a hint for the snapping algorithm.
+ simplify : None or bool, optional
+ If True, perform simplification, to remove
+ vertices that do not affect the appearance of the path. If
+ False, perform no simplification. If None, use the
+ should_simplify member variable.
+ curves : {True, False}, optional
+ If True, curve segments will be returned as curve
+ segments. If False, all curves will be converted to line
+ segments.
+ sketch : None or sequence, optional
+ If not None, must be a 3-tuple of the form
+ (scale, length, randomness), representing the sketch
+ parameters.
+ """
+ if not len(self):
return
- codes = self.codes
-
- NUM_VERTICES = self.NUM_VERTICES
- MOVETO = self.MOVETO
- LINETO = self.LINETO
- CLOSEPOLY = self.CLOSEPOLY
- STOP = self.STOP
+ cleaned = self.cleaned(transform=transform,
+ remove_nans=remove_nans, clip=clip,
+ snap=snap, stroke_width=stroke_width,
+ simplify=simplify, curves=curves,
+ sketch=sketch)
+ vertices = cleaned.vertices
+ codes = cleaned.codes
+ len_vertices = vertices.shape[0]
- vertices, codes = _path.cleanup_path(
- self, transform, remove_nans, clip,
- snap, stroke_width, simplify, curves,
- sketch)
- len_vertices = len(vertices)
+ # Cache these object lookups for performance in the loop.
+ NUM_VERTICES_FOR_CODE = self.NUM_VERTICES_FOR_CODE
+ STOP = self.STOP
i = 0
while i < len_vertices:
@@ -366,11 +412,37 @@ def iter_segments(self, transform=None, remove_nans=True, clip=None,
if code == STOP:
return
else:
- num_vertices = NUM_VERTICES[int(code) & 0xf]
+ num_vertices = NUM_VERTICES_FOR_CODE[code]
curr_vertices = vertices[i:i+num_vertices].flatten()
yield curr_vertices, code
i += num_vertices
+ def cleaned(self, transform=None, remove_nans=False, clip=None,
+ quantize=False, simplify=False, curves=False,
+ stroke_width=1.0, snap=False, sketch=None):
+ """
+ Cleans up the path according to the parameters returning a new
+ Path instance.
+
+ .. seealso::
+
+ See :meth:`iter_segments` for details of the keyword arguments.
+
+ Returns
+ -------
+ Path instance with cleaned up vertices and codes.
+
+ """
+ vertices, codes = cleanup_path(self, transform,
+ remove_nans, clip,
+ snap, stroke_width,
+ simplify, curves, sketch)
+ internals = {'should_simplify': self.should_simplify and not simplify,
+ 'has_nonfinite': self.has_nonfinite and not remove_nans,
+ 'simplify_threshold': self.simplify_threshold,
+ 'interpolation_steps': self._interpolation_steps}
+ return Path._fast_from_codes_and_verts(vertices, codes, internals)
+
def transformed(self, transform):
"""
Return a transformed copy of the path.
@@ -519,7 +591,7 @@ def to_polygons(self, transform=None, width=0, height=0):
@classmethod
def unit_rectangle(cls):
"""
- (staticmethod) Returns a :class:`Path` of the unit rectangle
+ Return a :class:`Path` instance of the unit rectangle
from (0, 0) to (1, 1).
"""
if cls._unit_rectangle is None:
@@ -534,7 +606,7 @@ def unit_rectangle(cls):
@classmethod
def unit_regular_polygon(cls, numVertices):
"""
- (staticmethod) Returns a :class:`Path` for a unit regular
+ Return a :class:`Path` instance for a unit regular
polygon with the given *numVertices* and radius of 1.0,
centered at (0, 0).
"""
@@ -563,7 +635,7 @@ def unit_regular_polygon(cls, numVertices):
@classmethod
def unit_regular_star(cls, numVertices, innerCircle=0.5):
"""
- (staticmethod) Returns a :class:`Path` for a unit regular star
+ Return a :class:`Path` for a unit regular star
with the given numVertices and radius of 1.0, centered at (0,
0).
"""
@@ -592,7 +664,7 @@ def unit_regular_star(cls, numVertices, innerCircle=0.5):
@classmethod
def unit_regular_asterisk(cls, numVertices):
"""
- (staticmethod) Returns a :class:`Path` for a unit regular
+ Return a :class:`Path` for a unit regular
asterisk with the given numVertices and radius of 1.0,
centered at (0, 0).
"""
@@ -603,7 +675,7 @@ def unit_regular_asterisk(cls, numVertices):
@classmethod
def unit_circle(cls):
"""
- (staticmethod) Returns a :class:`Path` of the unit circle.
+ Return a :class:`Path` of the unit circle.
The circle is approximated using cubic Bezier curves. This
uses 8 splines around the circle using the approach presented
here:
@@ -666,7 +738,7 @@ def unit_circle(cls):
@classmethod
def unit_circle_righthalf(cls):
"""
- (staticmethod) Returns a :class:`Path` of the right half
+ Return a :class:`Path` of the right half
of a unit circle. The circle is approximated using cubic Bezier
curves. This uses 4 splines around the circle using the approach
presented here:
@@ -712,7 +784,7 @@ def unit_circle_righthalf(cls):
@classmethod
def arc(cls, theta1, theta2, n=None, is_wedge=False):
"""
- (staticmethod) Returns an arc on the unit circle from angle
+ Return an arc on the unit circle from angle
*theta1* to angle *theta2* (in degrees).
If *n* is provided, it is the number of spline segments to make.
@@ -790,7 +862,7 @@ def arc(cls, theta1, theta2, n=None, is_wedge=False):
@classmethod
def wedge(cls, theta1, theta2, n=None):
"""
- (staticmethod) Returns a wedge of the unit circle from angle
+ Return a wedge of the unit circle from angle
*theta1* to angle *theta2* (in degrees).
If *n* is provided, it is the number of spline segments to make.
Something went wrong with that request. Please try again.