Skip to content

Commit 4ae406e

Browse files
authored
Merge pull request matplotlib#32021 from meeseeksmachine/auto-backport-of-pr-31992-on-v3.11.x
Backport PR matplotlib#31992 on branch v3.11.x (Fix: Axis and grid panes out of sync when inverting an axis)
2 parents 35ebac1 + 697a8ae commit 4ae406e

3 files changed

Lines changed: 32 additions & 2 deletions

File tree

lib/mpl_toolkits/mplot3d/axis3d.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,13 @@ def get_rotate_label(self, text):
269269
def _get_coord_info(self):
270270
# Get scaled limits directly from the axes helper
271271
xmin, xmax, ymin, ymax, zmin, zmax = self.axes._get_scaled_limits()
272-
mins = np.array([xmin, ymin, zmin])
273-
maxs = np.array([xmax, ymax, zmax])
272+
# When axes are inverted the min and max values returned by _get_scaled_limits
273+
# are reversed (min > max). Here we need to ensure max > min so that the axis
274+
# lines and grid panes are placed consistently regardless of inversion.
275+
# Note that we cannot ensure this inside _get_scaled_limits itself, as other
276+
# callers rely on the signed range to detect and handle axis inversion.
277+
mins = np.array([min(xmin, xmax), min(ymin, ymax), min(zmin, zmax)])
278+
maxs = np.array([max(xmin, xmax), max(ymin, ymax), max(zmin, zmax)])
274279

275280
# Get data-space bounds for _transformed_cube
276281
bounds = (*self.axes.get_xbound(),
103 KB
Loading

lib/mpl_toolkits/mplot3d/tests/test_axes3d.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,31 @@ def test_axis_positions():
7373
ax.set(xlabel='x', ylabel='y', zlabel='z', title=title)
7474

7575

76+
@mpl3d_image_comparison(['axis_positions_inverted.png'], remove_text=False,
77+
style='mpl20')
78+
def test_axis_positions_inverted():
79+
# Regression test for https://github.com/matplotlib/matplotlib/issues/31989
80+
# Check the visual placement of axes, ticks and labels for
81+
# every combination of inverted x, y and z axes.
82+
combinations = list(itertools.product([False, True], repeat=3))
83+
fig, axs = plt.subplots(2, 4, figsize=(10, 6),
84+
subplot_kw={'projection': '3d'})
85+
for ax, (invert_x, invert_y, invert_z) in zip(axs.flatten(), combinations):
86+
# Plot an asymmetric line so that inverting an axis visibly mirrors the data,
87+
# ensuring the projection is exercised for every inversion combination
88+
ax.plot([0, 1, 1], [0, 0, 1], [0, 1, 1])
89+
if invert_x:
90+
ax.invert_xaxis()
91+
if invert_y:
92+
ax.invert_yaxis()
93+
if invert_z:
94+
ax.invert_zaxis()
95+
title = (f'{"-" if invert_x else ""}x, '
96+
f'{"-" if invert_y else ""}y, '
97+
f'{"-" if invert_z else ""}z')
98+
ax.set(xlabel='x', ylabel='y', zlabel='z', title=title)
99+
100+
76101
@mpl3d_image_comparison(['aspects.png'], remove_text=False, style='mpl20')
77102
def test_aspects():
78103
aspects = ('auto', 'equal', 'equalxy', 'equalyz', 'equalxz', 'equal')

0 commit comments

Comments
 (0)