feat(router): prune dangling copper automatically at the end of route_all#676
Conversation
…_all The autorouter's speculative emitters (fan-out escape vias, stubs whose route rip-up later replaced, stitching vias for a plane that never materialized) left copper stranded in islands touching no pad and no pour — DRC's NetIslands "copper only, no pads". Removing it required running the standalone prune_copper example by hand. route_all now runs the prune itself, after legalization, so the copper it returns is final and never dead. Opt out via RouteOptions::prune_dangling_copper (default on). Soundness: islands are maximal connected components, so dropping a whole unanchored island cannot disconnect copper that stays, and one pass is already the fixpoint. Every connection the router counts as placed runs pad to pad and therefore lives in a pad-anchored island — routability, the routed/unrouted split, and the pending diagnostics are computed before the prune and mean exactly what they meant. Only new copper is dropped; board copper is judged (it anchors islands) but never removed. The session is not consulted after this point and never escapes the function, so there is no session state left to keep in step. The invariant is enforced rather than only argued: a net the prune empties out is reported unrouted with a diagnostic instead of being claimed as routed with no copper behind it. CM5 40-net subset (VCAD_GPU_BATCH=1 VCAD_GPU_NEGOTIATE=1, effort 1): before: 1292 segments, 84 vias, routability 1.000, 24 island violations after: 789 segments, 84 vias, routability 1.000, 13 island violations All 13 remaining islands are zone-pour fragments, not routed copper: 12 are present on the bare unrouted board, and the 13th (Net-(C240-Pad2)) survives deletion of that net's router copper. Router-attributable copper-only islands go 12 -> 0, and re-running prune_copper on the finished board removes nothing. Prune cost: ~0.4s on the subset. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Choji has used up today's reviewsYour team has reached the free plan's limit of 5 reviews per day, so Choji stepped aside on this pull request. That's a plan limit, not a reflection of the change itself. Your free reviews reset tomorrow. To have Choji review every push without waiting, upgrade to Pro for unlimited reviews. You're on the free plan · 5 reviews/day. |
Full-board CM5 run — confirmed at scale
Routability 0.985 — matches the CM5 record set in #669, no regression at scale. Zero dangling copper in the output
DRC reports 7
Proof for the second row, which is the interesting one: re-running the prune with the anchor rule tightened from "pad or pour" to pad only still removes 0 traces and 0 vias. Those islands contain no router copper of any kind under either rule — they are fill polygons. Deleting the four nets' router copper makes the islands vanish only because it removes the anti-pads that were splitting the pours (verified: doing so also re-merges The bare unrouted board reports 12 such islands; the finished route reports 7 — routing reduced pour fragmentation on net. Where the acceptance criterion landed
Follow-up worth its own change: orphaned pour fragments — KiCad models this as a per-zone island-removal mode, and |
Picks up the phyz sibling-crate version bump to 0.3 (#675), without which CI cannot resolve phyz-md against phyz's main tip.
vcad_ecad_pcb::drc::prune_dangling_copperremoves traces and vias whose galvanic island touches no pad and no pour fragment — debris left behind by rip-up/restore. Until now it only ran when invoked by hand via theprune_copperexample. It now runs as a post-pass insideroute_all, so a fresh route never emits dead copper.What changed
drc.rs— splitdangling_copper_maskout ofprune_dangling_copper, so a caller owning only part of a board's copper can judge its own pieces without touching copper it did not place. Behavior of the public pruner is unchanged.router/legalize.rs— newprune_dangling, running the mask over the candidate board (existing copper + router output) and dropping only the new dead traces/vias. Reusescandidate_pcb, the same subjectlegalizealready judges.router/auto.rs— called at the end ofroute_all_with_opts, after the final negotiation/rip-up phases and after legalization, with alog::info!naming what it removed. NewRouteOptions::prune_dangling_copper(defaulttrue).Interaction with
pendingand the session — auditedstill_unrouted(and theroutability,unrouted_netsand diagnostics derived from it) is computed before the prune and is unaffected by it.trace_arcs, but the router emits none and never deletes the board's.RouteSessiondoes not escaperoute_all_with_optsand is not consulted after this point, so there is no session state left to keep in step with the pruned copper.Measurements — CM5 40-net subset
VCAD_GPU_BATCH=1 VCAD_GPU_NEGOTIATE=1 cm5_bench .scratch/CM5RevEng.kicad_pcb 1 40copper only, no padsRoutability does not regress (1.000 either way, identical net split); 503 dead segments removed. Prune cost ~0.4s.
The 13 remaining islands are pour fragments, not routed copper
Acceptance asked for
grep -c "copper only, no pads"→ 0. That count cannot reach zero by pruning traces and vias, because it also counts zone-fill fragments, which are design intent and not the router's to delete. Evidence:Net-(U5-LX1), which has zero traces and zero vias on the routed board.Net-(C240-Pad2), persists when that net's router copper is deleted from the finished board (6 islands, the pad-less one still there) — it is a pour fragment carved off by a foreign-net trace's anti-pad, not router copper.prune_copperon the freshly routed board removes 0 traces, 0 vias — the router's output is at the prune fixpoint.Follow-up worth its own change: orphaned zone-fill fragments (KiCad's per-zone island-removal mode) are the remaining source of
NetIslandsnoise.Checks
cargo test -p vcad-ecad-pcb --features gpu— 256 passed, 0 failed (4 new tests: dangling island pruned / live copper kept, board copper never pruned, via-chained copper kept, and an end-to-endroute_allinvariant test asserting its output is at the prune fixpoint and every net it reports routed owns copper).cargo clippy -p vcad-ecad-pcb --features gpu -- -D warnings— clean.cargo fmt --all --check— clean.1 999999) is in progress; results will be posted here.🤖 Generated with Claude Code