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

Skip to content

Commit e59721e

Browse files
patquemscottshambaugh
authored andcommitted
Provide axis('equal') for Axes3D
Whats new for 3D plot equal aspect ratio Code review updates set_aspect('equal') now adopts current box aspect Update test image Update whats new Update whats new test image
1 parent 37ccdca commit e59721e

4 files changed

Lines changed: 63 additions & 13 deletions

File tree

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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+
aspects = ('auto', 'equal', 'equalxy', 'equalyz', 'equalxz')
11+
fig, axs = plt.subplots(1, len(aspects), subplot_kw={'projection': '3d'})
12+
13+
# Draw rectangular cuboid with side lengths [1, 1, 2]
14+
r = [0, 1]
15+
scale = np.array([1, 1, 2])
16+
pts = itertools.combinations(np.array(list(itertools.product(r, r, r))), 2)
17+
for start, end in pts:
18+
if np.sum(np.abs(start - end)) == r[1] - r[0]:
19+
for ax in axs:
20+
ax.plot3D(*zip(start*scale, end*scale), color='C0')
21+
22+
# Set the aspect ratios
23+
for i, ax in enumerate(axs):
24+
ax.set_box_aspect((3, 4, 5))
25+
ax.set_aspect(aspects[i])
26+
ax.title(f"set_aspect('{aspects[i]}')")
27+
28+
plt.show()

lib/mpl_toolkits/mplot3d/axes3d.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -272,22 +272,16 @@ def set_aspect(self, aspect, adjustable=None, anchor=None, share=False):
272272
"""
273273
Set the aspect ratios.
274274
275-
Axes 3D does not current support any aspect but 'auto' which fills
276-
the Axes with the data limits.
277-
278-
To simulate having equal aspect in data space, set the ratio
279-
of your data limits to match the value of `.get_box_aspect`.
280-
To control box aspect ratios use `~.Axes3D.set_box_aspect`.
281-
282275
Parameters
283276
----------
284-
aspect : {'auto'}
277+
aspect : {'auto', 'equal'}
285278
Possible values:
286279
287280
========= ==================================================
288281
value description
289282
========= ==================================================
290283
'auto' automatic; fill the position rectangle with data.
284+
'equal' adapt the axes to have equal aspect ratios.
291285
========= ==================================================
292286
293287
adjustable : None
@@ -321,14 +315,26 @@ def set_aspect(self, aspect, adjustable=None, anchor=None, share=False):
321315
--------
322316
mpl_toolkits.mplot3d.axes3d.Axes3D.set_box_aspect
323317
"""
324-
if aspect != 'auto':
318+
if aspect not in ('auto', 'equal'):
325319
raise NotImplementedError(
326320
"Axes3D currently only supports the aspect argument "
327-
f"'auto'. You passed in {aspect!r}."
321+
f"'auto' or 'equal'. You passed in {aspect!r}."
328322
)
329323
super().set_aspect(
330324
aspect, adjustable=adjustable, anchor=anchor, share=share)
331325

326+
if aspect == 'equal':
327+
v_intervals = np.vstack((self.xaxis.get_view_interval(),
328+
self.yaxis.get_view_interval(),
329+
self.zaxis.get_view_interval()))
330+
mean = np.mean(v_intervals, axis=1)
331+
delta = np.max(np.ptp(v_intervals, axis=1))
332+
deltas = delta * self._box_aspect / min(self._box_aspect)
333+
334+
self.set_xlim3d(mean[0] - deltas[0] / 2., mean[0] + deltas[0] / 2.)
335+
self.set_ylim3d(mean[1] - deltas[1] / 2., mean[1] + deltas[1] / 2.)
336+
self.set_zlim3d(mean[2] - deltas[2] / 2., mean[2] + deltas[2] / 2.)
337+
332338
def set_box_aspect(self, aspect, *, zoom=1):
333339
"""
334340
Set the Axes box aspect.
36.2 KB
Loading

lib/mpl_toolkits/tests/test_mplot3d.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,27 @@ def test_invisible_axes(fig_test, fig_ref):
2727
ax.set_visible(False)
2828

2929

30-
def test_aspect_equal_error():
30+
@mpl3d_image_comparison(['aspect_equal.png'], remove_text=False)
31+
def test_aspect_equal():
3132
fig = plt.figure()
3233
ax = fig.add_subplot(projection='3d')
33-
with pytest.raises(NotImplementedError):
34-
ax.set_aspect('equal')
34+
35+
points = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 0],
36+
[0, 0, 3], [1, 0, 3], [0, 1, 3], [1, 1, 3]])
37+
38+
x = np.asarray([coord[0] for coord in points])
39+
y = np.asarray([coord[1] for coord in points])
40+
z = np.asarray([coord[2] for coord in points])
41+
42+
def plot_edge(i, j):
43+
ax.plot([x[i], x[j]], [y[i], y[j]], [z[i], z[j]], c='r')
44+
45+
# hexaedron creation
46+
plot_edge(0, 1), plot_edge(1, 3), plot_edge(3, 2), plot_edge(2, 0)
47+
plot_edge(4, 5), plot_edge(5, 7), plot_edge(7, 6), plot_edge(6, 4)
48+
plot_edge(0, 4), plot_edge(1, 5), plot_edge(3, 7), plot_edge(2, 6)
49+
50+
ax.set_aspect('equal')
3551

3652

3753
@mpl3d_image_comparison(['bar3d.png'])

0 commit comments

Comments
 (0)