numpy-stl实战3D建模
想象一下,我们需要用 python 编程语言构建某个物体的三维模型,然后将其可视化,或者准备一个文件以便在 3D 打印机上打印。 有几个库可以解决这些问题。 让我们来看看,如何在 Python 中从点、边和图元构建 3D 模型。 如何执行基本的 3D 建模技术:移动、旋转、合并、减去等。
我们将使用numpy-stl构建一个 Menger Sponge分形,将模型保存到 stl 文件,然后渲染图像。 在此过程中,我们简要了解了数据结构和术语。
所有示例均针对 Linux 操作系统提供。代码示例可以在 GitHub 存储库中找到。
1、Numpy-stl概述
在Numpy-stl中, 多边形网格的结构如下:
Vertices - 点列表。 每个点由三个数字描述——3 维空间中的坐标。
接下来,我们将使用 Jupyter notebook。示例:numpy_stl_example_01.ipynb
import numpy as np
from myplot import plot_verticles
vertices = np.array([
[-3, -3, 0],
[+3, -3, 0],
[+3, +3, 0],
[-3, +3, 0],
[+0, +0, +3]
])
plot_verticles(vertices = vertices, isosurf = False)
尽管只描述了顶点,但你已经可以看到如果将它们与三角形连接起来模型会是什么样子:
plot_verticles(vertices = vertices, isosurf = True)
看起来面孔已经存在。 但现在我们只有顶点。 要创建一个 STL 文件,让我们描述面,这可以手动完成,或者提供 scipy 库中的 spatial.ConvexHull 函数的这种操作。
import numpy as np
from scipy import spatial
from stl import mesh
from myplot import plot_mesh
vertices = np.array(
[
[-3, -3, 0],
[+3, -3, 0],
[+3, +3, 0],
[-3, +3, 0],
[+0, +0, +3]
]
)
hull = spatial.ConvexHull(vertices)
faces = hull.simplices
结果,faces 数组包含了这个 faces 描述:
array([
[4, 1, 0],
[4, 2, 1],
[3, 4, 0],
[3, 4, 2],
[3, 2, 1],
[3, 1, 0]
], dtype=int32)
Faces - 面列表。 每个三角形面由三个顶点(点)描述。 换句话说,点在顶点数组中的位置。
例如,最后一个面包含数字 3、1、0。所以面与顶点数组的第 0、1 和 3 个元素的点组装在一起:
Mesh ——一组顶点和面,决定多面体物体的形状。
myramid_mesh = mesh.Mesh(
np.zeros(faces.shape[0], dtype=mesh.Mesh.dtype)
)
for i, f in enumerate(faces):
for j in range(3):
myramid_mesh.vectors[i][j] = vertices[f[j],:]
plot_mesh(myramid_mesh)
从图中可以看出,金字塔的一个面被颠倒了。 在下面的例子中,构造分形时,不会使用ConvexHull方法,因为它把面的点按任意顺序排列,这会导致一些面发生翻转。
myramid_mesh.save('numpy_stl_example_02.stl')
要查看 STL 文件,我使用免费软件程序:Blender。
spatial.convexhull 方法旨在计算凸壳,可以很好地处理金字塔和立方体。 但在有空腔的物体中,由于点数不一致,会丢失部分点,拼装STL时会出错。
这在二维示例中清晰可见:numpy_stl_example_03.ipynb
import matplotlib.pyplot as plt
from scipy import spatial
import numpy as np
points = np.array([
[0,0],
[-2,0],
[-2,2],
[0,1.5],
[2,2],
[2,0]
])
hull = spatial.ConvexHull(points)
hull.simplices 包含面描述:
array([
[2, 1],
[2, 4],
[5, 1],
[5, 4]
], dtype=int32)
让我们绘制顶点和面:
plt.plot(points[:,0], points[:,1], 'o')
for simplex in hull.simplices:
plt.plot(points[simplex, 0], points[simplex, 1], 'k-')
对于这种情况,你可以找到 convexhull 的替代方法,或手动描述边缘:
faces = np.array([
[0, 1],
[1, 2],
[2, 3],
[3, 4],
[4, 5],
[5, 0]
])
plt.plot(points[:,0], points[:,1], 'o')
for simplex in faces:
plt.plot(points[simplex, 0], points[simplex, 1], 'k-')
2、Numpy-stl构建分形
是时候建立一个分形了。 Numpy-stl 中没有布尔减法函数。 为了构建 Menger Sponge 分形,我们采用了相反的方法。 有两种方法:
- 构建一个基本的立方体网格。 我们称它为体素。
- 将多个体素组合成一个网格。
我们将从立方体构造一个分形,就像构造函数一样。
构造分形的逻辑说明:
Suppose the fractal face length is 1. Depth of fractal is the count of unique hole sizes. Voxel length depends on depth of the fractal, it is divided by 3 with each new level of depth.
We gonna find the voxel side at depths 1 and 2. Let's simplify the task, turning the fractal from 3 to 1-dimensional case:
If fractal level is 2, then the length of the cube side will be 1 / (3 ** 2) which is equivalent to 1/9. Let's make a set of cubes so that they filled resulting voxel cube by their location. Let's calculate holes area. Exclude voxels that are in holes. In conclusion, unite the remaining voxels in one object and save.
3、Numpy-stl渲染
为了渲染图像,我们将从 STL 文件加载的网格发送到 plot_mesh 函数。
BimAnt翻译整理,转载请标明出处