feat(kicad9): snap 2-pin parts onto IC pins, post-route visual optimization#302
feat(kicad9): snap 2-pin parts onto IC pins, post-route visual optimization#302lachlanfysh wants to merge 7 commits into
Conversation
Post-route visual optimization that moves resistors, caps, and LEDs onto their connected IC pins for cleaner schematics. Includes T-junction staggering for repeated patterns, power cap offset wires, power bus wire generation, no-connect flags, and label suppression for wired pins. All snap features are guarded behind snap_ran detection so they only activate when _snap_two_pin_parts actually modified the node, preserving identical behavior for circuits that don't use auto_stub or don't have snap-eligible parts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Snap geometry functions (snap_two_pin_parts, stagger, pre-shift) are backend-agnostic — they operate on Point/Tx/orientation strings, not KiCad S-expressions. Moved to schematics/ per maintainer feedback. KiCad-specific sexp output (wireable nets, power bus wires, no-connect flags) stays in tools/kicad9/sexp_schematic.py. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Re: architecture feedback from #297 — on review the snap geometry was already backend-agnostic (operates on Point/Tx/orientation strings, no S-expression references), so I've moved it to The split is now:
|
|
Thanks for making the changes and rebasing onto the |
|
I'm going to close this PR. Eventually we'll merge code from your |
The snap-v2 refactor lost two features from the original snap-placement branch: 1. Connectivity-aware fallback: when routing fails, try placement with real connectivity at increasing expansion factors before falling back to full label-only output. Calls snap_two_pin_parts() to wire close 2-pin parts directly. 2. Label deconfliction: post-pass that detects global_label overlap with component bounding boxes and nudges labels along their direction axis. MR-1 test: switch sheet wires 0→36 (was 63 on v1 branch — remaining gap is power symbol rendering, not yet ported to v2). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…sing Two fixes to match v1 branch output quality: 1. Power symbol rotation: rotate GND/supply symbols based on calc_pin_dir() so they point away from the connected component, matching KiCad convention. Previously all power symbols were at angle=0 regardless of pin direction. 2. Remove snap_ran guard: _find_wireable_nets, _gen_power_bus_wires, and _gen_no_connect_flags now run unconditionally instead of only when snap marker attributes are present. This matches v1 behavior where these always ran. Also removes snap_ran from the force-emit condition for power labels on 2-pin parts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…l wires When a hierarchical subcircuit's routing fails (e.g. daisy_seed with 27 parts), the RoutingFailure propagated to the root and triggered the full-circuit fallback — stubbing ALL nets across ALL subcircuits and destroying wires in successfully-routed sheets like switch_input and led_drivers. Now each child's routing failure is caught independently: the failed child's nets are stubbed and re-routed with labels, while all other children keep their wired connections. This is the correct behavior — one complex subcircuit should degrade gracefully, not poison the whole schematic. MR-1 result: switch_input wires 36→45, routing succeeds on normal path instead of falling through to fallback. Only daisy_seed (the actual failure) gets labels-only output. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
High-fanout nets are now marked _deferred_stub instead of immediately stubbed, preserving connectivity for placement grouping. After placement, _apply_deferred_stubs() classifies each net per-subcircuit: short-distance nets with few pins stay as wires, others get stubbed. A rescue pass un-stubs auto-stubbed nets that ended up close together. Also adds SIGALRM-based routing timeouts (child + top-level), progressive stubbing fallback when routing fails, and KeyError handling for switchbox creation edge cases. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reverting the removal of net.stub checks in get_internal_nets() and get_internal_pins(). Without these checks, fully-stubbed nets (power, classified-as-too-far) leak into the routing graph, causing the router to attempt wires instead of clean labels — producing broken connections and displaced labels. Deferred-stub nets don't need this removal since _stub stays False during placement; only _deferred_stub is set. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Note: I'm currently debugging regressions in the v2 branch ( |
|
Update: regressions resolved on Root cause identified: the
Fix approach on
Output now exactly matches #297 quality across all 14 hierarchical sheets (verified wire/label counts per sheet). 63 tests pass. Branch: |
|
Which of my branches should I merge |
|
`lachlanfysh-pr/kicad9-snap-v2` would be cleanest — v3 is based on `development` (same ancestor as v2) so it should merge without conflicts, and it supersedes everything on v2. v3 is 2 commits on `development`:
Both are self-contained against `development`, so merging into `development` directly would also work if you'd prefer to skip the intermediate branch. |
|
I've merged your |
|
Hi Dave — first, an apology for the revert: I needed those lines somewhere, but dropping them in at that point in your flow was pigheaded and ran over your sequencing. I lean on AI for a lot of the coding; architecture is more my wheelhouse, and I should have respected your design intent there. So I went back and did it properly. I wrote up the design — the snap work as tool-agnostic decisions behind a thin backend interface — and implemented it on a local branch: the snap feature now sits on top of your refactor rather than replacing it, verified on your The open problem is placement. The downstream routines paper over it — deconfliction nudges labels off bodies, a label-aware orientation pass rotates parts so their labels splay — but that's symptom treatment; the root is that the placer reserves space for part bodies + routing channels but not for net labels, so on denser boards labels collide. I've been chasing the root fix and want to flag the dead-ends so you don't spend time on them:
Two small isolated things I also hit:
I can send isolated PRs for those two, and for any of the snap/architecture work that's useful once you've had a look. |
… false trail 4bd527d swapped the vertical orient_map values the wrong way (overcorrecting the maintainer's own tested-correct b3728d2); old deconflict masked it as the step-out. Fixed in net_label_to_sexp. Our devbisme#302 'false trail' claim was wrong. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…, orphan-net lint Browser-viewable copies of work that otherwise lived only in /tmp: - dave-302-reply-DRAFT.md: draft follow-up for devbisme#302 (NOT posted) - pcb-arranger/: standalone first-pass PCB arranger PoC (group-by-sheet -> relational passives -> two-level grid) + design doc - net-lint/: orphan / same-base-name cross-sheet-disconnect lint Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…fore/after for grid_blocks
…ct fix in table; drop grid before/after
|
Thanks for the detailed write-up. I think I need a little less detail and a little more high-level understanding. To wit: are net labels the cause of all this? In other words, if there were no net labels to deal with, would you be able to do your other optimizations (such as two-pin snapping) within the |
…abel question + dead-end map
…to architecture doc
…ranger PoC, net-lint)
…ranger PoC, net-lint)
|
Good timing — I was just prepping the message below when your question came through. Mostly yes — and it maps onto the refactor. The label and wire decisions are now tool-agnostic in Your 8-transistor rotation/mirror test: Labels face away from the body in every rotation and mirror, sit on their pins, no stray cross-sheet wires.
U3 renders fully, the decoupling caps fan to VCC, and the EN pull-up and reset cap tee off the EN pin cleanly. The small fixes are below, one per commit. The bigger structural change — pulling the snap/label/wire work out of the backend into tool-agnostic decisions in I've held off opening any PRs so you can get your head around it at your own pace — but it's all ready as isolated commits against Each fix is its own commit, if it helps to see them mapped to what you flagged:
Two additive placement bits beyond your findings:
On |
|
At this point, I think it's more efficient for you to issue a PR of your stuff against the As for dual visions of schematic generation enhancements, I hope you can come to an arrangement with @rhaingenix that gives us a PR with the best of both. As always, I appreciate your efforts. There wouldn't be any schematic generation for KiCad 9 (or 6, 7, 8) without you. |
|
Thanks Dave - I'm enjoying working through the problems and I've got a lot more ideas for PCBs I want to build than I do time to sit in Kicad hand doing them so effort here that gives me better throughput is selfishly very helpful, but glad to do so in a way that also helps others. I was thinking the conceptual PRs were layering up a bit thick - I will do a clean up, most likely closing this PR and starting afresh with one or more. Further on the topic of throughput of boards - in the last couple of days I've been playing out with some code to get to a decent starter on the layout stage of the actual PCB - It's always maddening to me when Kicad first just drops every part in the corner of the board sorted by component type and you need to unpick it (which decoupling cap goes with which IC?? which power pin did I want that one on?). I I've got quite a nice v1 going there to get initial layout to respect schematic structure also which I will share via PR - will value your architectural feedback there on how you'd like to see extensions of Skidl like that fit in. |
|
Here is one tool I built to quickly make a PCB from a netlist where the parts are arranged into nested groups by hierarchy. But it doesn't do anything to place parts to reduce the lengths of airwires. |
|
OK, I'm closing this again since we have all the other schematics generation PRs integrated into the |








Summary
Post-route visual optimization for KiCad 9 schematic generation that snaps 2-pin parts (resistors, caps, LEDs) onto their connected IC pins, producing cleaner schematics with fewer floating labels.
Rebased onto
developmentbranch per review feedback on #297. Adopts the upstream hierarchical UUID path threading and 4-tuple return pattern fromnode_to_sexp_schematic.Features (gen_schematic.py)
Features (sexp_schematic.py)
no_connectmarkersSafety
snap_randetection — only activates when_snap_two_pin_partsactually modified the nodedevelopmentTest plan
test_and_gate_erc_cleanpasses (0-2 fixable ERC errors)🤖 Generated with Claude Code