Files
ESP32_Serial_Swiss_Army_Knife/hardware/PCB/reference/db9-cad/extract.py
T

87 lines
3.5 KiB
Python

"""Read pinned STEP B-rep; write reproducible solid/face measurements beside it."""
import json
import re
from pathlib import Path
from OCP.STEPControl import STEPControl_Reader
from OCP.IFSelect import IFSelect_RetDone
from OCP.TopExp import TopExp_Explorer
from OCP.TopAbs import TopAbs_SOLID, TopAbs_FACE
from OCP.TopoDS import TopoDS
from OCP.Bnd import Bnd_Box
from OCP.BRepBndLib import BRepBndLib
from OCP.BRepAdaptor import BRepAdaptor_Surface
from OCP.GeomAbs import GeomAbs_Cylinder, GeomAbs_Plane
from OCP.BRepCheck import BRepCheck_Analyzer
HERE = Path(__file__).resolve().parent
def bounds(s):
b = Bnd_Box()
BRepBndLib.AddOptimal_s(s, b, False, False)
return list(b.Get())
def xyz(p):
return [p.X(), p.Y(), p.Z()]
r = STEPControl_Reader()
assert r.ReadFile(str(HERE / '5988 RS-232 Level Shifter.step')) == IFSelect_RetDone
r.TransferRoots()
entities = dict(re.findall(r'#(\d+)\s*=\s*(.*?);', (HERE / '5988 RS-232 Level Shifter.step').read_text(), re.S))
styles = {}
for text in entities.values():
if text.startswith('STYLED_ITEM('):
refs = re.findall(r'#(\d+)', text)
styles[refs[-1]] = refs[:-1]
def colors(refs, seen=None):
seen = set() if seen is None else seen
result = []
for ref in refs:
if ref in seen:
continue
seen.add(ref)
text = entities[ref]
if text.startswith('COLOUR_RGB('):
result.append(text.replace('\n', ''))
else:
result.extend(colors(re.findall(r'#(\d+)', text), seen))
return result
e = TopExp_Explorer(r.OneShape(), TopAbs_SOLID)
out = []
while e.More():
s = TopoDS.Solid_s(e.Current())
item = dict(id=len(out)+1, bounds=bounds(s), valid=BRepCheck_Analyzer(s).IsValid(), faces=[])
f = TopExp_Explorer(s, TopAbs_FACE)
while f.More():
face = TopoDS.Face_s(f.Current())
a = BRepAdaptor_Surface(face)
# These unique bodies retain CLOSED_SHELL face order on this pinned import.
shell_id = {1: '14428', 12: '14431', 13: '14432', 14: '14433'}.get(item['id'])
refs = re.findall(r'#(\d+)', entities[shell_id]) if shell_id else []
step_id = refs[len(item['faces'])] if refs else '0'
if refs:
surface_id = re.findall(r'#(\d+)', entities[step_id])[-1]
surface = entities[surface_id]
expected = str(a.GetType()).split('_')[-1].upper()
assert surface.startswith({'PLANE': 'PLANE(', 'CYLINDER': 'CYLINDRICAL_SURFACE(', 'CONE': 'CONICAL_SURFACE(', 'TORUS': 'TOROIDAL_SURFACE('}[expected])
if a.GetType() == GeomAbs_Cylinder:
assert abs(float(surface.rsplit(',', 1)[1].rstrip(')')) - a.Cylinder().Radius()) < 1e-8
row = dict(id=len(item['faces'])+1, bounds=bounds(face), type=str(a.GetType()), orientation=str(face.Orientation()))
row.update(step_id=step_id, colors=colors(styles.get(step_id, [])))
if a.GetType() == GeomAbs_Cylinder:
c = a.Cylinder()
row.update(radius=c.Radius(), location=xyz(c.Location()), axis=xyz(c.Axis().Direction()))
elif a.GetType() == GeomAbs_Plane:
p = a.Plane()
row.update(location=xyz(p.Location()), normal=xyz(p.Axis().Direction()))
item['faces'].append(row)
f.Next()
if shell_id:
assert len(item['faces']) == len(refs)
out.append(item)
e.Next()
(HERE / 'geometry.json').write_text(json.dumps(out, indent=2)+'\n')
for s in out:
print(s['id'], 'valid', s['valid'], 'bounds', [round(v,6) for v in s['bounds']], 'faces', len(s['faces']))