沿3D路径在Python中扫掠形状
使用Python(写这段内容时是3.10.14),我们想要创建一个3D网格对象(可以保存为STL、PLY或GLB/GLTF格式),方法是:
- 用一个3D路径作为扫掠轴,
- 用一个2D矩形形状。
需要满足以下条件:
- 这个3D路径是真正的3D路径,意味着每个坐标在空间中都是变化的;它不局限于一个平面。
- 矩形形状的上边和下边必须始终保持水平(这意味着在沿着3D轴扫掠时,形状不会倾斜,也就是没有旋转)。
- 3D路径始终垂直穿过矩形的中心。
?
我们可以把3D轨迹看作是由直线段组成的(没有曲线)。这意味着3D轴的两个线段在一个角度相交,也就是说在这个点的导数是不连续的。生成的3D网格在这些位置上不应该有孔。因此,“3D连接样式”应该根据给定的封闭样式来确定(例如,关于2D的描述可以在这里找到)。
3D路径以numpy 3D数组的形式给出,如下所示:
import numpy as np
path = np.array([
[ 5.6, 10.1, 3.3],
[ 5.6, 12.4, 9.7],
[10.2, 27.7, 17.1],
[25.3, 34.5, 19.2],
[55. , 28.3, 18.9],
[80.3, 24.5, 15.4]
])
2D矩形形状则是通过Shapely 2.0.3的Polygon特性给出的:
from shapely.geometry import Polygon
polygon = Polygon([[0, 0],[1.2, 0], [1.2, 0.8], [0, 0.8], [0, 0]])
我目前的进展
我正在尝试使用Trimesh 4.2.3(同时有Numpy 1.26.4可用),通过使用sweep_polygon
,但没有成功,因为每次矩形形状需要改变方向时,它也会围绕一个垂直于由两个边在那个顶点相交所定义的平面旋转,这违反了上面提到的第二个条件。
import numpy as np
from shapely.geometry import Polygon
from trimesh.creation import sweep_polygon
polygon = Polygon([[0, 0],[1.2, 0], [1.2, 0.8], [0, 0.8], [0, 0]])
path = np.array([
[ 5.6, 10.1, 3.3],
[ 5.6, 12.4, 9.7],
[10.2, 27.7, 17.1],
[25.3, 34.5, 19.2],
[55. , 28.3, 18.9],
[80.3, 24.5, 15.4]
])
mesh = sweep_polygon(polygon, path)
此外,sweep_polygon
的文档中提到:
不太适合处理尖锐的曲率。
这有点模糊。
在meshlab中渲染的网格。形状的倾斜在右侧上升时清晰可见。
最终目标是在无头服务器上的Docker容器中运行这个。
1 个回答
1
有趣!我一直在处理这种关于商店数字双胞胎的问题。这里有一段我一直在用的代码,稍微调整了一下,适合你使用。我是在Jupyter Notebook里运行的,所以如果你用的是图形界面,可能需要做一些额外的工作:
import numpy as np
from shapely.geometry import Polygon
import trimesh
path = np.array([
[5.6, 10.1, 3.3],
[5.6, 12.4, 9.7],
[10.2, 27.7, 17.1],
[25.3, 34.5, 19.2],
[55.0, 28.3, 18.9],
[80.3, 24.5, 15.4]
])
rect_width = 1.2
rect_height = 0.8
def generate_mesh_vertices(path, width, height):
vertices = []
for point in path:
vertices.append([point[0] - width / 2, point[1] - height / 2, point[2]])
vertices.append([point[0] + width / 2, point[1] - height / 2, point[2]])
vertices.append([point[0] + width / 2, point[1] + height / 2, point[2]])
vertices.append([point[0] - width / 2, point[1] + height / 2, point[2]])
return np.array(vertices)
def generate_faces_for_path(num_path_points):
faces = []
for i in range(num_path_points - 1):
base_index = i * 4
faces += [
[base_index, base_index + 4, base_index + 1],
[base_index + 1, base_index + 4, base_index + 5],
[base_index + 1, base_index + 5, base_index + 2],
[base_index + 2, base_index + 5, base_index + 6],
[base_index + 2, base_index + 6, base_index + 3],
[base_index + 3, base_index + 6, base_index + 7],
[base_index + 3, base_index + 7, base_index],
[base_index, base_index + 7, base_index + 4]
]
return np.array(faces)
vertices = generate_mesh_vertices(path, rect_width, rect_height)
faces = generate_faces_for_path(len(path))
mesh = trimesh.Trimesh(vertices=vertices, faces=faces)
mesh.export('path_mesh.stl')
import matplotlib.pyplot as plt
scene = trimesh.Scene(mesh)
scene.show()
这段代码的运行结果是
你可以放大、缩小、旋转等等。商店的边缘比较尖锐,但我觉得如果你提供了正确的数据,平滑的边缘也能很好地工作。