首次提交: OnebotCatalog 项目代码与文档(含 NX 按需生成服务二期、后台、一键启动)
This commit is contained in:
74
onebot-data/tools/step-to-stl.py
Normal file
74
onebot-data/tools/step-to-stl.py
Normal file
@@ -0,0 +1,74 @@
|
||||
# -*- 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()
|
||||
Reference in New Issue
Block a user