Skip to content

Commit 96670aa

Browse files
authored
Merge pull request #18536 from emilyfy/my-feature
axes3d panning
2 parents 1b23ec1 + f21c1bb commit 96670aa

File tree

5 files changed

+103
-18
lines changed

5 files changed

+103
-18
lines changed

.test_pan.py.kate-swp

4.3 KB
Binary file not shown.
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Panning for mplot3d
2+
-------------------
3+
4+
Click and drag with the middle mouse button to pan 3d axes.

lib/matplotlib/testing/widgets.py

+43-16
Original file line numberDiff line numberDiff line change
@@ -18,31 +18,35 @@ def get_ax():
1818
return ax
1919

2020

21-
def do_event(tool, etype, button=1, xdata=0, ydata=0, key=None, step=1):
22-
"""
23-
Trigger an event
21+
def mock_event(ax, button=1, xdata=0, ydata=0, key=None, step=1):
22+
r"""
23+
Create a mock event that can stand in for `.Event` and its subclasses.
24+
25+
This event is intended to be used in tests where it can be passed into
26+
event handling functions.
2427
2528
Parameters
2629
----------
27-
tool : matplotlib.widgets.RectangleSelector
28-
etype
29-
the event to trigger
30+
ax : `matplotlib.axes.Axes`
31+
The axes the event will be in.
3032
xdata : int
31-
x coord of mouse in data coords
33+
x coord of mouse in data coords.
3234
ydata : int
33-
y coord of mouse in data coords
34-
button : int or str
35-
button pressed None, 1, 2, 3, 'up', 'down' (up and down are used
36-
for scroll events)
37-
key
38-
the key depressed when the mouse event triggered (see
39-
:class:`KeyEvent`)
35+
y coord of mouse in data coords.
36+
button : None or `MouseButton` or {'up', 'down'}
37+
The mouse button pressed in this event (see also `.MouseEvent`).
38+
key : None or str
39+
The key pressed when the mouse event triggered (see also `.KeyEvent`).
4040
step : int
41-
number of scroll steps (positive for 'up', negative for 'down')
41+
Number of scroll steps (positive for 'up', negative for 'down').
42+
43+
Returns
44+
-------
45+
event
46+
A `.Event`\-like Mock instance.
4247
"""
4348
event = mock.Mock()
4449
event.button = button
45-
ax = tool.ax
4650
event.x, event.y = ax.transData.transform([(xdata, ydata),
4751
(xdata, ydata)])[0]
4852
event.xdata, event.ydata = xdata, ydata
@@ -52,6 +56,29 @@ def do_event(tool, etype, button=1, xdata=0, ydata=0, key=None, step=1):
5256
event.step = step
5357
event.guiEvent = None
5458
event.name = 'Custom'
59+
return event
60+
61+
62+
def do_event(tool, etype, button=1, xdata=0, ydata=0, key=None, step=1):
63+
"""
64+
Trigger an event on the given tool.
5565
66+
Parameters
67+
----------
68+
tool : matplotlib.widgets.RectangleSelector
69+
etype : str
70+
The event to trigger.
71+
xdata : int
72+
x coord of mouse in data coords.
73+
ydata : int
74+
y coord of mouse in data coords.
75+
button : None or `MouseButton` or {'up', 'down'}
76+
The mouse button pressed in this event (see also `.MouseEvent`).
77+
key : None or str
78+
The key pressed when the mouse event triggered (see also `.KeyEvent`).
79+
step : int
80+
Number of scroll steps (positive for 'up', negative for 'down').
81+
"""
82+
event = mock_event(tool.ax, button, xdata, ydata, key, step)
5683
func = getattr(tool, etype)
5784
func(event)

lib/mpl_toolkits/mplot3d/axes3d.py

+16-2
Original file line numberDiff line numberDiff line change
@@ -1206,11 +1206,25 @@ def _on_move(self, event):
12061206
self.stale = True
12071207
self.figure.canvas.draw_idle()
12081208

1209-
# elif self.button_pressed == 2:
1209+
elif self.button_pressed == 2:
12101210
# pan view
1211+
# get the x and y pixel coords
1212+
if dx == 0 and dy == 0:
1213+
return
1214+
minx, maxx, miny, maxy, minz, maxz = self.get_w_lims()
1215+
dx = 1-((w - dx)/w)
1216+
dy = 1-((h - dy)/h)
1217+
elev, azim = np.deg2rad(self.elev), np.deg2rad(self.azim)
12111218
# project xv, yv, zv -> xw, yw, zw
1219+
dxx = (maxx-minx)*(dy*np.sin(elev)*np.cos(azim) + dx*np.sin(azim))
1220+
dyy = (maxy-miny)*(-dx*np.cos(azim) + dy*np.sin(elev)*np.sin(azim))
1221+
dzz = (maxz-minz)*(-dy*np.cos(elev))
12121222
# pan
1213-
# pass
1223+
self.set_xlim3d(minx + dxx, maxx + dxx)
1224+
self.set_ylim3d(miny + dyy, maxy + dyy)
1225+
self.set_zlim3d(minz + dzz, maxz + dzz)
1226+
self.get_proj()
1227+
self.figure.canvas.draw_idle()
12141228

12151229
# Zoom
12161230
elif self.button_pressed in self._zoom_btn:

lib/mpl_toolkits/tests/test_mplot3d.py

+40
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55

66
from mpl_toolkits.mplot3d import Axes3D, axes3d, proj3d, art3d
77
import matplotlib as mpl
8+
from matplotlib.backend_bases import MouseButton
89
from matplotlib import cm
910
from matplotlib import colors as mcolors
1011
from matplotlib.testing.decorators import image_comparison, check_figures_equal
12+
from matplotlib.testing.widgets import mock_event
1113
from matplotlib.collections import LineCollection, PolyCollection
1214
from matplotlib.patches import Circle
1315
import matplotlib.pyplot as plt
@@ -1250,3 +1252,41 @@ def test_colorbar_pos():
12501252
fig.canvas.draw()
12511253
# check that actually on the bottom
12521254
assert cbar.ax.get_position().extents[1] < 0.2
1255+
1256+
1257+
def test_pan():
1258+
"""Test mouse panning using the middle mouse button."""
1259+
1260+
def convert_lim(dmin, dmax):
1261+
"""Convert min/max limits to center and range."""
1262+
center = (dmin + dmax) / 2
1263+
range_ = dmax - dmin
1264+
return center, range_
1265+
1266+
ax = plt.figure().add_subplot(projection='3d')
1267+
ax.scatter(0, 0, 0)
1268+
ax.figure.canvas.draw()
1269+
1270+
x_center0, x_range0 = convert_lim(*ax.get_xlim3d())
1271+
y_center0, y_range0 = convert_lim(*ax.get_ylim3d())
1272+
z_center0, z_range0 = convert_lim(*ax.get_zlim3d())
1273+
1274+
# move mouse diagonally to pan along all axis.
1275+
ax._button_press(
1276+
mock_event(ax, button=MouseButton.MIDDLE, xdata=0, ydata=0))
1277+
ax._on_move(
1278+
mock_event(ax, button=MouseButton.MIDDLE, xdata=1, ydata=1))
1279+
1280+
x_center, x_range = convert_lim(*ax.get_xlim3d())
1281+
y_center, y_range = convert_lim(*ax.get_ylim3d())
1282+
z_center, z_range = convert_lim(*ax.get_zlim3d())
1283+
1284+
# Ranges have not changed
1285+
assert x_range == pytest.approx(x_range0)
1286+
assert y_range == pytest.approx(y_range0)
1287+
assert z_range == pytest.approx(z_range0)
1288+
1289+
# But center positions have
1290+
assert x_center != pytest.approx(x_center0)
1291+
assert y_center != pytest.approx(y_center0)
1292+
assert z_center != pytest.approx(z_center0)

0 commit comments

Comments
 (0)