Patrick Schurig 66aa71c990
All checks were successful
Build & Deploy docs / build-deploy (push) Successful in 2m53s
mise a jour
2026-06-06 09:51:43 +02:00

95 lines
4.5 KiB
Python

# Assembleur libre : placement (x,y) + liens typés (puissance/can/rs485/eth) entre ports nommés.
import xml.etree.ElementTree as ET, json, sys
SVG="http://www.w3.org/2000/svg";INK="http://www.inkscape.org/namespaces/inkscape"
ET.register_namespace("",SVG);ET.register_namespace("inkscape",INK)
def lname(t):return t.split('}')[-1]
STYLE={
"power":{"stroke":"#0d2b3b","stroke-width":"1.8"},
"can" :{"stroke":"#31a3dd","stroke-width":"1.4","stroke-dasharray":"3 2"},
"rs485":{"stroke":"#e69500","stroke-width":"1.4","stroke-dasharray":"3 2"},
"eth" :{"stroke":"#00b4d8","stroke-width":"1.4","stroke-dasharray":"3 2"},
"relais":{"stroke":"#caa000","stroke-width":"1.3","stroke-dasharray":"1 1.5"},
"pwr" :{"stroke":"#e6194b","stroke-width":"1.6"},
"pe" :{"stroke":"#00ff00","stroke-width":"1.6"},
}
LBL={"can":"CAN","rs485":"RS485","eth":"ETH"}
def ports(root):
d={}
for el in root.iter():
l=el.get(f"{{{INK}}}label")
if l and l.startswith("term:") and lname(el.tag)=="circle":
d[l[5:]]=(float(el.get("cx")),float(el.get("cy")))
return d
def inject(root,disp,rep):
for el in root.iter():
l=el.get(f"{{{INK}}}label")
if not l or not l.startswith("lbl:"):continue
k=l[4:]
if k=="repere" and rep is not None:el.text=rep
elif k in disp:el.text=disp[k]
def vb(root):v=[float(x) for x in root.get("viewBox").split()];return v[2],v[3]
scene=json.load(open(sys.argv[1]))
W=scene.get("w",520);H=scene.get("h",420)
master=ET.Element(f"{{{SVG}}}svg",{"viewBox":f"0 0 {W} {H}"})
ET.SubElement(master,f"{{{SVG}}}rect",{"x":"0","y":"0","width":str(W),"height":str(H),"fill":"#fff"})
links_g=ET.SubElement(master,f"{{{SVG}}}g")
P={}; SIDE={}
for c in scene["blocks"]:
r=ET.parse(c["block"]).getroot();w,h=vb(r)
ox,oy=c["at"]
inject(r,c.get("display",{}),c.get("repere"))
r.set("x",str(ox));r.set("y",str(oy));r.set("width",str(w));r.set("height",str(h));r.set("overflow","visible")
master.append(r)
vbw,vbh=vb(r)
for k,(px,py) in ports(r).items():
P[f'{c["id"]}.{k}']=(ox+px,oy+py)
if px<=6: SIDE[f'{c["id"]}.{k}']="L"
elif px>=vbw-6: SIDE[f'{c["id"]}.{k}']="R"
elif py<=6: SIDE[f'{c["id"]}.{k}']="T"
else: SIDE[f'{c["id"]}.{k}']="B"
def elbow(p1,p2,horizfirst):
x1,y1=p1;x2,y2=p2
if abs(x1-x2)<0.6 or abs(y1-y2)<0.6: return [p1,p2]
return [p1,(x2,y1),p2] if horizfirst else [p1,(x1,y2),p2]
for w in scene["links"]:
typ=w.get("type","power")
a=P[w["from"]]
if w["to"].endswith(".RAIL"): # cible = barrette rail
bid=w["to"][:-5]
L=P[f"{bid}.RAIL_L"]; R=P[f"{bid}.RAIL_R"]
ry=L[1]; ex=min(max(a[0],L[0]),R[0]) # tombe à l'aplomb, borné à la barre
b=(ex,ry); pts=[a,(a[0],ry)] if abs(a[0]-ex)<0.6 else [a,(a[0],ry),(ex,ry)]
# pastille de jonction sur la barre
import xml.etree.ElementTree as _ET
_ET.SubElement(links_g,f"{{{SVG}}}circle",{"cx":f"{ex:.1f}","cy":f"{ry:.1f}","r":"2.4","fill":"#00ff00"})
elif w["to"]=="GND": # terre locale : symbole au port, écarté du corps
import xml.etree.ElementTree as _ET
sx,sy=a; side=SIDE.get(w["from"],"B"); LEAD=10
g=_ET.SubElement(links_g,f"{{{SVG}}}g")
def L(x1,y1,x2,y2,wd):
_ET.SubElement(g,f"{{{SVG}}}line",{"x1":f"{x1:.1f}","y1":f"{y1:.1f}","x2":f"{x2:.1f}","y2":f"{y2:.1f}","stroke":"#00ff00","stroke-width":wd})
if side in ("L","R"):
d=-1 if side=="L" else 1
bx=sx+d*LEAD # patte horizontale
L(sx,sy,bx,sy,"1.6")
# ⏚ vertical au bout (3 traits décroissants, axe vertical)
L(bx,sy-6,bx,sy+6,"2"); L(bx+d*3,sy-4,bx+d*3,sy+4,"2"); L(bx+d*6,sy-2,bx+d*6,sy+2,"2")
else:
by=sy+LEAD
L(sx,sy,sx,by,"1.6")
L(sx-6,by,sx+6,by,"2"); L(sx-4,by+3,sx+4,by+3,"2"); L(sx-2,by+6,sx+2,by+6,"2")
continue
else:
b=P[w["to"]]; hf=w.get("horizfirst",True); pts=elbow(a,b,hf)
d="M "+" L ".join(f"{x:.1f} {y:.1f}" for x,y in pts)
attrs={"d":d,"fill":"none"};attrs.update(STYLE[typ])
ET.SubElement(links_g,f"{{{SVG}}}path",attrs)
if typ in LBL:
mx,my=pts[len(pts)//2]
t=ET.SubElement(links_g,f"{{{SVG}}}text",{"x":f"{mx+3:.1f}","y":f"{my-3:.1f}",
"font-family":"'IBM Plex Mono',monospace","font-size":"7","fill":STYLE[typ]["stroke"]})
t.text=LBL[typ]
ET.ElementTree(master).write(sys.argv[2],encoding="utf-8",xml_declaration=True)
print("écrit",sys.argv[2])