Skip to content

Commit 3281435

Browse files
authored
Merge pull request matplotlib#31992 from HakonSohoel/fix-inverted-3d-axis-placement
Fix: Axis and grid panes out of sync when inverting an axis
2 parents 632d350 + d618766 commit 3281435

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
@@ -72,6 +72,31 @@ def test_axis_positions():
7272
ax.set(xlabel='x', ylabel='y', zlabel='z', title=title)
7373

7474

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

0 commit comments

Comments
 (0)