Skip to content
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

Fix inversion of 3d axis. #14579

Merged
merged 1 commit into from
Jun 25, 2019
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
97 changes: 38 additions & 59 deletions lib/matplotlib/axis.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
56 changes: 32 additions & 24 deletions lib/mpl_toolkits/mplot3d/axis3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -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!
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -420,42 +421,49 @@ 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):
# Currently returns None so that Axis.get_tightbbox
# 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")
12 changes: 12 additions & 0 deletions lib/mpl_toolkits/tests/test_mplot3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down