main
John Lauer Publish 0.2.15 bb17f84
#!/usr/bin/env python3
"""Fail-closed whole-board rectangular obstacle audit. Native CAD supplies the
transformed fitted-body/mask/hole envelopes and rendered text/leader bounds.
The inventory is independent of the obstacle export: missing geometry is an error.
"""
import argparse,json
from pathlib import Path

def overlaps(a,b):
 return a[0]<b[2] and b[0]<a[2] and a[1]<b[3] and b[1]<a[3]
def rects(item,what):
 # "box" | "boxes": dict.get(k, default) evaluates the default eagerly, so test the key.
 if 'boxes' in item:return item['boxes']
 if 'box' in item:return [item['box']]
 raise ValueError(f"{what} {item.get('id','?')!r} has neither 'box' nor 'boxes'")
def check(data):
 obs=data['obstacles'];inventory=data['inventory'];missing=[];collisions=[]
 present={(o['id'],o['kind'],o['layer']) for o in obs}
 for item in inventory:
  for layer in item['layers']:
   if (item['id'],item['kind'],layer) not in present:missing.append(dict(id=item['id'],kind=item['kind'],layer=layer))
 for label in data['marks']:
  for o in obs:
   if label['layer']==o['layer'] and any(overlaps(a,b) for a in rects(label,'mark') for b in rects(o,'obstacle')):collisions.append(dict(mark=label['id'],obstacle=o['id'],kind=o['kind'],layer=o['layer']))
 return dict(schemaVersion=1,passed=bool(inventory) and not missing and not collisions,coverageErrors=[] if inventory else ['Whole-board inventory is empty'],missingObstacles=missing,collisions=collisions,inventoryItems=len(inventory),obstacles=len(obs),marks=len(data['marks']),nativeVerified=False,note='Conservative rectangle audit only; geometry provenance, fitted transforms, label association and native 2D/3D review remain required.')
if __name__=='__main__':
 p=argparse.ArgumentParser();p.add_argument('--input',required=True,type=Path);p.add_argument('--out',required=True,type=Path);a=p.parse_args();result=check(json.loads(a.input.read_text()));a.out.write_text(json.dumps(result,indent=2));print(('OK' if result['passed'] else 'ERROR')+f": {len(result['coverageErrors'])} coverage errors; {len(result['missingObstacles'])} missing obstacles; {len(result['collisions'])} collisions");raise SystemExit(0 if result['passed'] else 2)