Skip to content

FIX: clean up re-limiting hysteresis #20168

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

Merged
merged 1 commit into from
Jun 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions lib/matplotlib/axes/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2863,8 +2863,8 @@ def autoscale_view(self, tight=None, scalex=True, scaley=True):
if self.get_yscale() == 'log':
y_stickies = y_stickies[y_stickies > 0]

def handle_single_axis(scale, autoscaleon, shared_axes, interval,
minpos, axis, margin, stickies, set_bound):
def handle_single_axis(scale, autoscaleon, shared_axes, name,
axis, margin, stickies, set_bound):

if not (scale and autoscaleon):
return # nothing to do...
Expand All @@ -2876,12 +2876,16 @@ def handle_single_axis(scale, autoscaleon, shared_axes, interval,
x_values = []
minimum_minpos = np.inf
for ax in shared:
x_values.extend(getattr(ax.dataLim, interval))
x_values.extend(getattr(ax.dataLim, f"interval{name}"))
minimum_minpos = min(minimum_minpos,
getattr(ax.dataLim, minpos))
getattr(ax.dataLim, f"minpos{name}"))
x_values = np.extract(np.isfinite(x_values), x_values)
if x_values.size >= 1:
x0, x1 = (x_values.min(), x_values.max())
elif getattr(self._viewLim, f"mutated{name}")():
# No data, but explicit viewLims already set:
# in mutatedx or mutatedy.
return
else:
x0, x1 = (-np.inf, np.inf)
# If x0 and x1 are non finite, use the locator to figure out
Expand Down Expand Up @@ -2925,11 +2929,11 @@ def handle_single_axis(scale, autoscaleon, shared_axes, interval,
# End of definition of internal function 'handle_single_axis'.

handle_single_axis(
scalex, self._autoscaleXon, self._shared_x_axes, 'intervalx',
'minposx', self.xaxis, self._xmargin, x_stickies, self.set_xbound)
scalex, self._autoscaleXon, self._shared_x_axes, 'x',
self.xaxis, self._xmargin, x_stickies, self.set_xbound)
handle_single_axis(
scaley, self._autoscaleYon, self._shared_y_axes, 'intervaly',
'minposy', self.yaxis, self._ymargin, y_stickies, self.set_ybound)
scaley, self._autoscaleYon, self._shared_y_axes, 'y',
self.yaxis, self._ymargin, y_stickies, self.set_ybound)

def _get_axis_list(self):
return self.xaxis, self.yaxis
Expand Down
26 changes: 11 additions & 15 deletions lib/matplotlib/axis.py
Original file line number Diff line number Diff line change
Expand Up @@ -1457,7 +1457,7 @@ def update_units(self, data):
if default is not None and self.units is None:
self.set_units(default)

if neednew:
elif neednew:
self._update_axisinfo()
self.stale = True
return True
Expand Down Expand Up @@ -2267,17 +2267,15 @@ def set_inverted(self, inverted):
def set_default_intervals(self):
# docstring inherited
xmin, xmax = 0., 1.
dataMutated = self.axes.dataLim.mutatedx()
viewMutated = self.axes.viewLim.mutatedx()
if not dataMutated or not viewMutated:
# only change view if dataLim has not changed and user has
# not changed the view:
if (not self.axes.dataLim.mutatedx() and
not self.axes.viewLim.mutatedx()):
if self.converter is not None:
info = self.converter.axisinfo(self.units, self)
if info.default_limits is not None:
xmin, xmax = self.convert_units(info.default_limits)
if not dataMutated:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just because we changed the view limits doesn't mean we should change the data limits, which is what this line does...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is likely the correct fix, thanks for figuring that out! :-)

self.axes.dataLim.intervalx = xmin, xmax
if not viewMutated:
self.axes.viewLim.intervalx = xmin, xmax
self.axes.viewLim.intervalx = xmin, xmax
self.stale = True

def get_tick_space(self):
Expand Down Expand Up @@ -2530,17 +2528,15 @@ def set_inverted(self, inverted):
def set_default_intervals(self):
# docstring inherited
ymin, ymax = 0., 1.
dataMutated = self.axes.dataLim.mutatedy()
viewMutated = self.axes.viewLim.mutatedy()
if not dataMutated or not viewMutated:
# only change view if dataLim has not changed and user has
# not changed the view:
if (not self.axes.dataLim.mutatedy() and
not self.axes.viewLim.mutatedy()):
if self.converter is not None:
info = self.converter.axisinfo(self.units, self)
if info.default_limits is not None:
ymin, ymax = self.convert_units(info.default_limits)
if not dataMutated:
self.axes.dataLim.intervaly = ymin, ymax
if not viewMutated:
self.axes.viewLim.intervaly = ymin, ymax
self.axes.viewLim.intervaly = ymin, ymax
self.stale = True

