abse/smartwatch-flex-top-only
Three-panel flex smartwatch with two parallel R3.9 bends, all components flat-top, low-profile JST connector harnesses, split heartbeat optical windows and screw-retained PCB mounting. Modeled 39 × 50.5 × 14.5mm prototype; 100% routing, zero native DRC and shorts. Hardware qualification pending.
- Version
- 0.2.20
- License
- unset
- Stars
- 0
scripts/audit-pcb-component-meshes.py
"""Check separate populated components in the actual folded PCB mesh."""
from pathlib import Path
import hashlib,itertools,json,sys
import numpy as np,trimesh
root=Path(__file__).resolve().parent.parent
source=root/'dist/index/circuit.json';asset=root/(sys.argv[1] if len(sys.argv)>1 else 'mechanical/upright-pcb.glb')
c=json.loads(source.read_text());names={e['source_component_id']:e['name'] for e in c if e['type']=='source_component'}
components=sorted({names[e['source_component_id']] for e in c if e['type']=='pcb_component'},key=len,reverse=True)
s=trimesh.load(asset,force='scene');parts=[]
for node in s.graph.nodes_geometry:
owner=next((n for n in components if node==n or node.startswith(n+'_')),None)
if owner is None:continue
T,k=s.graph[node];m=s.geometry[k].copy();m.apply_transform(T);parts.append((owner,node,m))
failures=[];pairs=0
for (a,an,x),(b,bn,y) in itertools.combinations(parts,2):
if a==b or np.any(np.maximum(x.bounds[0]-y.bounds[1],y.bounds[0]-x.bounds[1])>=-.000005):continue
cm=trimesh.collision.CollisionManager();cm.add_object(an,x);pairs+=1
_,_,data=cm.in_collision_single(y,return_names=True,return_data=True)
for p in data:
if p.depth>.000005:failures.append({'a':a,'b':b,'meshA':an,'meshB':bn,'depthMM':float(p.depth),'pointMM':p.point.tolist()})
r={'status':'FAIL' if failures else 'PASS','sourceSha256':hashlib.sha256(source.read_bytes()).hexdigest(),'assetSha256':hashlib.sha256(asset.read_bytes()).hexdigest(),'modeledComponents':len({a for a,_,_ in parts}),'meshes':len(parts),'overlappingAabbPairsCheckedWithFCL':pairs,'failures':failures,'scope':'Exact FCL triangles between distinct populated PCB component CAD models after native folding. Internals of a single supplied part and PCB substrate/copper are excluded; substrate panel and peripheral checks are separate. Conservative replacement package envelopes are included. Nominal CAD, not measured production tolerance.'}
unmodeled=sorted(set(components)-{a for a,_,_ in parts})
assert unmodeled==['J2','J5','J6','L6','TP1','TP2','TP3','TP4','TP5','TP6'],unmodeled
r['nonBodyCopperOnlyComponents']=unmodeled
assert r['modeledComponents']==152,r
(root/'reports/pcb-component-meshes.json').write_text(json.dumps(r,indent=2)+'\n');print(json.dumps(r,indent=2));assert not failures