Skip to content

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

Description

@tcsenpai

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.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions