Skip to content

Commit e9991b3

Browse files
committed
Consistent azim-elev-roll order with minimal changes
Implement changes requested for PR #28395: - remove incohesive section from view_angles.rst - move some of the information to .mplot3d.axes3d.Axes3D.view_init - remove redundant kwargs as in .view_init(elev=elev, azim=azim, roll=roll) - cleanup test_axes3d_primary_views() - remove outdated next_whats_new item
1 parent 89a4d55 commit e9991b3

File tree

4 files changed

+43
-58
lines changed

4 files changed

+43
-58
lines changed

doc/api/toolkits/mplot3d/view_angles.rst

-22
Original file line numberDiff line numberDiff line change
@@ -28,28 +28,6 @@ as well as roll, and all three angles can be set programmatically::
2828
ax = plt.figure().add_subplot(projection='3d')
2929
ax.view_init(elev=30, azim=45, roll=15)
3030

31-
Rotation of the plot
32-
====================
33-
34-
The *azim*, *elev*, *roll* rotation order corresponds to rotation of the scene
35-
observed by a stationary camera. First, a left-handed rotation about the z axis is
36-
applied (*azim*), then a right-handed rotation about the (camera) y axis (*elev*), then a
37-
right-handed rotation about the (camera) x axis (*roll*). Here, the z, y, and x axis are fixed
38-
axes (not the axes that rotate together with the original scene).
39-
40-
This can also be thought of as orbiting a camera around a fixed scene, by reversing
41-
the order of operations. First the camera is rotated about the scene's +x axis
42-
(*roll*), then the +y axis (*elev*), then the −z axis (*azim*).
43-
44-
If you would like to make the connection with quaternions (because
45-
`Euler angles are horrible <https://github.com/moble/quaternion/wiki/Euler-angles-are-horrible>`_):
46-
the *azim*, *elev*, *roll* angles relate to the (intrinsic) rotation of the plot via:
47-
48-
*q* = exp( +roll **** / 2) exp( +elev **ŷ** / 2) exp( −azim **** / 2)
49-
50-
(with angles given in radians instead of degrees). That is, the angles are a kind of
51-
Tait-Bryan angles: −z, +y', +x", rather than classic Euler angles.
52-
5331

5432
Primary view planes
5533
===================

doc/users/next_whats_new/view_angles_keyword_arguments.rst

-17
This file was deleted.

lib/mpl_toolkits/mplot3d/axes3d.py

