Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 59d7830

Browse files
committed
Fix axh{line,span} on polar axes.
For axhline, set the underlying path's _interpolation_steps to GRIDLINE_INTERPOLATION_STEPS (180), which amounts to using the same logic to draw it as for drawing gridlines. This ensures that a polar axhline goes (due to interpolation) around the whole circle, rather than being a trivial line connecting a point to itself. Also update axvline for consistency. (Note that _interpolation_steps has is ignored for rectilinear transforms and thus the change doesn't slow down the common, rectilinear case.) Increase the number of interpolation steps of ax{v,h}span likewise to GRIDLINE_INTERPOLATION_STEPS (again for consistency), and more importantly switch them to using Rectangle rather than Polygon, so that a polar axhspan is drawn as an annulus. This is necessary due to a separate "bug", whereby the CLOSEPOLY step on a Polygon would generate (effectively) a segment that's unfortunately ignored by _interpolation_steps (as it doesn't appear explicitly), whereas Rectangle explicitly includes the closing (4th) segment before emitting a CLOSEPOLY.
1 parent 5f785e3 commit 59d7830

File tree

7 files changed

+60
-16
lines changed

7 files changed

+60
-16
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
``axvspan`` and ``axhspan`` now return ``Rectangle``\s, not ``Polygons``
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
This change allows using `~.Axes.axhspan` to draw an annulus on polar axes.
4+
5+
This change also affects other elements built via `~.Axes.axvspan` and
6+
`~.Axes.axhspan`, such as ``Slider.poly``.

lib/matplotlib/axes/_axes.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,8 @@ def axhline(self, y=0, xmin=0, xmax=1, **kwargs):
783783
trans = self.get_yaxis_transform(which='grid')
784784
l = mlines.Line2D([xmin, xmax], [y, y], transform=trans, **kwargs)
785785
self.add_line(l)
786+
l.get_path()._interpolation_steps = \
787+
mpl.axis.GRIDLINE_INTERPOLATION_STEPS
786788
if scaley:
787789
self._request_autoscale_view("y")
788790
return l
@@ -851,6 +853,8 @@ def axvline(self, x=0, ymin=0, ymax=1, **kwargs):
851853
trans = self.get_xaxis_transform(which='grid')
852854
l = mlines.Line2D([x, x], [ymin, ymax], transform=trans, **kwargs)
853855
self.add_line(l)
856+
l.get_path()._interpolation_steps = \
857+
mpl.axis.GRIDLINE_INTERPOLATION_STEPS
854858
if scalex:
855859
self._request_autoscale_view("x")
856860
return l
@@ -978,10 +982,17 @@ def axhspan(self, ymin, ymax, xmin=0, xmax=1, **kwargs):
978982
self._check_no_units([xmin, xmax], ['xmin', 'xmax'])
979983
(ymin, ymax), = self._process_unit_info([("y", [ymin, ymax])], kwargs)
980984

981-
verts = (xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)
982-
p = mpatches.Polygon(verts, **kwargs)
985+
p = mpatches.Rectangle((xmin, ymin), xmax - xmin, ymax - ymin, **kwargs)
983986
p.set_transform(self.get_yaxis_transform(which="grid"))
987+
# For Rectangles and non-separable transforms, add_patch can be buggy
988+
# and update the x limits even though it shouldn't do so for an
989+
# yaxis_transformed patch, so undo that update.
990+
ix = self.dataLim.intervalx
991+
mx = self.dataLim.minposx
984992
self.add_patch(p)
993+
self.dataLim.intervalx = ix
994+
self.dataLim.minposx = mx
995+
p.get_path()._interpolation_steps = mpl.axis.GRIDLINE_INTERPOLATION_STEPS
985996
self._request_autoscale_view("y")
986997
return p
987998

@@ -1034,11 +1045,17 @@ def axvspan(self, xmin, xmax, ymin=0, ymax=1, **kwargs):
10341045
self._check_no_units([ymin, ymax], ['ymin', 'ymax'])
10351046
(xmin, xmax), = self._process_unit_info([("x", [xmin, xmax])], kwargs)
10361047

1037-
verts = [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)]
1038-
p = mpatches.Polygon(verts, **kwargs)
1048+
p = mpatches.Rectangle((xmin, ymin), xmax - xmin, ymax - ymin, **kwargs)
10391049
p.set_transform(self.get_xaxis_transform(which="grid"))
1040-
p.get_path()._interpolation_steps = 100
1050+
# For Rectangles and non-separable transforms, add_patch can be buggy
1051+
# and update the y limits even though it shouldn't do so for an
1052+
# xaxis_transformed patch, so undo that update.
1053+
iy = self.dataLim.intervaly.copy()
1054+
my = self.dataLim.minposy
10411055
self.add_patch(p)
1056+
self.dataLim.intervaly = iy
1057+
self.dataLim.minposy = my
1058+
p.get_path()._interpolation_steps = mpl.axis.GRIDLINE_INTERPOLATION_STEPS
10421059
self._request_autoscale_view("x")
10431060
return p
10441061

