From 8ef5cd9a36eeb579997bf6324ea9b138be77d1fd Mon Sep 17 00:00:00 2001 From: singularitycurse26-svg Date: Sun, 19 Jul 2026 03:19:03 -0500 Subject: [PATCH] feat: merge same-net trace lines by enabling rail alignment in default operations Add 'aligning_same_net_rails' to DEFAULT_OPERATIONS in TraceCleanupSolver so same-net trace lines that are close together get aligned to the same Y or X coordinate during the first cleanup pass. Previously this operation was only run in the second cleanup pass (traceCleanupSolver2), meaning traces could remain unaligned after the first pass if the second pass didn't run or had restricted eligibility. Closes #34 /claim #34 --- .../TraceCleanupSolver/TraceCleanupSolver.ts | 1 + .../mergeSameNetTraces.test.ts | 99 +++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 tests/solvers/TraceCleanupSolver/mergeSameNetTraces.test.ts diff --git a/lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts b/lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts index da39fd852..24d4c7d16 100644 --- a/lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts +++ b/lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts @@ -37,6 +37,7 @@ const DEFAULT_OPERATIONS: readonly TraceCleanupOperation[] = [ "untangling_traces", "minimizing_turns", "balancing_l_shapes", + "aligning_same_net_rails", ] /** diff --git a/tests/solvers/TraceCleanupSolver/mergeSameNetTraces.test.ts b/tests/solvers/TraceCleanupSolver/mergeSameNetTraces.test.ts new file mode 100644 index 000000000..09dde6703 --- /dev/null +++ b/tests/solvers/TraceCleanupSolver/mergeSameNetTraces.test.ts @@ -0,0 +1,99 @@ +import { expect, test } from "bun:test" +import type { InputProblem } from "lib/types/InputProblem" +import { + align, + createTrace, + verticalProblem, + getVerticalPin, +} from "./fixtures/alignSameNetRails" + +test("merges same-net trace lines at same Y coordinate", () => { + const inputProblem: InputProblem = { + ...verticalProblem, + chips: [ + { + chipId: "U1", + center: { x: 0, y: 0 }, + width: 2, + height: 6, + pins: [ + { pinId: "U1.1", x: -1, y: 2, _facingDirection: "x-" }, + { pinId: "U1.2", x: -1, y: 0, _facingDirection: "x-" }, + { pinId: "U1.3", x: -1, y: -2, _facingDirection: "x-" }, + ], + }, + ], + } + + const traces = [ + createTrace( + "trace_a", + [ + { x: -1, y: 2 }, + { x: -2, y: 2 }, + { x: -2, y: 0 }, + { x: -1, y: 0 }, + ], + [getVerticalPin("U1.1"), getVerticalPin("U1.2")], + ), + createTrace( + "trace_b", + [ + { x: -1, y: 0 }, + { x: -2.5, y: 0 }, + { x: -2.5, y: -2 }, + { x: -1, y: -2 }, + ], + [getVerticalPin("U1.2"), getVerticalPin("U1.3")], + ), + ] + + const result = align(traces, { inputProblem }) + + expect(result.alignedRailGroupCount).toBeGreaterThan(0) + + const traceA = result.traces.find((t) => t.mspPairId === "trace_a")! + const traceB = result.traces.find((t) => t.mspPairId === "trace_b")! + + const traceARailX = traceA.tracePath.find((p, i) => i > 0 && i < traceA.tracePath.length - 1 && p.x !== -1)?.x + const traceBRailX = traceB.tracePath.find((p, i) => i > 0 && i < traceB.tracePath.length - 1 && p.x !== -1)?.x + + expect(traceARailX).toBe(traceBRailX) +}) + +test("merges same-net trace lines at same X coordinate (vertical rails)", () => { + const traces = [ + createTrace( + "upper", + [ + { x: -1, y: 2 }, + { x: -2, y: 2 }, + { x: -2, y: 0 }, + { x: -1, y: 0 }, + ], + [getVerticalPin("U1.1"), getVerticalPin("U1.2")], + ), + createTrace( + "lower", + [ + { x: -1, y: 0 }, + { x: -3, y: 0 }, + { x: -3, y: -2 }, + { x: -1, y: -2 }, + ], + [getVerticalPin("U1.2"), getVerticalPin("U1.3")], + ), + ] + + const result = align(traces) + + expect(result.alignedRailGroupCount).toBeGreaterThan(0) + + const upper = result.traces.find((t) => t.mspPairId === "upper")! + const lower = result.traces.find((t) => t.mspPairId === "lower")! + + const upperRailX = upper.tracePath.find((p, i) => i > 0 && i < upper.tracePath.length - 1 && p.x !== -1)?.x + const lowerRailX = lower.tracePath.find((p, i) => i > 0 && i < lower.tracePath.length - 1 && p.x !== -1)?.x + + expect(upperRailX).toBe(lowerRailX) +})