app
AI Flow
Public Made by Adomby adom
Adom's AI Flow: a tool to help the AI follow all of the steps it takes to build a board.
main
John Lauer
Publish 0.1.26
0b71841
import importlib.util
from pathlib import Path
sp=importlib.util.spec_from_file_location('silk',Path(__file__).with_name('silkscreen-layout.py'));m=importlib.util.module_from_spec(sp);sp.loader.exec_module(m)
def c(x,font):return dict(box=[x,0,x+1,1],position=[x+.5,.5],font=font)
# The first label's locally best choice blocks the next; beam must revisit it.
s={'bounds':[-1,-1,6,3],'clearance':0,'labels':[{'id':'a','anchor':[.5,.5],'candidates':[c(0,.8),c(2,.7)]},{'id':'b','anchor':[.5,.5],'candidates':[c(0,.8),c(0,.7)]}]}
r=m.solve(s);assert not r['unresolved'];assert next(x for x in r['placements'] if x['id']=='a')['position'][0]==2.5
s['obstacles']=[{'box':[-1,-1,6,3]}];assert len(m.solve(s)['unresolved'])==2
print('PASS: revises earlier choices, prefers larger text and reports blocked labels')
# A grouped reference/value must not reserve the empty middle between their boxes.
s={'bounds':[-1,-1,6,3],'clearance':0,'obstacles':[{'box':[1.5,0,2.5,1]}],'labels':[{'id':'pair','anchor':[1.5,.5],'candidates':[dict(box=[0,0,4,1],boxes=[[0,0,1,1],[3,0,4,1]],position=[1.5,.5],font=.6)]}]}
assert not m.solve(s)['unresolved']
# Preserve connector row order even if a larger candidate would swap labels.
s={'bounds':[-2,-2,6,6],'clearance':0,'labels':[{'id':'p1','orderGroup':'left','order':1,'anchor':[0,0],'candidates':[dict(box=[0,0,1,1],position=[.5,.5],font=.6)]},{'id':'p3','orderGroup':'left','order':3,'anchor':[0,2],'candidates':[dict(box=[0,-2,1,-1],position=[.5,-1.5],font=.8),dict(box=[0,2,1,3],position=[.5,2.5],font=.5)]}]}
r=m.solve(s);assert next(x for x in r['placements'] if x['id']=='p3')['font']==.5
print('PASS: compound bounds retain empty space and pin rows stay ordered')
# Optional MILP must trade a larger label for two readable labels rather than a tiny fallback.
s={'solver':'milp','bounds':[-1,-1,6,3],'clearance':0,'preferredMinimumFont':.3,'smallFontPenalty':1000,'distanceWeight':0,'labels':[{'id':'a','anchor':[0,0],'candidates':[c(0,.6),c(2,.4)]},{'id':'b','anchor':[0,0],'candidates':[c(0,.4),c(4,.2)]}]}
r=m.solve(s);assert not r['unresolved'];assert all(x['font']>=.3 for x in r['placements']);print('PASS: MILP avoids a tiny fallback when repacking fits both labels')
# Missing values and equal-size values are invalid even if their score is higher.
g={'id':'C1','anchor':[0,0],'requiredRoles':['reference','value'],'maxValueRatio':.8,'candidates':[]}
for roles,font in [([{'role':'reference','size':.8}],.9),([{'role':'reference','size':.4},{'role':'value','size':.4}],.8),([{'role':'reference','size':.4},{'role':'value','size':.25}],.4)]:
x=c(0,font);x['children']=roles;g['candidates'].append(x)
r=m.solve({'bounds':[-1,-1,6,3],'labels':[g]});assert not r['unresolved'];assert r['placements'][0]['font']==.4
print('PASS: required pair roles and reference/value hierarchy cannot be traded for score')