+38-14
Original file line numberDiff line numberDiff line change
@@ -1119,15 +1119,44 @@ def view_init(self, elev=None, azim=None, roll=None, vertical_axis="z",
11191119
A roll angle of 0, 90, 180, or 270 degrees will rotate these views
11201120
while keeping the axes at right angles.
11211121
1122-
It is recommended to provide these parameter as keyword arguments:
1122+
The *azim*, *elev*, *roll* angles correspond to rotations of the scene
1123+
observed by a stationary camera, as follows (assuming a default vertical
1124+
axis of 'z'). First, a left-handed rotation about the z axis is applied
1125+
(*azim*), then a right-handed rotation about the (camera) y axis (*elev*),
1126+
then a right-handed rotation about the (camera) x axis (*roll*). Here,
1127+
the z, y, and x axis are fixed axes (not the axes that rotate together
1128+
with the original scene).
1129+
1130+
If you would like to make the connection with quaternions (because
1131+
`Euler angles are horrible
1132+
<https://github.com/moble/quaternion/wiki/Euler-angles-are-horrible>`_):
1133+
the *azim*, *elev*, *roll* angles relate to the (intrinsic) rotation of
1134+
the plot via:
1135+
1136+
*q* = exp(+roll **x̂** / 2) exp(+elev **ŷ** / 2) exp(−azim **ẑ** / 2)
1137+
1138+
(with angles given in radians instead of degrees). That is, the angles
1139+
are a kind of `Tait-Bryan angles
1140+
<https://en.wikipedia.org/wiki/Euler_angles#Tait%E2%80%93Bryan_angles>`_:
1141+
−z, +y', +x", rather than classic `Euler angles
1142+
<https://en.wikipedia.org/wiki/Euler_angles>`_.
1143+
1144+
To avoid confusion, it makes sense to provide the view angles as keyword
1145+
arguments:
11231146
``.view_init(azim=-60, elev=30, roll=0, ...)``
1124-
in this order (i.e., the order in which the rotations actually are
1125-
applied).
1147+
This specific order is consistent with the order in which the rotations
1148+
actually are applied. Moreover, this particular order appears to be most
1149+
common, see :ghissue:`28353`, and it is consistent with the ordering in
1150+
`matplotlib.colors.LightSource`.
1151+
11261152
For backwards compatibility, positional arguments in the old sequence
1127-
(first elev, then azim) will still be accepted; unfortunately,
1128-
this order does not match the actual order of the applied rotations,
1129-
and it differs from that used in other programs (`azim, elev`).
1130-
It would be nice if the sensible ordering could take over eventually.
1153+
(first ``elev``, then ``azim``) will still be accepted; but preferably,
1154+
use keyword arguments, to avoid confusion as to which angle is which.
1155+
Unfortunately, the order of the positional arguments does not match
1156+
the actual order of the applied rotations, and it differs from that
1157+
used in other programs (``azim, elev``). It would be nice if the sensible
1158+
(keyword) ordering could take over eventually.
1159+
11311160
11321161
Parameters
11331162
----------
@@ -1599,13 +1628,8 @@ def _on_move(self, event):
15991628
elev = np.rad2deg(elev)
16001629
roll = np.rad2deg(roll)
16011630
vertical_axis = self._axis_names[self._vertical_axis]
1602-
self.view_init(
1603-
elev=elev,
1604-
azim=azim,
1605-
roll=roll,
1606-
vertical_axis=vertical_axis,
1607-
share=True,
1608-
)
1631+
self.view_init(elev, azim, roll, vertical_axis=vertical_axis,
1632+
share=True)
16091633
self.stale = True
16101634

16111635
# Pan

lib/mpl_toolkits/mplot3d/tests/test_axes3d.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,12 @@ def test_axes3d_primary_views():
127127
# When viewing primary planes, draw the two visible axes so they intersect
128128
# at their low values
129129
fig, axs = plt.subplots(2, 3, subplot_kw={'projection': '3d'})
130-
for i, ax in enumerate(axs.flat):
130+
for ax, (azim, elev, roll) in zip(axs.flat, views):
131131
ax.set_xlabel('x')
132132
ax.set_ylabel('y')
133133
ax.set_zlabel('z')
134134
ax.set_proj_type('ortho')
135-
ax.view_init(elev=views[i][1], azim=views[i][0], roll=views[i][2])
135+
ax.view_init(elev, azim, roll)
136136
plt.tight_layout()
137137

138138

@@ -176,7 +176,7 @@ def test_bar3d_shaded():
176176
)
177177
for ax, (azim, elev, roll) in zip(axs, views):
178178
ax.bar3d(x2d, y2d, x2d * 0, 1, 1, z, shade=True)
179-
ax.view_init(elev=elev, azim=azim, roll=roll)
179+
ax.view_init(elev, azim, roll)
180180
fig.canvas.draw()
181181

182182

@@ -1832,11 +1832,11 @@ def test_shared_view(fig_test, fig_ref):
18321832
ax2 = fig_test.add_subplot(132, projection="3d", shareview=ax1)
18331833
ax3 = fig_test.add_subplot(133, projection="3d")
18341834
ax3.shareview(ax1)
1835-
ax2.view_init(elev=elev, azim=azim, roll=roll, share=True)
1835+
ax2.view_init(elev, azim, roll, share=True)
18361836

18371837
for subplot_num in (131, 132, 133):
18381838
ax = fig_ref.add_subplot(subplot_num, projection="3d")
1839-
ax.view_init(elev=elev, azim=azim, roll=roll)
1839+
ax.view_init(elev, azim, roll)
18401840

18411841

18421842
def test_shared_axes_retick():

0 commit comments

Comments
 (0)