diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index a2b487da785a..e6293a8db830 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -1890,6 +1890,36 @@ def get_minpos(self): raise NotImplementedError() +def _make_getset_interval(method_name, lim_name, attr_name): + """ + Helper to generate ``get_{data,view}_interval`` and + ``set_{data,view}_interval`` implementations. + """ + + def getter(self): + # docstring inherited. + return getattr(getattr(self.axes, lim_name), attr_name) + + def setter(self, vmin, vmax, ignore=False): + # docstring inherited. + if ignore: + setattr(getattr(self.axes, lim_name), attr_name, (vmin, vmax)) + else: + oldmin, oldmax = getter(self) + if oldmin < oldmax: + setter(self, min(vmin, vmax, oldmin), max(vmin, vmax, oldmax), + ignore=True) + else: + setter(self, max(vmin, vmax, oldmax), min(vmin, vmax, oldmin), + ignore=True) + self.stale = True + + getter.__name__ = f"get_{method_name}_interval" + setter.__name__ = f"set_{method_name}_interval" + + return getter, setter + + class XAxis(Axis): __name__ = 'xaxis' axis_name = 'x' @@ -2133,39 +2163,14 @@ def get_ticks_position(self): "default": "default", "unknown": "unknown"}[ self._get_ticks_position()] - def get_view_interval(self): - # docstring inherited - return self.axes.viewLim.intervalx - - def set_view_interval(self, vmin, vmax, ignore=False): - # docstring inherited - if ignore: - self.axes.viewLim.intervalx = vmin, vmax - else: - Vmin, Vmax = self.get_view_interval() - if Vmin < Vmax: - self.axes.viewLim.intervalx = (min(vmin, vmax, Vmin), - max(vmin, vmax, Vmax)) - else: - self.axes.viewLim.intervalx = (max(vmin, vmax, Vmin), - min(vmin, vmax, Vmax)) + get_view_interval, set_view_interval = _make_getset_interval( + "view", "viewLim", "intervalx") + get_data_interval, set_data_interval = _make_getset_interval( + "data", "dataLim", "intervalx") def get_minpos(self): return self.axes.dataLim.minposx - def get_data_interval(self): - # docstring inherited - return self.axes.dataLim.intervalx - - def set_data_interval(self, vmin, vmax, ignore=False): - # docstring inherited - if ignore: - self.axes.dataLim.intervalx = vmin, vmax - else: - Vmin, Vmax = self.get_data_interval() - self.axes.dataLim.intervalx = min(vmin, Vmin), max(vmax, Vmax) - self.stale = True - def set_default_intervals(self): # docstring inherited xmin, xmax = 0., 1. @@ -2460,40 +2465,14 @@ def get_ticks_position(self): "default": "default", "unknown": "unknown"}[ self._get_ticks_position()] - def get_view_interval(self): - # docstring inherited - return self.axes.viewLim.intervaly - - def set_view_interval(self, vmin, vmax, ignore=False): - # docstring inherited - if ignore: - self.axes.viewLim.intervaly = vmin, vmax - else: - Vmin, Vmax = self.get_view_interval() - if Vmin < Vmax: - self.axes.viewLim.intervaly = (min(vmin, vmax, Vmin), - max(vmin, vmax, Vmax)) - else: - self.axes.viewLim.intervaly = (max(vmin, vmax, Vmin), - min(vmin, vmax, Vmax)) - self.stale = True + get_view_interval, set_view_interval = _make_getset_interval( + "view", "viewLim", "intervaly") + get_data_interval, set_data_interval = _make_getset_interval( + "data", "dataLim", "intervaly") def get_minpos(self): return self.axes.dataLim.minposy - def get_data_interval(self): - # docstring inherited - return self.axes.dataLim.intervaly - - def set_data_interval(self, vmin, vmax, ignore=False): - # docstring inherited - if ignore: - self.axes.dataLim.intervaly = vmin, vmax - else: - Vmin, Vmax = self.get_data_interval() - self.axes.dataLim.intervaly = min(vmin, Vmin), max(vmax, Vmax) - self.stale = True - def set_default_intervals(self): # docstring inherited ymin, ymax = 0., 1. diff --git a/lib/mpl_toolkits/mplot3d/axis3d.py b/lib/mpl_toolkits/mplot3d/axis3d.py index 5c15751986a1..af862b6787ed 100644 --- a/lib/mpl_toolkits/mplot3d/axis3d.py +++ b/lib/mpl_toolkits/mplot3d/axis3d.py @@ -68,9 +68,6 @@ def __init__(self, adir, v_intervalx, d_intervalx, axes, *args, rotate_label=None, **kwargs): # adir identifies which axes this is self.adir = adir - # data and viewing intervals for this direction - self.d_interval = d_intervalx - self.v_interval = v_intervalx # This is a temporary member variable. # Do not depend on this existing in future releases! @@ -105,6 +102,10 @@ def __init__(self, adir, v_intervalx, d_intervalx, axes, *args, }) maxis.XAxis.__init__(self, axes, *args, **kwargs) + + # data and viewing intervals for this direction + self.d_interval = d_intervalx + self.v_interval = v_intervalx self.set_rotate_label(rotate_label) def init3d(self): @@ -420,18 +421,6 @@ def draw(self, renderer): renderer.close_group('axis3d') self.stale = False - def get_view_interval(self): - # docstring inherited - return self.v_interval - - def set_view_interval(self, vmin, vmax, ignore=False): - # docstring inherited - if ignore: - self.v_interval = vmin, vmax - else: - Vmin, Vmax = self.get_view_interval() - self.v_interval = min(vmin, Vmin), max(vmax, Vmax) - # TODO: Get this to work properly when mplot3d supports # the transforms framework. def get_tightbbox(self, renderer): @@ -439,23 +428,42 @@ def get_tightbbox(self, renderer): # doesn't return junk info. return None + @property + def d_interval(self): + return self.get_data_interval() + + @d_interval.setter + def d_interval(self, minmax): + return self.set_data_interval(*minmax) + + @property + def v_interval(self): + return self.get_view_interval() + + @d_interval.setter + def v_interval(self, minmax): + return self.set_view_interval(*minmax) + # Use classes to look at different data limits class XAxis(Axis): - def get_data_interval(self): - # docstring inherited - return self.axes.xy_dataLim.intervalx + get_view_interval, set_view_interval = maxis._make_getset_interval( + "view", "xy_viewLim", "intervalx") + get_data_interval, set_data_interval = maxis._make_getset_interval( + "data", "xy_dataLim", "intervalx") class YAxis(Axis): - def get_data_interval(self): - # docstring inherited - return self.axes.xy_dataLim.intervaly + get_view_interval, set_view_interval = maxis._make_getset_interval( + "view", "xy_viewLim", "intervaly") + get_data_interval, set_data_interval = maxis._make_getset_interval( + "data", "xy_dataLim", "intervaly") class ZAxis(Axis): - def get_data_interval(self): - # docstring inherited - return self.axes.zz_dataLim.intervalx + get_view_interval, set_view_interval = maxis._make_getset_interval( + "view", "zz_viewLim", "intervalx") + get_data_interval, set_data_interval = maxis._make_getset_interval( + "data", "zz_dataLim", "intervalx") diff --git a/lib/mpl_toolkits/tests/test_mplot3d.py b/lib/mpl_toolkits/tests/test_mplot3d.py index cb0ee9069f42..62c2979a5aea 100644 --- a/lib/mpl_toolkits/tests/test_mplot3d.py +++ b/lib/mpl_toolkits/tests/test_mplot3d.py @@ -801,6 +801,18 @@ def test_line3d_set_get_data_3d(): np.testing.assert_array_equal((x2, y2, z2), line.get_data_3d()) +@check_figures_equal(extensions=["png"]) +def test_inverted(fig_test, fig_ref): + # Plot then invert. + ax = fig_test.add_subplot(projection="3d") + ax.plot([1, 1, 10, 10], [1, 10, 10, 10], [1, 1, 1, 10]) + ax.invert_yaxis() + # Invert then plot. + ax = fig_ref.add_subplot(projection="3d") + ax.invert_yaxis() + ax.plot([1, 1, 10, 10], [1, 10, 10, 10], [1, 1, 1, 10]) + + def test_inverted_cla(): # Github PR #5450. Setting autoscale should reset # axes to be non-inverted.