app
Codex
Public Made by Adomby adom
Codex in Adom Hydrogen: ecosystem setup, dock dashboard and live engineering demos.
main
John Lauer
Corrected Astra ESC routing: native zero-open result, full accounting and voiceover handoff
a80479d
23d ago
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
from planner import *
import planner as m
# Component-aware search can start and end on copper on either layer.
def components(net):
ps=by[net];nodes=[dict(g=p['g'],zs=[0,1] if p['type']=='thru_hole' else [0],pad=p) for p in ps]+[dict(c) for c in m.copper if c['net']==net]
par=list(range(len(nodes)))
def find(i):
while par[i]!=i:par[i]=par[par[i]];i=par[i]
return i
tree=shapely.STRtree([c['g'] for c in nodes])
for i,c in enumerate(nodes):
for j in tree.query(c['g'],predicate='dwithin',distance=.004999):
if j<i and set(c['zs'])&set(nodes[j]['zs']):par[find(i)]=find(int(j))
out=defaultdict(list)
for i,c in enumerate(nodes):out[find(i)].append(c)
return [v for v in out.values() if any('pad' in c for c in v)]
def register2(net,points,width):
z=1 if isinstance(points[0],dict) and points[0].get('layer')=='B.Cu' else 0
for p,q in zip(points,points[1:]):
zz=q.get('layer','B.Cu' if z else 'F.Cu') if isinstance(q,dict) else ('B.Cu' if z else 'F.Cu')
pos=lambda v:(v['x'],v['y']) if isinstance(v,dict) else v
a,b=pos(p),pos(q)
if a!=b:m.copper.append(dict(net=net,g=LineString([a,b]).buffer(width/2),zs=[z]))
if isinstance(q,dict) and zz!=('B.Cu' if z else 'F.Cu'):
m.copper.append(dict(net=net,g=Point(*b).buffer(.4),zs=[0,1]));z=1 if zz=='B.Cu' else 0
def search_components(net,width,source,target):
a=masks(net,width);start=np.zeros((2,H,W),dtype=np.uint8);goal=np.zeros_like(start)
for collection,arr in [(source,start),(target,goal)]:
for c in collection:
for z in c['zs']:raster(arr[z],c['g'].buffer(width/2-.005))
arr[a[:2]>0]=0
ids=np.flatnonzero(start)
if not len(ids) or not np.any(goal):return None
g=unary_union([c['g'] for c in target]);t=g.centroid;tx,ty=ij(t.x,t.y)
a.tofile('mask.bin');goal.tofile('goal.bin')
r=subprocess.run(['./search2'],input=f'{W} {H} 0 {tx} {ty} {len(ids)} '+' '.join(map(str,ids)),capture_output=True,text=True)
raw=json.loads(r.stdout)
if not raw:return None
out=[];curz=0
for i,j,z in compress(raw):
x,y=xy(i,j)
if z!=curz or (not out and z):out.append(dict(x=x,y=y,layer='B.Cu' if z else 'F.Cu'))
elif not out or [x,y]!=out[-1]:out.append([x,y])
curz=z
return out
def route_components(plan):
net=plan['net'];width=plan['width'];cs=components(net)
while len(cs)>1:
pairs=sorted((unary_union([c['g'] for c in x]).distance(unary_union([c['g'] for c in y])),i,j) for i,x in enumerate(cs) for j,y in enumerate(cs[:i]))
found=False
for _,i,j in pairs:
route=search_components(net,width,cs[i],cs[j])
if route:
register2(net,route,width);plan['paths'].append(route);found=True;break
if not found:break
cs=components(net)
return len(cs)-1
def load(plans):
m.copper.clear()
for p in plans:
for path in p['paths']:register2(p['net'],path,p['width'])
if __name__=='__main__':
import sys
plans=json.load(open(sys.argv[1]));load(plans)
for plan in plans:
if plan['net'] in ['GND','+3V3']:continue
gaps=route_components(plan);print(plan['net'],gaps,flush=True)
Path('component-repaired.json').write_text(json.dumps(plans,indent=2))