diff --git a/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver.ts b/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver.ts index 305e4e0..5814a0b 100644 --- a/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver.ts +++ b/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver.ts @@ -158,18 +158,31 @@ export class SchematicTraceSingleLineSolver extends BaseSolver { // If this segment would cross any restricted center line, reject the candidate path. const EPS = 1e-9 for (const [, rcl] of restrictedCenterLines) { + const bounds = rcl.bounds if (rcl.axes.has("x") && typeof rcl.x === "number") { - // segment strictly crosses vertical center line + // segment strictly crosses vertical center line near the chip bounds if ((start.x - rcl.x) * (end.x - rcl.x) < -EPS) { - pathIsValid = false - break + const segMinY = Math.min(start.y, end.y) + const segMaxY = Math.max(start.y, end.y) + const overlapY = + Math.min(segMaxY, bounds.maxY) - Math.max(segMinY, bounds.minY) + if (overlapY > EPS) { + pathIsValid = false + break + } } } if (rcl.axes.has("y") && typeof rcl.y === "number") { - // segment strictly crosses horizontal center line + // segment strictly crosses horizontal center line near the chip bounds if ((start.y - rcl.y) * (end.y - rcl.y) < -EPS) { - pathIsValid = false - break + const segMinX = Math.min(start.x, end.x) + const segMaxX = Math.max(start.x, end.x) + const overlapX = + Math.min(segMaxX, bounds.maxX) - Math.max(segMinX, bounds.minX) + if (overlapX > EPS) { + pathIsValid = false + break + } } } } diff --git a/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getRestrictedCenterLines.ts b/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getRestrictedCenterLines.ts index 50d9dda..c5cd71b 100644 --- a/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getRestrictedCenterLines.ts +++ b/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getRestrictedCenterLines.ts @@ -5,6 +5,7 @@ import type { InputProblem, PinId, } from "lib/types/InputProblem" +import { getInputChipBounds } from "lib/solvers/GuidelinesSolver/getInputChipBounds" import { getPinDirection } from "./getPinDirection" type ChipPin = InputPin & { chipId: ChipId } @@ -19,6 +20,7 @@ export type RestrictedCenterLine = { x?: number y?: number axes: Set<"x" | "y"> + bounds: ReturnType } export const getRestrictedCenterLines = (params: { @@ -107,7 +109,10 @@ export const getRestrictedCenterLines = (params: { // are present among related pins on the chip. for (const [chipId, faces] of chipFacingMap) { const axes = new Set<"x" | "y">() - const rcl: RestrictedCenterLine = { axes } + const chip = chipMap[chipId] + if (!chip) continue + const bounds = getInputChipBounds(chip) + const rcl: RestrictedCenterLine = { axes, bounds } // determine whether any side on this chip has more than one pin const counts = faces.counts diff --git a/tests/solvers/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver_long_trace.test.ts b/tests/solvers/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver_long_trace.test.ts new file mode 100644 index 0000000..ebde811 --- /dev/null +++ b/tests/solvers/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver_long_trace.test.ts @@ -0,0 +1,66 @@ +import { expect, test } from "bun:test" +import { calculateElbow } from "calculate-elbow" +import { SchematicTraceSingleLineSolver } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver" +import type { InputChip, InputProblem } from "lib/types/InputProblem" + +const buildChip = ( + chipId: string, + center: { x: number; y: number }, + width: number, + height: number, + pins: Array<{ pinId: string; x: number; y: number }>, +): InputChip => ({ + chipId, + center, + width, + height, + pins, +}) + +test("allows long traces when restricted center line is far away", () => { + const chipA = buildChip("A", { x: 0, y: 0 }, 0.4, 0.4, [ + { pinId: "A1", x: 0, y: 0 }, + ]) + const chipB = buildChip("B", { x: 10, y: 0 }, 0.4, 0.4, [ + { pinId: "B1", x: 10, y: 0 }, + ]) + const chipC = buildChip("C", { x: 5, y: 10 }, 1, 1, [ + { pinId: "C1", x: 4.5, y: 10 }, + { pinId: "C2", x: 5.5, y: 10 }, + ]) + + const inputProblem: InputProblem = { + chips: [chipA, chipB, chipC], + directConnections: [ + { pinIds: ["A1", "C1"], netId: "N1" }, + { pinIds: ["B1", "C2"], netId: "N1" }, + ], + netConnections: [], + availableNetLabelOrientations: {}, + } + + const pins = [ + { pinId: "A1", x: 0, y: 0, chipId: "A", _facingDirection: "x+" as const }, + { pinId: "B1", x: 10, y: 0, chipId: "B", _facingDirection: "x-" as const }, + ] + + const solver = new SchematicTraceSingleLineSolver({ + pins: pins as any, + guidelines: [], + inputProblem, + chipMap: { A: chipA, B: chipB, C: chipC }, + }) + + solver.solve() + + expect(solver.solved).toBe(true) + expect(solver.failed).toBe(false) + + const baseElbow = calculateElbow( + { x: pins[0].x, y: pins[0].y, facingDirection: pins[0]._facingDirection }, + { x: pins[1].x, y: pins[1].y, facingDirection: pins[1]._facingDirection }, + { overshoot: 0.2 }, + ) + + expect(solver.solvedTracePath).toEqual(baseElbow) +})