8000 ENH: Add an "arc" spine type. · matplotlib/matplotlib@24cd817 · GitHub
[go: up one dir, main page]

Skip to content

Commit 24cd817

Browse files
committed
ENH: Add an "arc" spine type.
This can be used in the polar axes when plotting less than the full circle.
1 parent 3769792 commit 24cd817

File tree

1 file changed

+75
-20
lines changed

1 file changed

+75
-20
lines changed

lib/matplotlib/spines.py

Lines changed: 75 additions & 20 deletions
< 8000 td data-grid-cell-id="diff-4e07c249abaaf29d0bb2bef9c5b12a86ccaa4895fcae37004bacf9d406043f8b-255-268-0" data-selected="false" role="gridcell" style="background-color:var(--bgColor-default);text-align:center" tabindex="-1" valign="top" class="focusable-grid-cell diff-line-number position-relative diff-line-number-neutral left-side">255
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ class Spine(mpatches.Patch):
3232
Spines are subclasses of class:`~matplotlib.patches.Patch`, and
3333
inherit much of their behavior.
3434
35-
Spines draw a line or a circle, depending if
36-
function:`~matplotlib.spines.Spine.set_patch_line` or
37-
function:`~matplotlib.spines.Spine.set_patch_circle` has been
38-
called. Line-like is the default.
35+
Spines draw a line, a circle, or an arc depending if
36+
function:`~matplotlib.spines.Spine.set_patch_line`,
37+
function:`~matplotlib.spines.Spine.set_patch_circle`, or
38+
function:`~matplotlib.spines.Spine.set_patch_arc` has been called.
39+
Line-like is the default.
3940
4041
"""
4142
def __str__(self):
@@ -77,10 +78,11 @@ def __init__(self, axes, spine_type, path, **kwargs):
7778
self._path = path
7879

7980
# To support drawing both linear and circular spines, this
80-
# class implements Patch behavior two ways. If
81+
# class implements Patch behavior three ways. If
8182
# self._patch_type == 'line', behave like a mpatches.PathPatch
8283
# instance. If self._patch_type == 'circle', behave like a
83-
# mpatches.Ellipse instance.
84+
# mpatches.Ellipse instance. If self._patch_type == 'arc', behave like
85+
# a mpatches.Arc instance.
8486
self._patch_type = 'line'
8587

8688
# Behavior copied from mpatches.Ellipse:
@@ -102,13 +104,25 @@ def get_smart_bounds(self):
102104
"""get whether the spine has smart bounds"""
103105
return self._smart_bounds
104106

107+
def set_patch_arc(self, center, radius, theta1, theta2):
108+
"""set the spine to be arc-like"""
109+
self._patch_type = 'arc'
110+
self._center = center
111+
self._width = radius * 2
112+
self._height = radius * 2
113+
self._theta1 = theta1
114+
self._theta2 = theta2
115+
self._path = mpath.Path.arc(theta1, theta2)
116+
# arc drawn on axes transform
117+
self.set_transform(self.axes.transAxes)
118+
self.stale = True
119+
105120
def set_patch_circle(self, center, radius):
106121
"""set the spine to be circular"""
107122
self._patch_type = 'circle'
108123
self._center = center
109124
self._width = radius * 2
110125
self._height = radius * 2
111-
self._angle = 0
112126
# circle drawn on axes transform
113127
self.set_transform(self.axes.transAxes)
114128
self.stale = True
@@ -125,18 +139,17 @@ def _recompute_transform(self):
125139
maxes it very important to call the accessor method and
126140
not directly access the transformation member variable.
127141
"""
128-
assert self._patch_type == 'circle'
142+
assert self._patch_type in ('arc', 'circle')
129143
center = (self.convert_xunits(self._center[0]),
130144
self.convert_yunits(self._center[1]))
131145
width = self.convert_xunits(self._width)
132146
height = self.convert_yunits(self._height)
133147
self._patch_transform = mtransforms.Affine2D() \
134148
.scale(width * 0.5, height * 0.5) \
135-
.rotate_deg(self._angle) \
136149
.translate(*center)
137150

138151
def get_patch_transform(self):
139-
if self._patch_type == 'circle':
152+
if self._patch_type in ('arc', 'circle'):
140153
self._recompute_transform()
141154
return self._patch_transform
142155
else:
@@ -255,17 +268,48 @@ def _adjust_location(self):
268
else:
256269
low, high = self._bounds
257270

258-
v1 = self._path.vertices
259-
assert v1.shape == (2, 2), 'unexpected vertices shape'
260-
if self.spine_type in ['left', 'right']:
261-
v1[0, 1] = low
262-
v1[1, 1] = high
263-
elif self.spine_type in ['bottom', 'top']:
264-
v1[0, 0] = low
265-
v1[1, 0] = high
271+
if self._patch_type == 'arc':
272+
if self.spine_type in ('bottom', 'top'):
273+
try:
274+
direction = self.axes.get_theta_direction()
275+
except AttributeError:
276+
direction = 1
277+
try:
278+
offset = self.axes.get_theta_offset()
279+
except AttributeError:
280+
offset = 0
281+
low = low * direction + offset
282+
high = high * direction + offset
283+
if low > high:
284+
low, high = high, low
285+
286+
self._path = mpath.Path.arc(np.rad2deg(low), np.rad2deg(high))
287+
288+
if self.spine_type == 'bottom':
289+
rmin, rmax = self.axes.viewLim.intervaly
290+
try:
291+
rorigin = self.axes.get_rorigin()
292+
except AttributeError:
293+
rorigin = rmin
294+
scaled_diameter = (rmin - rorigin) / (rmax - rorigin)
295+
self._height = scaled_diameter
296+
self._width = scaled_diameter
297+
298+
else:
299+
raise ValueError('unable to set bounds for spine "%s"' %
300+
self.spine_type)
266301
else:
267-
raise ValueError('unable to set bounds for spine "%s"' %
268-
self.spine_type)
302+
v1 = self._path.vertices
303+
assert v1.shape == (2, 2), 'unexpected vertices shape'
304+
if self.spine_type in ['left', 'right']:
305+
v1[0, 1] = low
306+
v1[1, 1] = high
307+
elif self.spine_type in ['bottom', 'top']:
308+
v1[0, 0] = low
309+
v1[1, 0] = high
310+
else:
311+
raise ValueError('unable to set bounds for spine "%s"' %
312+
self.spine_type)
269313

270314
@allow_rasterization
271315
def draw(self, renderer):
@@ -463,6 +507,17 @@ def linear_spine(cls, axes, spine_type, **kwargs):
463507

464508
return result
465509

510+
@classmethod
511+
def arc_spine(cls, axes, spine_type, center, radius, theta1, theta2,
512+
**kwargs):
513+
"""
514+
(classmethod) Returns an arc :class:`Spine`.
515+
"""
516+
path = mpath.Path.arc(theta1, theta2)
517+
result = cls(axes, spine_type, path, **kwargs)
518+
result.set_patch_arc(center, radius, theta1, theta2)
519+
return result
520+
466521
@classmethod
467522
def circular_spine(cls, axes, center, radius, **kwargs):
468523
"""

0 commit comments

Comments
 (0)
0