better 3x3 doc
This commit is contained in:
@@ -0,0 +1,489 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Génère matrice_3x3.fzz : maquette 3x3 sur plaque à pastilles (vue platine)
|
||||
+ schéma électrique (vue schéma). Coordonnées en px Fritzing (90 dpi)."""
|
||||
import math, sys, zipfile, os
|
||||
from xml.sax.saxutils import escape
|
||||
|
||||
PARTS = "/usr/share/fritzing/fritzing-parts/core/"
|
||||
P = 9.0 # pas 0,1 pouce
|
||||
|
||||
# ---------------------------------------------------------------- modèle
|
||||
instances = [] # dict: id, module, path, title, props, views{view:{...}}
|
||||
idx = [1000]
|
||||
def nid():
|
||||
idx[0] += 1
|
||||
return idx[0]
|
||||
|
||||
conns = {} # (inst_id, view, connector) -> list of (other_id, other_connector, other_layer)
|
||||
def link(view, a, ca, la, b, cb, lb):
|
||||
conns.setdefault((a, view, ca), []).append((b, cb, lb))
|
||||
conns.setdefault((b, view, cb), []).append((a, ca, la))
|
||||
|
||||
# ---------------------------------------------------------------- géométries des pièces
|
||||
LED = dict(module="d4d5af9700923b8a114f57961f29a8a0ColorLEDModuleID",
|
||||
path=PARTS + "led-rgb-4pin-cathode_v5.fzp",
|
||||
legs={"connector0": (1.35, 40.6875), "connector1": (10.35, 40.6875),
|
||||
"connector2": (19.35, 40.6875), "connector3": (28.35, 40.6875)},
|
||||
size=(0.33 * 90, 0.4521 * 90))
|
||||
NPN = dict(module="2244EBEC10e", path=PARTS + "transistor_signal_NPN_TO92_CBE.fzp",
|
||||
# connector2 = C (gauche), connector1 = B, connector0 = E (droite)
|
||||
legs={"connector2": (1.35, 30.101), "connector1": (10.326, 30.101),
|
||||
"connector0": (19.309, 30.101)},
|
||||
size=(0.22954 * 90, 0.3344583 * 90))
|
||||
RES = dict(module="ResistorModuleID", path=PARTS + "resistor.fzp",
|
||||
legs={"connector0": (2.619, 4.5405), "connector1": (36.0063, 4.5405)},
|
||||
size=(0.42917 * 90, 0.0971 * 90))
|
||||
ARDUINO = dict(module="arduino_Uno_Rev3(fix)", path=PARTS + "arduino_Uno_Rev3(fix).fzp",
|
||||
size=(2.94961 * 90, 2.1 * 90))
|
||||
ARD_PIN = { # broche -> (connector id, x, y) dans le repère de la carte (non tournée)
|
||||
"D2": ("connector63", 229.47, 9.0), "D3": ("connector64", 220.46, 9.0),
|
||||
"D4": ("connector65", 211.46, 9.0), "D5": ("connector66", 202.46, 9.0),
|
||||
"D6": ("connector67", 193.46, 9.0), "D7": ("connector68", 184.47, 9.0),
|
||||
"D8": ("connector51", 170.06, 9.0), "D9": ("connector52", 161.06, 9.0),
|
||||
"D10": ("connector53", 152.06, 9.0), "D11": ("connector54", 143.06, 9.0),
|
||||
"D12": ("connector55", 134.06, 9.0), "D13": ("connector56", 125.06, 9.0),
|
||||
"GND": ("connector57", 116.06, 9.0),
|
||||
}
|
||||
|
||||
def rot_matrix(deg, w, h):
|
||||
"""Rotation autour du centre (w/2, h/2), au format QTransform de Fritzing."""
|
||||
cx, cy = w / 2, h / 2
|
||||
a = math.radians(deg)
|
||||
c, s = round(math.cos(a), 9), round(math.sin(a), 9)
|
||||
m11, m12, m21, m22 = c, s, -s, c
|
||||
m31 = cx - (m11 * cx + m21 * cy)
|
||||
m32 = cy - (m12 * cx + m22 * cy)
|
||||
return (m11, m12, m21, m22, m31, m32)
|
||||
|
||||
def apply(m, x, y):
|
||||
m11, m12, m21, m22, m31, m32 = m
|
||||
return (m11 * x + m21 * y + m31, m12 * x + m22 * y + m32)
|
||||
|
||||
def inv_apply(m, u, v):
|
||||
m11, m12, m21, m22, m31, m32 = m
|
||||
u, v = u - m31, v - m32
|
||||
det = m11 * m22 - m12 * m21
|
||||
return ((m22 * u - m21 * v) / det, (-m12 * u + m11 * v) / det)
|
||||
|
||||
# ---------------------------------------------------------------- vue platine (plaque à pastilles)
|
||||
COLS, ROWS = 32, 34
|
||||
BOARD = dict(id=nid(), module=f"{COLS}.{ROWS}PerfboardModuleID",
|
||||
path=":/resources/parts/core/perfboard.fzp", title="Plaque à pastilles",
|
||||
props={"size": f"{COLS}.{ROWS}"}, views={})
|
||||
BOARD["views"]["breadboardView"] = dict(layer="breadboardbreadboard", x=0, y=0, z=1.5)
|
||||
instances.append(BOARD)
|
||||
|
||||
def hole(c, r):
|
||||
return (9 + P * c, 9 + P * r)
|
||||
|
||||
def hole_id(c, r):
|
||||
return f"connector{r * 1000 + c}"
|
||||
|
||||
def add_part(spec, title, bb_pos, bb_rot=0, props=None, sch=None, legs_to=None):
|
||||
"""legs_to : {connector: (col, row)} trous de la plaque où arrivent les pattes."""
|
||||
inst = dict(id=nid(), module=spec["module"], path=spec["path"], title=title,
|
||||
props=props or {}, views={}, legs={})
|
||||
w, h = spec["size"]
|
||||
m = rot_matrix(bb_rot, w, h) if bb_rot else None
|
||||
inst["views"]["breadboardView"] = dict(layer="breadboard", x=bb_pos[0], y=bb_pos[1],
|
||||
z=2.5, m=m)
|
||||
if legs_to:
|
||||
for con, (c, r) in legs_to.items():
|
||||
sx, sy = spec["legs"][con]
|
||||
hx, hy = hole(c, r)
|
||||
u, v = hx - bb_pos[0], hy - bb_pos[1]
|
||||
lx, ly = inv_apply(m, u, v) if m else (u, v)
|
||||
inst["legs"][con] = ((sx, sy), (lx - sx, ly - sy))
|
||||
link("breadboardView", inst["id"], con, "breadboard", BOARD["id"],
|
||||
hole_id(c, r), "breadboardbreadboard")
|
||||
inst["sch"] = sch
|
||||
inst["holes"] = legs_to or {}
|
||||
instances.append(inst)
|
||||
return inst
|
||||
|
||||
def place_on_holes(spec, title, holes, rot=0, lift=1.0, props=None):
|
||||
"""Place la pièce pour que ses pattes soient au-dessus des trous, puis plie les
|
||||
pattes vers les trous. lift = longueur de patte visible, en pas de 0,1 po."""
|
||||
cons = list(holes)
|
||||
# centre des trous visés, centre des départs de pattes
|
||||
hx = sum(hole(*holes[c])[0] for c in cons) / len(cons)
|
||||
hy = sum(hole(*holes[c])[1] for c in cons) / len(cons)
|
||||
sx = sum(spec["legs"][c][0] for c in cons) / len(cons)
|
||||
sy = sum(spec["legs"][c][1] for c in cons) / len(cons)
|
||||
w, h = spec["size"]
|
||||
m = rot_matrix(rot, w, h) if rot else None
|
||||
tx, ty = apply(m, sx, sy) if m else (sx, sy)
|
||||
if rot == 0:
|
||||
pos = (hx - tx, hy - ty - lift * P)
|
||||
else:
|
||||
pos = (hx - tx, hy - ty)
|
||||
return add_part(spec, title, pos, rot, props, legs_to=holes)
|
||||
|
||||
wires = []
|
||||
board_wires = [] # (trou a, trou b, couleur) pour le plan d'implantation
|
||||
def bb_wire(a, b, color, title=None):
|
||||
"""Fil entre deux trous de la plaque (a, b = (col, row))."""
|
||||
(x1, y1), (x2, y2) = hole(*a), hole(*b)
|
||||
board_wires.append((a, b, color))
|
||||
w = dict(id=nid(), title=title or f"Fil{len(wires) + 1}", view="breadboardView",
|
||||
x=x1, y=y1, dx=x2 - x1, dy=y2 - y1, color=color, flags=64,
|
||||
layer="breadboardWire", z=(1.8 if color == NU else 3.5))
|
||||
for k, (c, r) in enumerate((a, b)):
|
||||
link("breadboardView", w["id"], f"connector{k}", "breadboardWire",
|
||||
BOARD["id"], hole_id(c, r), "breadboardbreadboard")
|
||||
wires.append(w)
|
||||
return w
|
||||
|
||||
ARD_POS = (40.0, -280.0)
|
||||
ARD_M = rot_matrix(180, *ARDUINO["size"])
|
||||
ARD = dict(id=nid(), module=ARDUINO["module"], path=ARDUINO["path"], title="Arduino Uno",
|
||||
props={}, views={}, legs={})
|
||||
ARD["views"]["breadboardView"] = dict(layer="breadboardbreadboard", x=ARD_POS[0],
|
||||
y=ARD_POS[1], z=1.6, m=ARD_M)
|
||||
instances.append(ARD)
|
||||
|
||||
def ard_pin_xy(name):
|
||||
_, px, py = ARD_PIN[name]
|
||||
u, v = apply(ARD_M, px, py)
|
||||
return (ARD_POS[0] + u, ARD_POS[1] + v)
|
||||
|
||||
def ard_wire(pin, hole_cr, color):
|
||||
x1, y1 = ard_pin_xy(pin)
|
||||
x2, y2 = hole(*hole_cr)
|
||||
w = dict(id=nid(), title=f"Fil {pin}", view="breadboardView", x=x1, y=y1,
|
||||
dx=x2 - x1, dy=y2 - y1, color=color, flags=64, layer="breadboardWire")
|
||||
link("breadboardView", w["id"], "connector0", "breadboardWire", ARD["id"],
|
||||
ARD_PIN[pin][0], "breadboardbreadboard")
|
||||
link("breadboardView", w["id"], "connector1", "breadboardWire", BOARD["id"],
|
||||
hole_id(*hole_cr), "breadboardbreadboard")
|
||||
wires.append(w)
|
||||
|
||||
# couleurs de fils Fritzing
|
||||
NU = "#a8a8a8" # fil nu étamé, côté soudure
|
||||
ROUGE, VERT, BLEU = "#cc1414", "#47cc79", "#418dd9"
|
||||
NOIR, JAUNE, ORANGE = "#404040", "#ffe24d", "#ff7033"
|
||||
|
||||
# --- implantation -------------------------------------------------------------
|
||||
PAS = 6 # pas entre LED, en trous (6 × 2,54 = 15,24 mm)
|
||||
X0, Y_LED = 2, (18, 24, 30) # LED (ligne r, colonne c) : pattes R K G B
|
||||
COUL = {"R": 0, "G": 2, "B": 3} # décalage de la patte dans la LED
|
||||
PIN_ANODE = {(c, k): f"D{2 + 3 * c + i}" for c in range(3) for i, k in enumerate("RGB")}
|
||||
RES_VAL = {"R": "220Ω", "G": "150Ω", "B": "150Ω"}
|
||||
COUL_FIL = {"R": ROUGE, "G": VERT, "B": BLEU}
|
||||
ard_jobs = [] # (broche, trou, couleur) routés à la fin
|
||||
|
||||
leds = {}
|
||||
for r in range(3):
|
||||
for c in range(3):
|
||||
x = X0 + PAS * c
|
||||
leds[(r, c)] = place_on_holes(
|
||||
LED, f"LED L{r}C{c}",
|
||||
{"connector0": (x, Y_LED[r]), "connector1": (x + 1, Y_LED[r]),
|
||||
"connector2": (x + 2, Y_LED[r]), "connector3": (x + 3, Y_LED[r])},
|
||||
lift=0.6)
|
||||
|
||||
res_anode = {}
|
||||
for c in range(3):
|
||||
for k, off in COUL.items():
|
||||
col = X0 + PAS * c + off
|
||||
top, bot = (2, 6) if k in "RG" else (7, 11)
|
||||
res_anode[(c, k)] = place_on_holes(
|
||||
RES, f"R {k}{c}", {"connector0": (col, top), "connector1": (col, bot)},
|
||||
rot=90, props={"resistance": RES_VAL[k]})
|
||||
bb_wire((col, 1), (col, top), NU)
|
||||
ard_jobs.append((PIN_ANODE[(c, k)], (col, 1), COUL_FIL[k]))
|
||||
bb_wire((col, bot), (col, Y_LED[0]), NU)
|
||||
bb_wire((col, Y_LED[0]), (col, Y_LED[1]), NU)
|
||||
bb_wire((col, Y_LED[1]), (col, Y_LED[2]), NU)
|
||||
|
||||
XC, XB, XE = 21, 22, 23 # pattes C, B, E
|
||||
X_GND = 30
|
||||
trans, res_base = {}, {}
|
||||
for r in range(3):
|
||||
y = Y_LED[r] + 1
|
||||
for c in range(3):
|
||||
xk = X0 + PAS * c + 1
|
||||
bb_wire((xk, Y_LED[r]), (xk, y), NU)
|
||||
bb_wire((X0 + 1, y), (X0 + 1 + PAS, y), NOIR)
|
||||
bb_wire((X0 + 1 + PAS, y), (X0 + 1 + 2 * PAS, y), NOIR)
|
||||
bb_wire((X0 + 1 + 2 * PAS, y), (XC - 1, y), NOIR)
|
||||
bb_wire((XC - 1, y), (XC, y), NU)
|
||||
trans[r] = place_on_holes(NPN, f"T{r} (BC337)",
|
||||
{"connector2": (XC, y), "connector1": (XB, y),
|
||||
"connector0": (XE, y)}, lift=0.6)
|
||||
res_base[r] = place_on_holes(RES, f"Rb{r}",
|
||||
{"connector0": (XB, y + 1), "connector1": (XB + 4, y + 1)},
|
||||
lift=0.0, props={"resistance": "1kΩ"})
|
||||
bb_wire((XB, y), (XB, y + 1), NU)
|
||||
xw = XB + 5 + r
|
||||
bb_wire((XB + 4, y + 1), (xw, y + 1), NU)
|
||||
ard_jobs.append((f"D{11 + r}", (xw, y + 1), JAUNE))
|
||||
bb_wire((XE, y), (X_GND, y), NU)
|
||||
yg = Y_LED[0] - 1
|
||||
bb_wire((X_GND, yg), (X_GND, Y_LED[0] + 1), NU)
|
||||
bb_wire((X_GND, Y_LED[0] + 1), (X_GND, Y_LED[1] + 1), NU)
|
||||
bb_wire((X_GND, Y_LED[1] + 1), (X_GND, Y_LED[2] + 1), NU)
|
||||
ard_jobs.append(("GND", (X_GND, yg), NOIR))
|
||||
|
||||
# routage des fils Arduino : descente, palier horizontal, descente (fils coudés)
|
||||
def chain(points, color, pin, hole_cr):
|
||||
ids = []
|
||||
for i in range(len(points) - 1):
|
||||
(x1, y1), (x2, y2) = points[i], points[i + 1]
|
||||
w = dict(id=nid(), title=f"Fil {pin}", view="breadboardView", x=x1, y=y1,
|
||||
dx=x2 - x1, dy=y2 - y1, color=color, flags=64, layer="breadboardWire")
|
||||
wires.append(w)
|
||||
ids.append(w["id"])
|
||||
link("breadboardView", ids[0], "connector0", "breadboardWire", ARD["id"],
|
||||
ARD_PIN[pin][0], "breadboardbreadboard")
|
||||
link("breadboardView", ids[-1], "connector1", "breadboardWire", BOARD["id"],
|
||||
hole_id(*hole_cr), "breadboardbreadboard")
|
||||
for a_, b_ in zip(ids, ids[1:]):
|
||||
link("breadboardView", a_, "connector1", "breadboardWire", b_, "connector0",
|
||||
"breadboardWire")
|
||||
|
||||
header_y = ard_pin_xy("D2")[1]
|
||||
jobs = [(pin, h, col, ard_pin_xy(pin)[0], hole(*h)[0]) for pin, h, col in ard_jobs]
|
||||
droite = sorted([j for j in jobs if j[4] > j[3] + 0.5], key=lambda j: j[3])
|
||||
gauche = sorted([j for j in jobs if j[4] < j[3] - 0.5], key=lambda j: -j[3])
|
||||
niveau = {}
|
||||
for groupe in (droite, gauche):
|
||||
n = len(groupe)
|
||||
for i, j in enumerate(groupe): # le plus à l'extérieur descend le plus bas
|
||||
niveau[j[0]] = n - i
|
||||
for pin, h, col, xp, xh in jobs:
|
||||
yp = header_y
|
||||
if pin in niveau:
|
||||
yl = header_y + 18 + 9 * niveau[pin]
|
||||
chain([(xp, yp), (xp, yl), (xh, yl), hole(*h)], col, pin, h)
|
||||
else:
|
||||
chain([(xp, yp), hole(*h)], col, pin, h)
|
||||
|
||||
# ---------------------------------------------------------------- vue schéma
|
||||
K = 90 / 96 # px Inkscape -> px Fritzing (SVG en pouces)
|
||||
SCH = { # terminaux dans le repère du symbole (px Fritzing)
|
||||
"LED": {"connector0": (5.126, 35.457), "connector2": (23.172, 35.457),
|
||||
"connector3": (41.297, 35.457), "connector1": (23.225, 1.5)},
|
||||
"NPN": {"connector1": (0.4375, 18.4375), "connector2": (18.4375, 0.4375),
|
||||
"connector0": (18.4375, 36.4375)},
|
||||
"RES": {"connector0": (0.4375, 3.87), "connector1": (36.4375, 3.87)},
|
||||
"RES_SIZE": (0.409722 * 90, 0.086 * 90),
|
||||
}
|
||||
def ard_sch(name):
|
||||
con = ARD_PIN[name][0]
|
||||
if name == "GND":
|
||||
return (72.0, 207.0)
|
||||
n = {"D2": 0, "D3": 1, "D4": 2, "D5": 3, "D6": 4, "D7": 5, "D8": 6, "D9": 7,
|
||||
"D10": 8, "D11": 9, "D12": 10, "D13": 11}[name]
|
||||
return (135.0, 54.0 + 9 * n)
|
||||
|
||||
def set_sch(inst, pos, rot=0, size=None):
|
||||
m = rot_matrix(rot, *size) if rot else None
|
||||
inst["views"]["schematicView"] = dict(layer="schematic", x=pos[0], y=pos[1], z=2.5, m=m)
|
||||
inst["sch_pos"], inst["sch_m"] = pos, m
|
||||
|
||||
def place_sch(inst, table, con, target, rot=0, size=None):
|
||||
"""Place le symbole pour que le terminal `con` tombe sur `target`."""
|
||||
m = rot_matrix(rot, *size) if rot else None
|
||||
lx, ly = SCH[table][con]
|
||||
if m:
|
||||
lx, ly = apply(m, lx, ly)
|
||||
set_sch(inst, (target[0] - lx, target[1] - ly), rot, size)
|
||||
|
||||
def term(inst, table, con):
|
||||
lx, ly = SCH[table][con]
|
||||
if inst.get("sch_m"):
|
||||
lx, ly = apply(inst["sch_m"], lx, ly)
|
||||
return (inst["sch_pos"][0] + lx, inst["sch_pos"][1] + ly)
|
||||
|
||||
ends = {} # point arrondi -> [(wire_id, connector)]
|
||||
def sch_poly(points, start=None, end=None):
|
||||
ids = []
|
||||
for (x1, y1), (x2, y2) in zip(points, points[1:]):
|
||||
w = dict(id=nid(), title="Fil", view="schematicView", x=x1, y=y1, dx=x2 - x1,
|
||||
dy=y2 - y1, color="#404040", flags=128, layer="schematicTrace",
|
||||
mils="9.7222", z=5.5)
|
||||
wires.append(w)
|
||||
ids.append(w["id"])
|
||||
for a_, b_ in zip(ids, ids[1:]):
|
||||
link("schematicView", a_, "connector1", "schematicTrace", b_, "connector0",
|
||||
"schematicTrace")
|
||||
if start:
|
||||
link("schematicView", ids[0], "connector0", "schematicTrace", start[0]["id"],
|
||||
start[1], "schematic")
|
||||
else:
|
||||
ends.setdefault((round(points[0][0], 2), round(points[0][1], 2)), []).append(
|
||||
(ids[0], "connector0"))
|
||||
if end:
|
||||
link("schematicView", ids[-1], "connector1", "schematicTrace", end[0]["id"],
|
||||
end[1], "schematic")
|
||||
else:
|
||||
ends.setdefault((round(points[-1][0], 2), round(points[-1][1], 2)), []).append(
|
||||
(ids[-1], "connector1"))
|
||||
|
||||
A_POS = (0.0, 450.0)
|
||||
ARD["views"]["schematicView"] = dict(layer="schematic", x=A_POS[0], y=A_POS[1], z=2.5)
|
||||
def apin(name):
|
||||
x, y = ard_sch(name)
|
||||
return (A_POS[0] + x, A_POS[1] + y)
|
||||
ARDP = lambda name: (ARD, ARD_PIN[name][0])
|
||||
|
||||
MX, MY, PASS = 225.0, 60.0, 90.0
|
||||
BUS_OFF = {"R": -27.0, "G": -18.0, "B": -9.0}
|
||||
PROF = {"R": 9.0, "G": 18.0, "B": 27.0}
|
||||
LEDCON = {"R": "connector0", "G": "connector2", "B": "connector3"}
|
||||
RES_TOP = {"R": 315.0, "G": 360.0, "B": 405.0}
|
||||
for (r, c), led in leds.items():
|
||||
set_sch(led, (MX + PASS * c, MY + PASS * r))
|
||||
for c in range(3):
|
||||
for k in "RGB":
|
||||
bx = MX + PASS * c + BUS_OFF[k]
|
||||
res = res_anode[(c, k)]
|
||||
# patte 1 (connector0) côté Arduino, en bas ; patte 2 côté LED, en haut
|
||||
place_sch(res, "RES", "connector1", (bx, RES_TOP[k]), rot=270, size=SCH["RES_SIZE"])
|
||||
top, bot = term(res, "RES", "connector1"), term(res, "RES", "connector0")
|
||||
pin = PIN_ANODE[(c, k)]
|
||||
px, py = apin(pin)
|
||||
sch_poly([(px, py), (bx, py), bot], start=ARDP(pin), end=(res, "connector0"))
|
||||
# bus : résistance -> jonctions des lignes 2, 1, 0
|
||||
pts = [top] + [(bx, MY + PASS * r + 35.457 + PROF[k]) for r in (2, 1, 0)]
|
||||
for i, (a_, b_) in enumerate(zip(pts, pts[1:])):
|
||||
sch_poly([a_, b_], start=(res, "connector1") if i == 0 else None)
|
||||
for r in range(3):
|
||||
led = leds[(r, c)]
|
||||
tx, ty = term(led, "LED", LEDCON[k])
|
||||
jy = MY + PASS * r + 35.457 + PROF[k]
|
||||
sch_poly([(tx, ty), (tx, jy), (bx, jy)], start=(led, LEDCON[k]))
|
||||
|
||||
XT = 545.0
|
||||
XV = {0: 462.0, 1: 471.0, 2: 480.0}
|
||||
X_GND_S, Y_GND_S = XT + 45.0, 690.0
|
||||
for r in range(3):
|
||||
yrl = MY + PASS * r + 1.5 - 9.0
|
||||
ks = [term(leds[(r, c)], "LED", "connector1") for c in range(3)]
|
||||
for c in range(3):
|
||||
sch_poly([ks[c], (ks[c][0], yrl)], start=(leds[(r, c)], "connector1"))
|
||||
t = trans[r]
|
||||
set_sch(t, (XT, yrl + 18.0 - 0.4375))
|
||||
cpt, bpt, ept = term(t, "NPN", "connector2"), term(t, "NPN", "connector1"), term(t, "NPN", "connector0")
|
||||
sch_poly([(ks[0][0], yrl), (ks[1][0], yrl)])
|
||||
sch_poly([(ks[1][0], yrl), (ks[2][0], yrl)])
|
||||
sch_poly([(ks[2][0], yrl), (cpt[0], yrl), cpt], end=(t, "connector2"))
|
||||
rb = res_base[r]
|
||||
# patte 1 (connector0) côté base, patte 2 côté Arduino
|
||||
place_sch(rb, "RES", "connector0", (bpt[0] - 9.0, bpt[1]), rot=180, size=SCH["RES_SIZE"])
|
||||
rbase, rard = term(rb, "RES", "connector0"), term(rb, "RES", "connector1")
|
||||
sch_poly([rbase, bpt], start=(rb, "connector0"), end=(t, "connector1"))
|
||||
pin = f"D{11 + r}"
|
||||
px, py = apin(pin)
|
||||
sch_poly([(px, py), (XV[r], py), (XV[r], rard[1]), rard], start=ARDP(pin), end=(rb, "connector1"))
|
||||
sch_poly([ept, (X_GND_S, ept[1])], start=(t, "connector0"))
|
||||
yE = ept[1]
|
||||
if r == 0:
|
||||
y_e0 = yE
|
||||
else:
|
||||
sch_poly([(X_GND_S, prev_e), (X_GND_S, yE)])
|
||||
prev_e = yE
|
||||
gx, gy = apin("GND")
|
||||
sch_poly([(X_GND_S, prev_e), (X_GND_S, Y_GND_S), (gx, Y_GND_S), (gx, gy)], end=ARDP("GND"))
|
||||
|
||||
for pt, lst in ends.items():
|
||||
for i in range(len(lst)):
|
||||
for j in range(i + 1, len(lst)):
|
||||
link("schematicView", lst[i][0], lst[i][1], "schematicTrace", lst[j][0],
|
||||
lst[j][1], "schematicTrace")
|
||||
|
||||
# ---------------------------------------------------------------- écriture XML
|
||||
def fmt(v):
|
||||
return f"{v:.4f}".rstrip("0").rstrip(".") if isinstance(v, float) else str(v)
|
||||
|
||||
def connects_xml(inst_id, view, con, indent):
|
||||
out = []
|
||||
for (oid, ocon, olayer) in conns.get((inst_id, view, con), []):
|
||||
out.append(f'{indent}<connect connectorId="{ocon}" modelIndex="{oid}" layer="{olayer}"/>')
|
||||
return "\n".join(out)
|
||||
|
||||
def part_xml(inst):
|
||||
L = []
|
||||
L.append(f'<instance moduleIdRef="{escape(inst["module"])}" modelIndex="{inst["id"]}" '
|
||||
f'path="{escape(inst["path"])}">')
|
||||
for k, v in inst["props"].items():
|
||||
L.append(f' <property name="{k}" value="{escape(v)}"/>')
|
||||
L.append(f' <title>{escape(inst["title"])}</title>')
|
||||
L.append(' <views>')
|
||||
for view, g in inst["views"].items():
|
||||
L.append(f' <{view} layer="{g["layer"]}">')
|
||||
if g.get("m"):
|
||||
m11, m12, m21, m22, m31, m32 = g["m"]
|
||||
L.append(f' <geometry z="{g["z"]}" x="{fmt(g["x"])}" y="{fmt(g["y"])}">')
|
||||
L.append(f' <transform m11="{fmt(m11)}" m12="{fmt(m12)}" m13="0" '
|
||||
f'm21="{fmt(m21)}" m22="{fmt(m22)}" m23="0" m31="{fmt(m31)}" '
|
||||
f'm32="{fmt(m32)}" m33="1"/>')
|
||||
L.append(' </geometry>')
|
||||
else:
|
||||
L.append(f' <geometry z="{g["z"]}" x="{fmt(g["x"])}" y="{fmt(g["y"])}"/>')
|
||||
cons = sorted({c for (i, v, c) in conns if i == inst["id"] and v == view}
|
||||
| (set(inst.get("legs", {})) if view == "breadboardView" else set()))
|
||||
if cons:
|
||||
L.append(' <connectors>')
|
||||
for con in cons:
|
||||
L.append(f' <connector connectorId="{con}" layer="{g["layer"]}">')
|
||||
if view == "breadboardView" and con in inst.get("legs", {}):
|
||||
(sx, sy), (lx, ly) = inst["legs"][con]
|
||||
L.append(f' <geometry x="{fmt(sx)}" y="{fmt(sy)}"/>')
|
||||
L.append(' <leg><point x="0" y="0"/><bezier/>'
|
||||
f'<point x="{fmt(lx)}" y="{fmt(ly)}"/><bezier/></leg>')
|
||||
else:
|
||||
L.append(' <geometry x="0" y="0"/>')
|
||||
L.append(' <connects>')
|
||||
L.append(connects_xml(inst["id"], view, con, " "))
|
||||
L.append(' </connects>')
|
||||
L.append(' </connector>')
|
||||
L.append(' </connectors>')
|
||||
L.append(f' </{view}>')
|
||||
L.append(' </views>')
|
||||
L.append('</instance>')
|
||||
return "\n".join(L)
|
||||
|
||||
def wire_xml(w):
|
||||
view, layer = w["view"], w["layer"]
|
||||
L = [f'<instance moduleIdRef="WireModuleID" modelIndex="{w["id"]}" '
|
||||
f'path=":/resources/parts/core/wire.fzp">',
|
||||
f' <title>{escape(w["title"])}</title>', ' <views>',
|
||||
f' <{view} layer="{layer}">',
|
||||
f' <geometry z="{w.get("z", 3.5)}" x="{fmt(w["x"])}" y="{fmt(w["y"])}" x1="0" y1="0" '
|
||||
f'x2="{fmt(w["dx"])}" y2="{fmt(w["dy"])}" wireFlags="{w["flags"]}"/>',
|
||||
f' <wireExtras mils="{w.get("mils", "22.2222")}" color="{w["color"]}" '
|
||||
f'opacity="1" banded="0"/>',
|
||||
' <connectors>']
|
||||
for k in ("connector0", "connector1"):
|
||||
L.append(f' <connector connectorId="{k}" layer="{layer}">')
|
||||
L.append(' <geometry x="0" y="0"/>')
|
||||
L.append(' <connects>')
|
||||
L.append(connects_xml(w["id"], view, k, " "))
|
||||
L.append(' </connects>')
|
||||
L.append(' </connector>')
|
||||
L += [' </connectors>', f' </{view}>', ' </views>', '</instance>']
|
||||
return "\n".join(L)
|
||||
|
||||
def build(out_path, ino_path=None):
|
||||
head = '''<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module fritzingVersion="1.0.5" icon=".png">
|
||||
<views>
|
||||
<view name="breadboardView" backgroundColor="#ffffff" gridSize="0.1in" showGrid="1" alignToGrid="1" viewFromBelow="0" colorWiresByLength="0"/>
|
||||
<view name="schematicView" backgroundColor="#ffffff" gridSize="0.1in" showGrid="1" alignToGrid="1" viewFromBelow="0"/>
|
||||
<view name="pcbView" backgroundColor="#333333" gridSize="0.05in" showGrid="1" alignToGrid="1" viewFromBelow="0"/>
|
||||
</views>
|
||||
<instances>
|
||||
'''
|
||||
body = "\n".join(part_xml(i) for i in instances) + "\n" + "\n".join(wire_xml(w) for w in wires)
|
||||
xml = head + body + "\n</instances>\n</module>\n"
|
||||
name = os.path.splitext(os.path.basename(out_path))[0]
|
||||
with zipfile.ZipFile(out_path, "w", zipfile.ZIP_DEFLATED) as z:
|
||||
z.writestr(name + ".fz", xml)
|
||||
if ino_path:
|
||||
z.write(ino_path, os.path.basename(ino_path))
|
||||
|
||||
if __name__ == "__main__" and sys.argv[1] != "/dev/null":
|
||||
build(sys.argv[1], sys.argv[2] if len(sys.argv) > 2 else None)
|
||||
print(len(instances), "pièces,", len(wires), "fils")
|
||||
@@ -0,0 +1,200 @@
|
||||
#!/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))
|
||||
@@ -0,0 +1,194 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Dessine schemas/schema_electrique.svg : schéma de la maquette 3 x 3,
|
||||
avec les symboles normalisés (résistances rectangulaires) et des fils colorés.
|
||||
Usage : python3 outils/schema_electrique.py schemas/schema_electrique.svg"""
|
||||
import sys
|
||||
|
||||
ROUGE, VERT, BLEU = "#d62828", "#2a9d3a", "#2f6fc4"
|
||||
TEINTE = {ROUGE: "#fbd5d5", VERT: "#d3f0d8", BLEU: "#d6e3f7"}
|
||||
LIGNE, BASE, MASSE, TEXTE = "#3a3a3a", "#c98a00", "#222222", "#333333"
|
||||
|
||||
LX = [170, 370, 570] # x de la diode rouge de chaque colonne
|
||||
LY = [330, 480, 630] # y des anodes de chaque ligne
|
||||
DX = 48 # écart entre les diodes R, G, B d'une LED
|
||||
COULEURS = [("R", ROUGE, "220 Ω"), ("G", VERT, "150 Ω"), ("B", BLEU, "150 Ω")]
|
||||
XT = 790 # centre des transistors
|
||||
XD = {0: 900, 1: 930, 2: 960} # descentes D11, D12, D13
|
||||
X_GND = 1000
|
||||
Y_PIN = 170 # bas du bloc Arduino
|
||||
|
||||
out = []
|
||||
A = out.append
|
||||
|
||||
def ligne(x1, y1, x2, y2, c, w=2.2):
|
||||
A(f'<line x1="{x1}" y1="{y1}" x2="{x2}" y2="{y2}" stroke="{c}" stroke-width="{w}" '
|
||||
f'stroke-linecap="round"/>')
|
||||
|
||||
def chemin(d, c, w=2.2):
|
||||
A(f'<path d="{d}" fill="none" stroke="{c}" stroke-width="{w}" stroke-linecap="round" '
|
||||
f'stroke-linejoin="round"/>')
|
||||
|
||||
def point(x, y, c):
|
||||
A(f'<circle cx="{x}" cy="{y}" r="4" fill="{c}"/>')
|
||||
|
||||
def texte(x, y, s, taille=13, ancre="middle", c=TEXTE, gras=False, rot=None, italique=False):
|
||||
tr = f' transform="rotate({rot} {x} {y})"' if rot else ""
|
||||
A(f'<text x="{x}" y="{y}" font-size="{taille}" text-anchor="{ancre}" fill="{c}"'
|
||||
f'{" font-weight=\"bold\"" if gras else ""}{" font-style=\"italic\"" if italique else ""}'
|
||||
f'{tr}>{s}</text>')
|
||||
|
||||
def resistance_v(x, y1, y2, c, valeur):
|
||||
"""Résistance verticale entre y1 et y2 (rectangle de 50 px au milieu)."""
|
||||
m = (y1 + y2) / 2
|
||||
ligne(x, y1, x, m - 25, c)
|
||||
ligne(x, m + 25, x, y2, c)
|
||||
A(f'<rect x="{x - 7}" y="{m - 25}" width="14" height="50" rx="2" fill="#fff" '
|
||||
f'stroke="{c}" stroke-width="2.2"/>')
|
||||
texte(x + 19, m, valeur, 12, c=c, rot=-90)
|
||||
|
||||
def resistance_h(x1, x2, y, c, valeur):
|
||||
m = (x1 + x2) / 2
|
||||
ligne(x1, y, m - 25, y, c)
|
||||
ligne(m + 25, y, x2, y, c)
|
||||
A(f'<rect x="{m - 25}" y="{y - 7}" width="50" height="14" rx="2" fill="#fff" '
|
||||
f'stroke="{c}" stroke-width="2.2"/>')
|
||||
texte(m, y - 13, valeur, 12, c=c)
|
||||
|
||||
def diode(x, ya, c):
|
||||
"""Diode verticale : anode en haut (ya), cathode en bas (ya + 32)."""
|
||||
ligne(x, ya, x, ya + 8, c)
|
||||
A(f'<path d="M{x - 9} {ya + 8} L{x + 9} {ya + 8} L{x} {ya + 22} Z" fill="{TEINTE[c]}" '
|
||||
f'stroke="{c}" stroke-width="2" stroke-linejoin="round"/>')
|
||||
ligne(x - 9, ya + 22, x + 9, ya + 22, c, 2.4)
|
||||
ligne(x, ya + 22, x, ya + 32, LIGNE)
|
||||
for dy in (0, 7): # lumière émise
|
||||
x1, y1, x2, y2 = x + 9, ya + 15 + dy, x + 18, ya + 6 + dy
|
||||
ligne(x1, y1, x2, y2, c, 1.4)
|
||||
A(f'<path d="M{x2} {y2} l-5 1 l4 4 Z" fill="{c}"/>')
|
||||
|
||||
def masse(x, y):
|
||||
ligne(x - 11, y, x + 11, y, MASSE, 2.4)
|
||||
ligne(x - 7, y + 5, x + 7, y + 5, MASSE, 2.4)
|
||||
ligne(x - 3, y + 10, x + 3, y + 10, MASSE, 2.4)
|
||||
|
||||
def transistor(r, yt):
|
||||
"""NPN dessiné en miroir : base à droite, collecteur en haut à gauche,
|
||||
émetteur en bas à gauche (vers la masse)."""
|
||||
xb = XT + 8
|
||||
A(f'<circle cx="{XT - 1}" cy="{yt}" r="25" fill="#fafafa" stroke="{TEXTE}" stroke-width="1.6"/>')
|
||||
ligne(xb, yt - 13, xb, yt + 13, TEXTE, 3)
|
||||
ligne(xb, yt - 6, XT - 12, yt - 20, LIGNE) # collecteur
|
||||
ligne(xb, yt + 6, XT - 12, yt + 20, MASSE) # émetteur
|
||||
A(f'<path d="M{XT - 12} {yt + 20} l3 -9 l5 7 Z" fill="{MASSE}"/>')
|
||||
ligne(xb, yt, XT + 40, yt, BASE) # base
|
||||
texte(XT - 44, yt - 2, f"T{r}", 14, gras=True)
|
||||
texte(XT - 44, yt + 14, "BC337", 11, c="#666")
|
||||
texte(XT + 22, yt + 18, "B", 10, c="#888")
|
||||
texte(XT - 22, yt - 26, "C", 10, c="#888")
|
||||
texte(XT - 26, yt + 28, "E", 10, c="#888")
|
||||
|
||||
W, H = 1080, 970
|
||||
A(f'<svg xmlns="http://www.w3.org/2000/svg" width="{W}" height="{H}" viewBox="0 0 {W} {H}" '
|
||||
f'font-family="Helvetica, Arial, sans-serif">')
|
||||
A('<title>Schéma électrique de la maquette 3 x 3</title>')
|
||||
A(f'<rect width="{W}" height="{H}" fill="#ffffff"/>')
|
||||
texte(40, 38, "Maquette 3 × 3 : schéma électrique", 22, "start", "#111", True)
|
||||
texte(40, 60, "9 LED RGB à cathode commune, allumées ligne par ligne (multiplexage)",
|
||||
14, "start", "#666")
|
||||
|
||||
# ---------------------------------------------------------------- Arduino
|
||||
A(f'<rect x="100" y="80" width="930" height="{Y_PIN - 80}" rx="10" fill="#e8f4f5" '
|
||||
f'stroke="#00878f" stroke-width="2.4"/>')
|
||||
texte(565, 112, "Arduino Uno", 20, gras=True, c="#00666d")
|
||||
texte(565, 132, "alimenté par le câble USB", 12, c="#4d8f94")
|
||||
broches = {}
|
||||
for c in range(3):
|
||||
for k, (_, coul, _) in enumerate(COULEURS):
|
||||
broches[f"D{2 + 3 * c + k}"] = (LX[c] + DX * k - 22, coul)
|
||||
for r in range(3):
|
||||
broches[f"D{11 + r}"] = (XD[r], BASE)
|
||||
broches["GND"] = (X_GND, MASSE)
|
||||
for nom, (x, coul) in broches.items():
|
||||
ligne(x, Y_PIN, x, Y_PIN + 20, coul)
|
||||
texte(x, Y_PIN - 10, nom, 12, gras=True, c=coul if coul != BASE else "#9a6a00")
|
||||
masse(X_GND, Y_PIN + 20)
|
||||
|
||||
# ---------------------------------------------------------------- colonnes (anodes)
|
||||
for c in range(3):
|
||||
for k, (_, coul, val) in enumerate(COULEURS):
|
||||
xb = LX[c] + DX * k - 22
|
||||
resistance_v(xb, Y_PIN + 20, 265, coul, val)
|
||||
y_fin = LY[2] - 12
|
||||
ligne(xb, 265, xb, y_fin, coul)
|
||||
for r in range(3):
|
||||
xd, ys = LX[c] + DX * k, LY[r] - 12
|
||||
chemin(f"M{xb} {ys} H{xd} V{LY[r]}", coul)
|
||||
if r < 2:
|
||||
point(xb, ys, coul)
|
||||
texte(LX[c] + DX, LY[2] + 100, f"colonne {c}", 14, c="#555", gras=True)
|
||||
|
||||
# ---------------------------------------------------------------- LED
|
||||
for r in range(3):
|
||||
for c in range(3):
|
||||
x0 = LX[c]
|
||||
A(f'<rect x="{x0 - 14}" y="{LY[r] - 4}" width="{2 * DX + 28}" height="40" rx="8" '
|
||||
f'fill="#f2f2f2"/>')
|
||||
texte(x0 + DX + 2, LY[r] - 22, f"L{r}C{c}", 11, c="#888", gras=True)
|
||||
for r in range(3):
|
||||
for c in range(3):
|
||||
for k, (_, coul, _) in enumerate(COULEURS):
|
||||
diode(LX[c] + DX * k, LY[r], coul)
|
||||
|
||||
# ---------------------------------------------------------------- lignes (cathodes)
|
||||
bus_x = sorted(LX[c] + DX * k - 22 for c in range(3) for k in range(3))
|
||||
for r in range(3):
|
||||
y = LY[r] + 60
|
||||
for c in range(3):
|
||||
for k in range(3):
|
||||
xd = LX[c] + DX * k
|
||||
ligne(xd, LY[r] + 32, xd, y, LIGNE)
|
||||
if not (c == 0 and k == 0):
|
||||
point(xd, y, LIGNE)
|
||||
# ligne horizontale avec des « ponts » là où elle croise une colonne
|
||||
d = f"M{LX[0]} {y}"
|
||||
if r < 2:
|
||||
for xb in bus_x:
|
||||
if LX[0] < xb < XT - 12:
|
||||
d += f" H{xb - 6} a6 6 0 0 1 12 0"
|
||||
d += f" H{XT - 12} V{y + 40 - 20}"
|
||||
chemin(d, LIGNE)
|
||||
texte(XT - 56, y - 8, f"ligne {r}", 13, "end", c="#555", gras=True)
|
||||
yt = y + 40
|
||||
transistor(r, yt)
|
||||
ligne(XT - 12, yt + 20, XT - 12, yt + 36, MASSE)
|
||||
masse(XT - 12, yt + 36)
|
||||
resistance_h(XT + 40, XD[r], yt, BASE, "1 kΩ")
|
||||
ligne(XD[r], yt, XD[r], Y_PIN + 20, BASE)
|
||||
|
||||
# ---------------------------------------------------------------- légende
|
||||
ly = 840
|
||||
A(f'<rect x="40" y="{ly - 26}" width="{W - 80}" height="136" rx="10" fill="#fafafa" stroke="#e2e2e2"/>')
|
||||
texte(60, ly, "Légende", 15, "start", "#111", True)
|
||||
items = [
|
||||
(lambda x, y: (ligne(x, y, x + 30, y, ROUGE), ligne(x + 34, y, x + 64, y, VERT),
|
||||
ligne(x + 68, y, x + 98, y, BLEU)),
|
||||
"colonnes : anodes R, G, B (D2 à D10)"),
|
||||
(lambda x, y: ligne(x, y, x + 98, y, LIGNE), "lignes : cathodes communes"),
|
||||
(lambda x, y: ligne(x, y, x + 98, y, BASE), "commande des lignes (D11 à D13)"),
|
||||
(lambda x, y: masse(x + 49, y - 6), "masse (GND) : symboles tous reliés"),
|
||||
(lambda x, y: point(x + 49, y, LIGNE), "point = fils connectés"),
|
||||
(lambda x, y: chemin(f"M{x} {y} H{x + 43} a6 6 0 0 1 12 0 H{x + 98}", LIGNE),
|
||||
"pont = croisement sans contact"),
|
||||
]
|
||||
for i, (dessin, legende) in enumerate(items):
|
||||
col, lig = i % 3, i // 3
|
||||
x, y = (60, 400, 700)[col], ly + 30 + lig * 32
|
||||
dessin(x, y)
|
||||
texte(x + 110, y + 4, legende, 12, "start")
|
||||
texte(60, ly + 98, "Un pixel s'allume quand sa ligne est active (D11, D12 ou D13 à HIGH : le "
|
||||
"transistor relie les cathodes à la masse) et que sa couleur l'est aussi (D2 à D10).",
|
||||
12, "start", "#555", italique=True)
|
||||
A("</svg>")
|
||||
|
||||
with open(sys.argv[1], "w") as f:
|
||||
f.write("\n".join(out))
|
||||
Reference in New Issue
Block a user