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-optical-paths.py

from pathlib import Path
import json,numpy as np,trimesh
root=Path(__file__).resolve().parent.parent
cx=-.02509520000035;cy=-8.99159126281738
opaque=[]
for f in ['case-body','optical-light-seal']:
    s=trimesh.load(root/f'mechanical/{f}.glb',force='scene')
    for n in s.graph.nodes_geometry:
        T,k=s.graph[n];m=s.geometry[k].copy();m.apply_transform(T);opaque.append((f+'/'+n,m))

def intersections(mesh,x,y):
    tri=mesh.triangles;a=tri[:,0];u=tri[:,1]-a;v=tri[:,2]-a
    det=u[:,0]*v[:,1]-u[:,1]*v[:,0];keep=np.abs(det)>1e-10
    a=a[keep];u=u[keep];v=v[keep];det=det[keep]
    dx=x-a[:,0];dy=y-a[:,1];b=(dx*v[:,1]-dy*v[:,0])/det;c=(u[:,0]*dy-u[:,1]*dx)/det
    keep=(b>=-1e-8)&(c>=-1e-8)&(b+c<=1+1e-8)
    z=a[keep,2]+b[keep]*u[keep,2]+c[keep]*v[keep,2]
    zs=sorted(float(z) for z in z);unique=[]
    for z in zs:
        if not unique or z-unique[-1]>1e-5:unique.append(z)
    return unique

points=[]
for name,x,y in [('LED0',-2.55,-.875),('LED1',-1.675,0),('LED2',-2.55,.875)]:
    for dx,dy in [(0,0),(-.2,-.2),(-.2,.2),(.2,-.2),(.2,.2)]:points.append((name,cx+x+dx,cy+y+dy))
for x in np.linspace(-.3,3.1,5):
    for y in np.linspace(-1.4,1.4,5):points.append(('PHOTODIODE_APERTURE',cx+x,cy+y))
blocked=[]
for name,x,y in points:
    for body,m in opaque:
        hits=[z for z in intersections(m,x,y) if -9.41999<z<-8.41004]
        if hits:blocked.append({'field':name,'xyMM':[x,y],'body':body,'hitZMM':hits})
assert not blocked,blocked[:3]
# The opaque divider spans from the external floor up to the gasket face.
intervals=[]
for body,m in opaque:
    zs=intersections(m,cx-.675,cy)
    assert len(zs)%2==0,(body,zs)
    intervals.extend(zip(zs[::2],zs[1::2]))
intervals.sort();end=-9.42
for lo,hi in intervals:
    if hi<end:continue
    assert lo<=end+.000005,('Gap in black optical divider',end,lo)
    end=max(end,hi)
assert end>=-8.410025,end
out={'status':'PASS','raysChecked':len(points),'blockedOpticalRays':blocked,
    'continuousOpaqueDividerFromZMM':-9.42,'continuousOpaqueDividerToZMM':end,
    'method':'Exact triangle intersections along skin-facing optical rays through three LED fields and a 5x5 photodiode aperture grid; exclude transparent inserts. Check solid vertical intervals along the black divider through case and gasket.',
    'qualification':'Geometric optical clearance and baffle continuity only; sensor signal quality, material transmission and seal compression need physical verification.'}
(root/'reports/optical-paths.json').write_text(json.dumps(out,indent=2)+'\n');print(json.dumps(out,indent=2))