NetLabelToTraceSolver can emit a slanted (non-orthogonal) trace when it recovers a connection between two net-label pins that are only approximately aligned.
Sub-solver involved
lib/solvers/NetLabelToTraceSolver/NetLabelToTraceSolver.ts
buildCandidatePairs deliberately accepts pin pairs whose perpendicular offset is non-zero but small:
const MAX_NAMED_NET_RECOVERY_PERPENDICULAR_OFFSET = 0.05
const MAX_ROUTED_COMPONENT_RECOVERY_PERPENDICULAR_OFFSET = 0.25
tryAcceptCurrentRoute then stores the routed path as-is. Because the two pins are not exactly aligned, the resulting two-point path is diagonal by up to that tolerance. This is the final pipeline stage (netLabelToTraceSolver is the last step in pipelineDef), so the slanted segment reaches the output. TraceCleanupSolver runs earlier and cannot correct it.
Minimized input
4 chips, no direct connections, a single 4-pin net.
{"chips":[{"chipId":"schematic_component_1","center":{"x":-11.8075,"y":9},"width":1.2850000000000001,"height":0.759999999999998,"pins":[{"pinId":"schematic_port_2","x":-12,"y":9.379999999999999,"_facingDirection":"y+"}]},{"chipId":"schematic_component_36","center":{"x":-15,"y":10.4},"width":0.6000000000000014,"height":0.6799999999999997,"pins":[{"pinId":"schematic_port_73","x":-14.7,"y":10.4,"_facingDirection":"x+"}]},{"chipId":"schematic_component_40","center":{"x":-14.044999999999998,"y":6.4},"width":1.209999999999999,"height":0.5999999999999996,"pins":[{"pinId":"schematic_port_81","x":-14.2,"y":6.7,"_facingDirection":"y+"}]},{"chipId":"schematic_component_56","center":{"x":-9.42,"y":10.09},"width":1.1400000000000006,"height":0.870000000000001,"pins":[{"pinId":"schematic_port_112","x":-9.99,"y":10.39,"_facingDirection":"x-"}]}],"directConnections":[],"netConnections":[{"netId":"NET_01","isGround":false,"netLabelWidth":0.42,"netLabelHeight":0.84,"pinIds":["schematic_port_2","schematic_port_73","schematic_port_81","schematic_port_112"]}],"availableNetLabelOrientations":{"NET_01":["y+"]},"maxMspPairDistance":2.4}
Reproduction
const solver = new SchematicTracePipelineSolver(inputProblem as any)
solver.solve()
const { traces } = solver.netLabelToTraceSolver!.getOutput()
Observed
solver.solved === true, solver.failed === false, and the output contains:
net-label-to-trace-schematic_port_112--schematic_port_73
(-9.99, 10.39) -> (-14.7, 10.4)
A 4.71-unit-long wire with 0.01 of vertical drift, which renders as a visibly slanted schematic trace.
Expected
Every segment of every emitted trace is axis-aligned: consecutive points share either an x or a y coordinate. Schematic traces are orthogonal by construction.
Also reachable from a committed fixture
tests/assets/example51.json on main produces three such segments:
net-label-to-trace-schematic_port_111--schematic_port_116 (10.85, 10.4) -> (0.9279125, 10.397375450000002)
net-label-to-trace-schematic_port_110--schematic_port_113 (-8.85, 10.4) -> (-0.12791249999999998, 10.40262455)
net-label-to-trace-schematic_port_112--schematic_port_73 (-9.99, 10.39) -> (-14.7, 10.4)
PR #722 ("straighten segments left slightly off-axis by imprecise pin input") addresses a related symptom, but only in TraceCleanupSolver, which runs before this stage. It does not cover traces that NetLabelToTraceSolver creates afterwards.
How this was found
Property-based fuzzing: mutating the committed fixtures (chip shifts and rotations, added and removed connections, pin-side changes, duplicated net labels, extra and degenerate chips) and asserting that every emitted trace segment is axis-aligned, judged differentially against each fixture's own baseline. Off-axis segments reproduced across several unrelated base fixtures.
NetLabelToTraceSolvercan emit a slanted (non-orthogonal) trace when it recovers a connection between two net-label pins that are only approximately aligned.Sub-solver involved
lib/solvers/NetLabelToTraceSolver/NetLabelToTraceSolver.tsbuildCandidatePairsdeliberately accepts pin pairs whose perpendicular offset is non-zero but small:tryAcceptCurrentRoutethen stores the routed path as-is. Because the two pins are not exactly aligned, the resulting two-point path is diagonal by up to that tolerance. This is the final pipeline stage (netLabelToTraceSolveris the last step inpipelineDef), so the slanted segment reaches the output.TraceCleanupSolverruns earlier and cannot correct it.Minimized input
4 chips, no direct connections, a single 4-pin net.
{"chips":[{"chipId":"schematic_component_1","center":{"x":-11.8075,"y":9},"width":1.2850000000000001,"height":0.759999999999998,"pins":[{"pinId":"schematic_port_2","x":-12,"y":9.379999999999999,"_facingDirection":"y+"}]},{"chipId":"schematic_component_36","center":{"x":-15,"y":10.4},"width":0.6000000000000014,"height":0.6799999999999997,"pins":[{"pinId":"schematic_port_73","x":-14.7,"y":10.4,"_facingDirection":"x+"}]},{"chipId":"schematic_component_40","center":{"x":-14.044999999999998,"y":6.4},"width":1.209999999999999,"height":0.5999999999999996,"pins":[{"pinId":"schematic_port_81","x":-14.2,"y":6.7,"_facingDirection":"y+"}]},{"chipId":"schematic_component_56","center":{"x":-9.42,"y":10.09},"width":1.1400000000000006,"height":0.870000000000001,"pins":[{"pinId":"schematic_port_112","x":-9.99,"y":10.39,"_facingDirection":"x-"}]}],"directConnections":[],"netConnections":[{"netId":"NET_01","isGround":false,"netLabelWidth":0.42,"netLabelHeight":0.84,"pinIds":["schematic_port_2","schematic_port_73","schematic_port_81","schematic_port_112"]}],"availableNetLabelOrientations":{"NET_01":["y+"]},"maxMspPairDistance":2.4}Reproduction
Observed
solver.solved === true,solver.failed === false, and the output contains:A 4.71-unit-long wire with 0.01 of vertical drift, which renders as a visibly slanted schematic trace.
Expected
Every segment of every emitted trace is axis-aligned: consecutive points share either an x or a y coordinate. Schematic traces are orthogonal by construction.
Also reachable from a committed fixture
tests/assets/example51.jsononmainproduces three such segments:Note on #722
PR #722 ("straighten segments left slightly off-axis by imprecise pin input") addresses a related symptom, but only in
TraceCleanupSolver, which runs before this stage. It does not cover traces thatNetLabelToTraceSolvercreates afterwards.How this was found
Property-based fuzzing: mutating the committed fixtures (chip shifts and rotations, added and removed connections, pin-side changes, duplicated net labels, extra and degenerate chips) and asserting that every emitted trace segment is axis-aligned, judged differentially against each fixture's own baseline. Off-axis segments reproduced across several unrelated base fixtures.