main
John Lauer README: component table per the wiki format, open-source section, no duplicate hero; generators at gen/ 39a59ef 7d ago
#!/usr/bin/env python3
"""Render and clearance-check the routing plan (container side, before KiCad)."""
import sys, os, json, math
sys.path.insert(0, os.path.dirname(__file__))
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from shapely.geometry import LineString, Point
from shapely.strtree import STRtree

COL = {"L1": "#d62728", "L3": "#2ca02c", "L5": "#1f77b4", "L6": "#9467bd"}


def shapes(tracks, vias):
    """list of (layerset, net, geom)"""
    out = []
    for (lay, net, x1, y1, x2, y2, w) in tracks:
        out.append(({lay}, net, LineString([(x1, y1), (x2, y2)]).buffer(w / 2, cap_style=1), ("t", lay, x1, y1, x2, y2)))
    for (x, y, net, d, h) in vias:
        out.append(({"L1", "L3", "L5", "L6"}, net, Point(x, y).buffer(d / 2), ("v", x, y)))
    return out


def clearance(items, clr=0.15, extra=()):
    geoms = [g for (_, _, g, _) in items]
    tree = STRtree(geoms)
    bad = []
    for i, (ls, net, g, tag) in enumerate(items):
        for j in tree.query(g.buffer(clr)):
            if j <= i:
                continue
            ls2, net2, g2, tag2 = items[j]
            if net2 == net or not (ls & ls2):
                continue
            dd = g.distance(g2)
            if dd < clr - 1e-6:
                bad.append((round(dd, 3), net, net2, sorted(ls & ls2)[0], tag[:4], tag2[:4]))
    return bad


def render(tracks, vias, path, outline=None, extra_polys=(), xlim=(0, 76), ylim=(115, 0), layers=("L1", "L3", "L5", "L6"), dpi=110):
    fig, ax = plt.subplots(figsize=(8, 12))
    if outline:
        xs, ys = zip(*outline)
        ax.plot(xs, ys, "k-", lw=0.6)
    for (lay, net, x1, y1, x2, y2, w) in tracks:
        if lay in layers:
            ax.plot([x1, x2], [y1, y2], "-", color=COL[lay], lw=max(0.3, w * 3), solid_capstyle="round", alpha=0.85)
    for (x, y, net, d, h) in vias:
        ax.add_patch(plt.Circle((x, y), d / 2, color="#444", lw=0, alpha=0.8))
    for poly, c in extra_polys:
        xs, ys = zip(*poly)
        ax.fill(xs, ys, color=c, alpha=0.35, lw=0)
    ax.set_xlim(*xlim); ax.set_ylim(*ylim); ax.set_aspect("equal")
    ax.grid(True, lw=0.2)
    fig.savefig(path, dpi=dpi, bbox_inches="tight")
    plt.close(fig)


if __name__ == "__main__":
    import hs
    pairs, lines = hs.all_routes()
    T = [t for p in pairs + lines for t in p.tracks]
    V = [v for p in pairs + lines for v in p.vias]
    bad = clearance(shapes(T, V), 0.15)
    print(len(bad), "clearance violations")
    for b in bad[:40]:
        print(b)
    os.makedirs("../design/plots", exist_ok=True)
    render(T, V, "../design/plots/hs_all.png")
    for k in range(4):
        cx = hs.CX[k]
        render(T, V, f"../design/plots/hs_port{k}_top.png", xlim=(cx - 9, cx + 9), ylim=(22, 2), dpi=160)
    render(T, V, "../design/plots/hs_fan.png", xlim=(15, 75), ylim=(113, 86), dpi=150)
    render(T, V, "../design/plots/hs_tune.png", xlim=(10, 76), ylim=(80, 34), dpi=150)
    render(T, V, "../design/plots/hs_bus.png", xlim=(5, 76), ylim=(110, 76), dpi=150)