diff --git a/doc/_static/mplot3d_view_angles.png b/doc/_static/mplot3d_view_angles.png new file mode 100644 index 000000000000..16d3c2f0d699 Binary files /dev/null and b/doc/_static/mplot3d_view_angles.png differ diff --git a/doc/api/toolkits/mplot3d.rst b/doc/api/toolkits/mplot3d.rst index 5b3cb52571bb..19005ddd383d 100644 --- a/doc/api/toolkits/mplot3d.rst +++ b/doc/api/toolkits/mplot3d.rst @@ -20,13 +20,15 @@ more information. The interactive backends also provide the ability to rotate and zoom the 3D scene. One can rotate the 3D scene by simply clicking-and-dragging the scene. -Zooming is done by right-clicking the scene and dragging the mouse up and down -(unlike 2D plots, the toolbar zoom button is not used). +Panning is done by clicking the middle mouse button, and zooming is done by +right-clicking the scene and dragging the mouse up and down. Unlike 2D plots, +the toolbar pan and zoom buttons are not used. .. toctree:: :maxdepth: 2 mplot3d/faq.rst + mplot3d/view_angles.rst .. note:: `.pyplot` cannot be used to add content to 3D plots, because its function diff --git a/doc/api/toolkits/mplot3d/view_angles.rst b/doc/api/toolkits/mplot3d/view_angles.rst new file mode 100644 index 000000000000..10d4fac39e8c --- /dev/null +++ b/doc/api/toolkits/mplot3d/view_angles.rst @@ -0,0 +1,40 @@ +.. _toolkit_mplot3d-view-angles: + +******************* +mplot3d View Angles +******************* + +How to define the view angle +============================ + +The position of the viewport "camera" in a 3D plot is defined by three angles: +*elevation*, *azimuth*, and *roll*. From the resulting position, it always +points towards the center of the plot box volume. The angle direction is a +common convention, and is shared with +`PyVista `_ and +`MATLAB `_ +(though MATLAB lacks a roll angle). Note that a positive roll angle rotates the +viewing plane clockwise, so the 3d axes will appear to rotate +counter-clockwise. + +.. image:: /_static/mplot3d_view_angles.png + :align: center + :scale: 50 + +Rotating the plot using the mouse will control only the azimuth and elevation, +but all three angles can be set programmatically:: + + import matplotlib.pyplot as plt + ax = plt.figure().add_subplot(projection='3d') + ax.view_init(elev=30, azim=45, roll=15) + + +Primary view planes +=================== + +To look directly at the primary view planes, the required elevation, azimuth, +and roll angles are shown in the diagram of an "unfolded" plot below. These are +further documented in the `.mplot3d.axes3d.Axes3D.view_init` API. + +.. plot:: gallery/mplot3d/view_planes_3d.py + :align: center diff --git a/examples/mplot3d/view_planes_3d.py b/examples/mplot3d/view_planes_3d.py new file mode 100644 index 000000000000..c4322d60fe93 --- /dev/null +++ b/examples/mplot3d/view_planes_3d.py @@ -0,0 +1,57 @@ +""" +====================== +Primary 3D view planes +====================== + +This example generates an "unfolded" 3D plot that shows each of the primary 3D +view planes. The elevation, azimuth, and roll angles required for each view are +labeled. You could print out this image and fold it into a box where each plane +forms a side of the box. +""" + +import matplotlib.pyplot as plt + + +def annotate_axes(ax, text, fontsize=18): + ax.text(x=0.5, y=0.5, z=0.5, s=text, + va="center", ha="center", fontsize=fontsize, color="black") + +# (plane, (elev, azim, roll)) +views = [('XY', (90, -90, 0)), + ('XZ', (0, -90, 0)), + ('YZ', (0, 0, 0)), + ('-XY', (-90, 90, 0)), + ('-XZ', (0, 90, 0)), + ('-YZ', (0, 180, 0))] + +layout = [['XY', '.', 'L', '.'], + ['XZ', 'YZ', '-XZ', '-YZ'], + ['.', '.', '-XY', '.']] +fig, axd = plt.subplot_mosaic(layout, subplot_kw={'projection': '3d'}, + figsize=(12, 8.5)) +for plane, angles in views: + axd[plane].set_xlabel('x') + axd[plane].set_ylabel('y') + axd[plane].set_zlabel('z') + axd[plane].set_proj_type('ortho') + axd[plane].view_init(elev=angles[0], azim=angles[1], roll=angles[2]) + axd[plane].set_box_aspect(None, zoom=1.25) + + label = f'{plane}\n{angles}' + annotate_axes(axd[plane], label, fontsize=14) + +for plane in ('XY', '-XY'): + axd[plane].set_zticklabels([]) + axd[plane].set_zlabel('') +for plane in ('XZ', '-XZ'): + axd[plane].set_yticklabels([]) + axd[plane].set_ylabel('') +for plane in ('YZ', '-YZ'): + axd[plane].set_xticklabels([]) + axd[plane].set_xlabel('') + +label = 'mplot3d primary view planes\n' + 'ax.view_init(elev, azim, roll)' +annotate_axes(axd['L'], label, fontsize=18) +axd['L'].set_axis_off() + +plt.show()