lib/matplotlib/tests/test_axes.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8849,3 +8849,15 @@ def test_xylim_changed_shared():
88498849
axs[1].callbacks.connect("ylim_changed", events.append)
88508850
axs[0].set(xlim=[1, 3], ylim=[2, 4])
88518851
assert events == [axs[1], axs[1]]
8852+
8853+
8854+
@image_comparison(["axhvlinespan_interpolation.png"], style="default")
8855+
def test_axhvlinespan_interpolation():
8856+
ax = plt.figure().add_subplot(projection="polar")
8857+
ax.set_axis_off()
8858+
ax.axvline(.1, c="C0")
8859+
ax.axvspan(.2, .3, fc="C1")
8860+
ax.axvspan(.4, .5, .1, .2, fc="C2")
8861+
ax.axhline(1, c="C0", alpha=.5)
8862+
ax.axhspan(.8, .9, fc="C1", alpha=.5)
8863+
ax.axhspan(.6, .7, .8, .9, fc="C2", alpha=.5)

lib/matplotlib/tests/test_path.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,11 +142,11 @@ def test_nonlinear_containment():
142142
ax.set(xscale="log", ylim=(0, 1))
143143
polygon = ax.axvspan(1, 10)
144144
assert polygon.get_path().contains_point(
145-
ax.transData.transform((5, .5)), ax.transData)
145+
ax.transData.transform((5, .5)), polygon.get_transform())
146146
assert not polygon.get_path().contains_point(
147-
ax.transData.transform((.5, .5)), ax.transData)
147+
ax.transData.transform((.5, .5)), polygon.get_transform())
148148
assert not polygon.get_path().contains_point(
149-
ax.transData.transform((50, .5)), ax.transData)
149+
ax.transData.transform((50, .5)), polygon.get_transform())
150150

151151

152152
@image_comparison(['arrow_contains_point.png'],

lib/matplotlib/transforms.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,7 @@ def intersection(bbox1, bbox2):
671671
y1 = np.minimum(bbox1.ymax, bbox2.ymax)
672672
return Bbox([[x0, y0], [x1, y1]]) if x0 <= x1 and y0 <= y1 else None
673673

674+
674675
_default_minpos = np.array([np.inf, np.inf])
675676

676677

@@ -1011,6 +1012,10 @@ def minpos(self):
10111012
"""
10121013
return self._minpos
10131014

1015+
@minpos.setter
1016+
def minpos(self, val):
1017+
self._minpos[:] = val
1018+
10141019
@property
10151020
def minposx(self):
10161021
"""
@@ -1022,6 +1027,10 @@ def minposx(self):
10221027
"""
10231028
return self._minpos[0]
10241029

1030+
@minposx.setter
1031+
def minposx(self, val):
1032+
self._minpos[0] = val
1033+
10251034
@property
10261035
def minposy(self):
10271036
"""
@@ -1033,6 +1042,10 @@ def minposy(self):
10331042
"""
10341043
return self._minpos[1]
10351044

1045+
@minposy.setter
1046+
def minposy(self, val):
1047+
self._minpos[1] = val
1048+
10361049
def get_points(self):
10371050
"""
10381051
Get the points of the bounding box as an array of the form

lib/matplotlib/widgets.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -433,8 +433,8 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None,
433433
Notes
434434
-----
435435
Additional kwargs are passed on to ``self.poly`` which is the
436-
`~matplotlib.patches.Polygon` that draws the slider knob. See the
437-
`.Polygon` documentation for valid property names (``facecolor``,
436+
`~matplotlib.patches.Rectangle` that draws the slider knob. See the
437+
`.Rectangle` documentation for valid property names (``facecolor``,
438438
``edgecolor``, ``alpha``, etc.).
439439
"""
440440
super().__init__(ax, orientation, closedmin, closedmax,
@@ -577,16 +577,12 @@ def set_val(self, val):
577577
----------
578578
val : float
579579
"""
580-
xy = self.poly.xy
581580
if self.orientation == 'vertical':
582-
xy[1] = .25, val
583-
xy[2] = .75, val
581+
self.poly.set_height(val - self.poly.get_y())
584582
self._handle.set_ydata([val])
585583
else:
586-
xy[2] = val, .75
587-
xy[3] = val, .25
584+
self.poly.set_width(val - self.poly.get_x())
588585
self._handle.set_xdata([val])
589-
self.poly.xy = xy
590586
self.valtext.set_text(self._format(val))
591587
if self.drawon:
592588
self.ax.figure.canvas.draw_idle()

0 commit comments

Comments
 (0)