Skip to content

feat(kicad9): snap 2-pin parts onto IC pins, power bus wires, label deconfliction#297

Closed
lachlanfysh wants to merge 2 commits into
devbisme:masterfrom
lachlanfysh:pr/kicad9-snap-placement
Closed

feat(kicad9): snap 2-pin parts onto IC pins, power bus wires, label deconfliction#297
lachlanfysh wants to merge 2 commits into
devbisme:masterfrom
lachlanfysh:pr/kicad9-snap-placement

Conversation

@lachlanfysh
Copy link
Copy Markdown

@lachlanfysh lachlanfysh commented May 2, 2026

What this does

When SKiDL's fallback placement kicks in (routing fails, schematic still needs to be readable), 2-pin passive parts used to float randomly around the page with net labels as the only clue they belonged to a particular IC. This PR makes the fallback output look like a human drew it.

Snap placement

Resistors, caps, LEDs, and switches now snap directly onto the IC pin they connect to, extending outward in the pin's direction. Three passes handle increasingly complex cases:

  1. Direct snap — each 2-pin part finds the IC pin it shares a signal net with and aligns to it.
  2. Chain snap — parts connected to already-snapped parts form chains (e.g. IC pin ← pull-up R ← indicator LED).
  3. Stack snap — when multiple 2-pin parts share the same IC pin (e.g. a switch and its pull-down resistor), they fan out perpendicular to avoid overlapping.

Decoupling caps get special treatment: they snap to power pins but offset away from the IC body so they don't obscure signal pins.

T-junction stagger

When an IC has a repeating pattern (3+ pins each driving the same number of parts, like 16 mux channels each with a pot), the parts are arranged in a stepped stagger extending outward from the IC. Each pin's group sits further from the body than the last, with junction wires connecting back. The step size adapts to the physical size of the parts being staggered (a row of switches needs more room than a row of resistors).

IC group redistribution

Before stagger fans are placed, ICs that will have large fans are shifted apart vertically so the fans don't overlap each other. Parts already snapped to the IC (from pass 1) move with it, so connectivity is preserved. The stagger fans are then placed at the final IC positions.

Power symbol orientation

GND bars and supply arrows now rotate to point away from their component pin. The previous code always placed them at angle 0, which meant ground symbols often sat on top of their IC instead of hanging below it. The fix accounts for the Y-axis flip between SKiDL's internal coordinate system (Y-up) and KiCad's schematic coordinates (Y-down).

Power bus wires

When multiple pins on the same power net are co-linear (e.g. a row of VCC pins along one side of an IC), a single bus wire connects them instead of each getting an independent power symbol.

Label cleanup

Redundant net labels on snapped pin clusters are suppressed (the physical wire connection makes them unnecessary). Label angles are computed from the transformed pin direction so they don't overlap their parent symbol.

Tested on

A 150+ part Daisy Seed carrier board design: CD74HC4067E mux with 16 pots, 4x SN74HC165N shift registers with 28 switches, 6x DM13A LED drivers with 85 LEDs, H11L1 optocoupler MIDI I/O, SSD1306 OLED, LD33V regulator. All through-hole DIP.

@lachlanfysh lachlanfysh force-pushed the pr/kicad9-snap-placement branch 2 times, most recently from 0162457 to d7c302e Compare May 3, 2026 02:14
@lachlanfysh lachlanfysh changed the title kicad9: snap 2-pin parts onto IC pins, power bus wires, and T-junction stagger kicad9: snap placement, power bus wires, T-junction stagger, power symbol orientation May 3, 2026
@lachlanfysh lachlanfysh force-pushed the pr/kicad9-snap-placement branch from d7c302e to e79fd48 Compare May 3, 2026 07:03
@lachlanfysh lachlanfysh force-pushed the pr/kicad9-snap-placement branch from e79fd48 to 7dced5b Compare May 11, 2026 05:54
@lachlanfysh
Copy link
Copy Markdown
Author

Pushed two additional fixes addressing decoupling-cap visual issues observed on the Korg ER-1 successor schematic:

7dced5ba schematic: wire snapped decoupling caps to IC, suppress redundant power-net labels

  1. Cap-to-IC wire generation — when _snap_two_pin_parts pass-1 places a decoupling cap offset (200 mil) from its target IC power pin (the both_power=True path), the cap was placed near the IC but visually disconnected: two duplicate power symbols served as the only indication of the connection. Now emits a wire from the IC pin to the cap's snapped +ve pin and suppresses that pin's label, so the connection is drawn rather than implied by overlapping labels.

    State threaded via node._power_cap_wires / node._power_cap_suppressed_pins, parallel to the existing _tjunction_wires pattern.

  2. _find_wireable_nets handles power nets — previously the function explicitly skipped power nets, so when pass-3 stack-snapped a second cap onto an already-occupied IC pin (exact-overlap snap), the redundant power symbol at the overlap point was still emitted. Now power nets go through the same cluster-and-suppress logic; for power nets all pins on the sheet are considered (not just stubbed ones, since power symbols emit regardless of stub state).

