master
John Lauer ESC routing round: planes fixture (GND In1, +3V3 In2), Fable's grid router, offline DRC gate, live replay f564403 25d ago
#!/usr/bin/env python3
"""Draw a routing plan over the board's pads as a PNG (a quick look without KiCad).

    python3 demo/routing/esc/render_plan.py BOARD.kicad_pcb PLAN.json OUT.png [--dpi 300] [--zoom x0,y0,x1,y1]

F.Cu copper red, B.Cu copper blue, vias black rings, pads grey (through pads darker), the outline
black. Plane nets' stubs and vias are drawn like any other copper.
"""
import argparse
import json
import os
import sys

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt  # noqa: E402
from matplotlib.patches import Polygon, Circle  # noqa: E402

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from escboard import Board  # noqa: E402
from write_copper import plan_to_copper  # noqa: E402


def main(argv=None):
    ap = argparse.ArgumentParser()
    ap.add_argument("board")
    ap.add_argument("plan")
    ap.add_argument("out")
    ap.add_argument("--dpi", type=int, default=250)
    ap.add_argument("--zoom", help="x0,y0,x1,y1 in mm")
    ap.add_argument("--labels", action="store_true", help="print reference designators")
    a = ap.parse_args(argv)
    board = Board(a.board)
    plan = json.load(open(a.plan, encoding="utf-8"))
    entries = plan["nets"] if isinstance(plan, dict) else plan
    x0, y0, x1, y1 = board.bbox
    if a.zoom:
        x0, y0, x1, y1 = [float(v) for v in a.zoom.split(",")]
    fig, ax = plt.subplots(figsize=((x1 - x0) / 10.0 + 1, (y1 - y0) / 10.0 + 1))
    ax.add_patch(Polygon(board.outline, closed=True, fill=False, lw=0.6, ec="black"))
    for p in board.pads:
        col = "#777777" if p.thru else "#b8b8b8"
        for poly in p.polys:
            ax.add_patch(Polygon(poly, closed=True, fc=col, ec="none", alpha=0.9))
        if p.thru and p.drill:
            ax.add_patch(Circle((p.x, p.y), p.drill / 2.0, fc="white", ec="none"))
    if a.labels:
        for ref, fp in board.footprints.items():
            if x0 <= fp["x"] <= x1 and y0 <= fp["y"] <= y1:
                ax.text(fp["x"], fp["y"], ref, fontsize=3, ha="center", va="center", color="#333333")
    for e in entries:
        segs, vias = plan_to_copper(board, e)
        for sx, sy, ex, ey, layer, w in segs:
            col = "#d0342c" if layer == "F.Cu" else "#2c6fd0"
            ax.plot([sx, ex], [sy, ey], color=col, lw=w * 72.0 / 25.4 * (a.dpi / 72.0) * 0.5, solid_capstyle="round", alpha=0.85, zorder=3 if layer == "F.Cu" else 2)
        for vx, vy, size, drill in vias:
            ax.add_patch(Circle((vx, vy), size / 2.0, fc="#222222", ec="none", zorder=4))
            ax.add_patch(Circle((vx, vy), drill / 2.0, fc="white", ec="none", zorder=5))
    ax.set_xlim(x0, x1)
    ax.set_ylim(y1, y0)
    ax.set_aspect("equal")
    ax.axis("off")
    fig.tight_layout(pad=0.1)
    fig.savefig(a.out, dpi=a.dpi)
    print(a.out)


if __name__ == "__main__":
    main()