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.
Summary
segmentIntersectsRectreports 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:EPSis1e-9, but real pin coordinates are not always exactly on the routing grid. A pin atx = 5.3999378connected to one atx = 5.4produces a segment withdx = 6.22e-5. That is far below the schematic grid, yet five orders of magnitude above1e-9, soisVerticalis false,isHorizontalis false, and the early return declares the segment collision free.Everything downstream trusts that answer.
UnroutedTraceRecoverySolverchecks each candidate withpathCollidesWithObstaclesand 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, andnetLabelToTraceSolver.getOutput().tracescontains one trace:schematic_component_8spans 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:
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
UnroutedTraceRecoverySolverrejects the candidate and picks a routed alternative.Sub-solver
Detection lives in
SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions.ts; the visible symptom surfaces throughUnroutedTraceRecoverySolver.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-reportsandtests/examples; two independent fuzzed inputs reduce to this same root cause.