Files
orisys_arm/scripts/convert_step_mesh.py
T

62 lines
3.1 KiB
Python

#!/usr/bin/env python3
"""Offline STEP -> triangle cache for Qt OpenGL. No robot/ROS/device operations.
Requires gmsh (OpenCascade). Runtime GUI needs only numpy and the generated NPZ.
"""
import argparse
import hashlib
import json
from pathlib import Path
import gmsh
import numpy as np
def convert(source, output):
source=Path(source).resolve();output=Path(output).resolve()
gmsh.initialize(['step-converter','-nopopup'])
try:
gmsh.option.setNumber('General.Terminal',0)
gmsh.option.setString('Geometry.OCCTargetUnit','MM')
gmsh.model.add('gripper')
gmsh.model.occ.importShapes(str(source));gmsh.model.occ.synchronize()
gmsh.option.setNumber('Mesh.MeshSizeMin',.8)
gmsh.option.setNumber('Mesh.MeshSizeMax',2.)
gmsh.option.setNumber('Mesh.MeshSizeFromCurvature',18)
gmsh.option.setNumber('Mesh.ElementOrder',1)
gmsh.model.mesh.generate(2)
tags,coords,_=gmsh.model.mesh.getNodes()
order=np.argsort(tags);sorted_tags=tags[order]
nodes=np.asarray(coords).reshape(-1,3)[order]
parts=[];colors=[];labels=[]
surfaces=gmsh.model.getEntities(2)
for part,(_,tag) in enumerate(surfaces):
kinds,_,element_nodes=gmsh.model.mesh.getElements(2,tag)
r,g,b,a=gmsh.model.getColor(2,tag)
base=np.array([r,g,b],float)/255.
if np.max(base)-np.min(base)<.03: base=np.array([.76,.79,.83])
for kind,indices in zip(kinds,element_nodes):
if kind!=2: raise ValueError('Only first order triangles are supported')
indices=np.asarray(indices).reshape(-1,3)
triangles=nodes[np.searchsorted(sorted_tags,indices)]
parts.append(triangles/1000.)
colors.append(np.tile(base,(len(triangles),1)))
labels.append(np.full(len(triangles),part,np.int32))
triangles=np.concatenate(parts).astype(np.float32)
rgb=np.concatenate(colors).astype(np.float32)
if not np.isfinite(triangles).all() or not len(triangles):raise ValueError('Invalid mesh')
lo=triangles.min(axis=(0,1));hi=triangles.max(axis=(0,1))
output.parent.mkdir(parents=True,exist_ok=True)
np.savez_compressed(output,triangles=triangles,colors=rgb,surface=np.concatenate(labels))
info=dict(source=source.name,source_sha256=hashlib.sha256(source.read_bytes()).hexdigest(),
units='m',source_units='mm',triangles=len(triangles),surfaces=len(surfaces),
solids=len(gmsh.model.getEntities(3)),bounds_m=[lo.tolist(),hi.tolist()],
dimensions_mm=((hi-lo)*1000).tolist(),articulated=False,
note='Original assembled STEP pose; no inferred joints or live finger animation.')
output.with_suffix('.json').write_text(json.dumps(info,ensure_ascii=False,indent=2)+'\n')
print(json.dumps(info,ensure_ascii=False))
finally:gmsh.finalize()
if __name__=='__main__':
parser=argparse.ArgumentParser();parser.add_argument('source');parser.add_argument('output')
args=parser.parse_args();convert(args.source,args.output)