Skip to content

Commit 0008c02

Browse files
Box zoom keeps existing aspect ratios
1 parent d55706c commit 0008c02

File tree

3 files changed

+29
-22
lines changed

3 files changed

+29
-22
lines changed

doc/users/next_whats_new/3d_plot_pan_zoom.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@
44
The pan and zoom buttons in the toolbar of 3D plots are now enabled.
55
Deselect both to rotate the plot. When the zoom button is pressed,
66
zoom in by using the left mouse button to draw a bounding box, and
7-
out by using the right mouse button to draw the box.
7+
out by using the right mouse button to draw the box. When zooming a
8+
3D plot, the current view aspect ratios are kept fixed.

lib/mpl_toolkits/mplot3d/axes3d.py

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,19 +1213,21 @@ def _set_view_from_bbox(self, bbox, direction='in',
12131213
dy = abs(start_y - stop_y)
12141214
scale_u = dx / (self.bbox.max[0] - self.bbox.min[0])
12151215
scale_v = dy / (self.bbox.max[1] - self.bbox.min[1])
1216-
scale_w = max(scale_u, scale_v)
12171216

12181217
# Limit box zoom to reasonable range, protect for divide by zero below
1219-
scale_u = np.clip(scale_u, 1e-2, 1e2)
1220-
scale_v = np.clip(scale_v, 1e-2, 1e2)
1218+
scale_u = np.clip(scale_u, 1e-2, 1)
1219+
scale_v = np.clip(scale_v, 1e-2, 1)
12211220

1221+
# Keep aspect ratios equal
1222+
scale = max(scale_u, scale_v)
1223+
1224+
# Zoom out
12221225
if direction == 'out':
1223-
scale_u = 1 / scale_u
1224-
scale_v = 1 / scale_v
1226+
scale = 1 / scale
12251227

1226-
self._zoom_data_limits(scale_u, scale_v, scale_w)
1228+
self.zoom_data_limits(scale, scale, scale)
12271229

1228-
def _zoom_data_limits(self, scale_u, scale_v, scale_w):
1230+
def zoom_data_limits(self, scale_u, scale_v, scale_w):
12291231
"""
12301232
Zoom in or out of a 3D plot.
12311233
Will scale the data limits by the scale factors, where scale_u,
@@ -1237,19 +1239,23 @@ def _zoom_data_limits(self, scale_u, scale_v, scale_w):
12371239
'equalyz', or 'equalxz', the relevant axes are constrained to zoom
12381240
equally.
12391241
"""
1240-
# Convert from the scale factors in the view frame to the data frame
1241-
R = np.array([self._view_u, self._view_v, self._view_w])
1242-
S = np.array([scale_u, scale_v, scale_w]) * np.eye(3)
1243-
scale = np.linalg.norm(R.T @ S, axis=1)
1242+
scale = np.array([scale_u, scale_v, scale_w])
1243+
1244+
# Only perform frame conversion if unequal scale factors
1245+
if not np.allclose(scale, scale_u):
1246+
# Convert from the scale factors in the view frame to the data frame
1247+
R = np.array([self._view_u, self._view_v, self._view_w])
1248+
S = scale * np.eye(3)
1249+
scale = np.linalg.norm(R.T @ S, axis=1)
12441250

1245-
# Set the constrained scale factors to the factor closest to 1
1246-
if self._aspect in ('equal', 'equalxy', 'equalxz', 'equalyz'):
1247-
ax_idx = self._equal_aspect_axis_indices(self._aspect)
1248-
scale[ax_idx] = scale[ax_idx][np.argmin(np.abs(scale[ax_idx] - 1))]
1251+
# Set the constrained scale factors to the factor closest to 1
1252+
if self._aspect in ('equal', 'equalxy', 'equalxz', 'equalyz'):
1253+
ax_idx = self._equal_aspect_axis_indices(self._aspect)
1254+
scale[ax_idx] = scale[ax_idx][np.argmin(np.abs(scale[ax_idx] - 1))]
12491255

1250-
self._scale_axis_limits(scale[0], scale[1], scale[2])
1256+
self.scale_axis_limits(scale[0], scale[1], scale[2])
12511257

1252-
def _scale_axis_limits(self, scale_x, scale_y, scale_z):
1258+
def scale_axis_limits(self, scale_x, scale_y, scale_z):
12531259
"""
12541260
Keeping the center of the x, y, and z data axes fixed, scale their
12551261
limits by scale factors. A scale factor > 1 zooms out and a scale

lib/mpl_toolkits/tests/test_mplot3d.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1622,13 +1622,13 @@ def convert_lim(dmin, dmax):
16221622

16231623
@pytest.mark.parametrize("tool,button,key,expected",
16241624
[("zoom", MouseButton.LEFT, None, # zoom in
1625-
((0.03, 0.61), (0.27, 0.71), (0.32, 0.89))),
1625+
((0.26, 0.38), (0.43, 0.55), (0.54, 0.66))),
16261626
("zoom", MouseButton.LEFT, 'x', # zoom in
1627-
((0.17, 0.73), (0.09, 0.43), (-0.06, 0.06))),
1627+
((0.39, 0.51), (0.20, 0.32), (-0.06, 0.06))),
16281628
("zoom", MouseButton.LEFT, 'y', # zoom in
1629-
((-0.23, -0.03), (0.07, 0.37), (0.32, 0.89))),
1629+
((-0.19, -0.07), (0.16, 0.28), (0.54, 0.66))),
16301630
("zoom", MouseButton.RIGHT, None, # zoom out
1631-
((0.29, 0.35), (0.44, 0.53), (0.57, 0.64))),
1631+
((0.26, 0.38), (0.43, 0.55), (0.54, 0.66))),
16321632
("pan", MouseButton.LEFT, None,
16331633
((-0.70, -0.58), (-1.03, -0.91), (-1.27, -1.15))),
16341634
("pan", MouseButton.LEFT, 'x',

0 commit comments

Comments
 (0)