Skip to content

Commit 91ff901

Browse files
authored
Merge pull request matplotlib#28331 from meeseeksmachine/auto-backport-of-pr-28329-on-v3.9.x
Backport PR matplotlib#28329 on branch v3.9.x (DOC: Add example for 3D intersecting planes)
2 parents 0776da6 + a251d42 commit 91ff901

File tree

1 file changed

+89
-0
lines changed

1 file changed

+89
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
"""
2+
===================
3+
Intersecting planes
4+
===================
5+
6+
This examples demonstrates drawing intersecting planes in 3D. It is a generalization
7+
of :doc:`/gallery/mplot3d/imshow3d`.
8+
9+
Drawing intersecting planes in `.mplot3d` is complicated, because `.mplot3d` is not a
10+
real 3D renderer, but only projects the Artists into 3D and draws them in the right
11+
order. This does not work correctly if Artists overlap each other mutually. In this
12+
example, we lift the problem of mutual overlap by segmenting the planes at their
13+
intersections, making four parts out of each plane.
14+
15+
This examples only works correctly for planes that cut each other in haves. This
16+
limitation is intentional to keep the code more readable. Cutting at arbitrary
17+
positions would of course be possible but makes the code even more complex.
18+
Thus, this example is more a demonstration of the concept how to work around
19+
limitations of the 3D visualization, it's not a refined solution for drawing
20+
arbitrary intersecting planes, which you can copy-and-paste as is.
21+
"""
22+
import matplotlib.pyplot as plt
23+
import numpy as np
24+
25+
26+
def plot_quadrants(ax, array, fixed_coord, cmap):
27+
"""For a given 3d *array* plot a plane with *fixed_coord*, using four quadrants."""
28+
nx, ny, nz = array.shape
29+
index = {
30+
'x': (nx // 2, slice(None), slice(None)),
31+
'y': (slice(None), ny // 2, slice(None)),
32+
'z': (slice(None), slice(None), nz // 2),
33+
}[fixed_coord]
34+
plane_data = array[index]
35+
36+
n0, n1 = plane_data.shape
37+
quadrants = [
38+
plane_data[:n0 // 2, :n1 // 2],
39+
plane_data[:n0 // 2, n1 // 2:],
40+
plane_data[n0 // 2:, :n1 // 2],
41+
plane_data[n0 // 2:, n1 // 2:]
42+
]
43+
44+
min_val = array.min()
45+
max_val = array.max()
46+
47+
cmap = plt.get_cmap(cmap)
48+
49+
for i, quadrant in enumerate(quadrants):
50+
facecolors = cmap((quadrant - min_val) / (max_val - min_val))
51+
if fixed_coord == 'x':
52+
Y, Z = np.mgrid[0:ny // 2, 0:nz // 2]
53+
X = nx // 2 * np.ones_like(Y)
54+
Y_offset = (i // 2) * ny // 2
55+
Z_offset = (i % 2) * nz // 2
56+
ax.plot_surface(X, Y + Y_offset, Z + Z_offset, rstride=1, cstride=1,
57+
facecolors=facecolors, shade=False)
58+
elif fixed_coord == 'y':
59+
X, Z = np.mgrid[0:nx // 2, 0:nz // 2]
60+
Y = ny // 2 * np.ones_like(X)
61+
X_offset = (i // 2) * nx // 2
62+
Z_offset = (i % 2) * nz // 2
63+
ax.plot_surface(X + X_offset, Y, Z + Z_offset, rstride=1, cstride=1,
64+
facecolors=facecolors, shade=False)
65+
elif fixed_coord == 'z':
66+
X, Y = np.mgrid[0:nx // 2, 0:ny // 2]
67+
Z = nz // 2 * np.ones_like(X)
68+
X_offset = (i // 2) * nx // 2
69+
Y_offset = (i % 2) * ny // 2
70+
ax.plot_surface(X + X_offset, Y + Y_offset, Z, rstride=1, cstride=1,
71+
facecolors=facecolors, shade=False)
72+
73+
74+
def figure_3D_array_slices(array, cmap=None):
75+
"""Plot a 3d array using three intersecting centered planes."""
76+
fig = plt.figure()
77+
ax = fig.add_subplot(projection='3d')
78+
ax.set_box_aspect(array.shape)
79+
plot_quadrants(ax, array, 'x', cmap=cmap)
80+
plot_quadrants(ax, array, 'y', cmap=cmap)
81+
plot_quadrants(ax, array, 'z', cmap=cmap)
82+
return fig, ax
83+
84+
85+
nx, ny, nz = 70, 100, 50
86+
r_square = (np.mgrid[-1:1:1j * nx, -1:1:1j * ny, -1:1:1j * nz] ** 2).sum(0)
87+
88+
figure_3D_array_slices(r_square, cmap='viridis_r')
89+
plt.show()

0 commit comments

Comments
 (0)