← Commit history

Delete design/gen/live_plan.py

John Lauer ·f33a4dabf7 ·7d ago ·parent 3310af8
1 file changed −117
design/gen/live_plan.pydeleted−117
@@ -1,117 +0,0 @@-#!/usr/bin/env python3-"""Split the finished routing into (a) the start board's copper and (b) what AI Flow lands live:-placement moves for every part that is not a fixed interface part, and a routing plan (one entry per-polyline, layer markers for vias) for every maze-routed net and GND fan-out."""-import json, os, math-from collections import defaultdict-ROOT = os.path.join(os.path.dirname(__file__), "..")-R = json.load(open(os.path.join(ROOT, "design", "routes.json")))-LS = json.load(open(os.path.join(ROOT, "design", "routes_ls.json")))-lay = json.load(open(os.path.join(ROOT, "design", "layout.json")))-KL = {"L1": "F.Cu", "L3": "In2.Cu", "L5": "In4.Cu", "L6": "B.Cu"}-FIXED = {"J1", "J2", "J3", "J4", "J5", "H1", "H2"}--# live copper: everything the maze produced (routes_ls minus the fixed copper it inherited) plus GND fan-outs-if "fixed_tracks" in LS:-    fixed_T = set(map(tuple, LS["fixed_tracks"])); fixed_V = set(map(tuple, LS["fixed_vias"]))-else:   # older routes_ls.json: rebuild the fixed copper exactly as route_all does before routing-    import sys; sys.path.insert(0, os.path.dirname(__file__))-    import route_all as RA-    fixed_T = set(map(tuple, RA.T)); fixed_V = set(map(tuple, RA.V))-# taken from the FINAL routes (routes.json): the finishing pass prunes stubs and may rip and re-route a few nets, so-# the maze's own output is stale for those; GND copper is live only where the maze or the fan-out made it-ls_T = {tuple(t) for t in LS["tracks"]} - fixed_T; ls_V = {tuple(v) for v in LS["vias"]} - fixed_V-fan_T = set(map(tuple, R["fanout_tracks"])); fan_V = set(map(tuple, R["fanout_vias"]))-live_T = [t for t in map(tuple, R["tracks"]) if t not in fixed_T and (t[1] != "GND" or t in ls_T or t in fan_T)]-live_V = [v for v in map(tuple, R["vias"]) if v not in fixed_V and (v[2] != "GND" or v in ls_V or v in fan_V)]-live_Ts, live_Vs = set(live_T), set(live_V)-start_T = [t for t in map(tuple, R["tracks"]) if t not in live_Ts]-start_V = [v for v in map(tuple, R["vias"]) if v not in live_Vs]---def key(x, y):-    return (round(x, 3), round(y, 3))---def chains(net, tracks, vias):-    """Decompose one net's live copper into polylines; a via joins layers at one point."""-    adj = defaultdict(list)    # (layer, pt) -> [(edge id, other node)]-    edges = []-    for (L, n, x1, y1, x2, y2, w) in tracks:-        a, b = (L, key(x1, y1)), (L, key(x2, y2))-        eid = len(edges); edges.append((a, b, w))-        adj[a].append((eid, b)); adj[b].append((eid, a))-    viapts = {key(v[0], v[1]) for v in vias}-    lone = []-    for vp in viapts:            # via: connect every layer node at that point-        nodes = [nd for nd in list(adj) if nd[1] == vp]-        if len(nodes) < 2:       # copper on one layer only (the other side is a pad or a pour): a via of its own-            lone.append((vp, nodes[0][0] if nodes else "L1"))-        for i in range(len(nodes) - 1):-            a, b = nodes[i], nodes[i + 1]-            eid = len(edges); edges.append((a, b, None))-            adj[a].append((eid, b)); adj[b].append((eid, a))-    used = set()-    out = []-    order = sorted(adj, key=lambda nd: len(adj[nd]) % 2 == 0)   # odd-degree nodes first-    for start in order:-        while any(e not in used for e, _ in adj[start]):-            path = [start]; cur = start; w = None-            while True:-                nxt = [(e, o) for e, o in adj[cur] if e not in used]-                if not nxt:-                    break-                e, o = nxt[0]-                used.add(e)-                if edges[e][2] is not None:-                    w = edges[e][2]-                path.append(o); cur = o-            out.append((path, w))-    entries = []-    for path, w in out:-        pts = []-        layer = path[0][0]-        for i, (L, (x, y)) in enumerate(path):-            if i == 0:-                pts.append([x, y])-            elif L != layer:-                pts.append({"x": x, "y": y, "layer": KL[L]})-                layer = L-            elif pts and isinstance(pts[-1], dict) and abs(pts[-1]["x"] - x) < 1e-6 and abs(pts[-1]["y"] - y) < 1e-6:-                continue-            else:-                pts.append([x, y])-        # a via at the start of a path: express as a layer marker on the first point-        entries.append({"net": net, "width": w or 0.15, "viaSize": 0.4, "viaDrill": 0.2, "layer": KL[path[0][0]], "points": pts})-    vsize = {key(v[0], v[1]): (v[3], v[4]) for v in vias}-    for (x, y), L in lone:-        other = "B.Cu" if KL[L] == "F.Cu" else "F.Cu"-        d, h = vsize[(x, y)]-        entries.append({"net": net, "width": 0.15, "viaSize": d, "viaDrill": h, "layer": KL[L],-                        "points": [[x, y], {"x": x, "y": y, "layer": other}]})-    return entries---bynet_T, bynet_V = defaultdict(list), defaultdict(list)-for t in live_T:-    bynet_T[t[1]].append(t)-for v in live_V:-    bynet_V[v[2]].append(v)-plan = []-for net in sorted(set(bynet_T) | set(bynet_V)):-    plan += chains(net, bynet_T[net], bynet_V[net])-moves = {}-stage = {}-k = 0-for ref, (x, y, rot, side) in lay["placement"].items():-    if ref in FIXED:-        continue-    moves[ref] = {"x": x, "y": y, "rotation": rot}-    stage[ref] = (84.0 + 9.0 * (k % 4), 8.0 + 6.0 * (k // 4), rot, side)-    k += 1-json.dump({"nets": plan}, open(os.path.join(ROOT, "aiflow", "route-plan.json"), "w"), indent=0)-json.dump(moves, open(os.path.join(ROOT, "aiflow", "moves.json"), "w"), indent=1)-json.dump({"tracks": start_T, "vias": start_V, "zones": R["zones"], "texts": R.get("texts", []), "stage": stage},-          open(os.path.join(ROOT, "design", "routes_start.json"), "w"))-print(f"live: {len(plan)} plan entries ({len(live_T)} tracks, {len(live_V)} vias); moves {len(moves)} parts;"-      f" start board copper {len(start_T)} tracks, {len(start_V)} vias")