Skip to content

Commit 59a6a2f

Browse files
committed
Fix incorrect transform in apply_aspect.
x_trf goes from rawdata-space to scaled-space, so it's what should get applied to datalims, not x_trf.inverted(). So ``` x0, x1 = map(x_trf.inverted().transform, dL.intervalx) y0, y1 = map(y_trf.inverted().transform, dL.intervaly) ``` from 87c742b should have been ``` x0, x1 = map(x_trf.transform, dL.intervalx) y0, y1 = map(y_trf.transform, dL.intervaly) ``` However, fixing that triggered a failure for test_aspect_nonlinear_adjustable_datalim which had been added in that commit, and fixing that unraveled more issues. The basic question is, when aspect is set and adjustable="datalim", should we change the x limits or the y limits to get the correct aspect? The old code used some complex conditions, which I actually haven't managed to fully understand, to either expand or shrink one of the axises. Instead, just choose to always expand (rather than shrink) one of the axises, which will avoid sending artists out-of-bounds. (The sole exception is in care of shared axes, which we do not touch as explained in the comment.) This patch caused a change in the autolimiting of test_axes.py::test_pie_frame_grid which was buggy anyways, I forced the old behavior by setting x/ylims manually (after checking that the default is to expand the limits).
1 parent 667a100 commit 59a6a2f

File tree

2 files changed

+35
-41
lines changed

2 files changed

+35
-41
lines changed

lib/matplotlib/axes/_base.py

Lines changed: 28 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1501,55 +1501,42 @@ def apply_aspect(self, position=None):
15011501
box_aspect = fig_aspect * (h / w)
15021502
data_ratio = box_aspect / aspect
15031503

1504-
y_expander = data_ratio * xsize / ysize - 1
1505-
# If y_expander > 0, the dy/dx viewLim ratio needs to increase
1506-
if abs(y_expander) < 0.005:
1504+
y_expander = data_ratio * xsize / ysize
1505+
# If y_expander > 1, the dy/dx viewLim ratio needs to increase
1506+
if abs(y_expander - 1) < 0.005:
15071507
return
15081508

1509-
dL = self.dataLim
1510-
x0, x1 = map(x_trf.inverted().transform, dL.intervalx)
1511-
y0, y1 = map(y_trf.inverted().transform, dL.intervaly)
1512-
xr = 1.05 * (x1 - x0)
1513-
yr = 1.05 * (y1 - y0)
1514-
1515-
xmarg = xsize - xr
1516-
ymarg = ysize - yr
1517-
Ysize = data_ratio * xsize
1518-
Xsize = ysize / data_ratio
1519-
Xmarg = Xsize - xr
1520-
Ymarg = Ysize - yr
1521-
# Setting these targets to, e.g., 0.05*xr does not seem to help.
1522-
xm = 0
1523-
ym = 0
1524-
1509+
# What axes do we adjust?
1510+
# - We can't adjust shared axes because it's too easy to break sharing
1511+
# given the sequential handling of axes (that may be possible if we
1512+
# first collected all the constraints on all axes and considered them
1513+
# all at once).
1514+
# - Assuming no constraints from sharing, prefer expanding axes rather
1515+
# than shrinking them, as the latter can hide plot elements.
15251516
shared_x = self in self._shared_x_axes
15261517
shared_y = self in self._shared_y_axes
1527-
# Not sure whether we need this check:
15281518
if shared_x and shared_y:
15291519
raise RuntimeError("adjustable='datalim' is not allowed when both "
15301520
"axes are shared")
1531-
1532-
# If y is shared, then we are only allowed to change x, etc.
1533-
if shared_y:
1534-
adjust_y = False
1535-
else:
1536-
if xmarg > xm and ymarg > ym:
1537-
adjy = ((Ymarg > 0 and y_expander < 0) or
1538-
(Xmarg < 0 and y_expander > 0))
1539-
else:
1540-
adjy = y_expander > 0
1541-
adjust_y = shared_x or adjy # (Ymarg > xmarg)
1542-
1543-
if adjust_y:
1544-
yc = 0.5 * (ymin + ymax)
1545-
y0 = yc - Ysize / 2.0
1546-
y1 = yc + Ysize / 2.0
1547-
self.set_ybound(*map(y_trf.inverted().transform, (y0, y1)))
1521+
if shared_x:
1522+
adjust = "y"
1523+
elif shared_y:
1524+
adjust = "x"
1525+
elif y_expander > 1:
1526+
adjust = "y"
1527+
else: # y_expander < 1
1528+
adjust = "x"
1529+
1530+
if adjust == "y":
1531+
yc = (ymin + ymax) / 2
1532+
ymin = yc - (yc - ymin) * y_expander
1533+
ymax = yc + (ymax - yc) * y_expander
1534+
self.set_ybound(*map(y_trf.inverted().transform, (ymin, ymax)))
15481535
else:
1549-
xc = 0.5 * (xmin + xmax)
1550-
x0 = xc - Xsize / 2.0
1551-
x1 = xc + Xsize / 2.0
1552-
self.set_xbound(*map(x_trf.inverted().transform, (x0, x1)))
1536+
xc = (xmin + xmax) / 2
1537+
xmin = xc - (xc - xmin) / y_expander
1538+
xmax = xc + (xmax - xc) / y_expander
1539+
self.set_xbound(*map(x_trf.inverted().transform, (xmin, xmax)))
15531540

15541541
def axis(self, *args, emit=True, **kwargs):
15551542
"""

lib/matplotlib/tests/test_axes.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4928,6 +4928,13 @@ def test_pie_frame_grid():
49284928
frame=True, center=(3, 5))
49294929
# Set aspect ratio to be equal so that pie is drawn as a circle.
49304930
plt.axis('equal')
4931+
plt.gcf().canvas.draw()
4932+
xmin, xmax = plt.xlim()
4933+
ymin, ymax = plt.ylim()
4934+
assert xmin < 0 and xmax > 7 and ymin == 0 and ymax == 7
4935+
# These limits reproduce an old, buggy implementation of axis("equal").
4936+
plt.xlim(0, 7)
4937+
plt.ylim(0.7903225806451615, 6.209677419354838)
49314938

49324939

49334940
@image_comparison(['pie_rotatelabels_true.png'])

0 commit comments

Comments
 (0)