From 0e413176143711bb417439634acd8d6031264612 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Wed, 19 Jun 2019 16:35:06 +0200 Subject: [PATCH] Fix inversion of 3d axis. ... which was broken because 3d axises stored and accessed their viewlim info sometimes in xy_viewLim/zz_viewLim and sometimes in v_interval (and likewise for datalim), leading to out-of-sync info... Not deprecating d_interval/v_interval for now both because this needs to go to 3.1.1 to fix the breakage and because I think storing the info in axis attributes actually makes *more* sense than in viewLim/dataLim -- but fully switching to d_interval/v_interval needs bigger surgery. --- lib/matplotlib/axis.py | 97 ++++++++++---------------- lib/mpl_toolkits/mplot3d/axis3d.py | 56 ++++++++------- lib/mpl_toolkits/tests/test_mplot3d.py | 12 ++++ 3 files changed, 82 insertions(+), 83 deletions(-) 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. pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy