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

Skip to content

Commit 853b224

Browse files
3D plots shared view angles
1 parent e7fd79f commit 853b224

File tree

5 files changed

+66
-16
lines changed

5 files changed

+66
-16
lines changed

doc/api/toolkits/mplot3d/axes3d.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ Sharing
209209
:nosignatures:
210210

211211
sharez
212+
shareview
212213

213214

214215
Interactive
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
3D plots can share view angles
2+
------------------------------
3+
4+
3D plots can now share the same view angles, so that when you rotate one plot,
5+
the other plots also rotate. This can be done with the `shareview` keyword when
6+
adding an axes, or by using the `ax1.shareview(ax2)` method of an existing
7+
axes.

lib/mpl_toolkits/mplot3d/axes3d.py

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class Axes3D(Axes):
5656

5757
_axis_names = ("x", "y", "z")
5858
Axes._shared_axes["z"] = cbook.Grouper()
59+
Axes._shared_axes["view"] = cbook.Grouper()
5960

6061
vvec = _api.deprecate_privatize_attribute("3.7")
6162
eye = _api.deprecate_privatize_attribute("3.7")
@@ -66,6 +67,7 @@ def __init__(
6667
self, fig, rect=None, *args,
6768
elev=30, azim=-60, roll=0, sharez=None, proj_type='persp',
6869
box_aspect=None, computed_zorder=True, focal_length=None,
70+
shareview=None,
6971
**kwargs):
7072
"""
7173
Parameters
@@ -111,6 +113,8 @@ def __init__(
111113
or infinity (numpy.inf). If None, defaults to infinity.
112114
The focal length can be computed from a desired Field Of View via
113115
the equation: focal_length = 1/tan(FOV/2)
116+
shareview : Axes3D, optional
117+
Other Axes to share view angles with.
114118
115119
**kwargs
116120
Other optional keyword arguments:
@@ -142,6 +146,10 @@ def __init__(
142146
self._shared_axes["z"].join(self, sharez)
143147
self._adjustable = 'datalim'
144148

149+
self._shareview = shareview
150+
if shareview is not None:
151+
self._shared_axes["view"].join(self, shareview)
152+
145153
if kwargs.pop('auto_add_to_figure', False):
146154
raise AttributeError(
147155
'auto_add_to_figure is no longer supported for Axes3D. '
@@ -757,7 +765,8 @@ def clabel(self, *args, **kwargs):
757765
"""Currently not implemented for 3D axes, and returns *None*."""
758766
return None
759767

760-
def view_init(self, elev=None, azim=None, roll=None, vertical_axis="z"):
768+
def view_init(self, elev=None, azim=None, roll=None, vertical_axis="z",
769+
share=False):
761770
"""
762771
Set the elevation and azimuth of the axes in degrees (not radians).
763772
@@ -804,29 +813,34 @@ def view_init(self, elev=None, azim=None, roll=None, vertical_axis="z"):
804813
constructor is used.
805814
vertical_axis : {"z", "x", "y"}, default: "z"
806815
The axis to align vertically. *azim* rotates about this axis.
816+
share : bool, default: False
817+
If ``True``, apply the settings to all Axes with shared views.
807818
"""
808819

809820
self._dist = 10 # The camera distance from origin. Behaves like zoom
810821

811822
if elev is None:
812-
self.elev = self.initial_elev
813-
else:
814-
self.elev = elev
815-
823+
elev = self.initial_elev
816824
if azim is None:
817-
self.azim = self.initial_azim
818-
else:
819-
self.azim = azim
820-
825+
azim = self.initial_azim
821826
if roll is None:
822-
self.roll = self.initial_roll
823-
else:
824-
self.roll = roll
825-
826-
self._vertical_axis = _api.check_getitem(
827+
roll = self.initial_roll
828+
vertical_axis = _api.check_getitem(
827829
dict(x=0, y=1, z=2), vertical_axis=vertical_axis
828830
)
829831

832+
if share:
833+
axes = {sibling for sibling
834+
in self._shared_axes['view'].get_siblings(self)}
835+
else:
836+
axes = [self]
837+
838+
for ax in axes:
839+
ax.elev = elev
840+
ax.azim = azim
841+
ax.roll = roll
842+
ax._vertical_axis = vertical_axis
843+
830844
def set_proj_type(self, proj_type, focal_length=None):
831845
"""
832846
Set the projection type.
@@ -975,6 +989,23 @@ def sharez(self, other):
975989
self.set_zlim(z0, z1, emit=False, auto=other.get_autoscalez_on())
976990
self.zaxis._scale = other.zaxis._scale
977991

992+
def shareview(self, other):
993+
"""
994+
Share the view angles with *other*.
995+
996+
This is equivalent to passing ``shareview=other`` when
997+
constructing the Axes, and cannot be used if the view angles are
998+
already being shared with another Axes.
999+
"""
1000+
_api.check_isinstance(maxes._base._AxesBase, other=other)
1001+
if self._shareview is not None and other is not self._shareview:
1002+
raise ValueError("view angles are already shared")
1003+
self._shared_axes["view"].join(self, other)
1004+
self._shareview = other
1005+
vertical_axis = {0: "x", 1: "y", 2: "z"}[other._vertical_axis]
1006+
self.view_init(elev=other.elev, azim=other.azim, roll=other.roll,
1007+
vertical_axis=vertical_axis, share=True)
1008+
9781009
def clear(self):
9791010
# docstring inherited.
9801011
super().clear()
@@ -1107,8 +1138,9 @@ def _on_move(self, event):
11071138
roll = np.deg2rad(self.roll)
11081139
delev = -(dy/h)*180*np.cos(roll) + (dx/w)*180*np.sin(roll)
11091140
dazim = -(dy/h)*180*np.sin(roll) - (dx/w)*180*np.cos(roll)
1110-
self.elev = self.elev + delev
1111-
self.azim = self.azim + dazim
1141+
elev = self.elev + delev
1142+
azim = self.azim + dazim
1143+
self.view_init(elev=elev, azim=azim, roll=roll, share=True)
11121144
self.stale = True
11131145

11141146
elif self.button_pressed in self._pan_btn:

lib/mpl_toolkits/mplot3d/tests/test_axes3d.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1689,6 +1689,16 @@ def test_set_zlim():
16891689
ax.set_zlim(top=0, zmax=1)
16901690

16911691

1692+
@image_comparison(["shared_view.png"], style="mpl20")
1693+
def test_shared_view():
1694+
fig = plt.figure()
1695+
ax1 = fig.add_subplot(131, projection="3d")
1696+
ax2 = fig.add_subplot(132, projection="3d", shareview=ax1)
1697+
ax3 = fig.add_subplot(133, projection="3d")
1698+
ax3.shareview(ax1)
1699+
ax2.view_init(elev=5, azim=20, roll=30, share=True)
1700+
1701+
16921702
def test_shared_axes_retick():
16931703
fig = plt.figure()
16941704
ax1 = fig.add_subplot(211, projection="3d")

0 commit comments

Comments
 (0)