-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Add an Annulus patch class #9888
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 11 commits
e4697e7
a05fb1e
ecd6083
bd3f498
e1f863e
ad81c4b
7bc465a
83047be
32a5daf
9f12372
05cd23a
a5cf0c5
d2fc81a
23bd2da
f0392f6
0882ed8
0ba1cba
b21d4ee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
Add ``Annulus`` patch | ||
--------------------- | ||
|
||
A new class for drawing elliptical annuli. | ||
|
||
.. plot:: | ||
|
||
import matplotlib.pyplot as plt | ||
from matplotlib.patches import Annulus | ||
|
||
fig, ax = plt.subplots() | ||
cir = Annulus((0.5, 0.5), 0.2, 0.05, fc='g') # circular annulus | ||
ell = Annulus((0.5, 0.5), (0.5, 0.3), 0.1, 45, # elliptical | ||
fc='m', ec='b', alpha=0.5, hatch='xxx') | ||
ax.add_patch(cir) | ||
ax.add_patch(ell) | ||
ax.set_aspect('equal') |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1552,9 +1552,96 @@ def get_angle(self): | |
angle = property(get_angle, set_angle) | ||
|
||
|
||
class Circle(Ellipse): | ||
"""A circle patch.""" | ||
class Annulus(Patch): | ||
""" | ||
An elliptical annulus. | ||
""" | ||
|
||
def __str__(self): | ||
astromancer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if self.a == self.b: | ||
r = self.a | ||
else: | ||
r = (self.a, self.b) | ||
|
||
return "Annulus(xy=(%s, %s), r=%s, width=%s, angle=%s)" % \ | ||
astromancer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
(self.center[0], self.center[1], r, self.width, self.angle) | ||
|
||
@docstring.dedent_interpd | ||
def __init__(self, xy, r, width, angle=0.0, **kwargs): | ||
""" | ||
xy : (float, float) | ||
xy coordinates of annulus centre | ||
astromancer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
r : scalar or 1D array_like | ||
astromancer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
The radius, or semi-major axes | ||
- If float: radius of the outer circle | ||
- If array_like of size 2: semi-major and -minor axes of outer | ||
astromancer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
ellipse | ||
width : float | ||
Width of the annulus | ||
astromancer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
angle: float, optional | ||
astromancer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Rotation angle in degrees (anti-clockwise). Ignored for circular | ||
annuli (ie. if `r` is a scalar). | ||
astromancer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Valid kwargs are: | ||
|
||
%(Patch_kwdoc)s | ||
""" | ||
Patch.__init__(self, **kwargs) | ||
astromancer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
if np.shape(r) == (2,): | ||
self.a, self.b = r | ||
elif np.shape(r) == (): | ||
self.a = self.b = float(r) | ||
else: | ||
raise ValueError( | ||
'r parameter should be either float, or array_like of size 2') | ||
astromancer marked this conversation as resolved.
Show resolved
Hide resolved
astromancer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
if min(self.a, self.b) <= width: | ||
raise ValueError( | ||
'Width of annulus should be smaller than semi-minor axis') | ||
astromancer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
self.center = xy | ||
self.width = width | ||
self.angle = angle | ||
self._path = None | ||
|
||
def _transform_verts(self, verts, a, b): | ||
center = (self.convert_xunits(self.center[0]), | ||
self.convert_yunits(self.center[1])) | ||
a = self.convert_xunits(a) | ||
b = self.convert_yunits(b) | ||
tr = transforms.Affine2D() \ | ||
.scale(a, b) \ | ||
.rotate_deg(self.angle) \ | ||
.translate(*center) | ||
|
||
return tr.transform(verts) | ||
|
||
def _recompute_path(self): | ||
# circular arc | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks great. However, do we understand why codecov is confused by this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have given up trying to understand codecov. Above it warns on a line that's part of a docstring. |
||
arc = Path.arc(0, 360) | ||
|
||
# annulus needs to draw an outer ring | ||
# followed by a reversed and scaled inner ring | ||
a, b, w = self.a, self.b, self.width | ||
v1 = self._transform_verts(arc.vertices, a, b) | ||
v2 = self._transform_verts(arc.vertices[::-1], a - w, b - w) | ||
v = np.vstack([v1, v2, v1[0, :], (0, 0)]) | ||
c = np.hstack([arc.codes, arc.codes, Path.MOVETO, Path.CLOSEPOLY]) | ||
c[len(arc.codes)] = Path.MOVETO | ||
astromancer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
self._path = Path(v, c) | ||
|
||
def get_path(self): | ||
if self._path is None: | ||
self._recompute_path() | ||
return self._path | ||
|
||
|
||
class Circle(Ellipse): | ||
""" | ||
A circle patch. | ||
""" | ||
def __str__(self): | ||
pars = self.center[0], self.center[1], self.radius | ||
fmt = "Circle(xy=(%g, %g), radius=%g)" | ||
|
Uh oh!
There was an error while loading. Please reload this page.