def get_tick_space(self):
Expand Down
Binary file not shown.
44 changes: 43 additions & 1 deletion lib/matplotlib/tests/test_dates.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,55 @@ def test_date2num_NaT_scalar(units):
assert np.isnan(tmpl)


@image_comparison(['date_empty.png'])
def test_date_empty():
# make sure we do the right thing when told to plot dates even
# if no date data has been presented, cf
# http://sourceforge.net/tracker/?func=detail&aid=2850075&group_id=80706&atid=560720
fig, ax = plt.subplots()
ax.xaxis_date()
fig.draw_no_output()
np.testing.assert_allclose(ax.get_xlim(),
[mdates.date2num(np.datetime64('2000-01-01')),
mdates.date2num(np.datetime64('2010-01-01'))])

mdates._reset_epoch_test_example()
mdates.set_epoch('0000-12-31')
fig, ax = plt.subplots()
ax.xaxis_date()
fig.draw_no_output()
np.testing.assert_allclose(ax.get_xlim(),
[mdates.date2num(np.datetime64('2000-01-01')),
mdates.date2num(np.datetime64('2010-01-01'))])
mdates._reset_epoch_test_example()


def test_date_not_empty():
fig = plt.figure()
ax = fig.add_subplot()

ax.plot([50, 70], [1, 2])
ax.xaxis.axis_date()
np.testing.assert_allclose(ax.get_xlim(), [50, 70])


def test_axhline():
# make sure that axhline doesn't set the xlimits...
fig, ax = plt.subplots()
ax.axhline(1.5)
ax.plot([np.datetime64('2016-01-01'), np.datetime64('2016-01-02')], [1, 2])
np.testing.assert_allclose(ax.get_xlim(),
[mdates.date2num(np.datetime64('2016-01-01')),
mdates.date2num(np.datetime64('2016-01-02'))])

mdates._reset_epoch_test_example()
mdates.set_epoch('0000-12-31')
fig, ax = plt.subplots()
ax.axhline(1.5)
ax.plot([np.datetime64('2016-01-01'), np.datetime64('2016-01-02')], [1, 2])
np.testing.assert_allclose(ax.get_xlim(),
[mdates.date2num(np.datetime64('2016-01-01')),
mdates.date2num(np.datetime64('2016-01-02'))])
mdates._reset_epoch_test_example()


@image_comparison(['date_axhspan.png'])
Expand Down
44 changes: 43 additions & 1 deletion lib/matplotlib/tests/test_units.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ def default_units(value, axis):
return None

qc.convert = MagicMock(side_effect=convert)
qc.axisinfo = MagicMock(side_effect=lambda u, a: munits.AxisInfo(label=u))
qc.axisinfo = MagicMock(side_effect=lambda u, a:
munits.AxisInfo(label=u, default_limits=(0, 100)))
qc.default_units = MagicMock(side_effect=default_units)
return qc

Expand Down Expand Up @@ -219,3 +220,44 @@ def test_shared_axis_categorical():
ax2.plot(d2.keys(), d2.values())
ax1.xaxis.set_units(UnitData(["c", "d"]))
assert "c" in ax2.xaxis.get_units()._mapping.keys()


def test_empty_default_limits(quantity_converter):
munits.registry[Quantity] = quantity_converter
fig, ax1 = plt.subplots()
ax1.xaxis.update_units(Quantity([10], "miles"))
fig.draw_no_output()
assert ax1.get_xlim() == (0, 100)
ax1.yaxis.update_units(Quantity([10], "miles"))
fig.draw_no_output()
assert ax1.get_ylim() == (0, 100)

fig, ax = plt.subplots()
ax.axhline(30)
ax.plot(Quantity(np.arange(0, 3), "miles"),
Quantity(np.arange(0, 6, 2), "feet"))
fig.draw_no_output()
assert ax.get_xlim() == (0, 2)
assert ax.get_ylim() == (0, 30)

fig, ax = plt.subplots()
ax.axvline(30)
ax.plot(Quantity(np.arange(0, 3), "miles"),
Quantity(np.arange(0, 6, 2), "feet"))
fig.draw_no_output()
assert ax.get_xlim() == (0, 30)
assert ax.get_ylim() == (0, 4)

fig, ax = plt.subplots()
ax.xaxis.update_units(Quantity([10], "miles"))
ax.axhline(30)
fig.draw_no_output()
assert ax.get_xlim() == (0, 100)
assert ax.get_ylim() == (28.5, 31.5)

fig, ax = plt.subplots()
ax.yaxis.update_units(Quantity([10], "miles"))
ax.axvline(30)
fig.draw_no_output()
assert ax.get_ylim() == (0, 100)
assert ax.get_xlim() == (28.5, 31.5)