rushabhcodes/fidget-counter
A tscircuit package created by rushabhcodes.
- Version
- 1.0.31
- License
- unset
- Stars
- 0
validation/check-cr2450-copper.py
"""Bound regenerated GND pours to changed battery/mounting regions."""
from pathlib import Path
import copy,hashlib,json,subprocess
from shapely.geometry import Polygon,Point,box,LineString
from shapely.ops import unary_union
if __name__=='__main__'and(Path(__file__).resolve().parents[1]/'validation/fresh-routing-reference.json').exists():
root=Path(__file__).resolve().parents[1]
subprocess.check_call(['python','validation/check-npth-copper.py'],cwd=root)
report=json.loads((root/'validation/npth-copper-audit.json').read_text())
report['scope']='Authorized fresh routing supersedes historical battery-only copper; strict full copper/electrical signature and actual NPTH clearance checked'
report['source_sha256']['validation/check-cr2450-copper.py']=hashlib.sha256(Path(__file__).read_bytes()).hexdigest()
(root/'validation/cr2450-copper-audit.json').write_text(json.dumps(report,indent=2)+'\n')
raise SystemExit(0)
R=Path(__file__).resolve().parents[1];COMMIT='346fade26c8934f9d0b6411d7e30c1c9b06e9dac'
old=json.loads(subprocess.check_output(['git','show',COMMIT+':published.circuit.json'],cwd=R));current=json.loads((R/'published.circuit.json').read_text())
def polygon(e):
b=e['brep_shape'];return Polygon([(v['x'],v['y'])for v in b['outer_ring']['vertices']], [[(v['x'],v['y'])for v in h['vertices']]for h in b.get('inner_rings',[])]).buffer(0)
def regions(c):
out={};nets={e['source_net_id']:e['name']for e in c if e['type']=='source_net'}
for e in c:
if e['type']!='pcb_copper_pour':continue
assert e['shape']=='brep'and nets[e['source_net_id']]=='GND'and e['covered_with_solder_mask'],'Unexpected ground-pour net/shape/mask'
out.setdefault(e['layer'],[]).append(polygon(e))
return {k:unary_union(v)for k,v in out.items()}
masks=[]
for c in [old,current]:
for h in c:
if h['type']=='pcb_hole':masks.append(Point(h['x'],h['y']).buffer(2.2))
sid=next(e['source_component_id']for e in c if e['type']=='source_component'and e['name']=='BT1');pid=next(e['pcb_component_id']for e in c if e['type']=='pcb_component'and e.get('source_component_id')==sid)
for p in c:
if p['type']=='pcb_smtpad'and p['pcb_component_id']==pid:masks.append(box(p['x']-p['width']/2,p['y']-p['height']/2,p['x']+p['width']/2,p['y']+p['height']/2).buffer(.6))
masks += [LineString([(-15.550007,0),(-12.9,.1)]).buffer(.6),Point(15.550007,-3).buffer(.6),Point(10,-3).buffer(.6)]
# Frozen published 1.0.27 contains a small historical pour lead-out change
# beside the old positive land. Bound that exact region, not all nearby copper.
reference=json.loads(subprocess.check_output(['git','show','a7c29c470c439e6f4e35314b9c68bba5c8d8c100:published.circuit.json'],cwd=R))
before=regions(old);historical=regions(reference)
leadout=before['bottom'].symmetric_difference(historical['bottom']).difference(unary_union(masks))
assert leadout.area<.235 and box(-12.426,2.499,-11.979,3.332).covers(leadout),'Unexpected historical holder lead-out footprint'
allowed=unary_union(masks+[leadout])
def verify(candidate):
after=regions(candidate);assert set(after)==set(before)=={'top','bottom'},'Ground-pour layer changed'
result={}
for layer in before:
delta=before[layer].symmetric_difference(after[layer]);unexplained=delta.difference(allowed).area
assert unexplained<1e-6,(layer,'Unrelated ground copper changed',unexplained)
result[layer]={'changed_area_mm2':delta.area,'unexplained_changed_area_mm2':unexplained}
return result
result=verify(current);negative=[]
for name in ['unrelated_copper_cutout','layer_change','mask_change']:
bad=copy.deepcopy(current);top=next(e for e in bad if e['type']=='pcb_copper_pour'and e['layer']=='top'and polygon(e).contains(Point(0,-17).buffer(.3)))
if name=='unrelated_copper_cutout':top['brep_shape'].setdefault('inner_rings',[]).append({'vertices':[{'x':x,'y':y}for x,y in Point(0,-17).buffer(.3).exterior.coords[:-1]]})
elif name=='layer_change':top['layer']='changed-layer'
else:top['covered_with_solder_mask']=False
try:verify(bad)
except AssertionError:negative.append(name)
else:raise AssertionError('Negative copper safeguard did not fail: '+name)
files=[Path(__file__),R/'published.circuit.json',R/'index.circuit.tsx',R/'parts.tsx',R/'routed-paths.json',R/'routing-contract.json']
report={'passed':True,'comparison_commit':COMMIT,'scope':'Union of actual BREP copper regions per layer/net, independent of generated island IDs','permitted_changes':'Old/new four mounting drills/keepouts, holder lands, positive entry segment, exact frozen historical positive-land lead-out (0.235 mm2 maximum), and two added GND vias','layer_results':result,'negative_checks_passed':negative,'source_sha256':{str(f.relative_to(R)):hashlib.sha256(f.read_bytes()).hexdigest()for f in files}}
(R/'validation/cr2450-copper-audit.json').write_text(json.dumps(report,indent=2)+'\n');print('PASS: regenerated top/bottom GND copper changes only in specified battery/mounting regions;3 real copper/layer/mask negative checks fail as required')