From c8e8d799c4ed0af1e2cbf6925d4015ad117ab44a Mon Sep 17 00:00:00 2001 From: khozakhulile27-netizen Date: Sun, 26 Jul 2026 15:44:30 +0200 Subject: [PATCH 01/17] fix: snap same-net parallel traces (fixes #34) --- .../minimizeTurnsWithFilteredLabels.ts | 125 ++++-------------- 1 file changed, 29 insertions(+), 96 deletions(-) diff --git a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts index 31616f03b..2502b7f6b 100644 --- a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +++ b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts @@ -1,21 +1,13 @@ -import type { InputProblem } from "lib/types/InputProblem" -import { minimizeTurns } from "./turnMinimization" -import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver" -import { getObstacleRects } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect" -import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver" -import { countTurns } from "./countTurns" -import { - getVisibleTraceLength, - getVisibleTraceSegmentCount, - RAIL_ALIGNMENT_EPSILON, -} from "./sameNetRailAlignment/geometry" +import type { InputProblem } from "../../types/InputProblem" +import type { NetLabelPlacement } from "../../types/NetLabelPlacement" +import type { SolvedTracePath } from "../../types/SolvedTracePath" +import { minimizeTurns } from "./minimizeTurns" +import { getObstacleRects as getObstacleRectsFromProblem } from "./getObstacleRects" + +function getObstacleRects(inputProblem: InputProblem) { + return getObstacleRectsFromProblem(inputProblem) +} -/** - * Minimizes turns with a strict pass that treats every other trace as an - * obstacle and a relaxed pass that permits joining endpoint-sharing same-net - * branches. Equivalent-turn routes are compared by their rendered same-net - * complexity so aligned rails win without arbitrarily shifting local routes. - */ export const minimizeTurnsWithFilteredLabels = ({ targetMspConnectionPairId, traces, @@ -31,29 +23,18 @@ export const minimizeTurnsWithFilteredLabels = ({ mergedLabelNetIdMap: Record> paddingBuffer: number }): SolvedTracePath => { - const targetTrace = traces.find( - (t) => t.mspPairId === targetMspConnectionPairId, - ) - if (!targetTrace) { - throw new Error(`Target trace ${targetMspConnectionPairId} not found`) - } - + const targetTrace = traces.find((t) => t.mspPairId === targetMspConnectionPairId) + if (!targetTrace) throw new Error(`Target trace ${targetMspConnectionPairId} not found`) const targetPinIds = new Set(targetTrace.pinIds) - const otherTraces = traces.filter( - (trace) => trace.mspPairId !== targetMspConnectionPairId, - ) - const relaxedObstacleTraces = otherTraces.filter((trace) => { - const sharesEndpoint = trace.pinIds.some((pinId) => targetPinIds.has(pinId)) - return ( - trace.globalConnNetId !== targetTrace.globalConnNetId || !sharesEndpoint - ) - }) - + const otherTraces = traces.filter((trace) => trace.mspPairId !== targetMspConnectionPairId) const TRACE_WIDTH = 0.01 const getTraceObstacles = (obstacleTraces: SolvedTracePath[]) => - obstacleTraces.flatMap((trace, i) => - trace.tracePath.slice(0, -1).map((p1, pi) => { - const p2 = trace.tracePath[pi + 1]! + obstacleTraces.flatMap((trace, i) => { + const path = (trace as any).tracePath ?? (trace as any).path ?? [] + if (!Array.isArray(path) || path.length < 2) return [] + return path.slice(0, -1).map((p1: any, pi: number) => { + const p2 = path[pi + 1] + if (!p2) return null return { chipId: `trace-obstacle-${i}-${pi}`, minX: Math.min(p1.x, p2.x) - TRACE_WIDTH / 2, @@ -61,9 +42,8 @@ export const minimizeTurnsWithFilteredLabels = ({ maxX: Math.max(p1.x, p2.x) + TRACE_WIDTH / 2, maxY: Math.max(p1.y, p2.y) + TRACE_WIDTH / 2, } - }), - ) - + }).filter(Boolean) as any[] + }) const staticObstaclesRaw = getObstacleRects(inputProblem) const PADDING = 0.01 const staticObstacles = staticObstaclesRaw.map((obs) => ({ @@ -73,76 +53,29 @@ export const minimizeTurnsWithFilteredLabels = ({ maxX: obs.maxX + PADDING, maxY: obs.maxY + PADDING, })) - - const originalPath = targetTrace.tracePath + const originalPath = (targetTrace as any).tracePath ?? (targetTrace as any).path ?? [] const filteredLabels = allLabelPlacements.filter((label) => { const originalNetIds = mergedLabelNetIdMap[label.globalConnNetId] - if (originalNetIds) { - return !originalNetIds.has(targetTrace.globalConnNetId) - } + if (originalNetIds) return !originalNetIds.has(targetTrace.globalConnNetId) return label.globalConnNetId !== targetTrace.globalConnNetId }) - const labelBounds = filteredLabels.map((nl) => ({ minX: nl.center.x - nl.width / 2 - paddingBuffer, maxX: nl.center.x + nl.width / 2 + paddingBuffer, minY: nl.center.y - nl.height / 2 - paddingBuffer, maxY: nl.center.y + nl.height / 2 + paddingBuffer, })) - const strictPath = minimizeTurns({ path: originalPath, obstacles: [...staticObstacles, ...getTraceObstacles(otherTraces)], labelBounds, originalPath: originalPath, + targetMspConnectionPairId, + traces, + inputProblem, + allLabelPlacements, + mergedLabelNetIdMap, + paddingBuffer, }) - - const relaxedPath = minimizeTurns({ - path: originalPath, - obstacles: [ - ...staticObstacles, - ...getTraceObstacles(relaxedObstacleTraces), - ], - labelBounds, - originalPath: originalPath, - }) - - const sameNetTraces = otherTraces.filter( - (trace) => trace.globalConnNetId === targetTrace.globalConnNetId, - ) - const getSameNetReadability = (tracePath: SolvedTracePath["tracePath"]) => { - const tracesWithCandidate = [ - ...sameNetTraces, - { ...targetTrace, tracePath }, - ] - return { - segmentCount: getVisibleTraceSegmentCount(tracesWithCandidate), - visibleLength: getVisibleTraceLength(tracesWithCandidate), - } - } - - const strictTurns = countTurns(strictPath) - const relaxedTurns = countTurns(relaxedPath) - let newPath = strictPath - - if (relaxedTurns < strictTurns) { - newPath = relaxedPath - } else if (relaxedTurns === strictTurns) { - const strictReadability = getSameNetReadability(strictPath) - const relaxedReadability = getSameNetReadability(relaxedPath) - - if ( - relaxedReadability.segmentCount < strictReadability.segmentCount || - (relaxedReadability.segmentCount === strictReadability.segmentCount && - relaxedReadability.visibleLength < - strictReadability.visibleLength - RAIL_ALIGNMENT_EPSILON) - ) { - newPath = relaxedPath - } - } - - return { - ...targetTrace, - tracePath: newPath, - } + return strictPath } From b39ef2386914bde3e8f6f0bf55f88eea8eb13377 Mon Sep 17 00:00:00 2001 From: Khoza khulile Date: Mon, 27 Jul 2026 14:16:52 +0200 Subject: [PATCH 02/17] Update minimizeTurnsWithFilteredLabels.ts Signed-off-by: Khoza khulile --- .../minimizeTurnsWithFilteredLabels.ts | 126 +++++++++++++----- 1 file changed, 96 insertions(+), 30 deletions(-) diff --git a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts index 2502b7f6b..7e0f97d2c 100644 --- a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +++ b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts @@ -1,13 +1,21 @@ -import type { InputProblem } from "../../types/InputProblem" -import type { NetLabelPlacement } from "../../types/NetLabelPlacement" -import type { SolvedTracePath } from "../../types/SolvedTracePath" -import { minimizeTurns } from "./minimizeTurns" -import { getObstacleRects as getObstacleRectsFromProblem } from "./getObstacleRects" - -function getObstacleRects(inputProblem: InputProblem) { - return getObstacleRectsFromProblem(inputProblem) -} +import type { InputProblem } from "lib/types/InputProblem" +import { minimizeTurns } from "./turnMinimization" +import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver" +import { getObstacleRects } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect" +import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver" +import { countTurns } from "./countTurns" +import { + getVisibleTraceLength, + getVisibleTraceSegmentCount, + RAIL_ALIGNMENT_EPSILON, +} from "./sameNetRailAlignment/geometry" +/** + * Minimizes turns with a strict pass that treats every other trace as an + * obstacle and a relaxed pass that permits joining endpoint-sharing same-net + * branches. Equivalent-turn routes are compared by their rendered same-net + * complexity so aligned rails win without arbitrarily shifting local routes. + */ export const minimizeTurnsWithFilteredLabels = ({ targetMspConnectionPairId, traces, @@ -23,18 +31,29 @@ export const minimizeTurnsWithFilteredLabels = ({ mergedLabelNetIdMap: Record> paddingBuffer: number }): SolvedTracePath => { - const targetTrace = traces.find((t) => t.mspPairId === targetMspConnectionPairId) - if (!targetTrace) throw new Error(`Target trace ${targetMspConnectionPairId} not found`) + const targetTrace = traces.find( + (t) => t.mspPairId === targetMspConnectionPairId, + ) + if (!targetTrace) { + throw new Error(`Target trace ${targetMspConnectionPairId} not found`) + } + const targetPinIds = new Set(targetTrace.pinIds) - const otherTraces = traces.filter((trace) => trace.mspPairId !== targetMspConnectionPairId) + const otherTraces = traces.filter( + (trace) => trace.mspPairId !== targetMspConnectionPairId, + ) + const relaxedObstacleTraces = otherTraces.filter((trace) => { + const sharesEndpoint = trace.pinIds.some((pinId) => targetPinIds.has(pinId)) + return ( + trace.globalConnNetId !== targetTrace.globalConnNetId || !sharesEndpoint + ) + }) + const TRACE_WIDTH = 0.01 const getTraceObstacles = (obstacleTraces: SolvedTracePath[]) => - obstacleTraces.flatMap((trace, i) => { - const path = (trace as any).tracePath ?? (trace as any).path ?? [] - if (!Array.isArray(path) || path.length < 2) return [] - return path.slice(0, -1).map((p1: any, pi: number) => { - const p2 = path[pi + 1] - if (!p2) return null + obstacleTraces.flatMap((trace, i) => + trace.tracePath.slice(0, -1).map((p1, pi) => { + const p2 = trace.tracePath[pi + 1]! return { chipId: `trace-obstacle-${i}-${pi}`, minX: Math.min(p1.x, p2.x) - TRACE_WIDTH / 2, @@ -42,8 +61,9 @@ export const minimizeTurnsWithFilteredLabels = ({ maxX: Math.max(p1.x, p2.x) + TRACE_WIDTH / 2, maxY: Math.max(p1.y, p2.y) + TRACE_WIDTH / 2, } - }).filter(Boolean) as any[] - }) + }), + ) + const staticObstaclesRaw = getObstacleRects(inputProblem) const PADDING = 0.01 const staticObstacles = staticObstaclesRaw.map((obs) => ({ @@ -53,29 +73,75 @@ export const minimizeTurnsWithFilteredLabels = ({ maxX: obs.maxX + PADDING, maxY: obs.maxY + PADDING, })) - const originalPath = (targetTrace as any).tracePath ?? (targetTrace as any).path ?? [] + + const originalPath = targetTrace.tracePath const filteredLabels = allLabelPlacements.filter((label) => { const originalNetIds = mergedLabelNetIdMap[label.globalConnNetId] - if (originalNetIds) return !originalNetIds.has(targetTrace.globalConnNetId) + if (originalNetIds) { + return !originalNetIds.has(targetTrace.globalConnNetId) + } return label.globalConnNetId !== targetTrace.globalConnNetId }) + const labelBounds = filteredLabels.map((nl) => ({ minX: nl.center.x - nl.width / 2 - paddingBuffer, maxX: nl.center.x + nl.width / 2 + paddingBuffer, minY: nl.center.y - nl.height / 2 - paddingBuffer, maxY: nl.center.y + nl.height / 2 + paddingBuffer, })) + const strictPath = minimizeTurns({ path: originalPath, obstacles: [...staticObstacles, ...getTraceObstacles(otherTraces)], labelBounds, originalPath: originalPath, - targetMspConnectionPairId, - traces, - inputProblem, - allLabelPlacements, - mergedLabelNetIdMap, - paddingBuffer, }) - return strictPath -} + + const relaxedPath = minimizeTurns({ + path: originalPath, + obstacles: [ + ...staticObstacles, + ...getTraceObstacles(relaxedObstacleTraces), + ], + labelBounds, + originalPath: originalPath, + }) + + const sameNetTraces = otherTraces.filter( + (trace) => trace.globalConnNetId === targetTrace.globalConnNetId, + ) + const getSameNetReadability = (tracePath: SolvedTracePath["tracePath"]) => { + const tracesWithCandidate = [ + ...sameNetTraces, + { ...targetTrace, tracePath }, + ] + return { + segmentCount: getVisibleTraceSegmentCount(tracesWithCandidate), + visibleLength: getVisibleTraceLength(tracesWithCandidate), + } + } + + const strictTurns = countTurns(strictPath) + const relaxedTurns = countTurns(relaxedPath) + let newPath = strictPath + + if (relaxedTurns < strictTurns) { + newPath = relaxedPath + } else if (relaxedTurns === strictTurns) { + const strictReadability = getSameNetReadability(strictPath) + const relaxedReadability = getSameNetReadability(relaxedPath) + + if ( + relaxedReadability.segmentCount < strictReadability.segmentCount || + (relaxedReadability.segmentCount === strictReadability.segmentCount && + relaxedReadability.visibleLength < + strictReadability.visibleLength - RAIL_ALIGNMENT_EPSILON) + ) { + newPath = relaxedPath + } + } + + return { + ...targetTrace, + tracePath: newPath, + } From 37efe3f5d4aecc38e422d472e700d5794b2c7afb Mon Sep 17 00:00:00 2001 From: Khoza khulile Date: Mon, 27 Jul 2026 14:23:42 +0200 Subject: [PATCH 03/17] Update minimizeTurnsWithFilteredLabels.ts Signed-off-by: Khoza khulile --- .../TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts index 7e0f97d2c..d71ab305c 100644 --- a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +++ b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts @@ -144,4 +144,5 @@ export const minimizeTurnsWithFilteredLabels = ({ return { ...targetTrace, tracePath: newPath, - } + } +} From 23c3f8f25243f1d917787f693133973e7041d8b1 Mon Sep 17 00:00:00 2001 From: Khoza khulile Date: Mon, 27 Jul 2026 14:32:23 +0200 Subject: [PATCH 04/17] Update minimizeTurnsWithFilteredLabels.ts Signed-off-by: Khoza khulile --- .../TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts index d71ab305c..3064ba41a 100644 --- a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +++ b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts @@ -145,4 +145,3 @@ export const minimizeTurnsWithFilteredLabels = ({ ...targetTrace, tracePath: newPath, } -} From aaf216fd758f453a375d0e360669aad62ae3a0ac Mon Sep 17 00:00:00 2001 From: Khoza khulile Date: Mon, 27 Jul 2026 14:34:26 +0200 Subject: [PATCH 05/17] Update minimizeTurnsWithFilteredLabels.ts Signed-off-by: Khoza khulile --- .../TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts index 3064ba41a..101b57564 100644 --- a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +++ b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts @@ -145,3 +145,4 @@ export const minimizeTurnsWithFilteredLabels = ({ ...targetTrace, tracePath: newPath, } + } From 52b8bd0be3b7e11b52c763198ed858ec4de843b0 Mon Sep 17 00:00:00 2001 From: Khoza khulile Date: Mon, 27 Jul 2026 14:41:50 +0200 Subject: [PATCH 06/17] Update minimizeTurnsWithFilteredLabels.ts Signed-off-by: Khoza khulile --- .../TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts index 101b57564..fe13f26eb 100644 --- a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +++ b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts @@ -141,8 +141,8 @@ export const minimizeTurnsWithFilteredLabels = ({ } } - return { + return { ...targetTrace, tracePath: newPath, - } - } + }) +} From 0d30ee47c9d487dcadd856ed9ff458c3a84f18b1 Mon Sep 17 00:00:00 2001 From: Khoza khulile Date: Mon, 27 Jul 2026 14:43:48 +0200 Subject: [PATCH 07/17] Update minimizeTurnsWithFilteredLabels.ts Signed-off-by: Khoza khulile --- .../TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts index fe13f26eb..cbcaf4c53 100644 --- a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +++ b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts @@ -144,5 +144,5 @@ export const minimizeTurnsWithFilteredLabels = ({ return { ...targetTrace, tracePath: newPath, - }) + } } From d1f03ff551ce86d9ae61526141364e2b21c36ab8 Mon Sep 17 00:00:00 2001 From: Khoza khulile Date: Mon, 27 Jul 2026 15:10:32 +0200 Subject: [PATCH 08/17] Update minimizeTurnsWithFilteredLabels.ts Signed-off-by: Khoza khulile --- .../TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts index cbcaf4c53..b32032c80 100644 --- a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +++ b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts @@ -46,8 +46,8 @@ export const minimizeTurnsWithFilteredLabels = ({ const sharesEndpoint = trace.pinIds.some((pinId) => targetPinIds.has(pinId)) return ( trace.globalConnNetId !== targetTrace.globalConnNetId || !sharesEndpoint - ) - }) + ) + } const TRACE_WIDTH = 0.01 const getTraceObstacles = (obstacleTraces: SolvedTracePath[]) => @@ -145,4 +145,4 @@ export const minimizeTurnsWithFilteredLabels = ({ ...targetTrace, tracePath: newPath, } -} +}) From 180679cafef8255b4122c4d58f0e1bd68a1693c2 Mon Sep 17 00:00:00 2001 From: Khoza khulile Date: Mon, 27 Jul 2026 15:12:03 +0200 Subject: [PATCH 09/17] Update minimizeTurnsWithFilteredLabels.ts Signed-off-by: Khoza khulile --- .../TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts index b32032c80..f3fa179df 100644 --- a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +++ b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts @@ -145,4 +145,4 @@ export const minimizeTurnsWithFilteredLabels = ({ ...targetTrace, tracePath: newPath, } -}) +} From 3bd278655749897f0179a0b1b763659f43d3b3d6 Mon Sep 17 00:00:00 2001 From: Khoza khulile Date: Mon, 27 Jul 2026 15:13:27 +0200 Subject: [PATCH 10/17] Update minimizeTurnsWithFilteredLabels.ts Signed-off-by: Khoza khulile From b65fe80c339eb85bdc46017b4b71e7439b40e807 Mon Sep 17 00:00:00 2001 From: Khoza khulile Date: Mon, 27 Jul 2026 15:15:35 +0200 Subject: [PATCH 11/17] Update minimizeTurnsWithFilteredLabels.ts Signed-off-by: Khoza khulile --- .../TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts index f3fa179df..8472f787b 100644 --- a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +++ b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts @@ -47,7 +47,7 @@ export const minimizeTurnsWithFilteredLabels = ({ return ( trace.globalConnNetId !== targetTrace.globalConnNetId || !sharesEndpoint ) - } + }) const TRACE_WIDTH = 0.01 const getTraceObstacles = (obstacleTraces: SolvedTracePath[]) => From 9b5cec6adb0e8acbb13b06a0d543f8230e1e544c Mon Sep 17 00:00:00 2001 From: Khoza khulile Date: Mon, 27 Jul 2026 15:21:30 +0200 Subject: [PATCH 12/17] Update minimizeTurnsWithFilteredLabels.ts Signed-off-by: Khoza khulile From 7f39b35d10454dcafc51d9d706855cc91571ca42 Mon Sep 17 00:00:00 2001 From: Khoza khulile Date: Mon, 27 Jul 2026 15:51:08 +0200 Subject: [PATCH 13/17] Update minimizeTurnsWithFilteredLabels.ts Signed-off-by: Khoza khulile --- .../minimizeTurnsWithFilteredLabels.ts | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts index 8472f787b..80a200230 100644 --- a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +++ b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts @@ -43,17 +43,18 @@ export const minimizeTurnsWithFilteredLabels = ({ (trace) => trace.mspPairId !== targetMspConnectionPairId, ) const relaxedObstacleTraces = otherTraces.filter((trace) => { - const sharesEndpoint = trace.pinIds.some((pinId) => targetPinIds.has(pinId)) - return ( - trace.globalConnNetId !== targetTrace.globalConnNetId || !sharesEndpoint - ) + const sharesEndpoint = trace.pinIds.some((pinId) => + targetPinIds.has(pinId), + ) + return trace.globalConnNetId !== targetTrace.globalConnNetId || !sharesEndpoint }) const TRACE_WIDTH = 0.01 const getTraceObstacles = (obstacleTraces: SolvedTracePath[]) => - obstacleTraces.flatMap((trace, i) => - trace.tracePath.slice(0, -1).map((p1, pi) => { - const p2 = trace.tracePath[pi + 1]! + obstacleTraces.flatMap((trace, i) => { + const tracePath = (trace as any).tracePath ?? (trace as any).path + return tracePath.slice(0, -1).map((p1: any, pi: number) => { + const p2 = tracePath[pi + 1]! return { chipId: `trace-obstacle-${i}-${pi}`, minX: Math.min(p1.x, p2.x) - TRACE_WIDTH / 2, @@ -61,8 +62,8 @@ export const minimizeTurnsWithFilteredLabels = ({ maxX: Math.max(p1.x, p2.x) + TRACE_WIDTH / 2, maxY: Math.max(p1.y, p2.y) + TRACE_WIDTH / 2, } - }), - ) + }) + }) const staticObstaclesRaw = getObstacleRects(inputProblem) const PADDING = 0.01 @@ -74,7 +75,7 @@ export const minimizeTurnsWithFilteredLabels = ({ maxY: obs.maxY + PADDING, })) - const originalPath = targetTrace.tracePath + const originalPath = (targetTrace as any).tracePath ?? (targetTrace as any).path const filteredLabels = allLabelPlacements.filter((label) => { const originalNetIds = mergedLabelNetIdMap[label.globalConnNetId] if (originalNetIds) { @@ -99,10 +100,7 @@ export const minimizeTurnsWithFilteredLabels = ({ const relaxedPath = minimizeTurns({ path: originalPath, - obstacles: [ - ...staticObstacles, - ...getTraceObstacles(relaxedObstacleTraces), - ], + obstacles: [...staticObstacles, ...getTraceObstacles(relaxedObstacleTraces)], labelBounds, originalPath: originalPath, }) @@ -141,7 +139,7 @@ export const minimizeTurnsWithFilteredLabels = ({ } } - return { + return { ...targetTrace, tracePath: newPath, } From da2606fcb23b4aa54134533544f8aeaa34c3b497 Mon Sep 17 00:00:00 2001 From: Khoza khulile Date: Mon, 27 Jul 2026 16:02:23 +0200 Subject: [PATCH 14/17] Update minimizeTurnsWithFilteredLabels.ts Signed-off-by: Khoza khulile --- .../minimizeTurnsWithFilteredLabels.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts index 80a200230..0c98daa0f 100644 --- a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +++ b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts @@ -43,10 +43,10 @@ export const minimizeTurnsWithFilteredLabels = ({ (trace) => trace.mspPairId !== targetMspConnectionPairId, ) const relaxedObstacleTraces = otherTraces.filter((trace) => { - const sharesEndpoint = trace.pinIds.some((pinId) => - targetPinIds.has(pinId), + const sharesEndpoint = trace.pinIds.some((pinId) => targetPinIds.has(pinId)) + return ( + trace.globalConnNetId !== targetTrace.globalConnNetId || !sharesEndpoint ) - return trace.globalConnNetId !== targetTrace.globalConnNetId || !sharesEndpoint }) const TRACE_WIDTH = 0.01 @@ -75,7 +75,8 @@ export const minimizeTurnsWithFilteredLabels = ({ maxY: obs.maxY + PADDING, })) - const originalPath = (targetTrace as any).tracePath ?? (targetTrace as any).path + const originalPath = + (targetTrace as any).tracePath ?? (targetTrace as any).path const filteredLabels = allLabelPlacements.filter((label) => { const originalNetIds = mergedLabelNetIdMap[label.globalConnNetId] if (originalNetIds) { @@ -100,7 +101,10 @@ export const minimizeTurnsWithFilteredLabels = ({ const relaxedPath = minimizeTurns({ path: originalPath, - obstacles: [...staticObstacles, ...getTraceObstacles(relaxedObstacleTraces)], + obstacles: [ + ...staticObstacles, + ...getTraceObstacles(relaxedObstacleTraces), + ], labelBounds, originalPath: originalPath, }) From 80fac51a8ccf5620568df0a6368f67f96bd60000 Mon Sep 17 00:00:00 2001 From: Khoza khulile Date: Mon, 27 Jul 2026 16:15:55 +0200 Subject: [PATCH 15/17] Delete lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts Signed-off-by: Khoza khulile --- .../minimizeTurnsWithFilteredLabels.ts | 150 ------------------ 1 file changed, 150 deletions(-) delete mode 100644 lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts diff --git a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts deleted file mode 100644 index 0c98daa0f..000000000 --- a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +++ /dev/null @@ -1,150 +0,0 @@ -import type { InputProblem } from "lib/types/InputProblem" -import { minimizeTurns } from "./turnMinimization" -import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver" -import { getObstacleRects } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect" -import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver" -import { countTurns } from "./countTurns" -import { - getVisibleTraceLength, - getVisibleTraceSegmentCount, - RAIL_ALIGNMENT_EPSILON, -} from "./sameNetRailAlignment/geometry" - -/** - * Minimizes turns with a strict pass that treats every other trace as an - * obstacle and a relaxed pass that permits joining endpoint-sharing same-net - * branches. Equivalent-turn routes are compared by their rendered same-net - * complexity so aligned rails win without arbitrarily shifting local routes. - */ -export const minimizeTurnsWithFilteredLabels = ({ - targetMspConnectionPairId, - traces, - inputProblem, - allLabelPlacements, - mergedLabelNetIdMap, - paddingBuffer, -}: { - targetMspConnectionPairId: string - traces: SolvedTracePath[] - inputProblem: InputProblem - allLabelPlacements: NetLabelPlacement[] - mergedLabelNetIdMap: Record> - paddingBuffer: number -}): SolvedTracePath => { - const targetTrace = traces.find( - (t) => t.mspPairId === targetMspConnectionPairId, - ) - if (!targetTrace) { - throw new Error(`Target trace ${targetMspConnectionPairId} not found`) - } - - const targetPinIds = new Set(targetTrace.pinIds) - const otherTraces = traces.filter( - (trace) => trace.mspPairId !== targetMspConnectionPairId, - ) - const relaxedObstacleTraces = otherTraces.filter((trace) => { - const sharesEndpoint = trace.pinIds.some((pinId) => targetPinIds.has(pinId)) - return ( - trace.globalConnNetId !== targetTrace.globalConnNetId || !sharesEndpoint - ) - }) - - const TRACE_WIDTH = 0.01 - const getTraceObstacles = (obstacleTraces: SolvedTracePath[]) => - obstacleTraces.flatMap((trace, i) => { - const tracePath = (trace as any).tracePath ?? (trace as any).path - return tracePath.slice(0, -1).map((p1: any, pi: number) => { - const p2 = tracePath[pi + 1]! - return { - chipId: `trace-obstacle-${i}-${pi}`, - minX: Math.min(p1.x, p2.x) - TRACE_WIDTH / 2, - minY: Math.min(p1.y, p2.y) - TRACE_WIDTH / 2, - maxX: Math.max(p1.x, p2.x) + TRACE_WIDTH / 2, - maxY: Math.max(p1.y, p2.y) + TRACE_WIDTH / 2, - } - }) - }) - - const staticObstaclesRaw = getObstacleRects(inputProblem) - const PADDING = 0.01 - const staticObstacles = staticObstaclesRaw.map((obs) => ({ - ...obs, - minX: obs.minX - PADDING, - minY: obs.minY - PADDING, - maxX: obs.maxX + PADDING, - maxY: obs.maxY + PADDING, - })) - - const originalPath = - (targetTrace as any).tracePath ?? (targetTrace as any).path - const filteredLabels = allLabelPlacements.filter((label) => { - const originalNetIds = mergedLabelNetIdMap[label.globalConnNetId] - if (originalNetIds) { - return !originalNetIds.has(targetTrace.globalConnNetId) - } - return label.globalConnNetId !== targetTrace.globalConnNetId - }) - - const labelBounds = filteredLabels.map((nl) => ({ - minX: nl.center.x - nl.width / 2 - paddingBuffer, - maxX: nl.center.x + nl.width / 2 + paddingBuffer, - minY: nl.center.y - nl.height / 2 - paddingBuffer, - maxY: nl.center.y + nl.height / 2 + paddingBuffer, - })) - - const strictPath = minimizeTurns({ - path: originalPath, - obstacles: [...staticObstacles, ...getTraceObstacles(otherTraces)], - labelBounds, - originalPath: originalPath, - }) - - const relaxedPath = minimizeTurns({ - path: originalPath, - obstacles: [ - ...staticObstacles, - ...getTraceObstacles(relaxedObstacleTraces), - ], - labelBounds, - originalPath: originalPath, - }) - - const sameNetTraces = otherTraces.filter( - (trace) => trace.globalConnNetId === targetTrace.globalConnNetId, - ) - const getSameNetReadability = (tracePath: SolvedTracePath["tracePath"]) => { - const tracesWithCandidate = [ - ...sameNetTraces, - { ...targetTrace, tracePath }, - ] - return { - segmentCount: getVisibleTraceSegmentCount(tracesWithCandidate), - visibleLength: getVisibleTraceLength(tracesWithCandidate), - } - } - - const strictTurns = countTurns(strictPath) - const relaxedTurns = countTurns(relaxedPath) - let newPath = strictPath - - if (relaxedTurns < strictTurns) { - newPath = relaxedPath - } else if (relaxedTurns === strictTurns) { - const strictReadability = getSameNetReadability(strictPath) - const relaxedReadability = getSameNetReadability(relaxedPath) - - if ( - relaxedReadability.segmentCount < strictReadability.segmentCount || - (relaxedReadability.segmentCount === strictReadability.segmentCount && - relaxedReadability.visibleLength < - strictReadability.visibleLength - RAIL_ALIGNMENT_EPSILON) - ) { - newPath = relaxedPath - } - } - - return { - ...targetTrace, - tracePath: newPath, - } -} From 0041205d3c1271d7abc282127c40a5f66e64aba6 Mon Sep 17 00:00:00 2001 From: Khoza khulile Date: Mon, 27 Jul 2026 16:55:50 +0200 Subject: [PATCH 16/17] Create minimizeTurnsWithFilteredLabels.ts Signed-off-by: Khoza khulile --- .../minimizeTurnsWithFilteredLabels.ts | 151 ++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 lib / solvers / TraceCleanupSolver /minimizeTurnsWithFilteredLabels.ts diff --git a/lib / solvers / TraceCleanupSolver /minimizeTurnsWithFilteredLabels.ts b/lib / solvers / TraceCleanupSolver /minimizeTurnsWithFilteredLabels.ts new file mode 100644 index 000000000..6f022005f --- /dev/null +++ b/lib / solvers / TraceCleanupSolver /minimizeTurnsWithFilteredLabels.ts @@ -0,0 +1,151 @@ +import type { InputProblem } from "lib/types/InputProblem" +import { minimizeTurns } from "./turnMinimization" +import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver" +import { getObstacleRects } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect" +import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver" +import { countTurns } from "./countTurns" +import { + getVisibleTraceLength, + getVisibleTraceSegmentCount, + RAIL_ALIGNMENT_EPSILON, +} from "./sameNetRailAlignment/geometry" + +/** + * Minimizes turns with a strict pass that treats every other trace as an + * obstacle and a relaxed pass that permits joining endpoint-sharing same-net + * branches. Equivalent-turn routes are compared by their rendered same-net + * complexity so aligned rails win without arbitrarily shifting local routes. + */ +export const minimizeTurnsWithFilteredLabels = ({ + targetMspConnectionPairId, + traces, + inputProblem, + allLabelPlacements, + mergedLabelNetIdMap, + paddingBuffer, +}: { + targetMspConnectionPairId: string + traces: SolvedTracePath[] + inputProblem: InputProblem + allLabelPlacements: NetLabelPlacement[] + mergedLabelNetIdMap: Record> + paddingBuffer: number +}): SolvedTracePath => { + const targetTrace = traces.find( + (t) => t.mspPairId === targetMspConnectionPairId, + ) + if (!targetTrace) { + throw new Error(`Target trace ${targetMspConnectionPairId} not found`) + } + + const targetPinIds = new Set(targetTrace.pinIds) + const otherTraces = traces.filter( + (trace) => trace.mspPairId !== targetMspConnectionPairId, + ) + const relaxedObstacleTraces = otherTraces.filter((trace) => { + const sharesEndpoint = trace.pinIds.some((pinId) => targetPinIds.has(pinId)) + return ( + trace.globalConnNetId !== targetTrace.globalConnNetId || !sharesEndpoint + ) + }) + + const TRACE_WIDTH = 0.01 + const getTraceObstacles = (obstacleTraces: SolvedTracePath[]) => + obstacleTraces.flatMap((trace, i) => { + const tracePath = (trace as any).tracePath ?? (trace as any).path + return tracePath.slice(0, -1).map((p1: any, pi: number) => { + const p2 = tracePath[pi + 1]! + return { + chipId: `trace-obstacle-${i}-${pi}`, + minX: Math.min(p1.x, p2.x) - TRACE_WIDTH / 2, + minY: Math.min(p1.y, p2.y) - TRACE_WIDTH / 2, + maxX: Math.max(p1.x, p2.x) + TRACE_WIDTH / 2, + maxY: Math.max(p1.y, p2.y) + TRACE_WIDTH / 2, + } + }) + }) + + const staticObstaclesRaw = getObstacleRects(inputProblem) + const PADDING = 0.01 + const staticObstacles = staticObstaclesRaw.map((obs) => ({ + ...obs, + minX: obs.minX - PADDING, + minY: obs.minY - PADDING, + maxX: obs.maxX + PADDING, + maxY: obs.maxY + PADDING, + })) + + const originalPath = + (targetTrace as any).tracePath ?? (targetTrace as any).path + const filteredLabels = allLabelPlacements.filter((label) => { + const originalNetIds = mergedLabelNetIdMap[label.globalConnNetId] + if (originalNetIds) { + return !originalNetIds.has(targetTrace.globalConnNetId) + } + return label.globalConnNetId !== targetTrace.globalConnNetId + }) + + const labelBounds = filteredLabels.map((nl) => ({ + minX: nl.center.x - nl.width / 2 - paddingBuffer, + maxX: nl.center.x + nl.width / 2 + paddingBuffer, + minY: nl.center.y - nl.height / 2 - paddingBuffer, + maxY: nl.center.y + nl.height / 2 + paddingBuffer, + })) + + const strictPath = minimizeTurns({ + path: originalPath, + obstacles: [...staticObstacles, ...getTraceObstacles(otherTraces)], + labelBounds, + originalPath: originalPath, + }) + + const relaxedPath = minimizeTurns({ + path: originalPath, + obstacles: [ + ...staticObstacles, + ...getTraceObstacles(relaxedObstacleTraces), + ], + labelBounds, + originalPath: originalPath, + }) + + const sameNetTraces = otherTraces.filter( + (trace) => trace.globalConnNetId === targetTrace.globalConnNetId, + ) + const getSameNetReadability = (tracePath: SolvedTracePath["tracePath"]) => { + const tracesWithCandidate = [ + ...sameNetTraces, + { ...targetTrace, tracePath }, + ] + return { + segmentCount: getVisibleTraceSegmentCount(tracesWithCandidate), + visibleLength: getVisibleTraceLength(tracesWithCandidate), + } + } + + const strictTurns = countTurns(strictPath) + const relaxedTurns = countTurns(relaxedPath) + let newPath = strictPath + + if (relaxedTurns < strictTurns) { + newPath = relaxedPath + } else if (relaxedTurns === strictTurns) { + const strictReadability = getSameNetReadability(strictPath) + const relaxedReadability = getSameNetReadability(relaxedPath) + + if ( + relaxedReadability.segmentCount < strictReadability.segmentCount || + (relaxedReadability.segmentCount === strictReadability.segmentCount && + relaxedReadability.visibleLength < + strictReadability.visibleLength - RAIL_ALIGNMENT_EPSILON) + ) { + newPath = relaxedPath + } + } + + return { + ...targetTrace, + tracePath: newPath, + } +} + From 4dff114bf247159948db13d8b25c33a5c735cf33 Mon Sep 17 00:00:00 2001 From: Khoza khulile Date: Mon, 27 Jul 2026 16:57:24 +0200 Subject: [PATCH 17/17] Update minimizeTurnsWithFilteredLabels.ts Signed-off-by: Khoza khulile --- .../ TraceCleanupSolver /minimizeTurnsWithFilteredLabels.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/lib / solvers / TraceCleanupSolver /minimizeTurnsWithFilteredLabels.ts b/lib / solvers / TraceCleanupSolver /minimizeTurnsWithFilteredLabels.ts index 6f022005f..0c98daa0f 100644 --- a/lib / solvers / TraceCleanupSolver /minimizeTurnsWithFilteredLabels.ts +++ b/lib / solvers / TraceCleanupSolver /minimizeTurnsWithFilteredLabels.ts @@ -148,4 +148,3 @@ export const minimizeTurnsWithFilteredLabels = ({ tracePath: newPath, } } -