Skip to content

export: breadboard SVG visualiser — Phase 2.6 - #28

Merged
raeq merged 2 commits into
mainfrom
phase-2.6-breadboard
May 19, 2026
Merged

export: breadboard SVG visualiser — Phase 2.6#28
raeq merged 2 commits into
mainfrom
phase-2.6-breadboard

Conversation

@raeq

@raeq raeq commented May 19, 2026

Copy link
Copy Markdown
Owner

Summary

Renders any wirebench design as a self-contained SVG of how it sits on a standard solderless breadboard. Sibling to the existing assembly_guide markdown — the two artefacts share assembly_guide.placement.place(), so the positions narrated by the step-by-step build instructions match the visible positions in the SVG (acceptance criterion #8 of .plans/phase-2.6-spec.md).

  • Subpackage src/framework/export/breadboard/: colors / svg / placement / routing / renderer modules.
  • Locked palette: red = + rail, black = − rail, separate analog & digital cycles. Ordinal (not hash) colour assignment avoids same-colour collisions on adjacent jumpers.
  • Cross-bank jumpers use a double-detour through a clear pivot column so the wire never traverses a chip body's opposite-bank tie strip (which a reader would misread as a connection).
  • Chip bodies render in front of jumpers — any wire passing "behind" a chip is hidden, matching how a real jumper physically routes around the plastic.
  • Pin-name labels (a_1, VSS, q_3) on every chip pin in small white text. DIP pin numbering is the universal convention; pin names are what the design's wire() calls reference.
  • End-cap discs at every plug-in point so a reader can distinguish a real connection from a wire that's merely passing through.
  • Per-endpoint row allocation: when two jumpers chain at the same tie strip, each plugs into a distinct hole (rows A→D for top bank, J→G for bottom).

Spec deferrals (locked in .plans/phase-2.6-spec.md §12):

  • Full-size 63-column board (assembly_guide places parts past col 30; half-size is impossible in practice).
  • Multi-board composite SVG (multi-Board designs raise BreadboardIncompatibleError, matching assembly_guide).

Test plan

  • 26 new tests under tests/framework/export/breadboard/ (placement, routing, renderer, byte-identical goldens for HelloLED / WaterAlarm / Dice / DigitalThermometer / DoorbellProtector).
  • Existing assembly_guide goldens unchanged (row D placement uses the any of <pos>A–<pos>E phrasing so the row letter never appears literally).
  • uv run pytest — 4360 passed.
  • uv run mypy src/ demos/ clean.
  • Every demo's docs/ folder gains a <Design>.breadboard.svg — visual inspection in a browser to confirm the rendering reads correctly. I have not opened the SVGs in a browser; the structural assertions are in tests but a human eyeball pass would catch any aesthetic issues my tests miss.

Render any wirebench design as it would sit on a standard solderless
breadboard. SVG output sibling to the assembly-guide markdown: the
two artefacts share `assembly_guide.placement.place()` so the
positions named in the assembly guide's step-by-step instructions
match the components' positions in the rendered SVG (acceptance
criterion #8 of `.plans/phase-2.6-spec.md`).

Subpackage `src/framework/export/breadboard/`:
  - colors.py — locked palette (red reserved for + rail, black for −,
    4-tone analog & digital signal cycles). Cycles are ordinal, not
    hash-based: adjacent nets on the board get adjacent colours so
    physically-close jumpers can't collide.
  - svg.py — coordinate system, inline-style SVG primitives, dynamic
    canvas width (grows for designs over 63 columns), dynamic
    viewBox y-offset for designs needing many detour bands.
  - placement.py — refusal of SMD / Board / multi-Board designs;
    wraps `assembly_guide.placement.place()`.
  - routing.py — net walker producing coloured Jumper records.
  - renderer.py — interval-coloured detour-band allocation, double-
    detour for cross-bank jumpers (pivots at clear columns so the
    wire never traverses a chip body's opposite-bank tie strip),
    pin-name labels on chip bodies, end-cap discs at every plug-in
    point, per-endpoint row assignment so chained nets land in
    distinct holes on a shared tie strip.

Z-order: surface → jumpers → components, so any jumper section that
would pass behind a chip is hidden by the opaque body — visually
representing how a real jumper physically routes around the chip.

Spec deferrals (`.plans/phase-2.6-spec.md` §12): full-size 63-column
board (assembly_guide places parts past col 30, half-size impossible
in practice); multi-board composite SVG (multi-Board designs raise
`BreadboardIncompatibleError`, matching assembly_guide).

Other changes:
- `assembly_guide.placement._place_2lead_part` now lands passive
  leads on row D (near the trough) instead of row A. Leaves rows
  A–C clear for jumpers terminating on the same tie strip; the
  assembly-guide goldens stay byte-identical because they use the
  `any of <pos>A–<pos>E` tie-strip phrasing.
- `scripts/render_demo_docs.py` adds `breadboard` to its format loop
  and emits an SVG refusal stub for designs the visualiser refuses.
- 26 new tests under `tests/framework/export/breadboard/` (placement,
  routing, renderer, byte-identical goldens for 5 demos).
- Main README and 12 per-demo READMEs reference the new format.
- Every demo's `docs/` folder gains a `<Design>.breadboard.svg`.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new breadboard SVG exporter that renders wirebench circuits onto a solderless breadboard layout, sharing assembly-guide placement so visual and textual build instructions align.

Changes:

  • Added breadboard exporter modules for palette, placement, routing, SVG primitives, and rendering.
  • Updated demo-doc generation and README links to include .breadboard.svg outputs or refusal stubs.
  • Added breadboard placement/routing/renderer/golden regression tests.

Reviewed changes

Copilot reviewed 36 out of 67 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/framework/export/breadboard/__init__.py Registers the new breadboard exporter adapter.
src/framework/export/breadboard/colors.py Defines locked SVG colors and signal palettes.
src/framework/export/breadboard/placement.py Wraps shared assembly-guide placement and refusal checks.
src/framework/export/breadboard/renderer.py Builds the complete breadboard SVG document.
src/framework/export/breadboard/routing.py Converts nets into routed jumpers with colors and rail handling.
src/framework/export/breadboard/svg.py Provides SVG geometry and primitive string emitters.
src/framework/export/assembly_guide/placement.py Moves 2-lead parts to row D for clearer jumper routing.
scripts/render_demo_docs.py Generates breadboard SVG docs and format-specific refusal stubs.
tests/framework/export/breadboard/test_breadboard_golden.py Adds golden regression coverage for breadboard SVG output.
tests/framework/export/breadboard/test_placement.py Tests placement sharing and refusal behavior.
tests/framework/export/breadboard/test_renderer.py Tests XML validity, structure, determinism, and refusals.
tests/framework/export/breadboard/test_routing.py Tests rail colors and signal palette behavior.
tests/framework/export/breadboard/__init__.py Adds the breadboard test package.
README.md Documents the new export type.
Demo README files Add links to generated breadboard SVG artifacts.
Demo .breadboard.svg files Add generated breadboard renderings or refusal stubs.
demos/penfold_one_second_timer/docs/* Adds generated docs artifacts for OneSecondTimer.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +308 to +314
rail_marker_top = '+TOP'
rail_marker_bot = '+BOT'
elif net_polarity is False:
color = RAIL_MINUS_JUMPER
kind = JumperKind.RAIL_MINUS
rail_marker_top = '-TOP'
rail_marker_bot = '-BOT'
Comment on lines +10 to +15
Refusal (acceptance criterion 7 in `.plans/phase-2.6-spec.md`):
- SMD parts (`is_breadboard_compatible == False`)
- Top-level `Board` (one populated PCB)
- Multi-board designs (nested `Board`s — v1 defers; spec §12)
- Designs whose placement exceeds 63 positions

- Rail jumpers always route to the TOP rails. The shared assembly
  guide instructs builders to wire only the top `+`/`-` rails to
  the supply; standard breadboards keep top and bottom rail strips
  electrically isolated, so a chip pin on the bot bank routing to
  `+BOT`/`-BOT` would show an unpowered connection. Bot-bank pins
  now cross the trough via the standard cross-bank double-detour,
  terminating at the powered top rail. Same-column rail verticals
  (DIP VCC pin at col X to +TOP at the same col X) get hidden in
  the middle by the chip body's z-order, visually representing the
  wire arching over the package.

- Refusal docstring updated to describe the actual capacity limit:
  designs that exceed `svg.MAX_POSITIONS` (four daisy-chained
  full-size breadboards) are refused. Designs that just exceed one
  board's 63 columns (Dice → 106, DoorbellProtector → 92) are
  rendered on a canvas wide enough to fit them, with a faint
  column-break marker every 63 columns.

Breadboard goldens for WaterAlarm, Dice, DoorbellProtector
refreshed; demo SVGs regenerated.
@raeq
raeq merged commit e6f7948 into main May 19, 2026
3 checks passed
@raeq
raeq deleted the phase-2.6-breadboard branch May 19, 2026 16:30
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.

2 participants