如何从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")
注意事项
- 顶点顺序调整:如果生成的模型出现"内部外翻"(显示透明或反面),说明顶点顺序不符合要求,可删除
faces = [face[::-1] for face in faces]这一行,部分STL文件的顶点顺序可能已经符合OpenSCAD要求。 - 精度处理:代码中对顶点坐标保留6位小数,避免浮点数精度差异导致的不必要顶点重复。
- 格式兼容性:两种方案均支持文本和二进制STL格式,无需额外判断。
内容的提问来源于stack exchange,提问作者Pazzel
相关产品推荐
相关产品推荐

