app
Fusion - the Fusion 360 Bridge
Public Made by Adomby adom
Drive Autodesk Fusion 360 from the cloud via Adom Bridge: component libraries, IPC package generation, board layout, exports (STEP/Gerbers/BOM/CPL), fast APS cloud search, and parametric modeling.
master
John Lauer
Publish thermal bottleneck planner, accepted candidate, analysis and native recording scripts
0049da5
29d ago
import sys,json,xml.etree.ElementTree as ET,math
from pathlib import Path
from shapely.geometry import LineString,Point
P=Path(__file__).resolve().parent;sys.path.insert(0,str(P.parent));from geometry import Geometry,LAYERS
sys.path.insert(0,str(P.parent/'thermal'));from plan_priority import detour,simple
m=Geometry();t=ET.parse(P/'candidate-01/filled.brd');b=t.find('drawing/board')
for s in b.findall('signals/signal'):
n=s.get('name')
for w in s.findall('wire'):
a=[float(w.get('x1')),float(w.get('y1'))];z=[float(w.get('x2')),float(w.get('y2'))]
m.copper.append((n,int(w.get('layer')),LineString([a,z]).buffer(float(w.get('width'))/2)))
for v in s.findall('via'):
a=[float(v.get('x')),float(v.get('y'))];dia=float(v.get('diameter'))
for l in LAYERS:m.copper.append((n,l,Point(a).buffer(dia/2)))
m.holes.append((a,float(v.get('drill'))/2))
net='SDA';s=next(s for s in b.findall('signals/signal') if s.get('name')==net)
line=LineString([(25.2,1.2),(22.2,4.2),(22.2,12.2),(19.2,15.2)])
starts=[];ends=[]
for distance in [.9,1.2,1.6,2.,2.5,3.,4.,5.,6.]:
for dest,at in [(starts,line.interpolate(distance)),(ends,line.interpolate(line.length-distance))]:
p=(at.x,at.y)
if m.via_clear(p,net):dest.append(p)
print('via sites',starts,ends,flush=True)
chosen=None
for a in starts[:3]:
for z in ends[:3]:
for l in [15,2,1]:
pts=detour(m,a,z,net,l,.15,limit=45000,step=.1)
print(a,z,l,pts,flush=True)
if pts:chosen=(a,z,l,pts);break
if chosen:break
if chosen:break
assert chosen,'No validated candidate path'
a,z,l,pts=chosen
# preserve original bottom path before/after the hop, including existing vertices.
coords=list(line.coords)
da=line.project(Point(a));dz=line.project(Point(z))
pre=[coords[0]]+[p for p in coords[1:-1] if line.project(Point(p))<da]+[a]
post=[z]+[p for p in coords[1:-1] if line.project(Point(p))>dz]+[coords[-1]]
removed=[dict(w.attrib) for w in s.findall('wire') if w.get('layer')=='16']
for w in list(s.findall('wire')):
if w.get('layer')=='16':s.remove(w)
for layer,points in [(16,pre),(l,pts),(16,post)]:
for x,y in zip(points,points[1:]):ET.SubElement(s,'wire',{'x1':str(x[0]),'y1':str(x[1]),'x2':str(y[0]),'y2':str(y[1]),'width':'.15','layer':str(layer)})
for p in [a,z]:ET.SubElement(s,'via',{'x':str(p[0]),'y':str(p[1]),'extent':'1-16','drill':'.2','diameter':'.45'})
for p in b.findall('signals/signal/polygonpour'):
for f in p.findall('polygonfilldetails'):p.remove(f)
out=P/'candidate-02';out.mkdir(exist_ok=False);t.write(out/'bq25792-thermal-reroute.brd',encoding='utf-8',xml_declaration=True)
r=json.loads((P/'candidate-01/plan.json').read_text());r.append({'net':'SDA','layer':l,'points':pts,'width':.15,'removed':removed,'newVias':[a,z],'bottomParts':[pre,post],'lengthMm':sum(LineString(p).length for p in [pre,pts,post])});(out/'plan.json').write_text(json.dumps(r,indent=2))