main
John Lauer Add local values, both-face contact leaders, J1 per-pin labels and MOSFET pin1 markers 181f6be 20d ago
from pathlib import Path
import json,re,uuid,math,sys
sys.path.insert(0,'/home/adom/aiflow-esc-astra');import copper_geometry as c
D=Path(__file__).parent;s=(D/'esc-g431-astra-silkscreen-v16.kicad_pcb').read_text();root=c.parse(D/'esc-g431-astra-silkscreen-v16.kicad_pcb');m=json.loads((D/'labels.json').read_text());old=[l for l in m['labels'] if l.get('reference','').startswith(('MC','MP'))];m['labels']=[l for l in m['labels'] if l not in old]
# Remove only top-level contact text; retain board geometry and supplementary tables.
def spans(s):
 depth=0;quoted=False;escape=False;start=0
 for i,ch in enumerate(s):
  if quoted:
   if escape:escape=False
   elif ch=='\\':escape=True
   elif ch=='"':quoted=False
  elif ch=='"':quoted=True
  elif ch=='(':
   depth+=1
   if depth==2:start=i
  elif ch==')':
   if depth==2:yield start,i+1
   depth-=1
for a,b in reversed(list(spans(s))):
 if re.match(r'\(gr_text "(?:MC|MP)\d+\\n',s[a:b]):s=s[:a]+s[b:]
new=[];leaders=[]
def uid(v):return str(uuid.uuid5(uuid.NAMESPACE_URL,'contact-positional-v17:'+v))
def text(t,x,y,size,angle,side,ref,role):
 mirror=' (justify mirror)' if side=='B.SilkS' else ''
 new.append(f'(gr_text {json.dumps(t)} (at {x:.4f} {y:.4f} {angle}) (layer "{side}") (uuid "{uid(ref+side+role)}") (effects (font (size {size} {size}) (thickness {size*.15})){mirror}))')
 m['labels'].append(dict(text=t,x=x,y=y,size=size,angle=angle,layer=side,reference=ref,role=role,source=ref+' actual pad net'))
for f in c.sub(root,'footprint'):
 props={x[1]:x[2] for x in c.sub(f,'property')};ref=props['Reference']
 if not ref.startswith(('MC','MP')):continue
 at=c.one(f,'at');px,py=map(float,at[:2]);l=next(l for l in old if l['reference']==ref);func=l['text'].split('\n')[1];bank=ref.startswith('MC') and px==108
 for side in ['F.SilkS','B.SilkS']:
  if bank:
   x,y,angle=105.5,py,0;rs,fs=.4,.25
  else:x,y,angle=l['x'],l['y'],l.get('angle',0);rs,fs=.6,.35
  # Local reading direction offset; KiCad y-down rotation.
  a=math.radians(-angle)
  for t,dy,size,role in [(ref,-.3,rs,'contact reference'),(func,.27,fs,'contact function')]:
   text(t,x-dy*math.sin(a),y+dy*math.cos(a),size,angle,side,ref,role)
  if bank:
   # Gentle small arc runs from the paired label to outside the contact mask.
   pts=((106.35,py+.20),(106.72,py+.18),(107.02,py))
   new.append(f'(gr_arc (start {pts[0][0]} {pts[0][1]}) (mid {pts[1][0]} {pts[1][1]}) (end {pts[2][0]} {pts[2][1]}) (stroke (width 0.06) (type default)) (layer "{side}") (uuid "{uid(ref+side+"leader")}"))')
   leaders.append(dict(reference=ref,layer=side,start=pts[0],mid=pts[1],end=pts[2],width=.06))
out=D/'esc-g431-astra-silkscreen-v17.kicad_pcb';out.write_text(s.rstrip()[:-1]+'\n'+'\n'.join(new)+'\n)\n');m['board']=str(out);m['contactLeaders']=leaders;m['notes'].append('v17 pairs independently sized contact reference/function on both faces; contact table supplementary only.');(D/'labels-v17.json').write_text(json.dumps(m,indent=2));print('paired contacts',len(old),'curved leaders',len(leaders))