main
John Lauer README: component table per the wiki format, open-source section, no duplicate hero; generators at gen/ 39a59ef 7d ago
"""Whole-board silkscreen audit input (KiCad python.exe): every fitted body (courtyard box), every pad with its mask
opening, every via and plated/non-plated hole as obstacles on both silk layers; every silk text and visible field as
a mark. Writes design/silk_audit_input.json for `adom-aiflow silkscreen-audit`."""
import json, os, sys
import pcbnew
TO = pcbnew.ToMM
b = pcbnew.LoadBoard(sys.argv[1])
def bx(e): return [round(TO(e.GetLeft()), 3), round(TO(e.GetTop()), 3), round(TO(e.GetRight()), 3), round(TO(e.GetBottom()), 3)]
obs, inv, marks = [], [], []
for fp in b.GetFootprints():
    ref = fp.GetReference()
    for side, lay, sl in (("F", pcbnew.F_CrtYd, "F.SilkS"), ("B", pcbnew.B_CrtYd, "B.SilkS")):
        cy = fp.GetCourtyard(lay)
        if cy.OutlineCount() and ref != "J1":
            obs.append(dict(id=f"{ref}:body", kind="body", layer=sl, box=bx(cy.BBox())))
            inv.append(dict(id=f"{ref}:body", kind="body", layers=[sl]))
    for p in fp.Pads():
        pid = f"{ref}.{p.GetNumber() or 'np'}@{TO(p.GetPosition().x):.2f},{TO(p.GetPosition().y):.2f}"
        e = p.GetBoundingBox()
        layers = []
        if p.IsOnLayer(pcbnew.F_Mask) or p.HasHole(): layers.append("F.SilkS")
        if p.IsOnLayer(pcbnew.B_Mask) or p.HasHole(): layers.append("B.SilkS")
        for sl in layers:
            obs.append(dict(id=pid, kind="pad", layer=sl, box=bx(e)))
        if layers: inv.append(dict(id=pid, kind="pad", layers=layers))
    for f in (fp.Reference(), fp.Value()):
        if f.IsVisible() and f.GetLayer() in (pcbnew.F_SilkS, pcbnew.B_SilkS):
            marks.append(dict(id=f"{ref}:{'ref' if f is fp.Reference() or f.GetText()==ref else 'value'}:{f.GetText()}", layer=b.GetLayerName(f.GetLayer()), box=bx(f.GetBoundingBox())))
k = 0
for t in b.GetTracks():
    if t.GetClass() == "PCB_VIA":
        k += 1
        x, y, r = TO(t.GetPosition().x), TO(t.GetPosition().y), TO(t.GetDrillValue()) / 2
        vid = f"via{k}@{x:.2f},{y:.2f}"
        for sl in ("F.SilkS", "B.SilkS"):
            obs.append(dict(id=vid, kind="hole", layer=sl, box=[round(x - r, 3), round(y - r, 3), round(x + r, 3), round(y + r, 3)]))
        inv.append(dict(id=vid, kind="hole", layers=["F.SilkS", "B.SilkS"]))
for d in b.GetDrawings():
    if d.GetClass() == "PCB_TEXT" and d.GetLayer() in (pcbnew.F_SilkS, pcbnew.B_SilkS):
        marks.append(dict(id=f"text:{d.GetText()}", layer=b.GetLayerName(d.GetLayer()), box=bx(d.GetBoundingBox())))
json.dump(dict(obstacles=obs, inventory=inv, marks=marks), open(sys.argv[2], "w"))
print(f"obstacles {len(obs)}, inventory {len(inv)}, marks {len(marks)}")