Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 437158c

Browse filesBrowse files
authored
Merge pull request #23409 from scottshambaugh/aspect_equal_3d
ENH: Provide axis('equal') for Axes3D replaces PR #23017
2 parents 16ad86e + bfd0afe commit 437158c
Copy full SHA for 437158c

File tree

Expand file treeCollapse file tree

7 files changed

+87
-19
lines changed
Filter options
Expand file treeCollapse file tree

7 files changed

+87
-19
lines changed
+32Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
Set equal aspect ratio for 3D plots
2+
-----------------------------------
3+
4+
Users can set the aspect ratio for the X, Y, Z axes of a 3D plot to be 'equal',
5+
'equalxy', 'equalxz', or 'equalyz' rather than the default of 'auto'.
6+
7+
.. plot::
8+
:include-source: true
9+
10+
import matplotlib.pyplot as plt
11+
import numpy as np
12+
from itertools import combinations, product
13+
14+
aspects = ('auto', 'equal', 'equalxy', 'equalyz', 'equalxz')
15+
fig, axs = plt.subplots(1, len(aspects), subplot_kw={'projection': '3d'})
16+
17+
# Draw rectangular cuboid with side lengths [1, 1, 5]
18+
r = [0, 1]
19+
scale = np.array([1, 1, 5])
20+
pts = combinations(np.array(list(product(r, r, r))), 2)
21+
for start, end in pts:
22+
if np.sum(np.abs(start - end)) == r[1] - r[0]:
23+
for ax in axs:
24+
ax.plot3D(*zip(start*scale, end*scale), color='C0')
25+
26+
# Set the aspect ratios
27+
for i, ax in enumerate(axs):
28+
ax.set_box_aspect((3, 4, 5))
29+
ax.set_aspect(aspects[i])
30+
ax.set_title("set_aspect('{aspects[i]}')")
31+
32+
plt.show()

‎examples/mplot3d/surface3d_2.py

Copy file name to clipboardExpand all lines: examples/mplot3d/surface3d_2.py
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,7 @@
2323
# Plot the surface
2424
ax.plot_surface(x, y, z)
2525

26+
# Set an equal aspect ratio
27+
ax.set_aspect('equal')
28+
2629
plt.show()

‎examples/mplot3d/voxels_numpy_logo.py

Copy file name to clipboardExpand all lines: examples/mplot3d/voxels_numpy_logo.py
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,6 @@ def explode(data):
4242

4343
ax = plt.figure().add_subplot(projection='3d')
4444
ax.voxels(x, y, z, filled_2, facecolors=fcolors_2, edgecolors=ecolors_2)
45+
ax.set_aspect('equal')
4546

4647
plt.show()

‎examples/mplot3d/voxels_rgb.py

Copy file name to clipboardExpand all lines: examples/mplot3d/voxels_rgb.py
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,6 @@ def midpoints(x):
3939
edgecolors=np.clip(2*colors - 0.5, 0, 1), # brighter
4040
linewidth=0.5)
4141
ax.set(xlabel='r', ylabel='g', zlabel='b')
42+
ax.set_aspect('equal')
4243

4344
plt.show()

‎lib/mpl_toolkits/mplot3d/axes3d.py

