沿3D路径在Python中扫掠形状

0 投票
1 回答
54 浏览
提问于 2025-04-12 02:19

使用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.3Polygon特性给出的:

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中渲染的网格。形状的倾斜在右侧上升时清晰可见。

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()

这段代码的运行结果是

这里输入图片描述

你可以放大、缩小、旋转等等。商店的边缘比较尖锐,但我觉得如果你提供了正确的数据,平滑的边缘也能很好地工作。

撰写回答