tscircuit / tscircuit/schematic-trace-solver

bug: nearly axis-aligned segments escape all obstacle collision checks, letting traces pass through chips

Open
#1,099 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
13
Forks
314
Avg merge
9h 3m
Merged PRs (30d)
132

Description

Summary

segmentIntersectsRect reports no collision for any segment that is neither exactly vertical nor exactly horizontal. Segments that are only nearly axis aligned therefore escape every obstacle check, and a recovered trace can run straight through a chip body.

Root cause

In lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions.ts:

const EPS = 1e-9
...
const vert = isVertical(a, b, eps)
const horz = isHorizontal(a, b, eps)
if (!vert && !horz) return false

EPS is 1e-9, but real pin coordinates are not always exactly on the routing grid. A pin at x = 5.3999378 connected to one at x = 5.4 produces a segment with dx = 6.22e-5. That is far below the schematic grid, yet five orders of magnitude above 1e-9, so isVertical is false, isHorizontal is false, and the early return declares the segment collision free.

Everything downstream trusts that answer. UnroutedTraceRecoverySolver checks each candidate with pathCollidesWithObstacles and accepts the first that passes, so the skewed candidate is accepted and emitted as the final trace.

Minimized reproducer

{
  "chips": [
    {
      "chipId": "schematic_component_4",
      "center": { "x": 5.4, "y": 2.4 },
      "width": 0.3041465,
      "height": 0.9,
      "pins": [
        { "pinId": "SJ2.1", "x": 5.3999378, "y": 1.9499999999999997, "_facingDirection": "y-" }
      ]
    },
    {
      "chipId": "schematic_component_5",
      "center": { "x": 5.555, "y": 1.2 },
      "width": 1.21,
      "height": 0.6,
      "pins": [
        { "pinId": "R3.2", "x": 5.4, "y": 1.5, "_facingDirection": "y+" }
      ]
    },
    {
      "chipId": "schematic_component_8",
      "center": { "x": 5.555, "y": 1.2 },
      "width": 0.555,
      "height": 0.9,
      "pins": []
    }
  ],
  "directConnections": [
    { "pinIds": ["SJ2.1", "R3.2"], "netId": ".SJ2 > .pin1 to .R3 > .pin2" }
  ],
  "netConnections": [],
  "availableNetLabelOrientations": {}
}

Observed

The solver reports solved, and netLabelToTraceSolver.getOutput().traces contains one trace:

SJ2.1-R3.2: (5.3999378, 1.95) -> (5.4, 1.5000000000000004)

schematic_component_8 spans x [5.2775, 5.8325], y [0.75, 1.65]. The segment enters that rect at y = 1.65 and ends at y = 1.5, so 0.15 units of it are inside a chip the trace does not terminate on.

Calling the helper directly confirms the check itself is what fails:

segmentIntersectsRect({x:5.3999378,y:1.95}, {x:5.4,y:1.5}, chip8) === false
segmentIntersectsRect({x:5.4,     y:1.95}, {x:5.4,y:1.5}, chip8) === true

Only the 6e-5 skew separates the two calls.

Expected

A segment that passes through a chip body should be reported as colliding regardless of a sub-grid skew, so UnroutedTraceRecoverySolver rejects the candidate and picks a routed alternative.

Sub-solver

Detection lives in SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions.ts; the visible symptom surfaces through UnroutedTraceRecoverySolver.

Notes

This is not specific to co-located chips. The same trace crosses the blocking chip when that chip is moved off the other chip's center and given a real pin, so it is a general obstacle-detection gap rather than a degenerate-input artifact. Found by differential fuzzing over the fixtures in tests/bug-reports and tests/examples; two independent fuzzed inputs reduce to this same root cause.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions.ts and reproduce the minimized case with segmentIntersectsRect. Check the related pathCollidesWithObstacles flow in UnroutedTraceRecoverySolver, using fixtures in tests/bug-reports and tests/examples. Done means the skewed segment is reported as colliding and recovery rejects it in favor of a routed alternative.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.