Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7325c4a

Browse files
committed
Snap 3D rotation to 5 degree increments with Ctrl
1 parent ce8a66e commit 7325c4a

2 files changed

Lines changed: 70 additions & 0 deletions

File tree

lib/mpl_toolkits/mplot3d/axes3d.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,12 +174,17 @@ def __init__(
174174

175175
self.mouse_init()
176176
fig = self.get_figure(root=True)
177+
self._snap_rotation = False
177178
fig.canvas.callbacks._connect_picklable(
178179
'motion_notify_event', self._on_move)
179180
fig.canvas.callbacks._connect_picklable(
180181
'button_press_event', self._button_press)
181182
fig.canvas.callbacks._connect_picklable(
182183
'button_release_event', self._button_release)
184+
fig.canvas.callbacks._connect_picklable(
185+
'key_press_event', self._key_press)
186+
fig.canvas.callbacks._connect_picklable(
187+
'key_release_event', self._key_release)
183188
self.set_top_view()
184189

185190
self.patch.set_linewidth(0)
@@ -1355,6 +1360,16 @@ def clear(self):
13551360

13561361
self.grid(mpl.rcParams['axes3d.grid'])
13571362

1363+
def _key_press(self, event):
1364+
if event.key in ("control", "ctrl"):
1365+
self._snap_rotation = True
1366+
1367+
1368+
def _key_release(self, event):
1369+
if event.key in ("control", "ctrl"):
1370+
self._snap_rotation = False
1371+
1372+
13581373
def _button_press(self, event):
13591374
if event.inaxes == self:
13601375
self.button_pressed = event.button
@@ -1597,6 +1612,11 @@ def _on_move(self, event):
15971612

15981613
q = dq * q
15991614
elev, azim, roll = np.rad2deg(q.as_cardan_angles())
1615+
if getattr(self, "_snap_rotation", False):
1616+
step = 5
1617+
elev = float(step * round(elev / step))
1618+
azim = float(step * round(azim / step))
1619+
roll = float(step * round(roll / step))
16001620

16011621
# update view
16021622
vertical_axis = self._axis_names[self._vertical_axis]

lib/mpl_toolkits/mplot3d/tests/test_axes3d.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2785,3 +2785,53 @@ def test_axis_get_tightbbox_includes_offset_text():
27852785
f"bbox.x1 ({bbox.x1}) should be >= offset_bbox.x1 ({offset_bbox.x1})"
27862786
assert bbox.y1 >= offset_bbox.y1 - 1e-6, \
27872787
f"bbox.y1 ({bbox.y1}) should be >= offset_bbox.y1 ({offset_bbox.y1})"
2788+
2789+
2790+
class DummyEvent:
2791+
def __init__(self, xdata=None, ydata=None, inaxes=None):
2792+
self.xdata = xdata
2793+
self.ydata = ydata
2794+
self.inaxes = inaxes
2795+
self.x = 0
2796+
self.y = 0
2797+
self.button = 1
2798+
2799+
2800+
def _is_multiple_of_step(val, step):
2801+
return np.isclose(val, step * round(val / step), atol=5e-2)
2802+
2803+
2804+
def test_ctrl_rotation_snaps_to_5deg(monkeypatch):
2805+
fig = plt.figure()
2806+
ax = fig.add_subplot(projection="3d")
2807+
2808+
ax.M = np.eye(4)
2809+
ax._pseudo_w = 1
2810+
ax._pseudo_h = 1
2811+
2812+
ax.button_pressed = 1
2813+
ax._rotate_btn = [1]
2814+
ax._sx, ax._sy = 0.0, 0.0
2815+
2816+
ax.elev = 12.3
2817+
ax.azim = 33.7
2818+
ax.roll = 2.2
2819+
2820+
ax._snap_rotation = True
2821+
2822+
monkeypatch.setitem(plt.rcParams, "axes3d.mouserotationstyle", "azel")
2823+
2824+
captured = {}
2825+
2826+
def fake_view_init(elev=None, azim=None, roll=None, **kwargs):
2827+
captured["elev"] = elev
2828+
captured["azim"] = azim
2829+
captured["roll"] = roll
2830+
2831+
monkeypatch.setattr(ax, "view_init", fake_view_init)
2832+
2833+
event = DummyEvent(xdata=0.1, ydata=0.1, inaxes=ax)
2834+
ax._on_move(event)
2835+
2836+
assert _is_multiple_of_step(captured["elev"], 5)
2837+
assert _is_multiple_of_step(captured["azim"], 5)

0 commit comments

Comments
 (0)