Files
2026-10-08 14:36:28 +02:00

490 lines
22 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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")