201 lines
9.3 KiB
Python
201 lines
9.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Plan d'implantation vu de dessus : côté composants + côté soudures (miroir)."""
|
|
import runpy, sys
|
|
OUT = sys.argv[1]
|
|
sys.argv = ["x", "/dev/null"]
|
|
g = runpy.run_path(sys.path[0] + "/gen_fzz.py", run_name="gen")
|
|
COLS, ROWS = g["COLS"], g["ROWS"]
|
|
NU = g["NU"]
|
|
leds, res_anode, res_base, trans = g["leds"], g["res_anode"], g["res_base"], g["trans"]
|
|
board_wires, ard_jobs = g["board_wires"], g["ard_jobs"]
|
|
|
|
PX = 16 # pas d'affichage
|
|
MARGE_X, MARGE_Y = 44, 120
|
|
PANNEAU = COLS * PX + 2 * MARGE_X
|
|
LARG = 2 * PANNEAU + 40
|
|
HAUT = MARGE_Y + ROWS * PX + 270
|
|
out = []
|
|
A = out.append
|
|
|
|
def xy(c, r, panneau):
|
|
if panneau == 1: # côté soudures : plaque retournée gauche/droite
|
|
c = COLS - 1 - c
|
|
return (panneau * (PANNEAU + 40) + MARGE_X + c * PX + PX / 2, MARGE_Y + r * PX + PX / 2)
|
|
|
|
def txt(x, y, s, size=11, anchor="middle", fill="#333", weight="normal", rot=None):
|
|
tr = f' transform="rotate({rot} {x:.1f} {y:.1f})"' if rot else ""
|
|
A(f'<text x="{x:.1f}" y="{y:.1f}" font-size="{size}" text-anchor="{anchor}" '
|
|
f'fill="{fill}" font-weight="{weight}"{tr}>{s}</text>')
|
|
|
|
A(f'<svg xmlns="http://www.w3.org/2000/svg" width="{LARG}" height="{HAUT}" '
|
|
f'viewBox="0 0 {LARG} {HAUT}" font-family="Helvetica, Arial, sans-serif">')
|
|
A('<title>Implantation de la matrice 3x3 sur plaque à pastilles</title>')
|
|
A(f'<rect width="{LARG}" height="{HAUT}" fill="#ffffff"/>')
|
|
|
|
for pan, titre, sous in ((0, "Côté composants (vu de dessus)",
|
|
"on pose les composants et les fils isolés"),
|
|
(1, "Côté soudures (plaque retournée)",
|
|
"on soude et on relie avec du fil nu. Attention : image en miroir !")):
|
|
x0 = pan * (PANNEAU + 40) + MARGE_X
|
|
txt(x0, 34, titre, 18, "start", "#222", "bold")
|
|
txt(x0, 54, sous, 13, "start", "#666")
|
|
# plaque
|
|
A(f'<rect x="{x0 - 8}" y="{MARGE_Y - 8}" width="{COLS * PX + 16}" height="{ROWS * PX + 16}" '
|
|
f'rx="6" fill="#e3c58f" stroke="#b8975c"/>')
|
|
# numéros de colonnes et de lignes (comptés à partir de 1)
|
|
for c in range(COLS):
|
|
x, _ = xy(c, 0, pan)
|
|
if (c + 1) % 5 == 0 or c == 0:
|
|
txt(x, MARGE_Y + ROWS * PX + 22, str(c + 1), 10, fill="#8a6d3b")
|
|
for r in range(ROWS):
|
|
_, y = xy(0, r, pan)
|
|
if (r + 1) % 5 == 0 or r == 0:
|
|
txt(x0 - 14, y + 4, str(r + 1), 10, fill="#8a6d3b")
|
|
# pastilles
|
|
for c in range(COLS):
|
|
for r in range(ROWS):
|
|
x, y = xy(c, r, pan)
|
|
A(f'<circle cx="{x:.1f}" cy="{y:.1f}" r="4.6" fill="#c9a063"/>'
|
|
f'<circle cx="{x:.1f}" cy="{y:.1f}" r="1.8" fill="#fff8ec"/>')
|
|
|
|
# ---------------------------------------------------------------- côté composants
|
|
def contour_led(inst, pan, plein):
|
|
hs = list(inst["holes"].values())
|
|
xs = [xy(c, r, pan)[0] for c, r in hs]
|
|
cx, cy = sum(xs) / 4, xy(*hs[0], pan)[1]
|
|
if plein:
|
|
A(f'<circle cx="{cx:.1f}" cy="{cy:.1f}" r="{PX * 1.0:.1f}" fill="#ffffff" '
|
|
f'fill-opacity="0.85" stroke="#555" stroke-width="1.5"/>')
|
|
for (c, r), lettre, coul in zip(hs, "RKGB", ("#cc1414", "#222", "#2a9d3a", "#2f6fc4")):
|
|
x, y = xy(c, r, pan)
|
|
A(f'<circle cx="{x:.1f}" cy="{y:.1f}" r="2.6" fill="{coul}"/>')
|
|
txt(x, y - 6, lettre, 9, fill=coul, weight="bold")
|
|
else:
|
|
A(f'<circle cx="{cx:.1f}" cy="{cy:.1f}" r="{PX * 1.0:.1f}" fill="none" '
|
|
f'stroke="#9a7b4a" stroke-dasharray="3 3"/>')
|
|
|
|
def contour_res(inst, pan, plein, valeur):
|
|
(c1, r1), (c2, r2) = inst["holes"].values()
|
|
(xa, ya), (xb, yb) = xy(c1, r1, pan), xy(c2, r2, pan)
|
|
if plein:
|
|
A(f'<line x1="{xa:.1f}" y1="{ya:.1f}" x2="{xb:.1f}" y2="{yb:.1f}" stroke="#8c8c8c" '
|
|
f'stroke-width="2"/>')
|
|
vertical = abs(xa - xb) < 1
|
|
L = 0.55 * (abs(ya - yb) if vertical else abs(xa - xb))
|
|
mx, my = (xa + xb) / 2, (ya + yb) / 2
|
|
w, h = (9, L) if vertical else (L, 9)
|
|
style = ('fill="#e8d3a8" stroke="#a88a52"' if plein
|
|
else 'fill="none" stroke="#9a7b4a" stroke-dasharray="3 3"')
|
|
A(f'<rect x="{mx - w / 2:.1f}" y="{my - h / 2:.1f}" width="{w:.1f}" height="{h:.1f}" '
|
|
f'rx="3" {style}/>')
|
|
if plein:
|
|
txt(mx + (0 if vertical else 0), my + (3 if not vertical else 3), valeur, 8,
|
|
fill="#5a4310", weight="bold", rot=(-90 if vertical else None))
|
|
|
|
def contour_npn(inst, pan, plein, nom):
|
|
hs = inst["holes"]
|
|
xc, y = xy(*hs["connector2"], pan)
|
|
xe, _ = xy(*hs["connector0"], pan)
|
|
cx = (xc + xe) / 2
|
|
R = PX * 1.25
|
|
# demi-cercle, face plate vers le bas de la plaque
|
|
d = f'M{cx - R:.1f} {y + 5:.1f} A{R:.1f} {R:.1f} 0 0 1 {cx + R:.1f} {y + 5:.1f} Z'
|
|
if plein:
|
|
A(f'<path d="{d}" fill="#2b2b2b" fill-opacity="0.9"/>')
|
|
for con, lettre in (("connector2", "C"), ("connector1", "B"), ("connector0", "E")):
|
|
x, yy = xy(*hs[con], pan)
|
|
A(f'<circle cx="{x:.1f}" cy="{yy:.1f}" r="2.6" fill="#ddd"/>')
|
|
txt(x, yy - 6, lettre, 9, fill="#fff", weight="bold")
|
|
txt(cx, y - R + 2, nom, 10, fill="#222", weight="bold")
|
|
else:
|
|
A(f'<path d="{d}" fill="none" stroke="#9a7b4a" stroke-dasharray="3 3"/>')
|
|
|
|
def composants(pan, plein):
|
|
for k, inst in res_anode.items():
|
|
contour_res(inst, pan, plein, inst["props"]["resistance"].replace("Ω", ""))
|
|
for r, inst in res_base.items():
|
|
contour_res(inst, pan, plein, "1k")
|
|
for inst in leds.values():
|
|
contour_led(inst, pan, plein)
|
|
for r, inst in trans.items():
|
|
contour_npn(inst, pan, plein, f"T{r}")
|
|
|
|
# fils isolés (côté composants)
|
|
for a, b, coul in board_wires:
|
|
if coul == NU:
|
|
continue
|
|
(xa, ya), (xb, yb) = xy(*a, 0), xy(*b, 0)
|
|
A(f'<line x1="{xa:.1f}" y1="{ya:.1f}" x2="{xb:.1f}" y2="{yb:.1f}" stroke="{coul}" '
|
|
f'stroke-width="5" stroke-linecap="round"/>')
|
|
composants(0, True)
|
|
|
|
# arrivées des fils de l'Arduino
|
|
for pin, (c, r), coul in ard_jobs:
|
|
x, y = xy(c, r, 0)
|
|
A(f'<circle cx="{x:.1f}" cy="{y:.1f}" r="5.5" fill="none" stroke="{coul}" stroke-width="3"/>')
|
|
if r <= 2: # entrées en haut de la plaque : étiquette verticale au-dessus
|
|
A(f'<line x1="{x:.1f}" y1="{y - 6:.1f}" x2="{x:.1f}" y2="{MARGE_Y - 26:.1f}" '
|
|
f'stroke="{coul}" stroke-width="2"/>')
|
|
txt(x + 4, MARGE_Y - 30, pin, 11, "start", coul if coul != "#ffe24d" else "#b39b00",
|
|
"bold", rot=-90)
|
|
else:
|
|
txt(x + 9, y + 4, pin, 11, "start", "#222", "bold")
|
|
|
|
# ---------------------------------------------------------------- côté soudures
|
|
composants(1, False)
|
|
for a, b, coul in board_wires:
|
|
(xa, ya), (xb, yb) = xy(*a, 1), xy(*b, 1)
|
|
if coul == NU:
|
|
A(f'<line x1="{xa:.1f}" y1="{ya:.1f}" x2="{xb:.1f}" y2="{yb:.1f}" stroke="#7d7d7d" '
|
|
f'stroke-width="4" stroke-linecap="round"/>')
|
|
soudures = set()
|
|
for inst in list(leds.values()) + list(res_anode.values()) + list(res_base.values()) + list(trans.values()):
|
|
soudures.update(inst["holes"].values())
|
|
for a, b, coul in board_wires:
|
|
soudures.update((a, b))
|
|
for _, h, _ in ard_jobs:
|
|
soudures.add(h)
|
|
for c, r in soudures:
|
|
x, y = xy(c, r, 1)
|
|
A(f'<circle cx="{x:.1f}" cy="{y:.1f}" r="4.4" fill="#bfbfbf" stroke="#6f6f6f"/>')
|
|
for a, b, coul in board_wires: # bouts des fils isolés qui traversent
|
|
if coul != NU:
|
|
for h in (a, b):
|
|
x, y = xy(*h, 1)
|
|
A(f'<circle cx="{x:.1f}" cy="{y:.1f}" r="2.2" fill="{coul}"/>')
|
|
for pin, h, coul in ard_jobs:
|
|
x, y = xy(*h, 1)
|
|
A(f'<circle cx="{x:.1f}" cy="{y:.1f}" r="2.2" fill="{coul}"/>')
|
|
|
|
# ---------------------------------------------------------------- légende
|
|
ly = MARGE_Y + ROWS * PX + 56
|
|
txt(MARGE_X, ly, "Légende", 15, "start", "#222", "bold")
|
|
items = [
|
|
('<circle cx="{x}" cy="{y}" r="9" fill="#fff" stroke="#555" stroke-width="1.5"/>',
|
|
"LED RGB vue de dessus : pattes R, K (cathode, la plus longue), G, B"),
|
|
('<rect x="{xm}" y="{ym}" width="22" height="9" rx="3" fill="#e8d3a8" stroke="#a88a52"/>',
|
|
"Résistance (valeur en ohms : 220, 150, 1k = 1000)"),
|
|
('<path d="M{xl} {y4} A11 11 0 0 1 {xr} {y4} Z" fill="#2b2b2b"/>',
|
|
"Transistor BC337, face plate vers le bas : pattes C, B, E de gauche à droite"),
|
|
('<line x1="{xl}" y1="{y}" x2="{xr}" y2="{y}" stroke="#404040" stroke-width="5" stroke-linecap="round"/>',
|
|
"Fil isolé, côté composants (cathodes des lignes)"),
|
|
('<line x1="{xl}" y1="{y}" x2="{xr}" y2="{y}" stroke="#7d7d7d" stroke-width="4" stroke-linecap="round"/>',
|
|
"Fil nu (ou patte repliée), côté soudures"),
|
|
('<circle cx="{x}" cy="{y}" r="5.5" fill="none" stroke="#cc1414" stroke-width="3"/>',
|
|
"Arrivée d'un fil vers l'Arduino (D2 à D13, GND)"),
|
|
]
|
|
for i, (forme, texte) in enumerate(items):
|
|
col, lig = i % 2, i // 2
|
|
bx = MARGE_X + col * (LARG / 2)
|
|
by = ly + 30 + lig * 30
|
|
A(forme.format(x=bx + 11, y=by - 4, xm=bx, ym=by - 8, xl=bx, xr=bx + 22, y4=by + 2))
|
|
txt(bx + 34, by, texte, 12, "start", "#333")
|
|
txt(MARGE_X, ly + 140, "Pas entre LED : 6 trous = 15,24 mm. Les numéros de colonnes et de "
|
|
"lignes sont ceux du côté composants ; côté soudures, gauche et droite sont inversées.",
|
|
12, "start", "#666")
|
|
txt(MARGE_X, ly + 160, "Correspondance avec le code : colonne c de la matrice = LED Lr Cc ; "
|
|
"ligne r = transistor Tr (broche D11 + r).", 12, "start", "#666")
|
|
A("</svg>")
|
|
with open(OUT, "w") as f:
|
|
f.write("\n".join(out))
|