Observed on MR-1 schematic:

  • pots_and_mux: 0→4 cap-to-mux wires drawn, ~4 redundant power symbols suppressed
  • led_drivers: 28→32 wires (additional pass-1 cap-snap successes)
  • switch_input: 32→36 wires

These were the two issues highlighted as the remaining visual gaps when reviewing the MR-1 generated schematic.

lfyysh and others added 2 commits May 17, 2026 22:05
When the connectivity-aware placer cannot route all nets, fallback
placement now snaps 2-pin passive components (resistors, capacitors,
LEDs) directly onto the IC pins they connect to.

The algorithm runs three passes:
1. Direct snap — place each 2-pin part at its connected IC pin
2. Chain snap — extend chains (IC ← R ← LED) with occupied-pin tracking
3. Stack snap — stack parts sharing the same IC pin vertically

Additional placement improvements:
- Pre-shift ICs before stagger to prevent overlap
- T-junction stagger for cleaner routing of fanout nets
- Power cap offset from IC body with connecting wires

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…napped parts

Improve schematic output quality when parts are snapped together:

- Suppress redundant net labels when pins physically overlap (snapped)
- Generate power bus wires connecting co-linear power net pins
- Emit T-junction and power-cap wires from stagger placement data
- Orient power symbols (GND down, VCC up) based on pin direction
- Skip labels for NCNet pins and emit no_connect flags instead
- Post-process label positions to avoid overlapping component bodies
- Suppress NetTerminal labels when real pins already provide connectivity

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@lachlanfysh lachlanfysh force-pushed the pr/kicad9-snap-placement branch from 7dced5b to 143f45e Compare May 17, 2026 12:06
@lachlanfysh lachlanfysh changed the title kicad9: snap placement, power bus wires, T-junction stagger, power symbol orientation feat(kicad9): snap 2-pin parts onto IC pins, power bus wires, label deconfliction May 17, 2026
@devbisme
Copy link
Copy Markdown
Owner

Thanks for your contribution!

Please base your PR relative to the development branch rather than the master branch. The master contains the code for the latest release on PyPi. The development branch has moved ahead and your code won't merge with it.

Also, your code seems to be doing a lot of place-and-route related work in the tools/kicad9 subdirectory. The code under tools is mainly intended for generating outputs for various backend tools. If you're actually changing part locations and wiring, then that should probably go into the schematics subdirectory so it can apply to all the backends rather than being replicated for each one.

@lachlanfysh
Copy link
Copy Markdown
Author

Thanks for the feedback!

Rebasing onto development: Will do. I see development has touched several of the same files (gen_schematic.py, sexp_schematic.py, place.py, etc.) so there'll be conflicts to resolve — I'll work through those and force-push a clean rebase.

Architecture split: Good point. The current split is:

  • schematics/place.py — the backend-agnostic auto_stub decision logic lives here already: _auto_stub_cross_group(), _auto_stub_large_groups(), and the row-based placer for large floating groups. These decide which nets to stub and modify the circuit graph.

  • tools/kicad9/gen_schematic.py_snap_two_pin_parts() and the stagger/shift helpers. These operate on placed parts using KiCad-specific pin transforms (pin.orientation, part Tx matrices) to compute physical snap positions in S-expression coordinates.

  • tools/kicad9/sexp_schematic.py_find_wireable_nets() and _deconflict_labels(). These work directly with the KiCad S-expression output (wire elements, label positions, power symbols).

The stub decision logic is already backend-agnostic. The snap/wire/label code is KiCad-specific because it needs pin world positions and S-expression geometry. I could refactor the geometric snap logic up to schematics/ behind an abstract pin-position interface if you'd prefer — happy to take direction on how you'd like this structured.

I'll get the rebase onto development done first, then we can iterate on the architecture if needed.

@lachlanfysh
Copy link
Copy Markdown
Author

Closing in favor of a new PR targeting development branch with rebased changes (as requested in review feedback).

@lachlanfysh
Copy link
Copy Markdown
Author

Both items addressed in #302:

  1. Rebased onto development — clean branch off upstream/development, adopting your hierarchical UUID path threading and 4-tuple return pattern.

  2. Architecture split — snap geometry moved to schematics/snap.py (backend-agnostic). KiCad S-expression output stays in tools/kicad9/sexp_schematic.py.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants