You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从STL文件提取OpenSCAD多面体格式的顶点与面?

实现STL到OpenSCAD Polyhedron格式的Python转换

核心思路

要完成转换,需要三个关键步骤:

  • 读取STL文件(支持文本/二进制格式),提取所有面的顶点信息
  • 对重复顶点去重,建立唯一顶点列表和顶点到索引的映射
  • 调整面的顶点顺序,确保从模型外部观察呈顺时针排列,再生成符合OpenSCAD格式的代码

方案一:使用numpy-stl快速实现

依赖numpy-stl库处理STL解析,无需手动处理复杂的文件格式:

安装依赖

pip install numpy-stl

完整代码

import numpy as np
from stl import mesh

def stl_to_openscad(stl_path, output_path=None):
    # 读取STL模型
    stl_mesh = mesh.Mesh.from_file(stl_path)
    
    # 提取所有顶点:将每个面的3个顶点展平,形状转为(n_faces*3, 3)
    all_vertices = stl_mesh.points.reshape(-1, 3)
    
    # 去重顶点,生成唯一顶点列表与索引映射
    unique_vertices, vertex_indices = np.unique(all_vertices, axis=0, return_inverse=True)
    # 将索引重组为每个面对应的3个顶点索引
    faces = vertex_indices.reshape(-1, 3).tolist()
    
    # 调整顶点顺序:STL默认采用右手定则(外部逆时针),转为OpenSCAD要求的顺时针
    faces = [face[::-1] for face in faces]
    
    # 生成OpenSCAD代码
    scad_code = "vertices = [\n"
    for vert in unique_vertices:
        scad_code += f"    [{vert[0]:.6f}, {vert[1]:.6f}, {vert[2]:.6f}],\n"
    scad_code = scad_code.rstrip(',\n') + "\n];\n\n"
    
    scad_code += "faces = [\n"
    for face in faces:
        scad_code += f"    [{', '.join(map(str, face))}],\n"
    scad_code = scad_code.rstrip(',\n') + "\n];\n\n"
    
    scad_code += "polyhedron(vertices=vertices, faces=faces);"
    
    # 保存到文件(可选)
    if output_path:
        with open(output_path, 'w', encoding='utf-8') as f:
            f.write(scad_code)
        print(f"转换完成,文件已保存至: {output_path}")
    
    return scad_code

# 使用示例
if __name__ == "__main__":
    stl_to_openscad("input_model.stl", "output_model.scad")

方案二:手动解析STL(无第三方库依赖)

如果不想使用外部库,可以手动处理文本/二进制STL格式:

完整代码

import struct

def _is_binary_stl(stl_path):
    # 判断是否为二进制STL:文本STL以"solid"开头
    with open(stl_path, 'rb') as f:
        header = f.read(5)
    return not header.startswith(b'solid')

def _parse_text_stl(stl_path):
    vertices = []
    face_verts = []
    with open(stl_path, 'r', encoding='utf-8') as f:
        for line in f:
            line = line.strip()
            if line.startswith('vertex'):
                x, y, z = map(float, line.split()[1:4])
                face_verts.append((round(x,6), round(y,6), round(z,6)))
            elif line.startswith('endloop'):
                vertices.append(face_verts)
                face_verts = []
    return vertices

def _parse_binary_stl(stl_path):
    vertices = []
    with open(stl_path, 'rb') as f:
        f.read(80)  # 跳过头部
        num_faces = struct.unpack('<I', f.read(4))[0]
        for _ in range(num_faces):
            f.read(12)  # 跳过法向量
            verts = []
            for _ in range(3):
                x, y, z = struct.unpack('<fff', f.read(12))
                verts.append((round(x,6), round(y,6), round(z,6)))
            f.read(2)  # 跳过属性
            vertices.append(verts)
    return vertices

def stl_to_openscad(stl_path, output_path=None):
    # 解析STL文件
    if _is_binary_stl(stl_path):
        face_vertices = _parse_binary_stl(stl_path)
    else:
        face_vertices = _parse_text_stl(stl_path)
    
    # 去重顶点,建立索引映射
    unique_verts = []
    vert_to_idx = {}
    faces = []
    for face in face_vertices:
        idx_face = []
        for vert in face:
            if vert not in vert_to_idx:
                vert_to_idx[vert] = len(unique_verts)
                unique_verts.append(list(vert))
            idx_face.append(vert_to_idx[vert])
        faces.append(idx_face)
    
    # 调整为外部顺时针顺序
    faces = [face[::-1] for face in faces]
    
    # 生成OpenSCAD代码
    scad_code = "vertices = [\n"
    for vert in unique_verts:
        scad_code += f"    [{vert[0]}, {vert[1]}, {vert[2]}],\n"
    scad_code = scad_code.rstrip(',\n') + "\n];\n\n"
    
    scad_code += "faces = [\n"
    for face in faces:
        scad_code += f"    [{', '.join(map(str, face))}],\n"
    scad_code = scad_code.rstrip(',\n') + "\n];\n\n"
    
    scad_code += "polyhedron(vertices=vertices, faces=faces);"
    
    if output_path:
        with open(output_path, 'w', encoding='utf-8') as f:
            f.write(scad_code)
        print(f"转换完成,文件已保存至: {output_path}")
    
    return scad_code

# 使用示例
if __name__ == "__main__":
    stl_to_openscad("input_model.stl", "output_model.scad")

注意事项

  1. 顶点顺序调整:如果生成的模型出现"内部外翻"(显示透明或反面),说明顶点顺序不符合要求,可删除faces = [face[::-1] for face in faces]这一行,部分STL文件的顶点顺序可能已经符合OpenSCAD要求。
  2. 精度处理:代码中对顶点坐标保留6位小数,避免浮点数精度差异导致的不必要顶点重复。
  3. 格式兼容性:两种方案均支持文本和二进制STL格式,无需额外判断。

内容的提问来源于stack exchange,提问作者Pazzel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 09:35:42