Copy file name to clipboardExpand all lines: lib/mpl_toolkits/mplot3d/axes3d.py
+34-14Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -271,22 +271,19 @@ def set_aspect(self, aspect, adjustable=None, anchor=None, share=False):
271271
"""
272272
Set the aspect ratios.
273273
274-
Axes 3D does not current support any aspect but 'auto' which fills
275-
the Axes with the data limits.
276-
277-
To simulate having equal aspect in data space, set the ratio
278-
of your data limits to match the value of `.get_box_aspect`.
279-
To control box aspect ratios use `~.Axes3D.set_box_aspect`.
280-
281274
Parameters
282275
----------
283-
aspect : {'auto'}
276+
aspect : {'auto', 'equal', 'equalxy', 'equalxz', 'equalyz'}
284277
Possible values:
285278
286279
========= ==================================================
287280
value description
288281
========= ==================================================
289282
'auto' automatic; fill the position rectangle with data.
283+
'equal' adapt all the axes to have equal aspect ratios.
284+
'equalxy' adapt the x and y axes to have equal aspect ratios.
285+
'equalxz' adapt the x and z axes to have equal aspect ratios.
286+
'equalyz' adapt the y and z axes to have equal aspect ratios.
290287
========= ==================================================
291288
292289
adjustable : None
@@ -320,13 +317,36 @@ def set_aspect(self, aspect, adjustable=None, anchor=None, share=False):
320317
--------
321318
mpl_toolkits.mplot3d.axes3d.Axes3D.set_box_aspect
322319
"""
323-
if aspect != 'auto':
324-
raise NotImplementedError(
325-
"Axes3D currently only supports the aspect argument "
326-
f"'auto'. You passed in {aspect!r}."
327-
)
320+
_api.check_in_list(('auto', 'equal', 'equalxy', 'equalyz', 'equalxz'),
321+
aspect=aspect)
328322
super().set_aspect(
329-
aspect, adjustable=adjustable, anchor=anchor, share=share)
323+
aspect='auto', adjustable=adjustable, anchor=anchor, share=share)
324+
self._aspect = aspect
325+
326+
if aspect in ('equal', 'equalxy', 'equalxz', 'equalyz'):
327+
if aspect == 'equal':
328+
ax_indices = [0, 1, 2]
329+
elif aspect == 'equalxy':
330+
ax_indices = [0, 1]
331+
elif aspect == 'equalxz':
332+
ax_indices = [0, 2]
333+
elif aspect == 'equalyz':
334+
ax_indices = [1, 2]
335+
336+
view_intervals = np.array([self.xaxis.get_view_interval(),
337+
self.yaxis.get_view_interval(),
338+
self.zaxis.get_view_interval()])
339+
mean = np.mean(view_intervals, axis=1)
340+
ptp = np.ptp(view_intervals, axis=1)
341+
delta = max(ptp[ax_indices])
342+
scale = self._box_aspect[ptp == delta][0]
343+
deltas = delta * self._box_aspect / scale
344+
345+
for i, set_lim in enumerate((self.set_xlim3d,
346+
self.set_ylim3d,
347+
self.set_zlim3d)):
348+
if i in ax_indices:
349+
set_lim(mean[i] - deltas[i]/2., mean[i] + deltas[i]/2.)
330350

331351
def set_box_aspect(self, aspect, *, zoom=1):
332352
"""
Loading

‎lib/mpl_toolkits/tests/test_mplot3d.py

Copy file name to clipboardExpand all lines: lib/mpl_toolkits/tests/test_mplot3d.py
+16-5Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,22 @@ def test_invisible_axes(fig_test, fig_ref):
2929
ax.set_visible(False)
3030

3131

32-
def test_aspect_equal_error():
33-
fig = plt.figure()
34-
ax = fig.add_subplot(projection='3d')
35-
with pytest.raises(NotImplementedError):
36-
ax.set_aspect('equal')
32+
@mpl3d_image_comparison(['aspects.png'], remove_text=False)
33+
def test_aspects():
34+
aspects = ('auto', 'equal', 'equalxy', 'equalyz', 'equalxz')
35+
fig, axs = plt.subplots(1, len(aspects), subplot_kw={'projection': '3d'})
36+
37+
# Draw rectangular cuboid with side lengths [1, 1, 5]
38+
r = [0, 1]
39+
scale = np.array([1, 1, 5])
40+
pts = itertools.combinations(np.array(list(itertools.product(r, r, r))), 2)
41+
for start, end in pts:
42+
if np.sum(np.abs(start - end)) == r[1] - r[0]:
43+
for ax in axs:
44+
ax.plot3D(*zip(start*scale, end*scale))
45+
for i, ax in enumerate(axs):
46+
ax.set_box_aspect((3, 4, 5))
47+
ax.set_aspect(aspects[i])
3748

3849

3950
def test_axes3d_repr():

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.