diff --git a/lib/mpl_toolkits/mplot3d/axes3d.py b/lib/mpl_toolkits/mplot3d/axes3d.py index 5d522cd0988a..10f693ace0cf 100644 --- a/lib/mpl_toolkits/mplot3d/axes3d.py +++ b/lib/mpl_toolkits/mplot3d/axes3d.py @@ -1568,10 +1568,11 @@ def _on_move(self, event): q = _Quaternion.from_cardan_angles(elev, azim, roll) # Update quaternion - a variation on Ken Shoemake's ARCBALL - current_vec = self._arcball(self._sx/w, self._sy/h) - new_vec = self._arcball(x/w, y/h) + scale = np.sqrt(2)/2 # slow down the rate of rotation + current_vec = self._arcball(self._sx*scale/w, self._sy*scale/h) + new_vec = self._arcball(x*scale/w, y*scale/h) dq = _Quaternion.rotate_from_to(current_vec, new_vec) - q = dq * q + q = dq * dq * q # Convert to elev, azim, roll elev, azim, roll = q.as_cardan_angles() @@ -4020,11 +4021,14 @@ def from_cardan_angles(cls, elev, azim, roll): def as_cardan_angles(self): """ The inverse of `from_cardan_angles()`. + This function acts on the quaternion as if it were unit normed. Note that the angles returned are in radians, not degrees. """ qw = self.scalar qx, qy, qz = self.vector[..., :] azim = np.arctan2(2*(-qw*qz+qx*qy), qw*qw+qx*qx-qy*qy-qz*qz) - elev = np.arcsin( 2*( qw*qy+qz*qx)/(qw*qw+qx*qx+qy*qy+qz*qz)) # noqa E201 + # Clip below is to avoid floating point round-off errors + elev = np.arcsin(np.clip(2*(qw*qy+qz*qx) + / (qw*qw+qx*qx+qy*qy+qz*qz), -1, 1)) roll = np.arctan2(2*( qw*qx-qy*qz), qw*qw-qx*qx-qy*qy+qz*qz) # noqa E201 return elev, azim, roll diff --git a/lib/mpl_toolkits/mplot3d/tests/test_axes3d.py b/lib/mpl_toolkits/mplot3d/tests/test_axes3d.py index 0afcae99c980..07ecb0086030 100644 --- a/lib/mpl_toolkits/mplot3d/tests/test_axes3d.py +++ b/lib/mpl_toolkits/mplot3d/tests/test_axes3d.py @@ -1950,10 +1950,10 @@ def test_rotate(): for roll, dx, dy, new_elev, new_azim, new_roll in [ [0, 0.5, 0, 0, -90, 0], [30, 0.5, 0, 30, -90, 0], - [0, 0, 0.5, -90, 0, 0], + [0, 0, 0.5, -90, -180, 180], [30, 0, 0.5, -60, -90, 90], - [0, 0.5, 0.5, -45, -90, 45], - [30, 0.5, 0.5, -15, -90, 45]]: + [0, np.sqrt(2)/4, np.sqrt(2)/4, -45, -90, 45], + [30, np.sqrt(2)/4, np.sqrt(2)/4, -15, -90, 45]]: fig = plt.figure() ax = fig.add_subplot(1, 1, 1, projection='3d') ax.view_init(0, 0, roll) @@ -1967,9 +1967,8 @@ def test_rotate(): xdata=dx*ax._pseudo_w, ydata=dy*ax._pseudo_h)) fig.canvas.draw() - assert np.isclose(ax.elev, new_elev) - assert np.isclose(ax.azim, new_azim) - assert np.isclose(ax.roll, new_roll) + np.testing.assert_allclose((ax.elev, ax.azim, ax.roll), + (new_elev, new_azim, new_roll), atol=1e-6) def test_pan():
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: