|
| 1 | +""" |
| 2 | +=============== |
| 3 | +2D images in 3D |
| 4 | +=============== |
| 5 | +
|
| 6 | +This example demonstrates how to plot 2D color coded images (similar to |
| 7 | +`.Axes.imshow`) as a plane in 3D. |
| 8 | +
|
| 9 | +Matplotlib does not have a native function for this. Below we build one by relying |
| 10 | +on `.Axes3D.plot_surface`. For simplicity, there are some differences to |
| 11 | +`.Axes.imshow`: This function does not set the aspect of the Axes, hence pixels are |
| 12 | +not necessarily square. Also, pixel edges are on integer values rather than pixel |
| 13 | +centers. Furthermore, many optional parameters of `.Axes.imshow` are not implemented. |
| 14 | +
|
| 15 | +Multiple calls of ``imshow3d`` use independent norms and thus different color scales |
| 16 | +by default. If you want to have a single common color scale, you need to construct |
| 17 | +a suitable norm beforehand and pass it to all ``imshow3d`` calls. |
| 18 | +
|
| 19 | +A fundamental limitation of the 3D plotting engine is that intersecting objects cannot |
| 20 | +be drawn correctly. One object will always be drawn after the other. Therefore, |
| 21 | +multiple image planes can well be used in the background as shown in this example. |
| 22 | +But this approach is not suitable if the planes intersect. |
| 23 | +""" |
| 24 | + |
| 25 | +import matplotlib.pyplot as plt |
| 26 | +import numpy as np |
| 27 | + |
| 28 | +from matplotlib.colors import Normalize |
| 29 | + |
| 30 | + |
| 31 | +def imshow3d(ax, array, value_direction='z', pos=0, norm=None, cmap=None): |
| 32 | + """ |
| 33 | + Display a 2D array as a color-coded 2D image embedded in 3d. |
| 34 | +
|
| 35 | + The image will be in a plane perpendicular to the coordinate axis *value_direction*. |
| 36 | +
|
| 37 | + Parameters |
| 38 | + ---------- |
| 39 | + ax : Axes3D |
| 40 | + The 3D Axes to plot into. |
| 41 | + array : 2D numpy array |
| 42 | + The image values. |
| 43 | + value_direction : {'x', 'y', 'z'} |
| 44 | + The axis normal to the image plane. |
| 45 | + pos : float |
| 46 | + The numeric value on the *value_direction* axis at which the image plane is |
| 47 | + located. |
| 48 | + norm : `~matplotlib.colors.Normalize`, default: Normalize |
| 49 | + The normalization method used to scale scalar data. See `imshow()`. |
| 50 | + cmap : str or `~matplotlib.colors.Colormap`, default: :rc:`image.cmap` |
| 51 | + The Colormap instance or registered colormap name used to map scalar data |
| 52 | + to colors. |
| 53 | + """ |
| 54 | + if norm is None: |
| 55 | + norm = Normalize() |
| 56 | + colors = plt.get_cmap(cmap)(norm(array)) |
| 57 | + |
| 58 | + if value_direction == 'x': |
| 59 | + nz, ny = array.shape |
| 60 | + zi, yi = np.mgrid[0:nz + 1, 0:ny + 1] |
| 61 | + xi = np.full_like(yi, pos) |
| 62 | + elif value_direction == 'y': |
| 63 | + nx, nz = array.shape |
| 64 | + xi, zi = np.mgrid[0:nx + 1, 0:nz + 1] |
| 65 | + yi = np.full_like(zi, pos) |
| 66 | + elif value_direction == 'z': |
| 67 | + ny, nx = array.shape |
| 68 | + yi, xi = np.mgrid[0:ny + 1, 0:nx + 1] |
| 69 | + zi = np.full_like(xi, pos) |
| 70 | + else: |
| 71 | + raise ValueError(f"Invalid value_direction: {value_direction!r}") |
| 72 | + ax.plot_surface(xi, yi, zi, rstride=1, cstride=1, facecolors=colors, shade=False) |
| 73 | + |
| 74 | + |
| 75 | +fig = plt.figure() |
| 76 | +ax = fig.add_subplot(projection='3d') |
| 77 | +ax.set(xlabel="x", ylabel="y", zlabel="z") |
| 78 | + |
| 79 | +nx, ny, nz = 8, 10, 5 |
| 80 | +data_xy = np.arange(ny * nx).reshape(ny, nx) + 15 * np.random.random((ny, nx)) |
| 81 | +data_yz = np.arange(nz * ny).reshape(nz, ny) + 10 * np.random.random((nz, ny)) |
| 82 | +data_zx = np.arange(nx * nz).reshape(nx, nz) + 8 * np.random.random((nx, nz)) |
| 83 | + |
| 84 | +imshow3d(ax, data_xy) |
| 85 | +imshow3d(ax, data_yz, value_direction='x', cmap='magma') |
| 86 | +imshow3d(ax, data_zx, value_direction='y', pos=ny, cmap='plasma') |
| 87 | + |
| 88 | +plt.show() |
0 commit comments