Files
OnebotCatalog/onebot-data/tools/step-to-stl.py

75 lines
3.1 KiB
Python

# -*- coding: utf-8 -*-
"""
step-to-stl.py —— 真实 STEP 批量转 STL 网格 (桌面软件窗口内 3D 预览用)
内核: OpenCASCADE 的 Python 绑定 (cadquery/OCP, 开源 LGPL)。
用途: 把 catalog\\step\\ 里的真实数模 (体积 > 20KB 的 .step/.stp, 占位几何 ~7KB 自动跳过)
转成 catalog\\mesh\\ 下同名 .stl 网格。桌面 3D 标签页发现有网格就在窗口内直渲。
断点续跑: 同名 STL 已存在且不比 STEP 旧 → 跳过。
以后新增真实 STEP 后重跑本脚本即可 (或用 NX journal 的 exportStl 自动导出)。
用法: python step-to-stl.py
"""
import os
import sys
BASE = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
STEP_DIR = os.path.join(BASE, 'catalog', 'step')
MESH_DIR = os.path.join(BASE, 'catalog', 'mesh')
REAL_MIN_BYTES = 20 * 1024 # 20KB 以上算真实数模 (占位几何 ~7KB)
LIN_DEFLECTION = 1.5 # 网格线性偏差 (mm): 越小越精细、文件越大 (1.5mm 屏幕预览足够, 保证流畅旋转)
def convert(step_path, stl_path):
from cadquery.occ_impl.shapes import Shape
from OCP.STEPControl import STEPControl_Reader
from OCP.StlAPI import StlAPI_Writer
from OCP.BRepMesh import BRepMesh_IncrementalMesh
from OCP.IFSelect import IFSelect_RetDone
reader = STEPControl_Reader()
if reader.ReadFile(str(step_path)) != IFSelect_RetDone:
raise RuntimeError('STEP 读取失败: %s' % step_path)
reader.TransferRoots()
shape = Shape(reader.OneShape())
BRepMesh_IncrementalMesh(shape.wrapped, LIN_DEFLECTION, False, 0.5, True).Perform()
writer = StlAPI_Writer()
writer.SetASCIIMode(False) # 二进制 STL (比 ASCII 小 ~3 倍)
if not writer.Write(shape.wrapped, str(stl_path)):
raise RuntimeError('STL 写出失败: %s' % stl_path)
def main():
if not os.path.isdir(MESH_DIR):
os.makedirs(MESH_DIR)
step_files = [f for f in os.listdir(STEP_DIR)
if f.lower().endswith(('.step', '.stp'))]
done = skip = 0
errors = []
for name in sorted(step_files):
step_path = os.path.join(STEP_DIR, name)
if os.path.getsize(step_path) < REAL_MIN_BYTES:
continue # 占位几何, 不转
stl_path = os.path.join(MESH_DIR, os.path.splitext(name)[0] + '.stl')
# 跳过条件: STL 存在、比 STEP 新、且不是占位网格 (<50KB 视为占位, 强制重转)
if (os.path.exists(stl_path) and os.path.getsize(stl_path) > 50 * 1024
and os.path.getmtime(stl_path) >= os.path.getmtime(step_path)):
skip += 1
continue
try:
convert(step_path, stl_path)
done += 1
print(' OK %-24s -> %s (%.1f KB)' % (name, os.path.basename(stl_path),
os.path.getsize(stl_path) / 1024.0))
except Exception as e:
errors.append('%s: %s' % (name, e))
print('FAIL %s: %s' % (name, e))
print('完成: 转换 %d, 跳过 %d, 失败 %d%s' % (done, skip, len(errors), MESH_DIR))
if errors:
sys.exit(1)
if __name__ == '__main__':
main()