A、使用voxels
从 matplotlib 2.1 开始,有一个Axes3D.voxels https://matplotlib.org/api/_as_gen/mpl_toolkits.mplot3d.axes3d.Axes3D.html#mpl_toolkits.mplot3d.axes3d.Axes3D.voxels功能可用,这几乎可以满足这里的要求。然而,它不太容易定制为不同的尺寸、位置或颜色。
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
N1 = 10
N2 = 10
N3 = 10
ma = np.random.choice([0,1], size=(N1,N2,N3), p=[0.99, 0.01])
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.set_aspect('equal')
ax.voxels(ma, edgecolor="k")
plt.show()
要将体素放置在不同的位置,请参见如何使用 Matplotlib 缩放体素尺寸? https://stackoverflow.com/a/56753251/4124317.
B、使用Poly3DCollection
手动创建体素可以使过程更加透明,并允许对体素的大小、位置和颜色进行任何类型的自定义。另一个优点是,这里我们创建一个单独的 Poly3DCollection,而不是多个,使得该解决方案比内置更快voxels
.
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
def cuboid_data(o, size=(1,1,1)):
X = [[[0, 1, 0], [0, 0, 0], [1, 0, 0], [1, 1, 0]],
[[0, 0, 0], [0, 0, 1], [1, 0, 1], [1, 0, 0]],
[[1, 0, 1], [1, 0, 0], [1, 1, 0], [1, 1, 1]],
[[0, 0, 1], [0, 0, 0], [0, 1, 0], [0, 1, 1]],
[[0, 1, 0], [0, 1, 1], [1, 1, 1], [1, 1, 0]],
[[0, 1, 1], [0, 0, 1], [1, 0, 1], [1, 1, 1]]]
X = np.array(X).astype(float)
for i in range(3):
X[:,:,i] *= size[i]
X += np.array(o)
return X
def plotCubeAt(positions,sizes=None,colors=None, **kwargs):
if not isinstance(colors,(list,np.ndarray)): colors=["C0"]*len(positions)
if not isinstance(sizes,(list,np.ndarray)): sizes=[(1,1,1)]*len(positions)
g = []
for p,s,c in zip(positions,sizes,colors):
g.append( cuboid_data(p, size=s) )
return Poly3DCollection(np.concatenate(g),
facecolors=np.repeat(colors,6, axis=0), **kwargs)
N1 = 10
N2 = 10
N3 = 10
ma = np.random.choice([0,1], size=(N1,N2,N3), p=[0.99, 0.01])
x,y,z = np.indices((N1,N2,N3))-.5
positions = np.c_[x[ma==1],y[ma==1],z[ma==1]]
colors= np.random.rand(len(positions),3)
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.set_aspect('equal')
pc = plotCubeAt(positions, colors=colors,edgecolor="k")
ax.add_collection3d(pc)
ax.set_xlim([0,10])
ax.set_ylim([0,10])
ax.set_zlim([0,10])
#plotMatrix(ax, ma)
#ax.voxels(ma, edgecolor="k")
plt.show()
C、使用plot_surface
改编代码来自这个答案 https://stackoverflow.com/a/40412894/4124317(部分基于这个答案 https://stackoverflow.com/a/35978146/4124317),我们可以轻松地将长方体绘制为曲面图 http://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html#surface-plots.
然后可以迭代输入数组并找到一个1
在与数组索引对应的位置绘制一个长方体。
这样做的优点是您可以在表面上获得漂亮的阴影,从而增加 3D 效果。缺点可能是立方体在某些情况下可能不具有物理行为,例如它们可能在某些视角下重叠。
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
def cuboid_data(pos, size=(1,1,1)):
# code taken from
# https://stackoverflow.com/a/35978146/4124317
# suppose axis direction: x: to left; y: to inside; z: to upper
# get the (left, outside, bottom) point
o = [a - b / 2 for a, b in zip(pos, size)]
# get the length, width, and height
l, w, h = size
x = [[o[0], o[0] + l, o[0] + l, o[0], o[0]],
[o[0], o[0] + l, o[0] + l, o[0], o[0]],
[o[0], o[0] + l, o[0] + l, o[0], o[0]],
[o[0], o[0] + l, o[0] + l, o[0], o[0]]]
y = [[o[1], o[1], o[1] + w, o[1] + w, o[1]],
[o[1], o[1], o[1] + w, o[1] + w, o[1]],
[o[1], o[1], o[1], o[1], o[1]],
[o[1] + w, o[1] + w, o[1] + w, o[1] + w, o[1] + w]]
z = [[o[2], o[2], o[2], o[2], o[2]],
[o[2] + h, o[2] + h, o[2] + h, o[2] + h, o[2] + h],
[o[2], o[2], o[2] + h, o[2] + h, o[2]],
[o[2], o[2], o[2] + h, o[2] + h, o[2]]]
return np.array(x), np.array(y), np.array(z)
def plotCubeAt(pos=(0,0,0),ax=None):
# Plotting a cube element at position pos
if ax !=None:
X, Y, Z = cuboid_data( pos )
ax.plot_surface(X, Y, Z, color='b', rstride=1, cstride=1, alpha=1)
def plotMatrix(ax, matrix):
# plot a Matrix
for i in range(matrix.shape[0]):
for j in range(matrix.shape[1]):
for k in range(matrix.shape[2]):
if matrix[i,j,k] == 1:
# to have the
plotCubeAt(pos=(i-0.5,j-0.5,k-0.5), ax=ax)
N1 = 10
N2 = 10
N3 = 10
ma = np.random.choice([0,1], size=(N1,N2,N3), p=[0.99, 0.01])
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.set_aspect('equal')
plotMatrix(ax, ma)
plt.show()