Skip to content

feat: add SameNetTraceSegmentMergingSolver pipeline phase to merge close same-net collinear segments#155

Open
CharlesWong wants to merge 2 commits intotscircuit:mainfrom
CharlesWong:feat/same-net-segment-merging
Open

feat: add SameNetTraceSegmentMergingSolver pipeline phase to merge close same-net collinear segments#155
CharlesWong wants to merge 2 commits intotscircuit:mainfrom
CharlesWong:feat/same-net-segment-merging

Conversation

@CharlesWong
Copy link
Copy Markdown

Summary

Closes #29

/claim #29

This PR implements a new pipeline phase — — that runs between TraceCleanupSolver and the final NetLabelPlacementSolver in the SchematicTracePipelineSolver. The phase reduces visual clutter in schematics by merging collinear trace segments belonging to the same net when they are close together (gap ≤ 0.15 units) or overlapping.


Problem

As shown in issue #29, when two trace segments of the same net run near each other along the same axis, they appear as visually redundant parallel lines with a tiny gap. This is aesthetically distracting and informationally redundant — the two segments represent the same electrical connection and should appear as a single line.


Implementation

New file: lib/solvers/SameNetTraceSegmentMergingSolver/SameNetTraceSegmentMergingSolver.ts

A new BaseSolver subclass with the following behavior:

  1. Groups traces by globalConnNetId — only traces on the same net are candidates for merging.

  2. Extracts all axis-aligned segments from each trace path in the group, classifying each as horizontal or vertical based on which axis has the larger extent.

  3. Finds mergeable pairs across different trace paths in the same net:

    • Horizontal segments: same y coordinate within ±0.05 tolerance, and gap in x ≤ 0.15 units (or overlapping)
    • Vertical segments: same x coordinate within ±0.05 tolerance, and gap in y ≤ 0.15 units (or overlapping)
  4. Performs the merge:

    • Extends the first segment to cover the full span (min to max across both segments)
    • Collapses the second segment to a single midpoint (zero-length degenerate segment), effectively removing it from the visual output
    • Averages the axis coordinate (y for horizontal, x for vertical) to handle micro-alignment differences
  5. Single-step synchronous solver — sets this.solved = true at the end of _step(), no iterative state machine needed.

Pipeline integration: lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts

  • Added sameNetTraceSegmentMergingSolver?: SameNetTraceSegmentMergingSolver property
  • Added a definePipelineStep(...) entry after traceCleanupSolver and before the final netLabelPlacementSolver
  • The subsequent netLabelPlacementSolver uses sameNetTraceSegmentMergingSolver?.getOutput().traces with fallback to traceCleanupSolver output

Constants (tunable):

const COLLINEAR_TOLERANCE = 0.05  // max axis deviation to be considered collinear
const GAP_TOLERANCE = 0.15        // max gap between segments to be merged

Tests

Five new tests in tests/solvers/same-net-trace-segment-merging.test.ts:

Test Expected behavior
Merge horizontal same-net segments with tiny gap (0.1) ✅ Merged — span extends from 0 to 5
Different nets with small gap ✅ NOT merged — different net, preserved
Same net but large gap (1.0) ✅ NOT merged — gap exceeds tolerance
Empty trace list ✅ No crash, returns empty
Vertical collinear same-net segments ✅ Merged — span extends from y=0 to y=4

All 5 tests pass:

bun test tests/solvers/same-net-trace-segment-merging.test.ts
✓ merges two horizontal same-net segments with a tiny gap [1.73ms]
✓ does NOT merge segments of different nets [0.19ms]
✓ does NOT merge same-net segments that are far apart [0.10ms]
✓ handles empty trace list without crashing
✓ merges vertical collinear same-net segments [0.16ms]

5 pass, 0 fail

TypeScript check clean:

bunx tsc --noEmit  # no output = no errors

Full test suite:

bun test
54 pass, 5 skip, 0 fail

(9 visual snapshot tests were updated to reflect the improved merged output — all previously passing, now updated)


Visual Changes

The updated snapshots show the effect: in examples like example29, the number of SVG line elements has been significantly reduced (from ~342 lines to much fewer) because formerly-redundant same-net segments are now merged into single longer lines.

The net topology is preserved — this is purely a cosmetic/clarity improvement with no semantic change to which components are connected.


Edge Cases Handled

  • No same-net groups: Solver exits gracefully with no modifications
  • Overlapping segments: gap calculation uses Math.max(0, ...), so overlapping segments (negative gap) correctly pass the ≤ 0.15 test
  • Micro y/x drift: averaging the axis coordinate handles floating-point imprecision from upstream solvers
  • Single trace per net: Skipped — only multi-trace net groups are processed (no intra-trace merging)
  • Degenerate segments (zero-length): The collapsed segment produces a zero-length [midpoint, midpoint] pair which downstream renderers ignore

Design Decisions

Why collapse the second segment instead of removing points? Removing points from the middle of tracePath could invalidate segment indices being processed in the same pass. Collapsing to zero-length is safe, non-destructive to array structure, and produces correct visual output (zero-length SVG line = invisible).

Why run after TraceCleanupSolver? TraceCleanup straightens, untangles, and L-balances traces first. Running the merger after ensures we operate on clean, already-simplified paths rather than fighting the cleanup.

Why before the final NetLabelPlacementSolver? Net labels need to know the final trace positions to avoid overlap. Merging first gives the label placer correct, final segment positions.

CharlesWong and others added 2 commits March 31, 2026 05:43
Implements new pipeline phase that combines collinear trace segments
belonging to the same net when they are close together (gap ≤ 0.15 units)
or overlapping. This reduces visual clutter in schematics where same-net
traces run near each other.

Closes tscircuit#29

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The new pipeline phase changes the visual output of schematic traces by
merging close collinear same-net segments. Update snapshots accordingly.
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 31, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
schematic-trace-solver Ready Ready Preview, Comment Mar 31, 2026 0:49am

Request Review

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New Phase To combine same-net trace segments that are close together

1 participant