From 70e611f368a6748a444ea5b6d7a936252b9ecd0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?0hm=E2=98=98=EF=B8=8F?= Date: Sat, 11 Oct 2025 19:31:22 +0530 Subject: [PATCH] refactor(pipeline): Decompose overlap solver and add unconditional final label placement The monolithic TraceLabelOverlapAvoidanceSolver has been decomposed into a more modular, multi-step pipeline to improve separation of concerns: - LabelMergingSolver: Merges adjacent net labels into single targets. - TraceLabelOverlapAvoidanceSolver: Reroutes traces around merged labels. - TraceCleanupSolver: Optimizes the geometry of modified traces. This refactoring revealed a bug where the final label placement was skipped if no traces were rerouted. A new, unconditional `finalNetLabelPlacementSolver` step has been added to the pipeline to fix this, ensuring labels are always drawn correctly. --- .../SchematicTracePipelineSolver.ts | 49 +- .../TraceLabelOverlapAvoidanceSolver.ts | 292 ++---- .../rerouteCollidingTrace.ts | 37 +- .../LabelMergingSolver/LabelMergingSolver.ts | 191 ++++ .../OverlapAvoidanceStepSolver.ts | 144 +++ .../SingleOverlapSolver.ts | 117 +++ .../TraceCleanupSolver/TraceCleanupSolver.ts | 114 ++ .../TraceCleanupSolver}/balanceLShapes.ts | 16 +- .../TraceCleanupSolver}/countTurns.ts | 0 .../TraceCleanupSolver}/hasCollisions.ts | 2 +- .../hasCollisionsWithLabels.ts | 2 +- .../minimizeTurnsWithFilteredLabels.ts | 6 +- .../TraceCleanupSolver}/simplifyPath.ts | 2 +- .../TraceCleanupSolver}/tryConnectPoints.ts | 0 .../TraceCleanupSolver}/turnMinimization.ts | 2 - .../tryFourPointDetour.ts | 104 ++ .../trySnipAndReconnect.ts | 178 +--- .../examples/__snapshots__/example25.snap.svg | 6 +- .../examples/__snapshots__/example26.snap.svg | 6 +- .../TraceLabelOverlapAvoidanceSolver.test.ts | 437 ++++++++ .../TraceLabelOverlapAvoidanceSolver.snap.svg | 243 +++++ ...etLabelObstacles-to-SingleOverlapSolver.ts | 654 ++++++++++++ ...cementSolver-to-MergedNetLabelObstacles.ts | 967 +++++++++++++++++ ...ngleOverlapSolver-to-TraceCleanupSolver.ts | 987 ++++++++++++++++++ ...stacles-to-SingleOverlapSolver.ts.snap.svg | 375 +++++++ ...ver-to-MergedNetLabelObstacles.ts.snap.svg | 444 ++++++++ ...apSolver-to-TraceCleanupSolver.ts.snap.svg | 351 +++++++ .../MergedNetLabelObstacles.test.ts | 363 +++++++ .../OverlapAvoidanceStepSolver.test.ts | 698 +++++++++++++ .../sub-solver/SingleOverlapSolver.test.ts | 285 +++++ .../sub-solver/TraceCleanupSolver.test.ts | 288 +++++ .../MergedNetLabelObstacles.snap.svg | 240 +++++ .../OverlapAvoidanceStepSolver.snap.svg | 216 ++++ .../SingleOverlapSolver.snap.svg | 195 ++++ .../__snapshots__/TraceCleanupSolver.snap.svg | 141 +++ 35 files changed, 7742 insertions(+), 410 deletions(-) create mode 100644 lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver.ts create mode 100644 lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver.ts create mode 100644 lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts create mode 100644 lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/TraceCleanupSolver.ts rename lib/solvers/TraceLabelOverlapAvoidanceSolver/{ => sub-solvers/TraceCleanupSolver}/balanceLShapes.ts (91%) rename lib/solvers/TraceLabelOverlapAvoidanceSolver/{ => sub-solvers/TraceCleanupSolver}/countTurns.ts (100%) rename lib/solvers/TraceLabelOverlapAvoidanceSolver/{ => sub-solvers/TraceCleanupSolver}/hasCollisions.ts (80%) rename lib/solvers/TraceLabelOverlapAvoidanceSolver/{ => sub-solvers/TraceCleanupSolver}/hasCollisionsWithLabels.ts (77%) rename lib/solvers/TraceLabelOverlapAvoidanceSolver/{ => sub-solvers/TraceCleanupSolver}/minimizeTurnsWithFilteredLabels.ts (84%) rename lib/solvers/TraceLabelOverlapAvoidanceSolver/{ => sub-solvers/TraceCleanupSolver}/simplifyPath.ts (91%) rename lib/solvers/TraceLabelOverlapAvoidanceSolver/{ => sub-solvers/TraceCleanupSolver}/tryConnectPoints.ts (100%) rename lib/solvers/TraceLabelOverlapAvoidanceSolver/{ => sub-solvers/TraceCleanupSolver}/turnMinimization.ts (98%) create mode 100644 lib/solvers/TraceLabelOverlapAvoidanceSolver/tryFourPointDetour.ts create mode 100644 tests/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.test.ts create mode 100644 tests/solvers/TraceLabelOverlapAvoidanceSolver/__snapshots__/TraceLabelOverlapAvoidanceSolver.snap.svg create mode 100644 tests/solvers/TraceLabelOverlapAvoidanceSolver/stackGraphicsHorizontally/MergedNetLabelObstacles-to-SingleOverlapSolver.ts create mode 100644 tests/solvers/TraceLabelOverlapAvoidanceSolver/stackGraphicsHorizontally/NetLabelPlacementSolver-to-MergedNetLabelObstacles.ts create mode 100644 tests/solvers/TraceLabelOverlapAvoidanceSolver/stackGraphicsHorizontally/SingleOverlapSolver-to-TraceCleanupSolver.ts create mode 100644 tests/solvers/TraceLabelOverlapAvoidanceSolver/stackGraphicsHorizontally/__snapshots__/MergedNetLabelObstacles-to-SingleOverlapSolver.ts.snap.svg create mode 100644 tests/solvers/TraceLabelOverlapAvoidanceSolver/stackGraphicsHorizontally/__snapshots__/NetLabelPlacementSolver-to-MergedNetLabelObstacles.ts.snap.svg create mode 100644 tests/solvers/TraceLabelOverlapAvoidanceSolver/stackGraphicsHorizontally/__snapshots__/SingleOverlapSolver-to-TraceCleanupSolver.ts.snap.svg create mode 100644 tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/MergedNetLabelObstacles.test.ts create mode 100644 tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/OverlapAvoidanceStepSolver.test.ts create mode 100644 tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/SingleOverlapSolver.test.ts create mode 100644 tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/TraceCleanupSolver.test.ts create mode 100644 tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/MergedNetLabelObstacles.snap.svg create mode 100644 tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/OverlapAvoidanceStepSolver.snap.svg create mode 100644 tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/SingleOverlapSolver.snap.svg create mode 100644 tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/TraceCleanupSolver.snap.svg diff --git a/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts b/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts index 7d220b3..7315c53 100644 --- a/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +++ b/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts @@ -7,16 +7,19 @@ import type { GraphicsObject } from "graphics-debug" import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver" import type { InputProblem } from "lib/types/InputProblem" import { MspConnectionPairSolver } from "../MspConnectionPairSolver/MspConnectionPairSolver" -import { SchematicTraceLinesSolver } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver" +import { + SchematicTraceLinesSolver, + type SolvedTracePath, +} from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver" import { TraceOverlapShiftSolver } from "../TraceOverlapShiftSolver/TraceOverlapShiftSolver" import { NetLabelPlacementSolver } from "../NetLabelPlacementSolver/NetLabelPlacementSolver" import { visualizeInputProblem } from "./visualizeInputProblem" -import { GuidelinesSolver } from "../GuidelinesSolver/GuidelinesSolver" import { TraceLabelOverlapAvoidanceSolver } from "../TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver" -import { getInputChipBounds } from "../GuidelinesSolver/getInputChipBounds" import { correctPinsInsideChips } from "./correctPinsInsideChip" import { expandChipsToFitPins } from "./expandChipsToFitPins" import { LongDistancePairSolver } from "../LongDistancePairSolver/LongDistancePairSolver" +import { MergedNetLabelObstacles } from "../TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver" +import { TraceCleanupSolver } from "../TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/TraceCleanupSolver" type PipelineStep BaseSolver> = { solverName: string @@ -63,7 +66,9 @@ export class SchematicTracePipelineSolver extends BaseSolver { longDistancePairSolver?: LongDistancePairSolver traceOverlapShiftSolver?: TraceOverlapShiftSolver netLabelPlacementSolver?: NetLabelPlacementSolver + labelMergingSolver?: MergedNetLabelObstacles traceLabelOverlapAvoidanceSolver?: TraceLabelOverlapAvoidanceSolver + traceCleanupSolver?: TraceCleanupSolver startTimeOfPhase: Record endTimeOfPhase: Record @@ -176,24 +181,29 @@ export class SchematicTracePipelineSolver extends BaseSolver { return [ { - inputProblem: instance.inputProblem, + problem: instance.inputProblem, traces, netLabelPlacements, }, ] }, - { - onSolved: (instance) => { - if ( - instance.traceLabelOverlapAvoidanceSolver && - instance.netLabelPlacementSolver - ) { - const { netLabelPlacements } = - instance.traceLabelOverlapAvoidanceSolver.getOutput() - instance.netLabelPlacementSolver.netLabelPlacements = - netLabelPlacements - } - }, + ), + definePipelineStep( + "netLabelPlacementSolver", + NetLabelPlacementSolver, + (instance) => { + const traces = + instance.traceCleanupSolver?.getOutput().traces ?? + instance.traceLabelOverlapAvoidanceSolver!.getOutput().traces + + return [ + { + inputProblem: instance.inputProblem, + inputTraceMap: Object.fromEntries( + traces.map((trace: SolvedTracePath) => [trace.mspPairId, trace]), + ), + }, + ] }, ), ] @@ -303,16 +313,19 @@ export class SchematicTracePipelineSolver extends BaseSolver { }) as GraphicsObject[]), ] - if (visualizations.length === 1) return visualizations[0]! + if (visualizations.length === 1) { + return visualizations[0]! + } // Simple combination of visualizations - return { + const finalGraphics = { points: visualizations.flatMap((v) => v.points || []), rects: visualizations.flatMap((v) => v.rects || []), lines: visualizations.flatMap((v) => v.lines || []), circles: visualizations.flatMap((v) => v.circles || []), texts: visualizations.flatMap((v) => v.texts || []), } + return finalGraphics } /** diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts index 511c9d3..5ca1ac0 100644 --- a/lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts @@ -1,239 +1,145 @@ import { BaseSolver } from "../BaseSolver/BaseSolver" -import { detectTraceLabelOverlap } from "./detectTraceLabelOverlap" -import { rerouteCollidingTrace } from "./rerouteCollidingTrace" import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver" import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver" import type { GraphicsObject } from "graphics-debug" import { visualizeInputProblem } from "../SchematicTracePipelineSolver/visualizeInputProblem" -import { getRectBounds } from "../NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry" +import type { InputProblem } from "../../types/InputProblem" +import { MergedNetLabelObstacles } from "./sub-solvers/LabelMergingSolver/LabelMergingSolver" +import { TraceCleanupSolver } from "./sub-solvers/TraceCleanupSolver/TraceCleanupSolver" import { getColorFromString } from "lib/utils/getColorFromString" -import type { InputProblem } from "lib/types/InputProblem" -import { minimizeTurnsWithFilteredLabels } from "./minimizeTurnsWithFilteredLabels" -import { balanceLShapes } from "./balanceLShapes" -import { NetLabelPlacementSolver } from "../NetLabelPlacementSolver/NetLabelPlacementSolver" +import { OverlapAvoidanceStepSolver } from "./sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver" interface TraceLabelOverlapAvoidanceSolverInput { - inputProblem: InputProblem + problem: InputProblem traces: SolvedTracePath[] netLabelPlacements: NetLabelPlacement[] } +/** + * This solver is a pipeline that runs a series of sub-solvers to resolve + * trace-label overlaps and clean up the resulting traces. + */ export class TraceLabelOverlapAvoidanceSolver extends BaseSolver { - private problem: InputProblem - private traces: SolvedTracePath[] - private netTempLabelPlacements: NetLabelPlacement[] - private netLabelPlacements: NetLabelPlacement[] - private updatedTraces: SolvedTracePath[] - private mergedLabelNetIdMap: Record> - private detourCountByLabel: Record - private readonly PADDING_BUFFER = 0.1 + problem: InputProblem + traces: SolvedTracePath[] + netLabelPlacements: NetLabelPlacement[] + + // sub-solver instances + labelMergingSolver?: MergedNetLabelObstacles + overlapAvoidanceSolver?: OverlapAvoidanceStepSolver + traceCleanupSolver?: TraceCleanupSolver + pipelineStepIndex = 0 constructor(solverInput: TraceLabelOverlapAvoidanceSolverInput) { super() - this.problem = solverInput.inputProblem + this.problem = solverInput.problem this.traces = solverInput.traces - this.updatedTraces = [...solverInput.traces] - this.mergedLabelNetIdMap = {} - this.detourCountByLabel = {} - - const originalLabels = solverInput.netLabelPlacements - this.netLabelPlacements = originalLabels - if (!originalLabels || originalLabels.length === 0) { - this.netTempLabelPlacements = [] - return - } - - const labelGroups: Record = {} - - for (const p of originalLabels) { - if (p.pinIds.length === 0) continue - const chipId = p.pinIds[0].split(".")[0] - if (!chipId) continue - const key = `${chipId}-${p.orientation}` - if (!(key in labelGroups)) { - labelGroups[key] = [] - } - labelGroups[key]!.push(p) - } - - const finalPlacements: NetLabelPlacement[] = [] - for (const [key, group] of Object.entries(labelGroups)) { - if (group.length <= 1) { - finalPlacements.push(...group) - continue - } - - let minX = Infinity, - minY = Infinity, - maxX = -Infinity, - maxY = -Infinity - for (const p of group) { - const bounds = getRectBounds(p.center, p.width, p.height) - minX = Math.min(minX, bounds.minX) - minY = Math.min(minY, bounds.minY) - maxX = Math.max(maxX, bounds.maxX) - maxY = Math.max(maxY, bounds.maxY) - } - - const newWidth = maxX - minX - const newHeight = maxY - minY - const template = group[0]! - const syntheticId = `merged-group-${key}` - const originalNetIds = new Set(group.map((p) => p.globalConnNetId)) - this.mergedLabelNetIdMap[syntheticId] = originalNetIds - - finalPlacements.push({ - ...template, - globalConnNetId: syntheticId, - width: newWidth, - height: newHeight, - center: { x: minX + newWidth / 2, y: minY + newHeight / 2 }, - pinIds: [...new Set(group.flatMap((p) => p.pinIds))], - mspConnectionPairIds: [ - ...new Set(group.flatMap((p) => p.mspConnectionPairIds)), - ], - }) - } - - this.netTempLabelPlacements = finalPlacements + this.netLabelPlacements = solverInput.netLabelPlacements } override _step() { - if ( - !this.traces || - this.traces.length === 0 || - !this.netTempLabelPlacements || - this.netTempLabelPlacements.length === 0 - ) { - this.solved = true - return - } - - this.detourCountByLabel = {} - - const overlaps = detectTraceLabelOverlap( - this.traces, - this.netTempLabelPlacements, - ) - - if (overlaps.length === 0) { - this.solved = true - return - } - - const unfriendlyOverlaps = overlaps.filter((o) => { - const originalNetIds = this.mergedLabelNetIdMap[o.label.globalConnNetId] - if (originalNetIds) { - return !originalNetIds.has(o.trace.globalConnNetId) + // If a sub-solver is active, step it and check for completion. + if (this.activeSubSolver) { + this.activeSubSolver.step() + + if (this.activeSubSolver.solved) { + this.activeSubSolver = null + this.pipelineStepIndex++ + } else if (this.activeSubSolver.failed) { + this.failed = true // If any sub-solver fails, the whole thing fails + this.activeSubSolver = null } - return o.trace.globalConnNetId !== o.label.globalConnNetId - }) - - if (unfriendlyOverlaps.length === 0) { - this.solved = true - return - } - - const updatedTracesMap: Record = {} - for (const trace of this.traces) { - updatedTracesMap[trace.mspPairId] = trace - } - - const processedTraceIds = new Set() - - for (const overlap of unfriendlyOverlaps) { - if (processedTraceIds.has(overlap.trace.mspPairId)) { - continue - } - - const currentTraceState = updatedTracesMap[overlap.trace.mspPairId]! - const labelId = overlap.label.globalConnNetId - const detourCount = this.detourCountByLabel[labelId] || 0 - - const newTrace = rerouteCollidingTrace({ - trace: currentTraceState, - label: overlap.label, - problem: this.problem, - paddingBuffer: this.PADDING_BUFFER, - detourCount, - }) - - if (newTrace.tracePath !== currentTraceState.tracePath) { - this.detourCountByLabel[labelId] = detourCount + 1 - } - - updatedTracesMap[currentTraceState.mspPairId] = newTrace - processedTraceIds.add(currentTraceState.mspPairId) - } - - this.updatedTraces = Object.values(updatedTracesMap) - - const minimizedTraces = minimizeTurnsWithFilteredLabels({ - traces: this.updatedTraces, - problem: this.problem, - allLabelPlacements: this.netTempLabelPlacements, // Use temp labels which include merged ones - mergedLabelNetIdMap: this.mergedLabelNetIdMap, - paddingBuffer: this.PADDING_BUFFER, - }) - if (minimizedTraces) { - this.updatedTraces = minimizedTraces - } - const balancedTraces = balanceLShapes({ - traces: this.updatedTraces, - problem: this.problem, - allLabelPlacements: this.netLabelPlacements, - }) - if (balancedTraces) { - this.updatedTraces = balancedTraces + return // Return to allow the sub-solver to run } - const finalLabelPlacementSolver = new NetLabelPlacementSolver({ - inputProblem: this.problem, - inputTraceMap: Object.fromEntries( - this.updatedTraces.map((trace) => [trace.mspPairId, trace]), - ), - }) - finalLabelPlacementSolver.solve() - if (!finalLabelPlacementSolver.failed) { - this.netLabelPlacements = finalLabelPlacementSolver.netLabelPlacements + // If no sub-solver is active, create the next one in the pipeline. + switch (this.pipelineStepIndex) { + case 0: + // Step 1: Label Merging (non-iterative) + this.labelMergingSolver = new MergedNetLabelObstacles({ + netLabelPlacements: this.netLabelPlacements, + problem: this.problem, + }) + this.labelMergingSolver.step() // Non-iterative, so one step is enough + this.pipelineStepIndex++ + break + + case 1: + // Step 2: Overlap Avoidance (iterative) + this.overlapAvoidanceSolver = new OverlapAvoidanceStepSolver({ + problem: this.problem, + traces: this.traces, + netLabelPlacements: + this.labelMergingSolver!.getOutput().netLabelPlacements, + mergedLabelNetIdMap: + this.labelMergingSolver!.getOutput().mergedLabelNetIdMap, + }) + this.activeSubSolver = this.overlapAvoidanceSolver + break + + case 2: + // Step 3: Trace Cleanup (non-iterative) + this.traceCleanupSolver = new TraceCleanupSolver({ + problem: this.problem, + allTraces: this.overlapAvoidanceSolver!.getOutput().allTraces, + targetTraceIds: new Set( + this.overlapAvoidanceSolver!.getOutput().modifiedTraces.map( + (t) => t.mspPairId, + ), + ), + allLabelPlacements: + this.labelMergingSolver!.getOutput().netLabelPlacements, + mergedLabelNetIdMap: + this.labelMergingSolver!.getOutput().mergedLabelNetIdMap, + paddingBuffer: 0.01, + }) + this.traceCleanupSolver.step() // Non-iterative + this.pipelineStepIndex++ + break + + default: + this.solved = true + break } - - this.solved = true } getOutput() { return { - traces: this.updatedTraces, - netLabelPlacements: this.netLabelPlacements, + traces: this.traceCleanupSolver?.getOutput().traces ?? this.traces, + netLabelPlacements: + this.labelMergingSolver?.getOutput().netLabelPlacements ?? + this.netLabelPlacements, } } override visualize(): GraphicsObject { - const graphics = visualizeInputProblem(this.problem) + if (this.activeSubSolver) { + return this.activeSubSolver.visualize() + } + // When no sub-solver is active, show the current state of the pipeline + const graphics = visualizeInputProblem(this.problem) if (!graphics.lines) graphics.lines = [] - if (!graphics.circles) graphics.circles = [] - if (!graphics.texts) graphics.texts = [] if (!graphics.rects) graphics.rects = [] - for (const trace of this.updatedTraces) { + const output = this.getOutput() + + for (const trace of output.traces) { graphics.lines!.push({ points: trace.tracePath, strokeColor: "purple", }) } - for (const p of this.netLabelPlacements) { + for (const label of output.netLabelPlacements) { + const color = getColorFromString(label.globalConnNetId, 0.3) graphics.rects!.push({ - center: p.center, - width: p.width, - height: p.height, - fill: getColorFromString(p.globalConnNetId, 0.35), - }) - graphics.points!.push({ - x: p.anchorPoint.x, - y: p.anchorPoint.y, - color: getColorFromString(p.globalConnNetId, 0.9), + center: label.center, + width: label.width, + height: label.height, + fill: color, + stroke: color.replace("0.3", "1"), + label: label.globalConnNetId, }) } diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace.ts index ec70cf1..bc8d349 100644 --- a/lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace.ts +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace.ts @@ -1,16 +1,16 @@ +import type { Point } from "@tscircuit/math-utils" import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver" import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver" import { getRectBounds } from "../NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry" -import { getObstacleRects } from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect" import type { InputProblem } from "lib/types/InputProblem" import { findTraceViolationZone } from "./violation" -import { tryFourPointDetour, trySnipAndReconnect } from "./trySnipAndReconnect" -import { simplifyPath } from "./simplifyPath" +import { generateSnipAndReconnectCandidates } from "./trySnipAndReconnect" +import { simplifyPath } from "./sub-solvers/TraceCleanupSolver/simplifyPath" +import { generateFourPointDetourCandidates } from "./tryFourPointDetour" -export const rerouteCollidingTrace = ({ +export const generateRerouteCandidates = ({ trace, label, - problem, paddingBuffer, detourCount, }: { @@ -19,14 +19,13 @@ export const rerouteCollidingTrace = ({ problem: InputProblem paddingBuffer: number detourCount: number -}): SolvedTracePath => { +}): Point[][] => { const initialTrace = { ...trace, tracePath: simplifyPath(trace.tracePath) } if (trace.globalConnNetId === label.globalConnNetId) { - return initialTrace + return [initialTrace.tracePath] } - const obstacles = getObstacleRects(problem) const labelPadding = paddingBuffer const labelBoundsRaw = getRectBounds(label.center, label.width, label.height) const labelBounds = { @@ -37,37 +36,27 @@ export const rerouteCollidingTrace = ({ chipId: `netlabel-${label.netId}`, } - const fourPointResult = tryFourPointDetour({ + const fourPointCandidates = generateFourPointDetourCandidates({ initialTrace, label, labelBounds, - obstacles, paddingBuffer, detourCount, }) - if (fourPointResult) { - initialTrace.tracePath = fourPointResult.tracePath - } + const { firstInsideIndex, lastInsideIndex } = findTraceViolationZone( initialTrace.tracePath, labelBounds, ) - const snipReconnectResult = trySnipAndReconnect({ + const snipReconnectCandidates = generateSnipAndReconnectCandidates({ initialTrace, firstInsideIndex, lastInsideIndex, labelBounds, - obstacles, + paddingBuffer, + detourCount, }) - if (snipReconnectResult) { - return snipReconnectResult - } - - if (fourPointResult) { - return fourPointResult - } - - return initialTrace + return [...fourPointCandidates, ...snipReconnectCandidates] } diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver.ts new file mode 100644 index 0000000..b350a4e --- /dev/null +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver.ts @@ -0,0 +1,191 @@ +import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver" +import type { NetLabelPlacement } from "../../../NetLabelPlacementSolver/NetLabelPlacementSolver" +import { getRectBounds } from "../../../NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry" +import type { GraphicsObject } from "graphics-debug" +import { getColorFromString } from "lib/utils/getColorFromString" +import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem" +import type { InputProblem } from "lib/types/InputProblem" + +interface LabelMergingSolverInput { + netLabelPlacements: NetLabelPlacement[] + problem: InputProblem +} + +interface LabelMergingSolverOutput { + netLabelPlacements: NetLabelPlacement[] + mergedLabelNetIdMap: Record> +} + +/** + * Merges multiple net labels into a single, larger label if they are on the + * same side of the same chip, reducing schematic clutter. + */ +export class MergedNetLabelObstacles extends BaseSolver { + private input: LabelMergingSolverInput + private output!: LabelMergingSolverOutput + private problem: InputProblem + + constructor(solverInput: LabelMergingSolverInput) { + super() + this.input = solverInput + this.problem = solverInput.problem + + // Initialize output to a default state to allow visualization before the first step + this.output = { + netLabelPlacements: solverInput.netLabelPlacements, + mergedLabelNetIdMap: {}, + } + } + + override _step() { + const originalLabels = this.input.netLabelPlacements + const mergedLabelNetIdMap: Record> = {} + + if (!originalLabels || originalLabels.length === 0) { + this.output = { + netLabelPlacements: [], + mergedLabelNetIdMap: {}, + } + this.solved = true + return + } + + const labelGroups: Record = {} + + for (const p of originalLabels) { + if (p.pinIds.length === 0) continue + const chipId = p.pinIds[0].split(".")[0] + if (!chipId) continue + const key = `${chipId}-${p.orientation}` + if (!(key in labelGroups)) { + labelGroups[key] = [] + } + labelGroups[key]!.push(p) + } + + const finalPlacements: NetLabelPlacement[] = [] + for (const [key, group] of Object.entries(labelGroups)) { + if (group.length <= 1) { + finalPlacements.push(...group) + continue + } + + let minX = Infinity + let minY = Infinity + let maxX = -Infinity + let maxY = -Infinity + for (const p of group) { + const bounds = getRectBounds(p.center, p.width, p.height) + minX = Math.min(minX, bounds.minX) + minY = Math.min(minY, bounds.minY) + maxX = Math.max(maxX, bounds.maxX) + maxY = Math.max(maxY, bounds.maxY) + } + + const newWidth = maxX - minX + const newHeight = maxY - minY + const template = group[0]! + const syntheticId = `merged-group-${key}` + const originalNetIds = new Set(group.map((p) => p.globalConnNetId)) + mergedLabelNetIdMap[syntheticId] = originalNetIds + + finalPlacements.push({ + ...template, + globalConnNetId: syntheticId, + width: newWidth, + height: newHeight, + center: { x: minX + newWidth / 2, y: minY + newHeight / 2 }, + pinIds: [...new Set(group.flatMap((p) => p.pinIds))], + mspConnectionPairIds: [ + ...new Set(group.flatMap((p) => p.mspConnectionPairIds)), + ], + }) + } + + this.output = { + netLabelPlacements: finalPlacements, + mergedLabelNetIdMap, + } + this.solved = true + } + + getOutput(): LabelMergingSolverOutput { + return this.output + } + + override visualize(): GraphicsObject { + const graphics = visualizeInputProblem(this.problem, { + chipAlpha: 0.1, + connectionAlpha: 0.1, + }) + + if (!graphics.rects) graphics.rects = [] + if (!graphics.lines) graphics.lines = [] + if (!graphics.points) graphics.points = [] + if (!graphics.texts) graphics.texts = [] + + const originalLabelsById = new Map() + for (const label of this.input.netLabelPlacements) { + originalLabelsById.set(label.globalConnNetId, label) + } + + for (const finalLabel of this.output.netLabelPlacements) { + const isMerged = finalLabel.globalConnNetId.startsWith("merged-group-") + const color = getColorFromString(finalLabel.globalConnNetId) + + if (isMerged) { + // Draw the new merged label + graphics.rects.push({ + center: finalLabel.center, + width: finalLabel.width, + height: finalLabel.height, + fill: color.replace(/, 1\)/, ", 0.2)"), // semi-transparent + stroke: color, + label: finalLabel.globalConnNetId, + }) + + const originalNetIds = + this.output.mergedLabelNetIdMap[finalLabel.globalConnNetId] + if (originalNetIds) { + for (const originalNetId of originalNetIds) { + const originalLabel = originalLabelsById.get(originalNetId) + if (originalLabel) { + // Draw the original label as a dashed box + const bounds = getRectBounds( + originalLabel.center, + originalLabel.width, + originalLabel.height, + ) + const p1 = { x: bounds.minX, y: bounds.minY } + const p2 = { x: bounds.maxX, y: bounds.minY } + const p3 = { x: bounds.maxX, y: bounds.maxY } + const p4 = { x: bounds.minX, y: bounds.maxY } + graphics.lines.push({ + points: [p1, p2, p3, p4, p1], + strokeColor: color, + strokeDash: "4 4", + }) + // Draw line from original to new center + graphics.lines.push({ + points: [originalLabel.center, finalLabel.center], + strokeColor: color, + strokeDash: "2 2", + }) + } + } + } + } else { + // Draw un-merged labels + graphics.rects.push({ + center: finalLabel.center, + width: finalLabel.width, + height: finalLabel.height, + stroke: color, + label: finalLabel.globalConnNetId, + }) + } + } + + return graphics + } +} diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver.ts new file mode 100644 index 0000000..30eed75 --- /dev/null +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver.ts @@ -0,0 +1,144 @@ +import type { GraphicsObject } from "graphics-debug" +import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver" +import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver" +import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver" +import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem" +import type { InputProblem } from "lib/types/InputProblem" +import { detectTraceLabelOverlap } from "../../detectTraceLabelOverlap" +import { SingleOverlapSolver } from "../SingleOverlapSolver/SingleOverlapSolver" + +type Overlap = ReturnType[0] + +// Define a type for the input of the internal overlap solver to avoid conflicts +interface OverlapCollectionSolverInput { + problem: InputProblem + traces: SolvedTracePath[] + netLabelPlacements: NetLabelPlacement[] + mergedLabelNetIdMap: Record> +} + +/** + * This is an internal solver that manages the step-by-step process of avoiding + * multiple overlaps. It follows the pattern of SchematicTraceLinesSolver. + */ +export class OverlapAvoidanceStepSolver extends BaseSolver { + problem: InputProblem + netLabelPlacements: NetLabelPlacement[] + mergedLabelNetIdMap: Record> + + allTraces: SolvedTracePath[] + modifiedTraces: SolvedTracePath[] = [] + + private detourCountByLabel: Record = {} + private readonly PADDING_BUFFER = 0.1 + + public override activeSubSolver: SingleOverlapSolver | null = null + private overlapQueue: Overlap[] = [] + private recentlyFailed: Set = new Set() + + constructor(solverInput: OverlapCollectionSolverInput) { + super() + this.problem = solverInput.problem + this.netLabelPlacements = solverInput.netLabelPlacements + this.mergedLabelNetIdMap = solverInput.mergedLabelNetIdMap + this.allTraces = [...solverInput.traces] + } + + override _step() { + if (this.activeSubSolver) { + this.activeSubSolver.step() + + if (this.activeSubSolver.solved) { + const solvedPath = this.activeSubSolver.solvedTracePath + if (solvedPath) { + const traceIndex = this.allTraces.findIndex( + (t) => t.mspPairId === this.activeSubSolver!.initialTrace.mspPairId, + ) + if (traceIndex !== -1) { + this.allTraces[traceIndex].tracePath = solvedPath + this.modifiedTraces.push(this.allTraces[traceIndex]) + } + } + this.activeSubSolver = null + this.recentlyFailed.clear() + } else if (this.activeSubSolver.failed) { + const overlapId = `${this.activeSubSolver.initialTrace.mspPairId}-${this.activeSubSolver.label.globalConnNetId}` + this.recentlyFailed.add(overlapId) + this.activeSubSolver = null + } + return + } + + const overlaps = detectTraceLabelOverlap( + this.allTraces, + this.netLabelPlacements, + ).filter((o) => { + const originalNetIds = this.mergedLabelNetIdMap[o.label.globalConnNetId] + if (originalNetIds) { + return !originalNetIds.has(o.trace.globalConnNetId) + } + return o.trace.globalConnNetId !== o.label.globalConnNetId + }) + + if (overlaps.length === 0) { + this.solved = true + return + } + + const nonFailedOverlaps = overlaps.filter((o) => { + const overlapId = `${o.trace.mspPairId}-${o.label.globalConnNetId}` + return !this.recentlyFailed.has(overlapId) + }) + + if (nonFailedOverlaps.length === 0) { + this.solved = true // No more progress can be made + return + } + + this.overlapQueue = nonFailedOverlaps + + const nextOverlap = this.overlapQueue.shift() + + if (nextOverlap) { + const traceToFix = this.allTraces.find( + (t) => t.mspPairId === nextOverlap.trace.mspPairId, + ) + if (traceToFix) { + const labelId = nextOverlap.label.globalConnNetId + const detourCount = this.detourCountByLabel[labelId] || 0 + this.detourCountByLabel[labelId] = detourCount + 1 + + this.activeSubSolver = new SingleOverlapSolver({ + trace: traceToFix, + label: nextOverlap.label, + problem: this.problem, + paddingBuffer: this.PADDING_BUFFER, + detourCount, + }) + } + } + } + + getOutput() { + return { + allTraces: this.allTraces, + modifiedTraces: this.modifiedTraces, + } + } + + override visualize(): GraphicsObject { + if (this.activeSubSolver) { + return this.activeSubSolver.visualize() + } + // When idle, show all the traces + const graphics = visualizeInputProblem(this.problem) + if (!graphics.lines) graphics.lines = [] + for (const trace of this.allTraces) { + graphics.lines!.push({ + points: trace.tracePath, + strokeColor: "purple", + }) + } + return graphics + } +} diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts new file mode 100644 index 0000000..19bd1ae --- /dev/null +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts @@ -0,0 +1,117 @@ +import type { Point } from "@tscircuit/math-utils" +import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver" +import type { NetLabelPlacement } from "../../../NetLabelPlacementSolver/NetLabelPlacementSolver" +import type { InputProblem } from "lib/types/InputProblem" +import type { GraphicsObject } from "graphics-debug" +import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver" +import { isPathCollidingWithObstacles } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions" +import { getObstacleRects } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect" +import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem" +import { generateRerouteCandidates } from "../../rerouteCollidingTrace" +import { simplifyPath } from "../TraceCleanupSolver/simplifyPath" + +interface SingleOverlapSolverInput { + trace: SolvedTracePath + label: NetLabelPlacement + problem: InputProblem + paddingBuffer: number + detourCount: number +} + +/** + * This solver attempts to find a valid rerouting for a single trace that is + * overlapping with a net label. It tries various candidate paths until it + * finds one that does not introduce new collisions. + */ +export class SingleOverlapSolver extends BaseSolver { + queuedCandidatePaths: Point[][] + solvedTracePath: Point[] | null = null + initialTrace: SolvedTracePath + problem: InputProblem + obstacles: ReturnType + label: NetLabelPlacement + + constructor(solverInput: SingleOverlapSolverInput) { + super() + this.initialTrace = solverInput.trace + this.problem = solverInput.problem + this.label = solverInput.label + + const candidates = generateRerouteCandidates({ + ...solverInput, + }) + + const getPathLength = (pts: Point[]) => { + let len = 0 + for (let i = 0; i < pts.length - 1; i++) { + const dx = pts[i + 1].x - pts[i].x + const dy = pts[i + 1].y - pts[i].y + len += Math.sqrt(dx * dx + dy * dy) + } + return len + } + + this.queuedCandidatePaths = candidates.sort( + (a, b) => getPathLength(a) - getPathLength(b), + ) + this.obstacles = getObstacleRects(this.problem) + } + + override _step() { + if (this.queuedCandidatePaths.length === 0) { + this.failed = true + return + } + + const nextCandidatePath = this.queuedCandidatePaths.shift()! + const simplifiedPath = simplifyPath(nextCandidatePath) + + if (!isPathCollidingWithObstacles(simplifiedPath, this.obstacles)) { + this.solvedTracePath = simplifiedPath + this.solved = true + } + } + + override visualize(): GraphicsObject { + const graphics = visualizeInputProblem(this.problem, { + chipAlpha: 0.1, + connectionAlpha: 0.1, + }) + + if (!graphics.lines) graphics.lines = [] + if (!graphics.rects) graphics.rects = [] + + // Draw initial trace + graphics.lines.push({ + points: this.initialTrace.tracePath, + strokeColor: "red", + strokeDash: "4 4", + }) + + // Draw label + graphics.rects.push({ + center: this.label.center, + width: this.label.width, + height: this.label.height, + fill: "rgba(255, 0, 0, 0.2)", + }) + + // Draw next candidate + if (this.queuedCandidatePaths.length > 0) { + graphics.lines.push({ + points: this.queuedCandidatePaths[0], + strokeColor: "orange", + }) + } + + // Draw solved path + if (this.solvedTracePath) { + graphics.lines.push({ + points: this.solvedTracePath, + strokeColor: "green", + }) + } + + return graphics + } +} diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/TraceCleanupSolver.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/TraceCleanupSolver.ts new file mode 100644 index 0000000..e702caa --- /dev/null +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/TraceCleanupSolver.ts @@ -0,0 +1,114 @@ +import type { InputProblem } from "lib/types/InputProblem" +import type { GraphicsObject, Line } from "graphics-debug" +import type { NetLabelPlacement } from "../../../NetLabelPlacementSolver/NetLabelPlacementSolver" +import { minimizeTurnsWithFilteredLabels } from "./minimizeTurnsWithFilteredLabels" +import { balanceLShapes } from "./balanceLShapes" +import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver" +import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver" +import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem" + +interface TraceCleanupSolverInput { + problem: InputProblem + allTraces: SolvedTracePath[] + targetTraceIds: Set + allLabelPlacements: NetLabelPlacement[] + mergedLabelNetIdMap: Record> + paddingBuffer: number +} + +/** + * Cleans up traces by minimizing turns and balancing L-shapes to improve + * the overall aesthetics and readability of the schematic. + */ +export class TraceCleanupSolver extends BaseSolver { + private input: TraceCleanupSolverInput + private outputTraces: SolvedTracePath[] + + constructor(solverInput: TraceCleanupSolverInput) { + super() + this.input = solverInput + this.outputTraces = [...solverInput.allTraces] + } + + override _step() { + const { + allTraces, + targetTraceIds, + problem, + allLabelPlacements, + mergedLabelNetIdMap, + paddingBuffer, + } = this.input + + if (targetTraceIds.size === 0) { + this.solved = true + return + } + + const tracesToProcess = allTraces.filter((t) => + targetTraceIds.has(t.mspPairId), + ) + + let cleanedTraces: SolvedTracePath[] | null = tracesToProcess + + const minimizedTraces = minimizeTurnsWithFilteredLabels({ + traces: cleanedTraces, + problem, + allLabelPlacements, + mergedLabelNetIdMap, + paddingBuffer, + }) + + if (minimizedTraces) { + cleanedTraces = minimizedTraces + } + + const balancedTraces = balanceLShapes({ + traces: cleanedTraces, + problem, + allLabelPlacements, + }) + + if (balancedTraces) { + cleanedTraces = balancedTraces + } + + // Merge the cleaned traces back into the main list + const tracesMap = new Map(allTraces.map((t) => [t.mspPairId, t])) + for (const cleanedTrace of cleanedTraces) { + tracesMap.set(cleanedTrace.mspPairId, cleanedTrace) + } + + this.outputTraces = Array.from(tracesMap.values()) + this.solved = true + } + + getOutput() { + return { + traces: this.outputTraces, + } + } + + override visualize(): GraphicsObject { + const graphics = visualizeInputProblem(this.input.problem, { + chipAlpha: 0.1, + connectionAlpha: 0.1, + }) + + if (!graphics.lines) graphics.lines = [] + if (!graphics.points) graphics.points = [] + if (!graphics.rects) graphics.rects = [] + if (!graphics.circles) graphics.circles = [] + if (!graphics.texts) graphics.texts = [] + + for (const trace of this.outputTraces) { + const line: Line = { + points: trace.tracePath.map((p) => ({ x: p.x, y: p.y })), + strokeColor: "blue", + strokeWidth: 2, + } + graphics.lines!.push(line) + } + return graphics + } +} diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/balanceLShapes.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/balanceLShapes.ts similarity index 91% rename from lib/solvers/TraceLabelOverlapAvoidanceSolver/balanceLShapes.ts rename to lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/balanceLShapes.ts index d6817f5..e544fdb 100644 --- a/lib/solvers/TraceLabelOverlapAvoidanceSolver/balanceLShapes.ts +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/balanceLShapes.ts @@ -1,10 +1,10 @@ import type { Point } from "graphics-debug" import type { InputProblem } from "lib/types/InputProblem" -import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver" -import { getObstacleRects } from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect" -import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver" -import { segmentIntersectsRect } from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions" +import type { NetLabelPlacement } from "../../../NetLabelPlacementSolver/NetLabelPlacementSolver" import { simplifyPath } from "./simplifyPath" +import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver" +import { segmentIntersectsRect } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions" +import { getObstacleRects } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect" export const balanceLShapes = ({ traces, @@ -81,7 +81,7 @@ export const balanceLShapes = ({ } const newTraces = traces.map((trace) => { - let newPath = [...trace.tracePath] + const newPath = [...trace.tracePath] if (newPath.length < 4) { return { ...trace } @@ -94,7 +94,8 @@ export const balanceLShapes = ({ if (newPath.length === 4) { const [p0, p1, p2, p3] = newPath - let p1New: Point, p2New: Point + let p1New: Point + let p2New: Point const isHVHShape = p0.y === p1.y && p1.x === p2.x && p2.y === p3.y @@ -154,7 +155,8 @@ export const balanceLShapes = ({ continue } - let p2New: Point, p3New: Point + let p2New: Point + let p3New: Point const len1Original = isHVHZShape ? Math.abs(p1.x - p2.x) : Math.abs(p1.y - p2.y) diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/countTurns.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/countTurns.ts similarity index 100% rename from lib/solvers/TraceLabelOverlapAvoidanceSolver/countTurns.ts rename to lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/countTurns.ts diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/hasCollisions.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/hasCollisions.ts similarity index 80% rename from lib/solvers/TraceLabelOverlapAvoidanceSolver/hasCollisions.ts rename to lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/hasCollisions.ts index 8eaec68..0dab251 100644 --- a/lib/solvers/TraceLabelOverlapAvoidanceSolver/hasCollisions.ts +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/hasCollisions.ts @@ -1,5 +1,5 @@ import type { Point } from "graphics-debug" -import { segmentIntersectsRect } from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions" +import { segmentIntersectsRect } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions" export const hasCollisions = ( pathSegments: Point[], diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/hasCollisionsWithLabels.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/hasCollisionsWithLabels.ts similarity index 77% rename from lib/solvers/TraceLabelOverlapAvoidanceSolver/hasCollisionsWithLabels.ts rename to lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/hasCollisionsWithLabels.ts index c698b19..26fe68e 100644 --- a/lib/solvers/TraceLabelOverlapAvoidanceSolver/hasCollisionsWithLabels.ts +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/hasCollisionsWithLabels.ts @@ -1,5 +1,5 @@ import type { Point } from "@tscircuit/math-utils" -import { segmentIntersectsRect } from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions" +import { segmentIntersectsRect } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions" export const hasCollisionsWithLabels = ( pathSegments: Point[], diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/minimizeTurnsWithFilteredLabels.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts similarity index 84% rename from lib/solvers/TraceLabelOverlapAvoidanceSolver/minimizeTurnsWithFilteredLabels.ts rename to lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts index e9dee09..5fa8d53 100644 --- a/lib/solvers/TraceLabelOverlapAvoidanceSolver/minimizeTurnsWithFilteredLabels.ts +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts @@ -1,8 +1,8 @@ import type { InputProblem } from "lib/types/InputProblem" -import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver" -import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver" -import { getObstacleRects } from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect" +import type { NetLabelPlacement } from "../../../NetLabelPlacementSolver/NetLabelPlacementSolver" import { minimizeTurns } from "./turnMinimization" +import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver" +import { getObstacleRects } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect" export const minimizeTurnsWithFilteredLabels = ({ traces, diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/simplifyPath.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/simplifyPath.ts similarity index 91% rename from lib/solvers/TraceLabelOverlapAvoidanceSolver/simplifyPath.ts rename to lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/simplifyPath.ts index 12ee70e..e17bfb5 100644 --- a/lib/solvers/TraceLabelOverlapAvoidanceSolver/simplifyPath.ts +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/simplifyPath.ts @@ -2,7 +2,7 @@ import type { Point } from "graphics-debug" import { isHorizontal, isVertical, -} from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions" +} from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions" export const simplifyPath = (path: Point[]): Point[] => { if (path.length < 3) return path diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/tryConnectPoints.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/tryConnectPoints.ts similarity index 100% rename from lib/solvers/TraceLabelOverlapAvoidanceSolver/tryConnectPoints.ts rename to lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/tryConnectPoints.ts diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/turnMinimization.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/turnMinimization.ts similarity index 98% rename from lib/solvers/TraceLabelOverlapAvoidanceSolver/turnMinimization.ts rename to lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/turnMinimization.ts index e5cff33..87414e3 100644 --- a/lib/solvers/TraceLabelOverlapAvoidanceSolver/turnMinimization.ts +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/turnMinimization.ts @@ -105,8 +105,6 @@ export const minimizeTurns = ({ if (!collidesWithObstacles && !collidesWithLabels) { const newTurns = countTurns(testPath) - const turnsRemoved = stairEndIdx - startIdx - 1 - optimizedPath = testPath currentTurns = newTurns improved = true diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/tryFourPointDetour.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/tryFourPointDetour.ts new file mode 100644 index 0000000..0a4d1c2 --- /dev/null +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/tryFourPointDetour.ts @@ -0,0 +1,104 @@ +import type { Point } from "graphics-debug" +import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver" +import { getRectBounds } from "../NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry" +import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver" +import { + segmentIntersectsRect, + isVertical, +} from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions" + +export const generateFourPointDetourCandidates = ({ + initialTrace, + label, + labelBounds, + paddingBuffer, + detourCount, +}: { + initialTrace: SolvedTracePath + label: NetLabelPlacement + labelBounds: any + paddingBuffer: number + detourCount: number +}): Point[][] => { + let collidingSegIndex = -1 + for (let i = 0; i < initialTrace.tracePath.length - 1; i++) { + if ( + segmentIntersectsRect( + initialTrace.tracePath[i], + initialTrace.tracePath[i + 1], + labelBounds, + ) + ) { + collidingSegIndex = i + break + } + } + + if (collidingSegIndex === -1) return [] + + const pA = initialTrace.tracePath[collidingSegIndex] + const pB = initialTrace.tracePath[collidingSegIndex + 1] + + if (!pA || !pB) return [] + + const candidateDetours: Point[][] = [] + const paddedLabelBounds = getRectBounds( + label.center, + label.width, + label.height, + ) + + const effectivePadding = paddingBuffer + detourCount * paddingBuffer + + if (isVertical(pA, pB)) { + const xCandidates = [ + paddedLabelBounds.maxX + effectivePadding, + paddedLabelBounds.minX - effectivePadding, + ] + for (const newX of xCandidates) { + candidateDetours.push( + pB.y > pA.y + ? [ + { x: pA.x, y: paddedLabelBounds.minY - effectivePadding }, + { x: newX, y: paddedLabelBounds.minY - effectivePadding }, + { x: newX, y: paddedLabelBounds.maxY + effectivePadding }, + { x: pB.x, y: paddedLabelBounds.maxY + effectivePadding }, + ] + : [ + { x: pA.x, y: paddedLabelBounds.maxY + effectivePadding }, + { x: newX, y: paddedLabelBounds.maxY + effectivePadding }, + { x: newX, y: paddedLabelBounds.minY - effectivePadding }, + { x: pB.x, y: paddedLabelBounds.minY - effectivePadding }, + ], + ) + } + } else { + const yCandidates = [ + paddedLabelBounds.maxY + effectivePadding, + paddedLabelBounds.minY - effectivePadding, + ] + for (const newY of yCandidates) { + candidateDetours.push( + pB.x > pA.x + ? [ + { x: paddedLabelBounds.minX - effectivePadding, y: pA.y }, + { x: paddedLabelBounds.minX - effectivePadding, y: newY }, + { x: paddedLabelBounds.maxX + effectivePadding, y: newY }, + { x: paddedLabelBounds.maxX + effectivePadding, y: pB.y }, + ] + : [ + { x: paddedLabelBounds.maxX + effectivePadding, y: pA.y }, + { x: paddedLabelBounds.maxX + effectivePadding, y: newY }, + { x: paddedLabelBounds.minX - effectivePadding, y: newY }, + { x: paddedLabelBounds.minX - effectivePadding, y: pB.y }, + ], + ) + } + } + + return candidateDetours.map((detourPoints) => [ + ...initialTrace.tracePath.slice(0, collidingSegIndex + 1), + ...detourPoints, + ...initialTrace.tracePath.slice(collidingSegIndex + 1), + ]) +} diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/trySnipAndReconnect.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/trySnipAndReconnect.ts index 1da3865..cb95d07 100644 --- a/lib/solvers/TraceLabelOverlapAvoidanceSolver/trySnipAndReconnect.ts +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/trySnipAndReconnect.ts @@ -1,32 +1,26 @@ import type { Point } from "@tscircuit/math-utils" -import { getRectBounds } from "../NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry" import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver" -import { - isPathCollidingWithObstacles, - isVertical, - segmentIntersectsRect, -} from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions" -import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver" -import { simplifyPath } from "./simplifyPath" -export const trySnipAndReconnect = ({ +export const generateSnipAndReconnectCandidates = ({ initialTrace, firstInsideIndex, lastInsideIndex, labelBounds, - obstacles, + paddingBuffer, + detourCount, }: { initialTrace: SolvedTracePath firstInsideIndex: number lastInsideIndex: number labelBounds: any - obstacles: any[] -}): SolvedTracePath | null => { + paddingBuffer: number + detourCount: number +}): Point[][] => { if ( firstInsideIndex <= 0 || lastInsideIndex >= initialTrace.tracePath.length - 1 ) { - return null + return [] } const entryPoint = initialTrace.tracePath[firstInsideIndex - 1] @@ -35,32 +29,19 @@ export const trySnipAndReconnect = ({ const pathToEntry = initialTrace.tracePath.slice(0, firstInsideIndex) const pathFromExit = initialTrace.tracePath.slice(lastInsideIndex + 1) - const candidateDetours: Point[][] = [] + const allCandidateDetours: Point[][] = [] + // Candidate type 1: simple elbow if (entryPoint.x !== exitPoint.x && entryPoint.y !== exitPoint.y) { - candidateDetours.push([{ x: exitPoint.x, y: entryPoint.y }]) - candidateDetours.push([{ x: entryPoint.x, y: exitPoint.y }]) + allCandidateDetours.push([{ x: exitPoint.x, y: entryPoint.y }]) + allCandidateDetours.push([{ x: entryPoint.x, y: exitPoint.y }]) } else if (entryPoint.x === exitPoint.x || entryPoint.y === exitPoint.y) { - const newPath = [...pathToEntry, ...pathFromExit] - const simplified = simplifyPath(newPath) - if (!isPathCollidingWithObstacles(simplified, obstacles)) { - return { ...initialTrace, tracePath: simplified } - } + // Candidate type 2: direct connection (if points are aligned) + allCandidateDetours.push([]) // No detour points needed } - for (let i = 0; i < candidateDetours.length; i++) { - const detour = candidateDetours[i] - const newFullPath = [...pathToEntry, ...detour, ...pathFromExit] - const simplified = simplifyPath(newFullPath) - - if (!isPathCollidingWithObstacles(simplified, obstacles)) { - return { ...initialTrace, tracePath: simplified } - } - } - - candidateDetours.length = 0 - - const buffer = 0.1 + // Candidate type 3: routing around the label bounds + const buffer = paddingBuffer + detourCount * paddingBuffer const leftX = labelBounds.minX - buffer const rightX = labelBounds.maxX + buffer const topY = labelBounds.maxY + buffer @@ -71,7 +52,7 @@ export const trySnipAndReconnect = ({ entryPoint.x < labelBounds.maxX && exitPoint.x < labelBounds.maxX ) { - candidateDetours.push([ + allCandidateDetours.push([ { x: leftX, y: entryPoint.y }, { x: leftX, y: exitPoint.y }, ]) @@ -82,7 +63,7 @@ export const trySnipAndReconnect = ({ entryPoint.x > labelBounds.minX && exitPoint.x > labelBounds.minX ) { - candidateDetours.push([ + allCandidateDetours.push([ { x: rightX, y: entryPoint.y }, { x: rightX, y: exitPoint.y }, ]) @@ -93,7 +74,7 @@ export const trySnipAndReconnect = ({ entryPoint.y > labelBounds.minY && exitPoint.y > labelBounds.minY ) { - candidateDetours.push([ + allCandidateDetours.push([ { x: entryPoint.x, y: topY }, { x: exitPoint.x, y: topY }, ]) @@ -104,126 +85,15 @@ export const trySnipAndReconnect = ({ entryPoint.y < labelBounds.maxY && exitPoint.y < labelBounds.maxY ) { - candidateDetours.push([ + allCandidateDetours.push([ { x: entryPoint.x, y: bottomY }, { x: exitPoint.x, y: bottomY }, ]) } - for (let i = 0; i < candidateDetours.length; i++) { - const detour = candidateDetours[i] - const newFullPath = [...pathToEntry, ...detour, ...pathFromExit] - const simplified = simplifyPath(newFullPath) - - if (!isPathCollidingWithObstacles(simplified, obstacles)) { - return { ...initialTrace, tracePath: simplified } - } - } - - return null -} - -export const tryFourPointDetour = ({ - initialTrace, - label, - labelBounds, - obstacles, - paddingBuffer, - detourCount, -}: { - initialTrace: SolvedTracePath - label: NetLabelPlacement - labelBounds: any - obstacles: any[] - paddingBuffer: number - detourCount: number -}): SolvedTracePath | null => { - let collidingSegIndex = -1 - for (let i = 0; i < initialTrace.tracePath.length - 1; i++) { - if ( - segmentIntersectsRect( - initialTrace.tracePath[i], - initialTrace.tracePath[i + 1], - labelBounds, - ) - ) { - collidingSegIndex = i - break - } - } - - if (collidingSegIndex === -1) return initialTrace - - const pA = initialTrace.tracePath[collidingSegIndex] - const pB = initialTrace.tracePath[collidingSegIndex + 1] - - if (!pA || !pB) return null - - const candidateDetours: Point[][] = [] - const paddedLabelBounds = getRectBounds( - label.center, - label.width, - label.height, - ) - - const effectivePadding = paddingBuffer + detourCount * paddingBuffer - - if (isVertical(pA, pB)) { - const xCandidates = [ - paddedLabelBounds.maxX + effectivePadding, - paddedLabelBounds.minX - effectivePadding, - ] - for (const newX of xCandidates) { - candidateDetours.push( - pB.y > pA.y - ? [ - { x: pA.x, y: paddedLabelBounds.minY - effectivePadding }, - { x: newX, y: paddedLabelBounds.minY - effectivePadding }, - { x: newX, y: paddedLabelBounds.maxY + effectivePadding }, - { x: pB.x, y: paddedLabelBounds.maxY + effectivePadding }, - ] - : [ - { x: pA.x, y: paddedLabelBounds.maxY + effectivePadding }, - { x: newX, y: paddedLabelBounds.maxY + effectivePadding }, - { x: newX, y: paddedLabelBounds.minY - effectivePadding }, - { x: pB.x, y: paddedLabelBounds.minY - effectivePadding }, - ], - ) - } - } else { - const yCandidates = [ - paddedLabelBounds.maxY + effectivePadding, - paddedLabelBounds.minY - effectivePadding, - ] - for (const newY of yCandidates) { - candidateDetours.push( - pB.x > pA.x - ? [ - { x: paddedLabelBounds.minX - effectivePadding, y: pA.y }, - { x: paddedLabelBounds.minX - effectivePadding, y: newY }, - { x: paddedLabelBounds.maxX + effectivePadding, y: newY }, - { x: paddedLabelBounds.maxX + effectivePadding, y: pB.y }, - ] - : [ - { x: paddedLabelBounds.maxX + effectivePadding, y: pA.y }, - { x: paddedLabelBounds.maxX + effectivePadding, y: newY }, - { x: paddedLabelBounds.minX - effectivePadding, y: newY }, - { x: paddedLabelBounds.minX - effectivePadding, y: pB.y }, - ], - ) - } - } - - for (const detourPoints of candidateDetours) { - const finalPath = [ - ...initialTrace.tracePath.slice(0, collidingSegIndex + 1), - ...detourPoints, - ...initialTrace.tracePath.slice(collidingSegIndex + 1), - ] - const simplifiedFinalPath = simplifyPath(finalPath) - if (!isPathCollidingWithObstacles(simplifiedFinalPath, obstacles)) { - return { ...initialTrace, tracePath: simplifiedFinalPath } - } - } - return null + return allCandidateDetours.map((detour) => [ + ...pathToEntry, + ...detour, + ...pathFromExit, + ]) } diff --git a/tests/examples/__snapshots__/example25.snap.svg b/tests/examples/__snapshots__/example25.snap.svg index ea060fe..3e27a7e 100644 --- a/tests/examples/__snapshots__/example25.snap.svg +++ b/tests/examples/__snapshots__/example25.snap.svg @@ -64,7 +64,7 @@ x+" data-x="0.6" data-y="-2.8" cx="363.855421686747" cy="416.14457831325296" r=" - + @@ -82,7 +82,7 @@ x+" data-x="0.6" data-y="-2.8" cx="363.855421686747" cy="416.14457831325296" r=" - + @@ -97,7 +97,7 @@ x+" data-x="0.6" data-y="-2.8" cx="363.855421686747" cy="416.14457831325296" r=" - + diff --git a/tests/examples/__snapshots__/example26.snap.svg b/tests/examples/__snapshots__/example26.snap.svg index c24bd61..df9ca74 100644 --- a/tests/examples/__snapshots__/example26.snap.svg +++ b/tests/examples/__snapshots__/example26.snap.svg @@ -53,7 +53,7 @@ y-" data-x="-0.4" data-y="0.5" cx="296.3855421686747" cy="311.566265060241" r="3 y-" data-x="0.4" data-y="0.5" cx="350.3614457831325" cy="311.566265060241" r="3" fill="hsl(203, 100%, 50%, 0.8)" /> - + @@ -71,7 +71,7 @@ y-" data-x="0.4" data-y="0.5" cx="350.3614457831325" cy="311.566265060241" r="3" - + @@ -86,7 +86,7 @@ y-" data-x="0.4" data-y="0.5" cx="350.3614457831325" cy="311.566265060241" r="3" - + diff --git a/tests/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.test.ts b/tests/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.test.ts new file mode 100644 index 0000000..6a9f0ba --- /dev/null +++ b/tests/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.test.ts @@ -0,0 +1,437 @@ +import { expect } from "bun:test" +import { test } from "bun:test" +import { TraceLabelOverlapAvoidanceSolver } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver" +import inputProblem from "tests/assets/example25.json" +import "tests/fixtures/matcher" + +test("TraceLabelOverlapAvoidanceSolver snapshot", () => { + const solver = new TraceLabelOverlapAvoidanceSolver({ + problem: inputProblem as any, + netLabelPlacements: [ + { + globalConnNetId: "connectivity_net0", + dcConnNetId: "connectivity_net0", + netId: ".V1 > .pin1 to .L1 > .pin1", + mspConnectionPairIds: ["V1.1-L1.1"], + pinIds: ["V1.1", "L1.1"], + orientation: "x+", + anchorPoint: { + x: -5.005, + y: 2.7600000000000002, + }, + width: 0.45, + height: 0.2, + center: { + x: -4.78, + y: 2.7600000000000002, + }, + }, + { + globalConnNetId: "connectivity_net3", + dcConnNetId: "connectivity_net3", + netId: "GND", + mspConnectionPairIds: ["C1.2-M1.2"], + pinIds: ["C1.2", "M1.2"], + orientation: "y-", + anchorPoint: { + x: 3, + y: -0.78, + }, + width: 0.2, + height: 0.3, + center: { + x: 3, + y: -0.93, + }, + }, + { + globalConnNetId: "connectivity_net3", + netId: "GND", + mspConnectionPairIds: [], + pinIds: ["R1.2"], + orientation: "y-", + anchorPoint: { + x: 6, + y: -0.55, + }, + width: 0.2, + height: 0.3, + center: { + x: 6, + y: -0.7010000000000001, + }, + }, + { + globalConnNetId: "connectivity_net3", + dcConnNetId: "connectivity_net3", + netId: "GND", + mspConnectionPairIds: ["V1.2-V2.2"], + pinIds: ["V1.2", "V2.2"], + orientation: "y-", + anchorPoint: { + x: -4.995, + y: -0.6458008, + }, + width: 0.2, + height: 0.3, + center: { + x: -4.995, + y: -0.7958008, + }, + }, + { + globalConnNetId: "connectivity_net1", + dcConnNetId: "connectivity_net1", + netId: ".L1 > .pin2 to .M1 > .drain", + mspConnectionPairIds: ["L1.2-D1.1"], + pinIds: ["L1.2", "D1.1"], + orientation: "y+", + anchorPoint: { + x: 0.78, + y: 2.97, + }, + width: 0.2, + height: 0.45, + center: { + x: 0.78, + y: 3.1950000000000003, + }, + }, + { + globalConnNetId: "connectivity_net1", + netId: ".L1 > .pin2 to .M1 > .drain", + mspConnectionPairIds: [], + pinIds: ["M1.1"], + orientation: "y+", + anchorPoint: { + x: 0.3, + y: 0.58, + }, + width: 0.2, + height: 0.45, + center: { + x: 0.3, + y: 0.8059999999999999, + }, + }, + { + globalConnNetId: "connectivity_net2", + dcConnNetId: "connectivity_net2", + netId: ".D1 > .cathode to .R1 > .pin1", + mspConnectionPairIds: ["D1.2-C1.1"], + pinIds: ["D1.2", "C1.1"], + orientation: "y+", + anchorPoint: { + x: 3.7199999999999998, + y: 3, + }, + width: 0.2, + height: 0.45, + center: { + x: 3.7199999999999998, + y: 3.225, + }, + }, + { + globalConnNetId: "connectivity_net2", + netId: ".D1 > .cathode to .R1 > .pin1", + mspConnectionPairIds: [], + pinIds: ["R1.1"], + orientation: "y+", + anchorPoint: { + x: 6, + y: 0.55, + }, + width: 0.2, + height: 0.45, + center: { + x: 6, + y: 0.776, + }, + }, + { + globalConnNetId: "connectivity_net4", + dcConnNetId: "connectivity_net4", + netId: ".M1 > .gate to .V2 > .pin1", + mspConnectionPairIds: ["M1.3-V2.1"], + pinIds: ["M1.3", "V2.1"], + orientation: "y+", + anchorPoint: { + x: -1.08373445, + y: -0.09999999999999987, + }, + width: 0.2, + height: 0.45, + center: { + x: -1.08373445, + y: 0.12500000000000014, + }, + }, + ], + traces: [ + { + mspPairId: "L1.2-D1.1", + dcConnNetId: "connectivity_net1", + globalConnNetId: "connectivity_net1", + userNetId: ".L1 > .pin2 to .M1 > .drain", + pins: [ + { + pinId: "L1.2", + x: 0.58, + y: 2.97, + _facingDirection: "x+", + chipId: "schematic_component_1", + }, + { + pinId: "D1.1", + x: 2.48, + y: 3, + chipId: "schematic_component_2", + _facingDirection: "x-", + }, + ], + tracePath: [ + { + x: 0.58, + y: 2.97, + }, + { + x: 0.78, + y: 2.97, + }, + { + x: 1.53, + y: 2.97, + }, + { + x: 1.53, + y: 3, + }, + { + x: 2.28, + y: 3, + }, + { + x: 2.48, + y: 3, + }, + ], + mspConnectionPairIds: ["L1.2-D1.1"], + pinIds: ["L1.2", "D1.1"], + }, + { + mspPairId: "V1.1-L1.1", + dcConnNetId: "connectivity_net0", + globalConnNetId: "connectivity_net0", + pins: [ + { + pinId: "V1.1", + x: -5.005, + y: 2.54, + chipId: "schematic_component_0", + _facingDirection: "y+", + }, + { + pinId: "L1.1", + x: -0.58, + y: 2.98, + _facingDirection: "x-", + chipId: "schematic_component_1", + }, + ], + tracePath: [ + { + x: -5.005, + y: 2.54, + }, + { + x: -5.005, + y: 2.9800000000000004, + }, + { + x: -0.5800000000000001, + y: 2.9800000000000004, + }, + ], + mspConnectionPairIds: ["V1.1-L1.1"], + pinIds: ["V1.1", "L1.1"], + }, + { + mspPairId: "D1.2-C1.1", + dcConnNetId: "connectivity_net2", + globalConnNetId: "connectivity_net2", + pins: [ + { + pinId: "D1.2", + x: 3.52, + y: 3, + chipId: "schematic_component_2", + _facingDirection: "x+", + }, + { + pinId: "C1.1", + x: 3, + y: 0.49500000000000005, + _facingDirection: "y+", + chipId: "schematic_component_3", + }, + ], + tracePath: [ + { + x: 3.52, + y: 3, + }, + { + x: 3.7199999999999998, + y: 3, + }, + { + x: 3.7199999999999998, + y: 1.7474999999999996, + }, + { + x: 3, + y: 1.7474999999999996, + }, + { + x: 3, + y: 0.49500000000000005, + }, + ], + mspConnectionPairIds: ["D1.2-C1.1"], + pinIds: ["D1.2", "C1.1"], + }, + { + mspPairId: "C1.2-M1.2", + dcConnNetId: "connectivity_net3", + globalConnNetId: "connectivity_net3", + pins: [ + { + pinId: "C1.2", + x: 3, + y: -0.49500000000000005, + _facingDirection: "y-", + chipId: "schematic_component_3", + }, + { + pinId: "M1.2", + x: 0.31, + y: -0.58, + _facingDirection: "y-", + chipId: "schematic_component_6", + }, + ], + tracePath: [ + { + x: 3, + y: -0.4950000000000001, + }, + { + x: 3, + y: -0.78, + }, + { + x: 0.31, + y: -0.78, + }, + { + x: 0.31, + y: -0.58, + }, + ], + mspConnectionPairIds: ["C1.2-M1.2"], + pinIds: ["C1.2", "M1.2"], + }, + { + mspPairId: "V1.2-V2.2", + dcConnNetId: "connectivity_net3", + globalConnNetId: "connectivity_net3", + pins: [ + { + pinId: "V1.2", + x: -4.995, + y: 1.46, + chipId: "schematic_component_0", + _facingDirection: "y-", + }, + { + pinId: "V2.2", + x: -3.0000622, + y: -0.4458008, + chipId: "schematic_component_5", + _facingDirection: "y-", + }, + ], + tracePath: [ + { + x: -4.995, + y: 1.46, + }, + { + x: -4.995, + y: -0.6458008, + }, + { + x: -3.0000622, + y: -0.6458008, + }, + { + x: -3.0000622, + y: -0.4458007999999998, + }, + ], + mspConnectionPairIds: ["V1.2-V2.2"], + pinIds: ["V1.2", "V2.2"], + }, + { + mspPairId: "M1.3-V2.1", + dcConnNetId: "connectivity_net4", + globalConnNetId: "connectivity_net4", + pins: [ + { + pinId: "M1.3", + x: -0.445, + y: -0.1, + _facingDirection: "x-", + chipId: "schematic_component_6", + }, + { + pinId: "V2.1", + x: -2.9999378, + y: 0.4458008, + chipId: "schematic_component_5", + _facingDirection: "y+", + }, + ], + tracePath: [ + { + x: -0.44499999999999984, + y: -0.09999999999999987, + }, + { + x: -1.7224689, + y: -0.09999999999999987, + }, + { + x: -1.7224689, + y: 0.6458008000000002, + }, + { + x: -2.9999378, + y: 0.6458008000000002, + }, + { + x: -2.9999378, + y: 0.4458008, + }, + ], + mspConnectionPairIds: ["M1.3-V2.1"], + pinIds: ["M1.3", "V2.1"], + }, + ], + }) + + solver.solve() + + expect(solver).toMatchSolverSnapshot(import.meta.path) +}) diff --git a/tests/solvers/TraceLabelOverlapAvoidanceSolver/__snapshots__/TraceLabelOverlapAvoidanceSolver.snap.svg b/tests/solvers/TraceLabelOverlapAvoidanceSolver/__snapshots__/TraceLabelOverlapAvoidanceSolver.snap.svg new file mode 100644 index 0000000..100281f --- /dev/null +++ b/tests/solvers/TraceLabelOverlapAvoidanceSolver/__snapshots__/TraceLabelOverlapAvoidanceSolver.snap.svg @@ -0,0 +1,243 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/solvers/TraceLabelOverlapAvoidanceSolver/stackGraphicsHorizontally/MergedNetLabelObstacles-to-SingleOverlapSolver.ts b/tests/solvers/TraceLabelOverlapAvoidanceSolver/stackGraphicsHorizontally/MergedNetLabelObstacles-to-SingleOverlapSolver.ts new file mode 100644 index 0000000..86a65c2 --- /dev/null +++ b/tests/solvers/TraceLabelOverlapAvoidanceSolver/stackGraphicsHorizontally/MergedNetLabelObstacles-to-SingleOverlapSolver.ts @@ -0,0 +1,654 @@ +import { MergedNetLabelObstacles } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver" +import { expect } from "bun:test" +import { test } from "bun:test" +import { + getSvgFromGraphicsObject, + stackGraphicsHorizontally, +} from "graphics-debug" +import { SingleOverlapSolver } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver" + +const input = [ + { + globalConnNetId: "connectivity_net4", + netId: "GND", + mspConnectionPairIds: [], + pinIds: ["U1.1"], + orientation: "x+", + anchorPoint: { + x: 1.2000000000000002, + y: -0.30000000000000004, + }, + width: 0.45, + height: 0.2, + center: { + x: 1.4260000000000002, + y: -0.30000000000000004, + }, + }, + { + globalConnNetId: "connectivity_net4", + dcConnNetId: "connectivity_net4", + netId: "GND", + mspConnectionPairIds: ["C1.2-C2.2"], + pinIds: ["C1.2", "C2.2"], + orientation: "y-", + anchorPoint: { + x: -1.2000000000000002, + y: -2.45, + }, + width: 0.2, + height: 0.45, + center: { + x: -1.2000000000000002, + y: -2.6750000000000003, + }, + }, + { + globalConnNetId: "connectivity_net1", + dcConnNetId: "connectivity_net1", + netId: "U1.THRES to U1.TRIG", + mspConnectionPairIds: ["U1.2-C1.1"], + pinIds: ["U1.2", "C1.1"], + orientation: "x-", + anchorPoint: { + x: -1.4000000000000001, + y: -0.30000000000000004, + }, + width: 0.45, + height: 0.2, + center: { + x: -1.6250000000000002, + y: -0.30000000000000004, + }, + }, + { + globalConnNetId: "connectivity_net3", + dcConnNetId: "connectivity_net3", + netId: "U1.OUT to R3.pin1", + mspConnectionPairIds: ["U1.3-R3.1"], + pinIds: ["U1.3", "R3.1"], + orientation: "x+", + anchorPoint: { + x: 1.4000000000000001, + y: 0.09999999999999998, + }, + width: 0.45, + height: 0.2, + center: { + x: 1.6250000000000002, + y: 0.09999999999999998, + }, + }, + { + globalConnNetId: "connectivity_net5", + netId: "VCC", + mspConnectionPairIds: [], + pinIds: ["U1.4"], + orientation: "x-", + anchorPoint: { + x: -1.2000000000000002, + y: 0.30000000000000004, + }, + width: 0.45, + height: 0.2, + center: { + x: -1.4260000000000002, + y: 0.30000000000000004, + }, + }, + { + globalConnNetId: "connectivity_net5", + dcConnNetId: "connectivity_net5", + netId: "VCC", + mspConnectionPairIds: ["U1.8-R1.1"], + pinIds: ["U1.8", "R1.1"], + orientation: "y+", + anchorPoint: { + x: 2.2, + y: 0.30000000000000004, + }, + width: 0.2, + height: 0.45, + center: { + x: 2.2, + y: 0.525, + }, + }, + { + globalConnNetId: "connectivity_net0", + dcConnNetId: "connectivity_net0", + netId: "U1.CTRL to C2.pin1", + mspConnectionPairIds: ["U1.5-C2.1"], + pinIds: ["U1.5", "C2.1"], + orientation: "y+", + anchorPoint: { + x: -1.5500000000000003, + y: 0.10000000000000003, + }, + width: 0.2, + height: 0.45, + center: { + x: -1.5500000000000003, + y: 0.32500000000000007, + }, + }, + { + globalConnNetId: "connectivity_net2", + dcConnNetId: "connectivity_net2", + netId: "U1.DISCH to R2.pin1", + mspConnectionPairIds: ["R2.1-U1.7"], + pinIds: ["R2.1", "U1.7"], + orientation: "y-", + anchorPoint: { + x: 1.4000000000000001, + y: -1.2944553500000002, + }, + width: 0.2, + height: 0.45, + center: { + x: 1.4000000000000001, + y: -1.5194553500000003, + }, + }, +] + +const problem = { + chips: [ + { + chipId: "schematic_component_0", + center: { + x: 0, + y: 0, + }, + width: 2.4000000000000004, + height: 1, + pins: [ + { + pinId: "U1.1", + x: 1.2000000000000002, + y: -0.30000000000000004, + }, + { + pinId: "U1.2", + x: -1.2000000000000002, + y: -0.30000000000000004, + }, + { + pinId: "U1.3", + x: 1.2000000000000002, + y: 0.09999999999999998, + }, + { + pinId: "U1.4", + x: -1.2000000000000002, + y: 0.30000000000000004, + }, + { + pinId: "U1.5", + x: -1.2000000000000002, + y: 0.10000000000000003, + }, + { + pinId: "U1.6", + x: -1.2000000000000002, + y: -0.09999999999999998, + }, + { + pinId: "U1.7", + x: 1.2000000000000002, + y: -0.10000000000000003, + }, + { + pinId: "U1.8", + x: 1.2000000000000002, + y: 0.30000000000000004, + }, + ], + }, + { + chipId: "schematic_component_1", + center: { + x: 2.45, + y: -0.10000000000000009, + }, + width: 1.0999999999999996, + height: 0.388910699999999, + pins: [ + { + pinId: "R1.1", + x: 3, + y: -0.10000000000000016, + }, + { + pinId: "R1.2", + x: 1.9000000000000004, + y: -0.10000000000000002, + }, + ], + }, + { + chipId: "schematic_component_2", + center: { + x: 0.6500000000000001, + y: -1.2944553500000002, + }, + width: 1.1, + height: 0.388910699999999, + pins: [ + { + pinId: "R2.1", + x: 1.2000000000000002, + y: -1.2944553500000002, + }, + { + pinId: "R2.2", + x: 0.10000000000000009, + y: -1.2944553500000002, + }, + ], + }, + { + chipId: "schematic_component_3", + center: { + x: -1.2000000000000002, + y: -1.7000000000000002, + }, + width: 1.06, + height: 1.1, + pins: [ + { + pinId: "C1.1", + x: -1.2000000000000002, + y: -1.1500000000000001, + }, + { + pinId: "C1.2", + x: -1.2000000000000002, + y: -2.25, + }, + ], + }, + { + chipId: "schematic_component_4", + center: { + x: -2.45, + y: 0.10000000000000009, + }, + width: 1.0999999999999996, + height: 0.84, + pins: [ + { + pinId: "C2.1", + x: -1.9000000000000004, + y: 0.10000000000000002, + }, + { + pinId: "C2.2", + x: -3, + y: 0.10000000000000016, + }, + ], + }, + { + chipId: "schematic_component_5", + center: { + x: 1.2000000000000002, + y: 1.7000000000000002, + }, + width: 1.06, + height: 1.1, + pins: [ + { + pinId: "R3.1", + x: 1.2000000000000002, + y: 1.1500000000000001, + }, + { + pinId: "R3.2", + x: 1.2000000000000002, + y: 2.25, + }, + ], + }, + ], + directConnections: [ + { + pinIds: ["U1.5", "C2.1"], + netId: "U1.CTRL to C2.pin1", + }, + { + pinIds: ["U1.6", "U1.2"], + netId: "U1.THRES to U1.TRIG", + }, + { + pinIds: ["R1.2", "U1.7"], + netId: "R1.pin2 to U1.DISCH", + }, + { + pinIds: ["U1.7", "R2.1"], + netId: "U1.DISCH to R2.pin1", + }, + { + pinIds: ["R2.2", "U1.6"], + netId: "R2.pin2 to U1.THRES", + }, + { + pinIds: ["U1.6", "C1.1"], + netId: "U1.THRES to C1.pin1", + }, + { + pinIds: ["U1.3", "R3.1"], + netId: "U1.OUT to R3.pin1", + }, + ], + netConnections: [ + { + netId: "GND", + pinIds: ["U1.1", "C1.2", "C2.2"], + }, + { + netId: "VCC", + pinIds: ["U1.4", "U1.8", "R1.1"], + }, + ], + availableNetLabelOrientations: { + VCC: ["y+"], + GND: ["y-"], + }, + maxMspPairDistance: 2.4, +} as any + +const paramForSolver2 = { + trace: { + mspPairId: "U1.3-R3.1", + dcConnNetId: "connectivity_net3", + globalConnNetId: "connectivity_net3", + userNetId: "U1.OUT to R3.pin1", + pins: [ + { + pinId: "U1.3", + x: 1.2000000000000002, + y: 0.09999999999999998, + chipId: "schematic_component_0", + _facingDirection: "x+", + }, + { + pinId: "R3.1", + x: 1.2000000000000002, + y: 1.1500000000000001, + chipId: "schematic_component_5", + _facingDirection: "y-", + }, + ], + tracePath: [ + { + x: 1.2000000000000002, + y: 0.09999999999999998, + }, + { + x: 1.4000000000000001, + y: 0.09999999999999998, + }, + { + x: 1.4000000000000001, + y: 0.625, + }, + { + x: 1.2000000000000002, + y: 0.625, + }, + { + x: 1.2000000000000002, + y: 1.1500000000000001, + }, + ], + mspConnectionPairIds: ["U1.3-R3.1"], + pinIds: ["U1.3", "R3.1"], + }, + label: { + globalConnNetId: "merged-group-U1-y+", + dcConnNetId: "connectivity_net5", + netId: "VCC", + mspConnectionPairIds: ["U1.8-R1.1", "U1.5-C2.1"], + pinIds: ["U1.8", "R1.1", "U1.5", "C2.1"], + orientation: "y+", + anchorPoint: { + x: 2.2, + y: 0.30000000000000004, + }, + width: 3.9500000000000006, + height: 0.6499999999999999, + center: { + x: 0.32499999999999996, + y: 0.42500000000000004, + }, + }, + problem: { + chips: [ + { + chipId: "schematic_component_0", + center: { + x: 0, + y: 0, + }, + width: 2.4000000000000004, + height: 1, + pins: [ + { + pinId: "U1.1", + x: 1.2000000000000002, + y: -0.30000000000000004, + }, + { + pinId: "U1.2", + x: -1.2000000000000002, + y: -0.30000000000000004, + }, + { + pinId: "U1.3", + x: 1.2000000000000002, + y: 0.09999999999999998, + }, + { + pinId: "U1.4", + x: -1.2000000000000002, + y: 0.30000000000000004, + }, + { + pinId: "U1.5", + x: -1.2000000000000002, + y: 0.10000000000000003, + }, + { + pinId: "U1.6", + x: -1.2000000000000002, + y: -0.09999999999999998, + }, + { + pinId: "U1.7", + x: 1.2000000000000002, + y: -0.10000000000000003, + }, + { + pinId: "U1.8", + x: 1.2000000000000002, + y: 0.30000000000000004, + }, + ], + }, + { + chipId: "schematic_component_1", + center: { + x: 2.45, + y: -0.10000000000000009, + }, + width: 1.0999999999999996, + height: 0.388910699999999, + pins: [ + { + pinId: "R1.1", + x: 3, + y: -0.10000000000000016, + }, + { + pinId: "R1.2", + x: 1.9000000000000004, + y: -0.10000000000000002, + }, + ], + }, + { + chipId: "schematic_component_2", + center: { + x: 0.6500000000000001, + y: -1.2944553500000002, + }, + width: 1.1, + height: 0.388910699999999, + pins: [ + { + pinId: "R2.1", + x: 1.2000000000000002, + y: -1.2944553500000002, + }, + { + pinId: "R2.2", + x: 0.10000000000000009, + y: -1.2944553500000002, + }, + ], + }, + { + chipId: "schematic_component_3", + center: { + x: -1.2000000000000002, + y: -1.7000000000000002, + }, + width: 1.06, + height: 1.1, + pins: [ + { + pinId: "C1.1", + x: -1.2000000000000002, + y: -1.1500000000000001, + }, + { + pinId: "C1.2", + x: -1.2000000000000002, + y: -2.25, + }, + ], + }, + { + chipId: "schematic_component_4", + center: { + x: -2.45, + y: 0.10000000000000009, + }, + width: 1.0999999999999996, + height: 0.84, + pins: [ + { + pinId: "C2.1", + x: -1.9000000000000004, + y: 0.10000000000000002, + }, + { + pinId: "C2.2", + x: -3, + y: 0.10000000000000016, + }, + ], + }, + { + chipId: "schematic_component_5", + center: { + x: 1.2000000000000002, + y: 1.7000000000000002, + }, + width: 1.06, + height: 1.1, + pins: [ + { + pinId: "R3.1", + x: 1.2000000000000002, + y: 1.1500000000000001, + }, + { + pinId: "R3.2", + x: 1.2000000000000002, + y: 2.25, + }, + ], + }, + ], + directConnections: [ + { + pinIds: ["U1.5", "C2.1"], + netId: "U1.CTRL to C2.pin1", + }, + { + pinIds: ["U1.6", "U1.2"], + netId: "U1.THRES to U1.TRIG", + }, + { + pinIds: ["R1.2", "U1.7"], + netId: "R1.pin2 to U1.DISCH", + }, + { + pinIds: ["U1.7", "R2.1"], + netId: "U1.DISCH to R2.pin1", + }, + { + pinIds: ["R2.2", "U1.6"], + netId: "R2.pin2 to U1.THRES", + }, + { + pinIds: ["U1.6", "C1.1"], + netId: "U1.THRES to C1.pin1", + }, + { + pinIds: ["U1.3", "R3.1"], + netId: "U1.OUT to R3.pin1", + }, + ], + netConnections: [ + { + netId: "GND", + pinIds: ["U1.1", "C1.2", "C2.2"], + }, + { + netId: "VCC", + pinIds: ["U1.4", "U1.8", "R1.1"], + }, + ], + availableNetLabelOrientations: { + VCC: ["y+"], + GND: ["y-"], + }, + maxMspPairDistance: 2.4, + }, + paddingBuffer: 0.1, + detourCount: 0, +} as any + +test("MergedNetLabelObstacles-to-SingleOverlapSolver snapshot", () => { + const solver1 = new MergedNetLabelObstacles({ + netLabelPlacements: input as any, + problem, + }) + const solver2 = new SingleOverlapSolver({ + ...paramForSolver2, + }) + solver1.solve() + solver2.solve() + const sideBySide = getSvgFromGraphicsObject( + stackGraphicsHorizontally([solver1.visualize(), solver2.visualize()], { + titles: ["MergedNetLabelObstacles", "SingleOverlapSolver"], + }), + { + backgroundColor: "white", + }, + ) + expect(sideBySide).toMatchSvgSnapshot(import.meta.path) +}) diff --git a/tests/solvers/TraceLabelOverlapAvoidanceSolver/stackGraphicsHorizontally/NetLabelPlacementSolver-to-MergedNetLabelObstacles.ts b/tests/solvers/TraceLabelOverlapAvoidanceSolver/stackGraphicsHorizontally/NetLabelPlacementSolver-to-MergedNetLabelObstacles.ts new file mode 100644 index 0000000..652f731 --- /dev/null +++ b/tests/solvers/TraceLabelOverlapAvoidanceSolver/stackGraphicsHorizontally/NetLabelPlacementSolver-to-MergedNetLabelObstacles.ts @@ -0,0 +1,967 @@ +import { MergedNetLabelObstacles } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver" +import { expect } from "bun:test" +import { test } from "bun:test" +import { + getSvgFromGraphicsObject, + stackGraphicsHorizontally, +} from "graphics-debug" +import { NetLabelPlacementSolver } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver" + +const input = [ + { + globalConnNetId: "connectivity_net4", + netId: "GND", + mspConnectionPairIds: [], + pinIds: ["U1.1"], + orientation: "x+", + anchorPoint: { + x: 1.2000000000000002, + y: -0.30000000000000004, + }, + width: 0.45, + height: 0.2, + center: { + x: 1.4260000000000002, + y: -0.30000000000000004, + }, + }, + { + globalConnNetId: "connectivity_net4", + dcConnNetId: "connectivity_net4", + netId: "GND", + mspConnectionPairIds: ["C1.2-C2.2"], + pinIds: ["C1.2", "C2.2"], + orientation: "y-", + anchorPoint: { + x: -1.2000000000000002, + y: -2.45, + }, + width: 0.2, + height: 0.45, + center: { + x: -1.2000000000000002, + y: -2.6750000000000003, + }, + }, + { + globalConnNetId: "connectivity_net1", + dcConnNetId: "connectivity_net1", + netId: "U1.THRES to U1.TRIG", + mspConnectionPairIds: ["U1.2-C1.1"], + pinIds: ["U1.2", "C1.1"], + orientation: "x-", + anchorPoint: { + x: -1.4000000000000001, + y: -0.30000000000000004, + }, + width: 0.45, + height: 0.2, + center: { + x: -1.6250000000000002, + y: -0.30000000000000004, + }, + }, + { + globalConnNetId: "connectivity_net3", + dcConnNetId: "connectivity_net3", + netId: "U1.OUT to R3.pin1", + mspConnectionPairIds: ["U1.3-R3.1"], + pinIds: ["U1.3", "R3.1"], + orientation: "x+", + anchorPoint: { + x: 1.4000000000000001, + y: 0.09999999999999998, + }, + width: 0.45, + height: 0.2, + center: { + x: 1.6250000000000002, + y: 0.09999999999999998, + }, + }, + { + globalConnNetId: "connectivity_net5", + netId: "VCC", + mspConnectionPairIds: [], + pinIds: ["U1.4"], + orientation: "x-", + anchorPoint: { + x: -1.2000000000000002, + y: 0.30000000000000004, + }, + width: 0.45, + height: 0.2, + center: { + x: -1.4260000000000002, + y: 0.30000000000000004, + }, + }, + { + globalConnNetId: "connectivity_net5", + dcConnNetId: "connectivity_net5", + netId: "VCC", + mspConnectionPairIds: ["U1.8-R1.1"], + pinIds: ["U1.8", "R1.1"], + orientation: "y+", + anchorPoint: { + x: 2.2, + y: 0.30000000000000004, + }, + width: 0.2, + height: 0.45, + center: { + x: 2.2, + y: 0.525, + }, + }, + { + globalConnNetId: "connectivity_net0", + dcConnNetId: "connectivity_net0", + netId: "U1.CTRL to C2.pin1", + mspConnectionPairIds: ["U1.5-C2.1"], + pinIds: ["U1.5", "C2.1"], + orientation: "y+", + anchorPoint: { + x: -1.5500000000000003, + y: 0.10000000000000003, + }, + width: 0.2, + height: 0.45, + center: { + x: -1.5500000000000003, + y: 0.32500000000000007, + }, + }, + { + globalConnNetId: "connectivity_net2", + dcConnNetId: "connectivity_net2", + netId: "U1.DISCH to R2.pin1", + mspConnectionPairIds: ["R2.1-U1.7"], + pinIds: ["R2.1", "U1.7"], + orientation: "y-", + anchorPoint: { + x: 1.4000000000000001, + y: -1.2944553500000002, + }, + width: 0.2, + height: 0.45, + center: { + x: 1.4000000000000001, + y: -1.5194553500000003, + }, + }, +] + +const problem = { + chips: [ + { + chipId: "schematic_component_0", + center: { + x: 0, + y: 0, + }, + width: 2.4000000000000004, + height: 1, + pins: [ + { + pinId: "U1.1", + x: 1.2000000000000002, + y: -0.30000000000000004, + }, + { + pinId: "U1.2", + x: -1.2000000000000002, + y: -0.30000000000000004, + }, + { + pinId: "U1.3", + x: 1.2000000000000002, + y: 0.09999999999999998, + }, + { + pinId: "U1.4", + x: -1.2000000000000002, + y: 0.30000000000000004, + }, + { + pinId: "U1.5", + x: -1.2000000000000002, + y: 0.10000000000000003, + }, + { + pinId: "U1.6", + x: -1.2000000000000002, + y: -0.09999999999999998, + }, + { + pinId: "U1.7", + x: 1.2000000000000002, + y: -0.10000000000000003, + }, + { + pinId: "U1.8", + x: 1.2000000000000002, + y: 0.30000000000000004, + }, + ], + }, + { + chipId: "schematic_component_1", + center: { + x: 2.45, + y: -0.10000000000000009, + }, + width: 1.0999999999999996, + height: 0.388910699999999, + pins: [ + { + pinId: "R1.1", + x: 3, + y: -0.10000000000000016, + }, + { + pinId: "R1.2", + x: 1.9000000000000004, + y: -0.10000000000000002, + }, + ], + }, + { + chipId: "schematic_component_2", + center: { + x: 0.6500000000000001, + y: -1.2944553500000002, + }, + width: 1.1, + height: 0.388910699999999, + pins: [ + { + pinId: "R2.1", + x: 1.2000000000000002, + y: -1.2944553500000002, + }, + { + pinId: "R2.2", + x: 0.10000000000000009, + y: -1.2944553500000002, + }, + ], + }, + { + chipId: "schematic_component_3", + center: { + x: -1.2000000000000002, + y: -1.7000000000000002, + }, + width: 1.06, + height: 1.1, + pins: [ + { + pinId: "C1.1", + x: -1.2000000000000002, + y: -1.1500000000000001, + }, + { + pinId: "C1.2", + x: -1.2000000000000002, + y: -2.25, + }, + ], + }, + { + chipId: "schematic_component_4", + center: { + x: -2.45, + y: 0.10000000000000009, + }, + width: 1.0999999999999996, + height: 0.84, + pins: [ + { + pinId: "C2.1", + x: -1.9000000000000004, + y: 0.10000000000000002, + }, + { + pinId: "C2.2", + x: -3, + y: 0.10000000000000016, + }, + ], + }, + { + chipId: "schematic_component_5", + center: { + x: 1.2000000000000002, + y: 1.7000000000000002, + }, + width: 1.06, + height: 1.1, + pins: [ + { + pinId: "R3.1", + x: 1.2000000000000002, + y: 1.1500000000000001, + }, + { + pinId: "R3.2", + x: 1.2000000000000002, + y: 2.25, + }, + ], + }, + ], + directConnections: [ + { + pinIds: ["U1.5", "C2.1"], + netId: "U1.CTRL to C2.pin1", + }, + { + pinIds: ["U1.6", "U1.2"], + netId: "U1.THRES to U1.TRIG", + }, + { + pinIds: ["R1.2", "U1.7"], + netId: "R1.pin2 to U1.DISCH", + }, + { + pinIds: ["U1.7", "R2.1"], + netId: "U1.DISCH to R2.pin1", + }, + { + pinIds: ["R2.2", "U1.6"], + netId: "R2.pin2 to U1.THRES", + }, + { + pinIds: ["U1.6", "C1.1"], + netId: "U1.THRES to C1.pin1", + }, + { + pinIds: ["U1.3", "R3.1"], + netId: "U1.OUT to R3.pin1", + }, + ], + netConnections: [ + { + netId: "GND", + pinIds: ["U1.1", "C1.2", "C2.2"], + }, + { + netId: "VCC", + pinIds: ["U1.4", "U1.8", "R1.1"], + }, + ], + availableNetLabelOrientations: { + VCC: ["y+"], + GND: ["y-"], + }, + maxMspPairDistance: 2.4, +} as any + +const paramForSolver2 = { + inputProblem: { + chips: [ + { + chipId: "schematic_component_0", + center: { + x: 0, + y: 0, + }, + width: 2.4000000000000004, + height: 1, + pins: [ + { + pinId: "U1.1", + x: 1.2000000000000002, + y: -0.30000000000000004, + }, + { + pinId: "U1.2", + x: -1.2000000000000002, + y: -0.30000000000000004, + }, + { + pinId: "U1.3", + x: 1.2000000000000002, + y: 0.09999999999999998, + }, + { + pinId: "U1.4", + x: -1.2000000000000002, + y: 0.30000000000000004, + }, + { + pinId: "U1.5", + x: -1.2000000000000002, + y: 0.10000000000000003, + }, + { + pinId: "U1.6", + x: -1.2000000000000002, + y: -0.09999999999999998, + }, + { + pinId: "U1.7", + x: 1.2000000000000002, + y: -0.10000000000000003, + }, + { + pinId: "U1.8", + x: 1.2000000000000002, + y: 0.30000000000000004, + }, + ], + }, + { + chipId: "schematic_component_1", + center: { + x: 2.45, + y: -0.10000000000000009, + }, + width: 1.0999999999999996, + height: 0.388910699999999, + pins: [ + { + pinId: "R1.1", + x: 3, + y: -0.10000000000000016, + }, + { + pinId: "R1.2", + x: 1.9000000000000004, + y: -0.10000000000000002, + }, + ], + }, + { + chipId: "schematic_component_2", + center: { + x: 0.6500000000000001, + y: -1.2944553500000002, + }, + width: 1.1, + height: 0.388910699999999, + pins: [ + { + pinId: "R2.1", + x: 1.2000000000000002, + y: -1.2944553500000002, + }, + { + pinId: "R2.2", + x: 0.10000000000000009, + y: -1.2944553500000002, + }, + ], + }, + { + chipId: "schematic_component_3", + center: { + x: -1.2000000000000002, + y: -1.7000000000000002, + }, + width: 1.06, + height: 1.1, + pins: [ + { + pinId: "C1.1", + x: -1.2000000000000002, + y: -1.1500000000000001, + }, + { + pinId: "C1.2", + x: -1.2000000000000002, + y: -2.25, + }, + ], + }, + { + chipId: "schematic_component_4", + center: { + x: -2.45, + y: 0.10000000000000009, + }, + width: 1.0999999999999996, + height: 0.84, + pins: [ + { + pinId: "C2.1", + x: -1.9000000000000004, + y: 0.10000000000000002, + }, + { + pinId: "C2.2", + x: -3, + y: 0.10000000000000016, + }, + ], + }, + { + chipId: "schematic_component_5", + center: { + x: 1.2000000000000002, + y: 1.7000000000000002, + }, + width: 1.06, + height: 1.1, + pins: [ + { + pinId: "R3.1", + x: 1.2000000000000002, + y: 1.1500000000000001, + }, + { + pinId: "R3.2", + x: 1.2000000000000002, + y: 2.25, + }, + ], + }, + ], + directConnections: [ + { + pinIds: ["U1.5", "C2.1"], + netId: "U1.CTRL to C2.pin1", + }, + { + pinIds: ["U1.6", "U1.2"], + netId: "U1.THRES to U1.TRIG", + }, + { + pinIds: ["R1.2", "U1.7"], + netId: "R1.pin2 to U1.DISCH", + }, + { + pinIds: ["U1.7", "R2.1"], + netId: "U1.DISCH to R2.pin1", + }, + { + pinIds: ["R2.2", "U1.6"], + netId: "R2.pin2 to U1.THRES", + }, + { + pinIds: ["U1.6", "C1.1"], + netId: "U1.THRES to C1.pin1", + }, + { + pinIds: ["U1.3", "R3.1"], + netId: "U1.OUT to R3.pin1", + }, + ], + netConnections: [ + { + netId: "GND", + pinIds: ["U1.1", "C1.2", "C2.2"], + }, + { + netId: "VCC", + pinIds: ["U1.4", "U1.8", "R1.1"], + }, + ], + availableNetLabelOrientations: { + VCC: ["y+"], + GND: ["y-"], + }, + maxMspPairDistance: 2.4, + }, + inputTraceMap: { + "U1.5-C2.1": { + mspPairId: "U1.5-C2.1", + dcConnNetId: "connectivity_net0", + globalConnNetId: "connectivity_net0", + userNetId: "U1.CTRL to C2.pin1", + pins: [ + { + pinId: "U1.5", + x: -1.2000000000000002, + y: 0.10000000000000003, + chipId: "schematic_component_0", + _facingDirection: "x-", + }, + { + pinId: "C2.1", + x: -1.9000000000000004, + y: 0.10000000000000002, + chipId: "schematic_component_4", + _facingDirection: "x+", + }, + ], + tracePath: [ + { + x: -1.2000000000000002, + y: 0.10000000000000003, + }, + { + x: -1.9000000000000004, + y: 0.10000000000000002, + }, + ], + mspConnectionPairIds: ["U1.5-C2.1"], + pinIds: ["U1.5", "C2.1"], + }, + "U1.2-C1.1": { + mspPairId: "U1.2-C1.1", + dcConnNetId: "connectivity_net1", + globalConnNetId: "connectivity_net1", + userNetId: "U1.THRES to U1.TRIG", + pins: [ + { + pinId: "U1.2", + x: -1.2000000000000002, + y: -0.30000000000000004, + chipId: "schematic_component_0", + _facingDirection: "x-", + }, + { + pinId: "C1.1", + x: -1.2000000000000002, + y: -1.1500000000000001, + chipId: "schematic_component_3", + _facingDirection: "y+", + }, + ], + tracePath: [ + { + x: -1.2000000000000002, + y: -0.30000000000000004, + }, + { + x: -1.4000000000000001, + y: -0.30000000000000004, + }, + { + x: -1.4000000000000001, + y: -0.7250000000000001, + }, + { + x: -1.2000000000000002, + y: -0.7250000000000001, + }, + { + x: -1.2000000000000002, + y: -1.1500000000000001, + }, + ], + mspConnectionPairIds: ["U1.2-C1.1"], + pinIds: ["U1.2", "C1.1"], + }, + "U1.6-U1.2": { + mspPairId: "U1.6-U1.2", + dcConnNetId: "connectivity_net1", + globalConnNetId: "connectivity_net1", + userNetId: "U1.THRES to C1.pin1", + pins: [ + { + pinId: "U1.6", + x: -1.2000000000000002, + y: -0.09999999999999998, + chipId: "schematic_component_0", + _facingDirection: "x-", + }, + { + pinId: "U1.2", + x: -1.2000000000000002, + y: -0.30000000000000004, + chipId: "schematic_component_0", + _facingDirection: "x-", + }, + ], + tracePath: [ + { + x: -1.2000000000000002, + y: -0.09999999999999998, + }, + { + x: -1.4000000000000001, + y: -0.09999999999999998, + }, + { + x: -1.4000000000000001, + y: -0.30000000000000004, + }, + { + x: -1.2000000000000002, + y: -0.30000000000000004, + }, + ], + mspConnectionPairIds: ["U1.6-U1.2"], + pinIds: ["U1.6", "U1.2"], + }, + "R2.2-C1.1": { + mspPairId: "R2.2-C1.1", + dcConnNetId: "connectivity_net1", + globalConnNetId: "connectivity_net1", + userNetId: "R2.pin2 to U1.THRES", + pins: [ + { + pinId: "R2.2", + x: 0.10000000000000009, + y: -1.2944553500000002, + chipId: "schematic_component_2", + _facingDirection: "x-", + }, + { + pinId: "C1.1", + x: -1.2000000000000002, + y: -1.1500000000000001, + chipId: "schematic_component_3", + _facingDirection: "y+", + }, + ], + tracePath: [ + { + x: 0.09999999999999987, + y: -1.2944553500000002, + }, + { + x: -0.55, + y: -1.2944553500000002, + }, + { + x: -0.55, + y: -0.9500000000000002, + }, + { + x: -1.2000000000000002, + y: -0.9500000000000002, + }, + { + x: -1.2000000000000002, + y: -1.1500000000000001, + }, + ], + mspConnectionPairIds: ["R2.2-C1.1"], + pinIds: ["R2.2", "C1.1"], + }, + "U1.7-R1.2": { + mspPairId: "U1.7-R1.2", + dcConnNetId: "connectivity_net2", + globalConnNetId: "connectivity_net2", + userNetId: "U1.DISCH to R2.pin1", + pins: [ + { + pinId: "U1.7", + x: 1.2000000000000002, + y: -0.10000000000000003, + chipId: "schematic_component_0", + _facingDirection: "x+", + }, + { + pinId: "R1.2", + x: 1.9000000000000004, + y: -0.10000000000000002, + chipId: "schematic_component_1", + _facingDirection: "x-", + }, + ], + tracePath: [ + { + x: 1.2000000000000002, + y: -0.10000000000000003, + }, + { + x: 1.9000000000000004, + y: -0.10000000000000002, + }, + ], + mspConnectionPairIds: ["U1.7-R1.2"], + pinIds: ["U1.7", "R1.2"], + }, + "R2.1-U1.7": { + mspPairId: "R2.1-U1.7", + dcConnNetId: "connectivity_net2", + globalConnNetId: "connectivity_net2", + userNetId: "U1.DISCH to R2.pin1", + pins: [ + { + pinId: "R2.1", + x: 1.2000000000000002, + y: -1.2944553500000002, + chipId: "schematic_component_2", + _facingDirection: "x+", + }, + { + pinId: "U1.7", + x: 1.2000000000000002, + y: -0.10000000000000003, + chipId: "schematic_component_0", + _facingDirection: "x+", + }, + ], + tracePath: [ + { + x: 1.2000000000000002, + y: -1.2944553500000002, + }, + { + x: 1.4000000000000001, + y: -1.2944553500000002, + }, + { + x: 1.4000000000000001, + y: -0.10000000000000003, + }, + { + x: 1.2000000000000002, + y: -0.10000000000000003, + }, + ], + mspConnectionPairIds: ["R2.1-U1.7"], + pinIds: ["R2.1", "U1.7"], + }, + "U1.3-R3.1": { + mspPairId: "U1.3-R3.1", + dcConnNetId: "connectivity_net3", + globalConnNetId: "connectivity_net3", + userNetId: "U1.OUT to R3.pin1", + pins: [ + { + pinId: "U1.3", + x: 1.2000000000000002, + y: 0.09999999999999998, + chipId: "schematic_component_0", + _facingDirection: "x+", + }, + { + pinId: "R3.1", + x: 1.2000000000000002, + y: 1.1500000000000001, + chipId: "schematic_component_5", + _facingDirection: "y-", + }, + ], + tracePath: [ + { + x: 1.2000000000000002, + y: 0.09999999999999998, + }, + { + x: 1.4000000000000001, + y: 0.09999999999999998, + }, + { + x: 1.4000000000000001, + y: 0.625, + }, + { + x: 1.2000000000000002, + y: 0.625, + }, + { + x: 1.2000000000000002, + y: 1.1500000000000001, + }, + ], + mspConnectionPairIds: ["U1.3-R3.1"], + pinIds: ["U1.3", "R3.1"], + }, + "U1.8-R1.1": { + mspPairId: "U1.8-R1.1", + dcConnNetId: "connectivity_net5", + globalConnNetId: "connectivity_net5", + userNetId: "VCC", + pins: [ + { + pinId: "U1.8", + x: 1.2000000000000002, + y: 0.30000000000000004, + chipId: "schematic_component_0", + _facingDirection: "x+", + }, + { + pinId: "R1.1", + x: 3, + y: -0.10000000000000016, + chipId: "schematic_component_1", + _facingDirection: "x+", + }, + ], + tracePath: [ + { + x: 1.2000000000000002, + y: 0.30000000000000004, + }, + { + x: 3.2, + y: 0.30000000000000004, + }, + { + x: 3.2, + y: -0.10000000000000016, + }, + { + x: 3, + y: -0.10000000000000016, + }, + ], + mspConnectionPairIds: ["U1.8-R1.1"], + pinIds: ["U1.8", "R1.1"], + }, + "C1.2-C2.2": { + mspPairId: "C1.2-C2.2", + dcConnNetId: "connectivity_net4", + globalConnNetId: "connectivity_net4", + pins: [ + { + pinId: "C1.2", + x: -1.2000000000000002, + y: -2.25, + chipId: "schematic_component_3", + _facingDirection: "y-", + }, + { + pinId: "C2.2", + x: -3, + y: 0.10000000000000016, + chipId: "schematic_component_4", + _facingDirection: "x-", + }, + ], + tracePath: [ + { + x: -1.2000000000000002, + y: -2.25, + }, + { + x: -1.2000000000000002, + y: -2.45, + }, + { + x: -3.2, + y: -2.45, + }, + { + x: -3.2, + y: 0.10000000000000016, + }, + { + x: -3, + y: 0.10000000000000016, + }, + ], + mspConnectionPairIds: ["C1.2-C2.2"], + pinIds: ["C1.2", "C2.2"], + }, + }, +} as any + +test("NetLabelPlacementSolver-to-MergedNetLabelObstacles snapshot", () => { + const solver1 = new MergedNetLabelObstacles({ + netLabelPlacements: input as any, + problem, + }) + const solver2 = new NetLabelPlacementSolver({ + ...paramForSolver2, + }) + solver1.solve() + solver2.solve() + const sideBySide = getSvgFromGraphicsObject( + stackGraphicsHorizontally([solver2.visualize(), solver1.visualize()], { + titles: ["NetLabelPlacementSolver", "MergedNetLabelObstacles"], + }), + { + backgroundColor: "white", + }, + ) + expect(sideBySide).toMatchSvgSnapshot(import.meta.path) +}) diff --git a/tests/solvers/TraceLabelOverlapAvoidanceSolver/stackGraphicsHorizontally/SingleOverlapSolver-to-TraceCleanupSolver.ts b/tests/solvers/TraceLabelOverlapAvoidanceSolver/stackGraphicsHorizontally/SingleOverlapSolver-to-TraceCleanupSolver.ts new file mode 100644 index 0000000..8284c1e --- /dev/null +++ b/tests/solvers/TraceLabelOverlapAvoidanceSolver/stackGraphicsHorizontally/SingleOverlapSolver-to-TraceCleanupSolver.ts @@ -0,0 +1,987 @@ +import { expect } from "bun:test" +import { test } from "bun:test" +import { + getSvgFromGraphicsObject, + stackGraphicsHorizontally, +} from "graphics-debug" +import { SingleOverlapSolver } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver" +import { TraceCleanupSolver } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/TraceCleanupSolver" + +const paramForSolver1 = { + problem: { + chips: [ + { + chipId: "schematic_component_0", + center: { + x: 0, + y: 0, + }, + width: 2.4000000000000004, + height: 1, + pins: [ + { + pinId: "U1.1", + x: 1.2000000000000002, + y: -0.30000000000000004, + }, + { + pinId: "U1.2", + x: -1.2000000000000002, + y: -0.30000000000000004, + }, + { + pinId: "U1.3", + x: 1.2000000000000002, + y: 0.09999999999999998, + }, + { + pinId: "U1.4", + x: -1.2000000000000002, + y: 0.30000000000000004, + }, + { + pinId: "U1.5", + x: -1.2000000000000002, + y: 0.10000000000000003, + }, + { + pinId: "U1.6", + x: -1.2000000000000002, + y: -0.09999999999999998, + }, + { + pinId: "U1.7", + x: 1.2000000000000002, + y: -0.10000000000000003, + }, + { + pinId: "U1.8", + x: 1.2000000000000002, + y: 0.30000000000000004, + }, + ], + }, + { + chipId: "schematic_component_1", + center: { + x: 2.45, + y: -0.10000000000000009, + }, + width: 1.0999999999999996, + height: 0.388910699999999, + pins: [ + { + pinId: "R1.1", + x: 3, + y: -0.10000000000000016, + }, + { + pinId: "R1.2", + x: 1.9000000000000004, + y: -0.10000000000000002, + }, + ], + }, + { + chipId: "schematic_component_2", + center: { + x: 0.6500000000000001, + y: -1.2944553500000002, + }, + width: 1.1, + height: 0.388910699999999, + pins: [ + { + pinId: "R2.1", + x: 1.2000000000000002, + y: -1.2944553500000002, + }, + { + pinId: "R2.2", + x: 0.10000000000000009, + y: -1.2944553500000002, + }, + ], + }, + { + chipId: "schematic_component_3", + center: { + x: -1.2000000000000002, + y: -1.7000000000000002, + }, + width: 1.06, + height: 1.1, + pins: [ + { + pinId: "C1.1", + x: -1.2000000000000002, + y: -1.1500000000000001, + }, + { + pinId: "C1.2", + x: -1.2000000000000002, + y: -2.25, + }, + ], + }, + { + chipId: "schematic_component_4", + center: { + x: -2.45, + y: 0.10000000000000009, + }, + width: 1.0999999999999996, + height: 0.84, + pins: [ + { + pinId: "C2.1", + x: -1.9000000000000004, + y: 0.10000000000000002, + }, + { + pinId: "C2.2", + x: -3, + y: 0.10000000000000016, + }, + ], + }, + { + chipId: "schematic_component_5", + center: { + x: 1.2000000000000002, + y: 1.7000000000000002, + }, + width: 1.06, + height: 1.1, + pins: [ + { + pinId: "R3.1", + x: 1.2000000000000002, + y: 1.1500000000000001, + }, + { + pinId: "R3.2", + x: 1.2000000000000002, + y: 2.25, + }, + ], + }, + ], + directConnections: [ + { + pinIds: ["U1.5", "C2.1"], + netId: "U1.CTRL to C2.pin1", + }, + { + pinIds: ["U1.6", "U1.2"], + netId: "U1.THRES to U1.TRIG", + }, + { + pinIds: ["R1.2", "U1.7"], + netId: "R1.pin2 to U1.DISCH", + }, + { + pinIds: ["U1.7", "R2.1"], + netId: "U1.DISCH to R2.pin1", + }, + { + pinIds: ["R2.2", "U1.6"], + netId: "R2.pin2 to U1.THRES", + }, + { + pinIds: ["U1.6", "C1.1"], + netId: "U1.THRES to C1.pin1", + }, + { + pinIds: ["U1.3", "R3.1"], + netId: "U1.OUT to R3.pin1", + }, + ], + netConnections: [ + { + netId: "GND", + pinIds: ["U1.1", "C1.2", "C2.2"], + }, + { + netId: "VCC", + pinIds: ["U1.4", "U1.8", "R1.1"], + }, + ], + availableNetLabelOrientations: { + VCC: ["y+"], + GND: ["y-"], + }, + maxMspPairDistance: 2.4, + }, + allTraces: [ + { + mspPairId: "U1.5-C2.1", + dcConnNetId: "connectivity_net0", + globalConnNetId: "connectivity_net0", + userNetId: "U1.CTRL to C2.pin1", + pins: [ + { + pinId: "U1.5", + x: -1.2000000000000002, + y: 0.10000000000000003, + chipId: "schematic_component_0", + _facingDirection: "x-", + }, + { + pinId: "C2.1", + x: -1.9000000000000004, + y: 0.10000000000000002, + chipId: "schematic_component_4", + _facingDirection: "x+", + }, + ], + tracePath: [ + { + x: -1.2000000000000002, + y: 0.10000000000000003, + }, + { + x: -1.9000000000000004, + y: 0.10000000000000002, + }, + ], + mspConnectionPairIds: ["U1.5-C2.1"], + pinIds: ["U1.5", "C2.1"], + }, + { + mspPairId: "U1.2-C1.1", + dcConnNetId: "connectivity_net1", + globalConnNetId: "connectivity_net1", + userNetId: "U1.THRES to U1.TRIG", + pins: [ + { + pinId: "U1.2", + x: -1.2000000000000002, + y: -0.30000000000000004, + chipId: "schematic_component_0", + _facingDirection: "x-", + }, + { + pinId: "C1.1", + x: -1.2000000000000002, + y: -1.1500000000000001, + chipId: "schematic_component_3", + _facingDirection: "y+", + }, + ], + tracePath: [ + { + x: -1.2000000000000002, + y: -0.30000000000000004, + }, + { + x: -1.4000000000000001, + y: -0.30000000000000004, + }, + { + x: -1.4000000000000001, + y: -0.7250000000000001, + }, + { + x: -1.2000000000000002, + y: -0.7250000000000001, + }, + { + x: -1.2000000000000002, + y: -1.1500000000000001, + }, + ], + mspConnectionPairIds: ["U1.2-C1.1"], + pinIds: ["U1.2", "C1.1"], + }, + { + mspPairId: "U1.6-U1.2", + dcConnNetId: "connectivity_net1", + globalConnNetId: "connectivity_net1", + userNetId: "U1.THRES to C1.pin1", + pins: [ + { + pinId: "U1.6", + x: -1.2000000000000002, + y: -0.09999999999999998, + chipId: "schematic_component_0", + _facingDirection: "x-", + }, + { + pinId: "U1.2", + x: -1.2000000000000002, + y: -0.30000000000000004, + chipId: "schematic_component_0", + _facingDirection: "x-", + }, + ], + tracePath: [ + { + x: -1.2000000000000002, + y: -0.09999999999999998, + }, + { + x: -1.4000000000000001, + y: -0.09999999999999998, + }, + { + x: -1.4000000000000001, + y: -0.30000000000000004, + }, + { + x: -1.2000000000000002, + y: -0.30000000000000004, + }, + ], + mspConnectionPairIds: ["U1.6-U1.2"], + pinIds: ["U1.6", "U1.2"], + }, + { + mspPairId: "R2.2-C1.1", + dcConnNetId: "connectivity_net1", + globalConnNetId: "connectivity_net1", + userNetId: "R2.pin2 to U1.THRES", + pins: [ + { + pinId: "R2.2", + x: 0.10000000000000009, + y: -1.2944553500000002, + chipId: "schematic_component_2", + _facingDirection: "x-", + }, + { + pinId: "C1.1", + x: -1.2000000000000002, + y: -1.1500000000000001, + chipId: "schematic_component_3", + _facingDirection: "y+", + }, + ], + tracePath: [ + { + x: 0.09999999999999987, + y: -1.2944553500000002, + }, + { + x: -0.55, + y: -1.2944553500000002, + }, + { + x: -0.55, + y: -0.9500000000000002, + }, + { + x: -1.2000000000000002, + y: -0.9500000000000002, + }, + { + x: -1.2000000000000002, + y: -1.1500000000000001, + }, + ], + mspConnectionPairIds: ["R2.2-C1.1"], + pinIds: ["R2.2", "C1.1"], + }, + { + mspPairId: "U1.7-R1.2", + dcConnNetId: "connectivity_net2", + globalConnNetId: "connectivity_net2", + userNetId: "U1.DISCH to R2.pin1", + pins: [ + { + pinId: "U1.7", + x: 1.2000000000000002, + y: -0.10000000000000003, + chipId: "schematic_component_0", + _facingDirection: "x+", + }, + { + pinId: "R1.2", + x: 1.9000000000000004, + y: -0.10000000000000002, + chipId: "schematic_component_1", + _facingDirection: "x-", + }, + ], + tracePath: [ + { + x: 1.2000000000000002, + y: -0.10000000000000003, + }, + { + x: 1.9000000000000004, + y: -0.10000000000000002, + }, + ], + mspConnectionPairIds: ["U1.7-R1.2"], + pinIds: ["U1.7", "R1.2"], + }, + { + mspPairId: "R2.1-U1.7", + dcConnNetId: "connectivity_net2", + globalConnNetId: "connectivity_net2", + userNetId: "U1.DISCH to R2.pin1", + pins: [ + { + pinId: "R2.1", + x: 1.2000000000000002, + y: -1.2944553500000002, + chipId: "schematic_component_2", + _facingDirection: "x+", + }, + { + pinId: "U1.7", + x: 1.2000000000000002, + y: -0.10000000000000003, + chipId: "schematic_component_0", + _facingDirection: "x+", + }, + ], + tracePath: [ + { + x: 1.2000000000000002, + y: -1.2944553500000002, + }, + { + x: 1.4000000000000001, + y: -1.2944553500000002, + }, + { + x: 1.4000000000000001, + y: -0.10000000000000003, + }, + { + x: 1.2000000000000002, + y: -0.10000000000000003, + }, + ], + mspConnectionPairIds: ["R2.1-U1.7"], + pinIds: ["R2.1", "U1.7"], + }, + { + mspPairId: "U1.3-R3.1", + dcConnNetId: "connectivity_net3", + globalConnNetId: "connectivity_net3", + userNetId: "U1.OUT to R3.pin1", + pins: [ + { + pinId: "U1.3", + x: 1.2000000000000002, + y: 0.09999999999999998, + chipId: "schematic_component_0", + _facingDirection: "x+", + }, + { + pinId: "R3.1", + x: 1.2000000000000002, + y: 1.1500000000000001, + chipId: "schematic_component_5", + _facingDirection: "y-", + }, + ], + tracePath: [ + { + x: 1.2000000000000002, + y: 0.09999999999999998, + }, + { + x: 1.4000000000000001, + y: 0.09999999999999998, + }, + { + x: 1.4000000000000001, + y: 0.625, + }, + { + x: 1.2000000000000002, + y: 0.625, + }, + { + x: 1.2000000000000002, + y: 1.1500000000000001, + }, + ], + mspConnectionPairIds: ["U1.3-R3.1"], + pinIds: ["U1.3", "R3.1"], + }, + { + mspPairId: "U1.8-R1.1", + dcConnNetId: "connectivity_net5", + globalConnNetId: "connectivity_net5", + userNetId: "VCC", + pins: [ + { + pinId: "U1.8", + x: 1.2000000000000002, + y: 0.30000000000000004, + chipId: "schematic_component_0", + _facingDirection: "x+", + }, + { + pinId: "R1.1", + x: 3, + y: -0.10000000000000016, + chipId: "schematic_component_1", + _facingDirection: "x+", + }, + ], + tracePath: [ + { + x: 1.2000000000000002, + y: 0.30000000000000004, + }, + { + x: 3.2, + y: 0.30000000000000004, + }, + { + x: 3.2, + y: -0.10000000000000016, + }, + { + x: 3, + y: -0.10000000000000016, + }, + ], + mspConnectionPairIds: ["U1.8-R1.1"], + pinIds: ["U1.8", "R1.1"], + }, + { + mspPairId: "C1.2-C2.2", + dcConnNetId: "connectivity_net4", + globalConnNetId: "connectivity_net4", + pins: [ + { + pinId: "C1.2", + x: -1.2000000000000002, + y: -2.25, + chipId: "schematic_component_3", + _facingDirection: "y-", + }, + { + pinId: "C2.2", + x: -3, + y: 0.10000000000000016, + chipId: "schematic_component_4", + _facingDirection: "x-", + }, + ], + tracePath: [ + { + x: -1.2000000000000002, + y: -2.25, + }, + { + x: -1.2000000000000002, + y: -2.45, + }, + { + x: -3.2, + y: -2.45, + }, + { + x: -3.2, + y: 0.10000000000000016, + }, + { + x: -3, + y: 0.10000000000000016, + }, + ], + mspConnectionPairIds: ["C1.2-C2.2"], + pinIds: ["C1.2", "C2.2"], + }, + ], + targetTraceIds: new Set([]), + allLabelPlacements: [ + { + globalConnNetId: "merged-group-U1-x+", + netId: "GND", + mspConnectionPairIds: ["U1.3-R3.1"], + pinIds: ["U1.1", "U1.3", "R3.1"], + orientation: "x+", + anchorPoint: { + x: 1.2000000000000002, + y: -0.30000000000000004, + }, + width: 0.6490000000000002, + height: 0.6, + center: { + x: 1.5255, + y: -0.10000000000000003, + }, + }, + { + globalConnNetId: "connectivity_net4", + dcConnNetId: "connectivity_net4", + netId: "GND", + mspConnectionPairIds: ["C1.2-C2.2"], + pinIds: ["C1.2", "C2.2"], + orientation: "y-", + anchorPoint: { + x: -1.2000000000000002, + y: -2.45, + }, + width: 0.2, + height: 0.45, + center: { + x: -1.2000000000000002, + y: -2.6750000000000003, + }, + }, + { + globalConnNetId: "merged-group-U1-x-", + dcConnNetId: "connectivity_net1", + netId: "U1.THRES to U1.TRIG", + mspConnectionPairIds: ["U1.2-C1.1"], + pinIds: ["U1.2", "C1.1", "U1.4"], + orientation: "x-", + anchorPoint: { + x: -1.4000000000000001, + y: -0.30000000000000004, + }, + width: 0.6490000000000002, + height: 0.8, + center: { + x: -1.5255, + y: 0, + }, + }, + { + globalConnNetId: "merged-group-U1-y+", + dcConnNetId: "connectivity_net5", + netId: "VCC", + mspConnectionPairIds: ["U1.8-R1.1", "U1.5-C2.1"], + pinIds: ["U1.8", "R1.1", "U1.5", "C2.1"], + orientation: "y+", + anchorPoint: { + x: 2.2, + y: 0.30000000000000004, + }, + width: 3.9500000000000006, + height: 0.6499999999999999, + center: { + x: 0.32499999999999996, + y: 0.42500000000000004, + }, + }, + { + globalConnNetId: "connectivity_net2", + dcConnNetId: "connectivity_net2", + netId: "U1.DISCH to R2.pin1", + mspConnectionPairIds: ["R2.1-U1.7"], + pinIds: ["R2.1", "U1.7"], + orientation: "y-", + anchorPoint: { + x: 1.4000000000000001, + y: -1.2944553500000002, + }, + width: 0.2, + height: 0.45, + center: { + x: 1.4000000000000001, + y: -1.5194553500000003, + }, + }, + ], + mergedLabelNetIdMap: { + "merged-group-U1-x+": new Set(["connectivity_net4", "connectivity_net3"]), + "merged-group-U1-x-": new Set(["connectivity_net1", "connectivity_net5"]), + "merged-group-U1-y+": new Set(["connectivity_net5", "connectivity_net0"]), + }, + paddingBuffer: 0.01, +} as any + +const paramForSolver2 = { + trace: { + mspPairId: "U1.3-R3.1", + dcConnNetId: "connectivity_net3", + globalConnNetId: "connectivity_net3", + userNetId: "U1.OUT to R3.pin1", + pins: [ + { + pinId: "U1.3", + x: 1.2000000000000002, + y: 0.09999999999999998, + chipId: "schematic_component_0", + _facingDirection: "x+", + }, + { + pinId: "R3.1", + x: 1.2000000000000002, + y: 1.1500000000000001, + chipId: "schematic_component_5", + _facingDirection: "y-", + }, + ], + tracePath: [ + { + x: 1.2000000000000002, + y: 0.09999999999999998, + }, + { + x: 1.4000000000000001, + y: 0.09999999999999998, + }, + { + x: 1.4000000000000001, + y: 0.625, + }, + { + x: 1.2000000000000002, + y: 0.625, + }, + { + x: 1.2000000000000002, + y: 1.1500000000000001, + }, + ], + mspConnectionPairIds: ["U1.3-R3.1"], + pinIds: ["U1.3", "R3.1"], + }, + label: { + globalConnNetId: "merged-group-U1-y+", + dcConnNetId: "connectivity_net5", + netId: "VCC", + mspConnectionPairIds: ["U1.8-R1.1", "U1.5-C2.1"], + pinIds: ["U1.8", "R1.1", "U1.5", "C2.1"], + orientation: "y+", + anchorPoint: { + x: 2.2, + y: 0.30000000000000004, + }, + width: 3.9500000000000006, + height: 0.6499999999999999, + center: { + x: 0.32499999999999996, + y: 0.42500000000000004, + }, + }, + problem: { + chips: [ + { + chipId: "schematic_component_0", + center: { + x: 0, + y: 0, + }, + width: 2.4000000000000004, + height: 1, + pins: [ + { + pinId: "U1.1", + x: 1.2000000000000002, + y: -0.30000000000000004, + }, + { + pinId: "U1.2", + x: -1.2000000000000002, + y: -0.30000000000000004, + }, + { + pinId: "U1.3", + x: 1.2000000000000002, + y: 0.09999999999999998, + }, + { + pinId: "U1.4", + x: -1.2000000000000002, + y: 0.30000000000000004, + }, + { + pinId: "U1.5", + x: -1.2000000000000002, + y: 0.10000000000000003, + }, + { + pinId: "U1.6", + x: -1.2000000000000002, + y: -0.09999999999999998, + }, + { + pinId: "U1.7", + x: 1.2000000000000002, + y: -0.10000000000000003, + }, + { + pinId: "U1.8", + x: 1.2000000000000002, + y: 0.30000000000000004, + }, + ], + }, + { + chipId: "schematic_component_1", + center: { + x: 2.45, + y: -0.10000000000000009, + }, + width: 1.0999999999999996, + height: 0.388910699999999, + pins: [ + { + pinId: "R1.1", + x: 3, + y: -0.10000000000000016, + }, + { + pinId: "R1.2", + x: 1.9000000000000004, + y: -0.10000000000000002, + }, + ], + }, + { + chipId: "schematic_component_2", + center: { + x: 0.6500000000000001, + y: -1.2944553500000002, + }, + width: 1.1, + height: 0.388910699999999, + pins: [ + { + pinId: "R2.1", + x: 1.2000000000000002, + y: -1.2944553500000002, + }, + { + pinId: "R2.2", + x: 0.10000000000000009, + y: -1.2944553500000002, + }, + ], + }, + { + chipId: "schematic_component_3", + center: { + x: -1.2000000000000002, + y: -1.7000000000000002, + }, + width: 1.06, + height: 1.1, + pins: [ + { + pinId: "C1.1", + x: -1.2000000000000002, + y: -1.1500000000000001, + }, + { + pinId: "C1.2", + x: -1.2000000000000002, + y: -2.25, + }, + ], + }, + { + chipId: "schematic_component_4", + center: { + x: -2.45, + y: 0.10000000000000009, + }, + width: 1.0999999999999996, + height: 0.84, + pins: [ + { + pinId: "C2.1", + x: -1.9000000000000004, + y: 0.10000000000000002, + }, + { + pinId: "C2.2", + x: -3, + y: 0.10000000000000016, + }, + ], + }, + { + chipId: "schematic_component_5", + center: { + x: 1.2000000000000002, + y: 1.7000000000000002, + }, + width: 1.06, + height: 1.1, + pins: [ + { + pinId: "R3.1", + x: 1.2000000000000002, + y: 1.1500000000000001, + }, + { + pinId: "R3.2", + x: 1.2000000000000002, + y: 2.25, + }, + ], + }, + ], + directConnections: [ + { + pinIds: ["U1.5", "C2.1"], + netId: "U1.CTRL to C2.pin1", + }, + { + pinIds: ["U1.6", "U1.2"], + netId: "U1.THRES to U1.TRIG", + }, + { + pinIds: ["R1.2", "U1.7"], + netId: "R1.pin2 to U1.DISCH", + }, + { + pinIds: ["U1.7", "R2.1"], + netId: "U1.DISCH to R2.pin1", + }, + { + pinIds: ["R2.2", "U1.6"], + netId: "R2.pin2 to U1.THRES", + }, + { + pinIds: ["U1.6", "C1.1"], + netId: "U1.THRES to C1.pin1", + }, + { + pinIds: ["U1.3", "R3.1"], + netId: "U1.OUT to R3.pin1", + }, + ], + netConnections: [ + { + netId: "GND", + pinIds: ["U1.1", "C1.2", "C2.2"], + }, + { + netId: "VCC", + pinIds: ["U1.4", "U1.8", "R1.1"], + }, + ], + availableNetLabelOrientations: { + VCC: ["y+"], + GND: ["y-"], + }, + maxMspPairDistance: 2.4, + }, + paddingBuffer: 0.1, + detourCount: 0, +} as any + +test("SingleOverlapSolver-to-TraceCleanupSolver snapshot", () => { + const solver1 = new TraceCleanupSolver({ + ...paramForSolver1, + }) + const solver2 = new SingleOverlapSolver({ + ...paramForSolver2, + }) + solver1.solve() + solver2.solve() + const sideBySide = getSvgFromGraphicsObject( + stackGraphicsHorizontally([solver2.visualize(), solver1.visualize()], { + titles: ["SingleOverlapSolver", "TraceCleanupSolver"], + }), + { + backgroundColor: "white", + }, + ) + expect(sideBySide).toMatchSvgSnapshot(import.meta.path) +}) diff --git a/tests/solvers/TraceLabelOverlapAvoidanceSolver/stackGraphicsHorizontally/__snapshots__/MergedNetLabelObstacles-to-SingleOverlapSolver.ts.snap.svg b/tests/solvers/TraceLabelOverlapAvoidanceSolver/stackGraphicsHorizontally/__snapshots__/MergedNetLabelObstacles-to-SingleOverlapSolver.ts.snap.svg new file mode 100644 index 0000000..9751441 --- /dev/null +++ b/tests/solvers/TraceLabelOverlapAvoidanceSolver/stackGraphicsHorizontally/__snapshots__/MergedNetLabelObstacles-to-SingleOverlapSolver.ts.snap.svg @@ -0,0 +1,375 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MergedNetLabelObstaclesSingleOverlapSolver + + + \ No newline at end of file diff --git a/tests/solvers/TraceLabelOverlapAvoidanceSolver/stackGraphicsHorizontally/__snapshots__/NetLabelPlacementSolver-to-MergedNetLabelObstacles.ts.snap.svg b/tests/solvers/TraceLabelOverlapAvoidanceSolver/stackGraphicsHorizontally/__snapshots__/NetLabelPlacementSolver-to-MergedNetLabelObstacles.ts.snap.svg new file mode 100644 index 0000000..a81e6cf --- /dev/null +++ b/tests/solvers/TraceLabelOverlapAvoidanceSolver/stackGraphicsHorizontally/__snapshots__/NetLabelPlacementSolver-to-MergedNetLabelObstacles.ts.snap.svg @@ -0,0 +1,444 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NetLabelPlacementSolverMergedNetLabelObstacles + + + \ No newline at end of file diff --git a/tests/solvers/TraceLabelOverlapAvoidanceSolver/stackGraphicsHorizontally/__snapshots__/SingleOverlapSolver-to-TraceCleanupSolver.ts.snap.svg b/tests/solvers/TraceLabelOverlapAvoidanceSolver/stackGraphicsHorizontally/__snapshots__/SingleOverlapSolver-to-TraceCleanupSolver.ts.snap.svg new file mode 100644 index 0000000..621a7df --- /dev/null +++ b/tests/solvers/TraceLabelOverlapAvoidanceSolver/stackGraphicsHorizontally/__snapshots__/SingleOverlapSolver-to-TraceCleanupSolver.ts.snap.svg @@ -0,0 +1,351 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SingleOverlapSolverTraceCleanupSolver + + + \ No newline at end of file diff --git a/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/MergedNetLabelObstacles.test.ts b/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/MergedNetLabelObstacles.test.ts new file mode 100644 index 0000000..386c787 --- /dev/null +++ b/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/MergedNetLabelObstacles.test.ts @@ -0,0 +1,363 @@ +import { MergedNetLabelObstacles } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver" +import { expect } from "bun:test" +import { test } from "bun:test" + +const input = [ + { + globalConnNetId: "connectivity_net4", + netId: "GND", + mspConnectionPairIds: [], + pinIds: ["U1.1"], + orientation: "x+", + anchorPoint: { + x: 1.2000000000000002, + y: -0.30000000000000004, + }, + width: 0.45, + height: 0.2, + center: { + x: 1.4260000000000002, + y: -0.30000000000000004, + }, + }, + { + globalConnNetId: "connectivity_net4", + dcConnNetId: "connectivity_net4", + netId: "GND", + mspConnectionPairIds: ["C1.2-C2.2"], + pinIds: ["C1.2", "C2.2"], + orientation: "y-", + anchorPoint: { + x: -1.2000000000000002, + y: -2.45, + }, + width: 0.2, + height: 0.45, + center: { + x: -1.2000000000000002, + y: -2.6750000000000003, + }, + }, + { + globalConnNetId: "connectivity_net1", + dcConnNetId: "connectivity_net1", + netId: "U1.THRES to U1.TRIG", + mspConnectionPairIds: ["U1.2-C1.1"], + pinIds: ["U1.2", "C1.1"], + orientation: "x-", + anchorPoint: { + x: -1.4000000000000001, + y: -0.30000000000000004, + }, + width: 0.45, + height: 0.2, + center: { + x: -1.6250000000000002, + y: -0.30000000000000004, + }, + }, + { + globalConnNetId: "connectivity_net3", + dcConnNetId: "connectivity_net3", + netId: "U1.OUT to R3.pin1", + mspConnectionPairIds: ["U1.3-R3.1"], + pinIds: ["U1.3", "R3.1"], + orientation: "x+", + anchorPoint: { + x: 1.4000000000000001, + y: 0.09999999999999998, + }, + width: 0.45, + height: 0.2, + center: { + x: 1.6250000000000002, + y: 0.09999999999999998, + }, + }, + { + globalConnNetId: "connectivity_net5", + netId: "VCC", + mspConnectionPairIds: [], + pinIds: ["U1.4"], + orientation: "x-", + anchorPoint: { + x: -1.2000000000000002, + y: 0.30000000000000004, + }, + width: 0.45, + height: 0.2, + center: { + x: -1.4260000000000002, + y: 0.30000000000000004, + }, + }, + { + globalConnNetId: "connectivity_net5", + dcConnNetId: "connectivity_net5", + netId: "VCC", + mspConnectionPairIds: ["U1.8-R1.1"], + pinIds: ["U1.8", "R1.1"], + orientation: "y+", + anchorPoint: { + x: 2.2, + y: 0.30000000000000004, + }, + width: 0.2, + height: 0.45, + center: { + x: 2.2, + y: 0.525, + }, + }, + { + globalConnNetId: "connectivity_net0", + dcConnNetId: "connectivity_net0", + netId: "U1.CTRL to C2.pin1", + mspConnectionPairIds: ["U1.5-C2.1"], + pinIds: ["U1.5", "C2.1"], + orientation: "y+", + anchorPoint: { + x: -1.5500000000000003, + y: 0.10000000000000003, + }, + width: 0.2, + height: 0.45, + center: { + x: -1.5500000000000003, + y: 0.32500000000000007, + }, + }, + { + globalConnNetId: "connectivity_net2", + dcConnNetId: "connectivity_net2", + netId: "U1.DISCH to R2.pin1", + mspConnectionPairIds: ["R2.1-U1.7"], + pinIds: ["R2.1", "U1.7"], + orientation: "y-", + anchorPoint: { + x: 1.4000000000000001, + y: -1.2944553500000002, + }, + width: 0.2, + height: 0.45, + center: { + x: 1.4000000000000001, + y: -1.5194553500000003, + }, + }, +] + +const problem = { + chips: [ + { + chipId: "schematic_component_0", + center: { + x: 0, + y: 0, + }, + width: 2.4000000000000004, + height: 1, + pins: [ + { + pinId: "U1.1", + x: 1.2000000000000002, + y: -0.30000000000000004, + }, + { + pinId: "U1.2", + x: -1.2000000000000002, + y: -0.30000000000000004, + }, + { + pinId: "U1.3", + x: 1.2000000000000002, + y: 0.09999999999999998, + }, + { + pinId: "U1.4", + x: -1.2000000000000002, + y: 0.30000000000000004, + }, + { + pinId: "U1.5", + x: -1.2000000000000002, + y: 0.10000000000000003, + }, + { + pinId: "U1.6", + x: -1.2000000000000002, + y: -0.09999999999999998, + }, + { + pinId: "U1.7", + x: 1.2000000000000002, + y: -0.10000000000000003, + }, + { + pinId: "U1.8", + x: 1.2000000000000002, + y: 0.30000000000000004, + }, + ], + }, + { + chipId: "schematic_component_1", + center: { + x: 2.45, + y: -0.10000000000000009, + }, + width: 1.0999999999999996, + height: 0.388910699999999, + pins: [ + { + pinId: "R1.1", + x: 3, + y: -0.10000000000000016, + }, + { + pinId: "R1.2", + x: 1.9000000000000004, + y: -0.10000000000000002, + }, + ], + }, + { + chipId: "schematic_component_2", + center: { + x: 0.6500000000000001, + y: -1.2944553500000002, + }, + width: 1.1, + height: 0.388910699999999, + pins: [ + { + pinId: "R2.1", + x: 1.2000000000000002, + y: -1.2944553500000002, + }, + { + pinId: "R2.2", + x: 0.10000000000000009, + y: -1.2944553500000002, + }, + ], + }, + { + chipId: "schematic_component_3", + center: { + x: -1.2000000000000002, + y: -1.7000000000000002, + }, + width: 1.06, + height: 1.1, + pins: [ + { + pinId: "C1.1", + x: -1.2000000000000002, + y: -1.1500000000000001, + }, + { + pinId: "C1.2", + x: -1.2000000000000002, + y: -2.25, + }, + ], + }, + { + chipId: "schematic_component_4", + center: { + x: -2.45, + y: 0.10000000000000009, + }, + width: 1.0999999999999996, + height: 0.84, + pins: [ + { + pinId: "C2.1", + x: -1.9000000000000004, + y: 0.10000000000000002, + }, + { + pinId: "C2.2", + x: -3, + y: 0.10000000000000016, + }, + ], + }, + { + chipId: "schematic_component_5", + center: { + x: 1.2000000000000002, + y: 1.7000000000000002, + }, + width: 1.06, + height: 1.1, + pins: [ + { + pinId: "R3.1", + x: 1.2000000000000002, + y: 1.1500000000000001, + }, + { + pinId: "R3.2", + x: 1.2000000000000002, + y: 2.25, + }, + ], + }, + ], + directConnections: [ + { + pinIds: ["U1.5", "C2.1"], + netId: "U1.CTRL to C2.pin1", + }, + { + pinIds: ["U1.6", "U1.2"], + netId: "U1.THRES to U1.TRIG", + }, + { + pinIds: ["R1.2", "U1.7"], + netId: "R1.pin2 to U1.DISCH", + }, + { + pinIds: ["U1.7", "R2.1"], + netId: "U1.DISCH to R2.pin1", + }, + { + pinIds: ["R2.2", "U1.6"], + netId: "R2.pin2 to U1.THRES", + }, + { + pinIds: ["U1.6", "C1.1"], + netId: "U1.THRES to C1.pin1", + }, + { + pinIds: ["U1.3", "R3.1"], + netId: "U1.OUT to R3.pin1", + }, + ], + netConnections: [ + { + netId: "GND", + pinIds: ["U1.1", "C1.2", "C2.2"], + }, + { + netId: "VCC", + pinIds: ["U1.4", "U1.8", "R1.1"], + }, + ], + availableNetLabelOrientations: { + VCC: ["y+"], + GND: ["y-"], + }, + maxMspPairDistance: 2.4, +} as any + +test("LabelMergingSolver snapshot", () => { + const solver = new MergedNetLabelObstacles({ + netLabelPlacements: input as any, + problem, + }) + solver.solve() + expect(solver).toMatchSolverSnapshot(import.meta.path) +}) diff --git a/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/OverlapAvoidanceStepSolver.test.ts b/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/OverlapAvoidanceStepSolver.test.ts new file mode 100644 index 0000000..a263f34 --- /dev/null +++ b/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/OverlapAvoidanceStepSolver.test.ts @@ -0,0 +1,698 @@ +import { OverlapAvoidanceStepSolver } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver" +import { expect } from "bun:test" +import { test } from "bun:test" + +const inputProblem = { + chips: [ + { + chipId: "schematic_component_0", + center: { + x: 0, + y: 0, + }, + width: 2.4000000000000004, + height: 1, + pins: [ + { + pinId: "U1.1", + x: 1.2000000000000002, + y: -0.30000000000000004, + }, + { + pinId: "U1.2", + x: -1.2000000000000002, + y: -0.30000000000000004, + }, + { + pinId: "U1.3", + x: 1.2000000000000002, + y: 0.09999999999999998, + }, + { + pinId: "U1.4", + x: -1.2000000000000002, + y: 0.30000000000000004, + }, + { + pinId: "U1.5", + x: -1.2000000000000002, + y: 0.10000000000000003, + }, + { + pinId: "U1.6", + x: -1.2000000000000002, + y: -0.09999999999999998, + }, + { + pinId: "U1.7", + x: 1.2000000000000002, + y: -0.10000000000000003, + }, + { + pinId: "U1.8", + x: 1.2000000000000002, + y: 0.30000000000000004, + }, + ], + }, + { + chipId: "schematic_component_1", + center: { + x: 2.45, + y: -0.10000000000000009, + }, + width: 1.0999999999999996, + height: 0.388910699999999, + pins: [ + { + pinId: "R1.1", + x: 3, + y: -0.10000000000000016, + }, + { + pinId: "R1.2", + x: 1.9000000000000004, + y: -0.10000000000000002, + }, + ], + }, + { + chipId: "schematic_component_2", + center: { + x: 0.6500000000000001, + y: -1.2944553500000002, + }, + width: 1.1, + height: 0.388910699999999, + pins: [ + { + pinId: "R2.1", + x: 1.2000000000000002, + y: -1.2944553500000002, + }, + { + pinId: "R2.2", + x: 0.10000000000000009, + y: -1.2944553500000002, + }, + ], + }, + { + chipId: "schematic_component_3", + center: { + x: -1.2000000000000002, + y: -1.7000000000000002, + }, + width: 1.06, + height: 1.1, + pins: [ + { + pinId: "C1.1", + x: -1.2000000000000002, + y: -1.1500000000000001, + }, + { + pinId: "C1.2", + x: -1.2000000000000002, + y: -2.25, + }, + ], + }, + { + chipId: "schematic_component_4", + center: { + x: -2.45, + y: 0.10000000000000009, + }, + width: 1.0999999999999996, + height: 0.84, + pins: [ + { + pinId: "C2.1", + x: -1.9000000000000004, + y: 0.10000000000000002, + }, + { + pinId: "C2.2", + x: -3, + y: 0.10000000000000016, + }, + ], + }, + { + chipId: "schematic_component_5", + center: { + x: 1.2000000000000002, + y: 1.7000000000000002, + }, + width: 1.06, + height: 1.1, + pins: [ + { + pinId: "R3.1", + x: 1.2000000000000002, + y: 1.1500000000000001, + }, + { + pinId: "R3.2", + x: 1.2000000000000002, + y: 2.25, + }, + ], + }, + ], + directConnections: [ + { + pinIds: ["U1.5", "C2.1"], + netId: "U1.CTRL to C2.pin1", + }, + { + pinIds: ["U1.6", "U1.2"], + netId: "U1.THRES to U1.TRIG", + }, + { + pinIds: ["R1.2", "U1.7"], + netId: "R1.pin2 to U1.DISCH", + }, + { + pinIds: ["U1.7", "R2.1"], + netId: "U1.DISCH to R2.pin1", + }, + { + pinIds: ["R2.2", "U1.6"], + netId: "R2.pin2 to U1.THRES", + }, + { + pinIds: ["U1.6", "C1.1"], + netId: "U1.THRES to C1.pin1", + }, + { + pinIds: ["U1.3", "R3.1"], + netId: "U1.OUT to R3.pin1", + }, + ], + netConnections: [ + { + netId: "GND", + pinIds: ["U1.1", "C1.2", "C2.2"], + }, + { + netId: "VCC", + pinIds: ["U1.4", "U1.8", "R1.1"], + }, + ], + availableNetLabelOrientations: { + VCC: ["y+"], + GND: ["y-"], + }, + maxMspPairDistance: 2.4, +} + +const inputTraces = [ + { + mspPairId: "U1.5-C2.1", + dcConnNetId: "connectivity_net0", + globalConnNetId: "connectivity_net0", + userNetId: "U1.CTRL to C2.pin1", + pins: [ + { + pinId: "U1.5", + x: -1.2000000000000002, + y: 0.10000000000000003, + chipId: "schematic_component_0", + _facingDirection: "x-", + }, + { + pinId: "C2.1", + x: -1.9000000000000004, + y: 0.10000000000000002, + chipId: "schematic_component_4", + _facingDirection: "x+", + }, + ], + tracePath: [ + { + x: -1.2000000000000002, + y: 0.10000000000000003, + }, + { + x: -1.9000000000000004, + y: 0.10000000000000002, + }, + ], + mspConnectionPairIds: ["U1.5-C2.1"], + pinIds: ["U1.5", "C2.1"], + }, + { + mspPairId: "U1.2-C1.1", + dcConnNetId: "connectivity_net1", + globalConnNetId: "connectivity_net1", + userNetId: "U1.THRES to U1.TRIG", + pins: [ + { + pinId: "U1.2", + x: -1.2000000000000002, + y: -0.30000000000000004, + chipId: "schematic_component_0", + _facingDirection: "x-", + }, + { + pinId: "C1.1", + x: -1.2000000000000002, + y: -1.1500000000000001, + chipId: "schematic_component_3", + _facingDirection: "y+", + }, + ], + tracePath: [ + { + x: -1.2000000000000002, + y: -0.30000000000000004, + }, + { + x: -1.4000000000000001, + y: -0.30000000000000004, + }, + { + x: -1.4000000000000001, + y: -0.7250000000000001, + }, + { + x: -1.2000000000000002, + y: -0.7250000000000001, + }, + { + x: -1.2000000000000002, + y: -1.1500000000000001, + }, + ], + mspConnectionPairIds: ["U1.2-C1.1"], + pinIds: ["U1.2", "C1.1"], + }, + { + mspPairId: "U1.6-U1.2", + dcConnNetId: "connectivity_net1", + globalConnNetId: "connectivity_net1", + userNetId: "U1.THRES to C1.pin1", + pins: [ + { + pinId: "U1.6", + x: -1.2000000000000002, + y: -0.09999999999999998, + chipId: "schematic_component_0", + _facingDirection: "x-", + }, + { + pinId: "U1.2", + x: -1.2000000000000002, + y: -0.30000000000000004, + chipId: "schematic_component_0", + _facingDirection: "x-", + }, + ], + tracePath: [ + { + x: -1.2000000000000002, + y: -0.09999999999999998, + }, + { + x: -1.4000000000000001, + y: -0.09999999999999998, + }, + { + x: -1.4000000000000001, + y: -0.30000000000000004, + }, + { + x: -1.2000000000000002, + y: -0.30000000000000004, + }, + ], + mspConnectionPairIds: ["U1.6-U1.2"], + pinIds: ["U1.6", "U1.2"], + }, + { + mspPairId: "R2.2-C1.1", + dcConnNetId: "connectivity_net1", + globalConnNetId: "connectivity_net1", + userNetId: "R2.pin2 to U1.THRES", + pins: [ + { + pinId: "R2.2", + x: 0.10000000000000009, + y: -1.2944553500000002, + chipId: "schematic_component_2", + _facingDirection: "x-", + }, + { + pinId: "C1.1", + x: -1.2000000000000002, + y: -1.1500000000000001, + chipId: "schematic_component_3", + _facingDirection: "y+", + }, + ], + tracePath: [ + { + x: 0.09999999999999987, + y: -1.2944553500000002, + }, + { + x: -0.55, + y: -1.2944553500000002, + }, + { + x: -0.55, + y: -0.9500000000000002, + }, + { + x: -1.2000000000000002, + y: -0.9500000000000002, + }, + { + x: -1.2000000000000002, + y: -1.1500000000000001, + }, + ], + mspConnectionPairIds: ["R2.2-C1.1"], + pinIds: ["R2.2", "C1.1"], + }, + { + mspPairId: "U1.7-R1.2", + dcConnNetId: "connectivity_net2", + globalConnNetId: "connectivity_net2", + userNetId: "U1.DISCH to R2.pin1", + pins: [ + { + pinId: "U1.7", + x: 1.2000000000000002, + y: -0.10000000000000003, + chipId: "schematic_component_0", + _facingDirection: "x+", + }, + { + pinId: "R1.2", + x: 1.9000000000000004, + y: -0.10000000000000002, + chipId: "schematic_component_1", + _facingDirection: "x-", + }, + ], + tracePath: [ + { + x: 1.2000000000000002, + y: -0.10000000000000003, + }, + { + x: 1.9000000000000004, + y: -0.10000000000000002, + }, + ], + mspConnectionPairIds: ["U1.7-R1.2"], + pinIds: ["U1.7", "R1.2"], + }, + { + mspPairId: "R2.1-U1.7", + dcConnNetId: "connectivity_net2", + globalConnNetId: "connectivity_net2", + userNetId: "U1.DISCH to R2.pin1", + pins: [ + { + pinId: "R2.1", + x: 1.2000000000000002, + y: -1.2944553500000002, + chipId: "schematic_component_2", + _facingDirection: "x+", + }, + { + pinId: "U1.7", + x: 1.2000000000000002, + y: -0.10000000000000003, + chipId: "schematic_component_0", + _facingDirection: "x+", + }, + ], + tracePath: [ + { + x: 1.2000000000000002, + y: -1.2944553500000002, + }, + { + x: 1.4000000000000001, + y: -1.2944553500000002, + }, + { + x: 1.4000000000000001, + y: -0.10000000000000003, + }, + { + x: 1.2000000000000002, + y: -0.10000000000000003, + }, + ], + mspConnectionPairIds: ["R2.1-U1.7"], + pinIds: ["R2.1", "U1.7"], + }, + { + mspPairId: "U1.3-R3.1", + dcConnNetId: "connectivity_net3", + globalConnNetId: "connectivity_net3", + userNetId: "U1.OUT to R3.pin1", + pins: [ + { + pinId: "U1.3", + x: 1.2000000000000002, + y: 0.09999999999999998, + chipId: "schematic_component_0", + _facingDirection: "x+", + }, + { + pinId: "R3.1", + x: 1.2000000000000002, + y: 1.1500000000000001, + chipId: "schematic_component_5", + _facingDirection: "y-", + }, + ], + tracePath: [ + { + x: 1.2000000000000002, + y: 0.09999999999999998, + }, + { + x: 1.4000000000000001, + y: 0.09999999999999998, + }, + { + x: 1.4000000000000001, + y: 0.625, + }, + { + x: 1.2000000000000002, + y: 0.625, + }, + { + x: 1.2000000000000002, + y: 1.1500000000000001, + }, + ], + mspConnectionPairIds: ["U1.3-R3.1"], + pinIds: ["U1.3", "R3.1"], + }, + { + mspPairId: "U1.8-R1.1", + dcConnNetId: "connectivity_net5", + globalConnNetId: "connectivity_net5", + userNetId: "VCC", + pins: [ + { + pinId: "U1.8", + x: 1.2000000000000002, + y: 0.30000000000000004, + chipId: "schematic_component_0", + _facingDirection: "x+", + }, + { + pinId: "R1.1", + x: 3, + y: -0.10000000000000016, + chipId: "schematic_component_1", + _facingDirection: "x+", + }, + ], + tracePath: [ + { + x: 1.2000000000000002, + y: 0.30000000000000004, + }, + { + x: 3.2, + y: 0.30000000000000004, + }, + { + x: 3.2, + y: -0.10000000000000016, + }, + { + x: 3, + y: -0.10000000000000016, + }, + ], + mspConnectionPairIds: ["U1.8-R1.1"], + pinIds: ["U1.8", "R1.1"], + }, + { + mspPairId: "C1.2-C2.2", + dcConnNetId: "connectivity_net4", + globalConnNetId: "connectivity_net4", + pins: [ + { + pinId: "C1.2", + x: -1.2000000000000002, + y: -2.25, + chipId: "schematic_component_3", + _facingDirection: "y-", + }, + { + pinId: "C2.2", + x: -3, + y: 0.10000000000000016, + chipId: "schematic_component_4", + _facingDirection: "x-", + }, + ], + tracePath: [ + { + x: -1.2000000000000002, + y: -2.25, + }, + { + x: -1.2000000000000002, + y: -2.45, + }, + { + x: -3.2, + y: -2.45, + }, + { + x: -3.2, + y: 0.10000000000000016, + }, + { + x: -3, + y: 0.10000000000000016, + }, + ], + mspConnectionPairIds: ["C1.2-C2.2"], + pinIds: ["C1.2", "C2.2"], + }, +] + +const inputNetLabelPlacements = [ + { + globalConnNetId: "merged-group-U1-x+", + netId: "GND", + mspConnectionPairIds: ["U1.3-R3.1"], + pinIds: ["U1.1", "U1.3", "R3.1"], + orientation: "x+", + anchorPoint: { + x: 1.2000000000000002, + y: -0.30000000000000004, + }, + width: 0.6490000000000002, + height: 0.6, + center: { + x: 1.5255, + y: -0.10000000000000003, + }, + }, + { + globalConnNetId: "connectivity_net4", + dcConnNetId: "connectivity_net4", + netId: "GND", + mspConnectionPairIds: ["C1.2-C2.2"], + pinIds: ["C1.2", "C2.2"], + orientation: "y-", + anchorPoint: { + x: -1.2000000000000002, + y: -2.45, + }, + width: 0.2, + height: 0.45, + center: { + x: -1.2000000000000002, + y: -2.6750000000000003, + }, + }, + { + globalConnNetId: "merged-group-U1-x-", + dcConnNetId: "connectivity_net1", + netId: "U1.THRES to U1.TRIG", + mspConnectionPairIds: ["U1.2-C1.1"], + pinIds: ["U1.2", "C1.1", "U1.4"], + orientation: "x-", + anchorPoint: { + x: -1.4000000000000001, + y: -0.30000000000000004, + }, + width: 0.6490000000000002, + height: 0.8, + center: { + x: -1.5255, + y: 0, + }, + }, + { + globalConnNetId: "merged-group-U1-y+", + dcConnNetId: "connectivity_net5", + netId: "VCC", + mspConnectionPairIds: ["U1.8-R1.1", "U1.5-C2.1"], + pinIds: ["U1.8", "R1.1", "U1.5", "C2.1"], + orientation: "y+", + anchorPoint: { + x: 2.2, + y: 0.30000000000000004, + }, + width: 3.9500000000000006, + height: 0.6499999999999999, + center: { + x: 0.32499999999999996, + y: 0.42500000000000004, + }, + }, + { + globalConnNetId: "connectivity_net2", + dcConnNetId: "connectivity_net2", + netId: "U1.DISCH to R2.pin1", + mspConnectionPairIds: ["R2.1-U1.7"], + pinIds: ["R2.1", "U1.7"], + orientation: "y-", + anchorPoint: { + x: 1.4000000000000001, + y: -1.2944553500000002, + }, + width: 0.2, + height: 0.45, + center: { + x: 1.4000000000000001, + y: -1.5194553500000003, + }, + }, +] + +const inputMergedLabelNetIdMap = { + "merged-group-U1-x+": new Set(["connectivity_net4", "connectivity_net3"]), + "merged-group-U1-x-": new Set(["connectivity_net1", "connectivity_net5"]), + "merged-group-U1-y+": new Set(["connectivity_net5", "connectivity_net0"]), +} + +test("OverlapAvoidanceStepSolver snapshot", () => { + const solver = new OverlapAvoidanceStepSolver({ + mergedLabelNetIdMap: inputMergedLabelNetIdMap as any, + netLabelPlacements: inputNetLabelPlacements as any, + problem: inputProblem as any, + traces: inputTraces as any, + }) + solver.solve() + expect(solver).toMatchSolverSnapshot(import.meta.path) +}) diff --git a/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/SingleOverlapSolver.test.ts b/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/SingleOverlapSolver.test.ts new file mode 100644 index 0000000..b8c443a --- /dev/null +++ b/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/SingleOverlapSolver.test.ts @@ -0,0 +1,285 @@ +import { expect } from "bun:test" +import { test } from "bun:test" +import { SingleOverlapSolver } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver" + +const input = { + trace: { + mspPairId: "U1.3-R3.1", + dcConnNetId: "connectivity_net3", + globalConnNetId: "connectivity_net3", + userNetId: "U1.OUT to R3.pin1", + pins: [ + { + pinId: "U1.3", + x: 1.2000000000000002, + y: 0.09999999999999998, + chipId: "schematic_component_0", + _facingDirection: "x+", + }, + { + pinId: "R3.1", + x: 1.2000000000000002, + y: 1.1500000000000001, + chipId: "schematic_component_5", + _facingDirection: "y-", + }, + ], + tracePath: [ + { + x: 1.2000000000000002, + y: 0.09999999999999998, + }, + { + x: 1.4000000000000001, + y: 0.09999999999999998, + }, + { + x: 1.4000000000000001, + y: 0.625, + }, + { + x: 1.2000000000000002, + y: 0.625, + }, + { + x: 1.2000000000000002, + y: 1.1500000000000001, + }, + ], + mspConnectionPairIds: ["U1.3-R3.1"], + pinIds: ["U1.3", "R3.1"], + }, + label: { + globalConnNetId: "merged-group-U1-y+", + dcConnNetId: "connectivity_net5", + netId: "VCC", + mspConnectionPairIds: ["U1.8-R1.1", "U1.5-C2.1"], + pinIds: ["U1.8", "R1.1", "U1.5", "C2.1"], + orientation: "y+", + anchorPoint: { + x: 2.2, + y: 0.30000000000000004, + }, + width: 3.9500000000000006, + height: 0.6499999999999999, + center: { + x: 0.32499999999999996, + y: 0.42500000000000004, + }, + }, + problem: { + chips: [ + { + chipId: "schematic_component_0", + center: { + x: 0, + y: 0, + }, + width: 2.4000000000000004, + height: 1, + pins: [ + { + pinId: "U1.1", + x: 1.2000000000000002, + y: -0.30000000000000004, + }, + { + pinId: "U1.2", + x: -1.2000000000000002, + y: -0.30000000000000004, + }, + { + pinId: "U1.3", + x: 1.2000000000000002, + y: 0.09999999999999998, + }, + { + pinId: "U1.4", + x: -1.2000000000000002, + y: 0.30000000000000004, + }, + { + pinId: "U1.5", + x: -1.2000000000000002, + y: 0.10000000000000003, + }, + { + pinId: "U1.6", + x: -1.2000000000000002, + y: -0.09999999999999998, + }, + { + pinId: "U1.7", + x: 1.2000000000000002, + y: -0.10000000000000003, + }, + { + pinId: "U1.8", + x: 1.2000000000000002, + y: 0.30000000000000004, + }, + ], + }, + { + chipId: "schematic_component_1", + center: { + x: 2.45, + y: -0.10000000000000009, + }, + width: 1.0999999999999996, + height: 0.388910699999999, + pins: [ + { + pinId: "R1.1", + x: 3, + y: -0.10000000000000016, + }, + { + pinId: "R1.2", + x: 1.9000000000000004, + y: -0.10000000000000002, + }, + ], + }, + { + chipId: "schematic_component_2", + center: { + x: 0.6500000000000001, + y: -1.2944553500000002, + }, + width: 1.1, + height: 0.388910699999999, + pins: [ + { + pinId: "R2.1", + x: 1.2000000000000002, + y: -1.2944553500000002, + }, + { + pinId: "R2.2", + x: 0.10000000000000009, + y: -1.2944553500000002, + }, + ], + }, + { + chipId: "schematic_component_3", + center: { + x: -1.2000000000000002, + y: -1.7000000000000002, + }, + width: 1.06, + height: 1.1, + pins: [ + { + pinId: "C1.1", + x: -1.2000000000000002, + y: -1.1500000000000001, + }, + { + pinId: "C1.2", + x: -1.2000000000000002, + y: -2.25, + }, + ], + }, + { + chipId: "schematic_component_4", + center: { + x: -2.45, + y: 0.10000000000000009, + }, + width: 1.0999999999999996, + height: 0.84, + pins: [ + { + pinId: "C2.1", + x: -1.9000000000000004, + y: 0.10000000000000002, + }, + { + pinId: "C2.2", + x: -3, + y: 0.10000000000000016, + }, + ], + }, + { + chipId: "schematic_component_5", + center: { + x: 1.2000000000000002, + y: 1.7000000000000002, + }, + width: 1.06, + height: 1.1, + pins: [ + { + pinId: "R3.1", + x: 1.2000000000000002, + y: 1.1500000000000001, + }, + { + pinId: "R3.2", + x: 1.2000000000000002, + y: 2.25, + }, + ], + }, + ], + directConnections: [ + { + pinIds: ["U1.5", "C2.1"], + netId: "U1.CTRL to C2.pin1", + }, + { + pinIds: ["U1.6", "U1.2"], + netId: "U1.THRES to U1.TRIG", + }, + { + pinIds: ["R1.2", "U1.7"], + netId: "R1.pin2 to U1.DISCH", + }, + { + pinIds: ["U1.7", "R2.1"], + netId: "U1.DISCH to R2.pin1", + }, + { + pinIds: ["R2.2", "U1.6"], + netId: "R2.pin2 to U1.THRES", + }, + { + pinIds: ["U1.6", "C1.1"], + netId: "U1.THRES to C1.pin1", + }, + { + pinIds: ["U1.3", "R3.1"], + netId: "U1.OUT to R3.pin1", + }, + ], + netConnections: [ + { + netId: "GND", + pinIds: ["U1.1", "C1.2", "C2.2"], + }, + { + netId: "VCC", + pinIds: ["U1.4", "U1.8", "R1.1"], + }, + ], + availableNetLabelOrientations: { + VCC: ["y+"], + GND: ["y-"], + }, + maxMspPairDistance: 2.4, + }, + paddingBuffer: 0.1, + detourCount: 0, +} as any + +test("SingleOverlapSolver snapshot", () => { + const solver = new SingleOverlapSolver({ + ...input, + }) + solver.solve() + expect(solver).toMatchSolverSnapshot(import.meta.path) +}) diff --git a/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/TraceCleanupSolver.test.ts b/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/TraceCleanupSolver.test.ts new file mode 100644 index 0000000..16dc792 --- /dev/null +++ b/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/TraceCleanupSolver.test.ts @@ -0,0 +1,288 @@ +import { expect } from "bun:test" +import { test } from "bun:test" +import { TraceCleanupSolver } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/TraceCleanupSolver" + +const input = { + problem: { + chips: [ + { + chipId: "schematic_component_0", + center: { + x: -3, + y: 0, + }, + width: 2.4000000000000004, + height: 1, + pins: [ + { + pinId: "U1.1", + x: -1.7999999999999998, + y: -0.30000000000000004, + }, + { + pinId: "U1.2", + x: -4.2, + y: -0.30000000000000004, + }, + { + pinId: "U1.3", + x: -1.7999999999999998, + y: 0.09999999999999998, + }, + { + pinId: "U1.4", + x: -4.2, + y: 0.30000000000000004, + }, + { + pinId: "U1.5", + x: -4.2, + y: 0.10000000000000003, + }, + { + pinId: "U1.6", + x: -4.2, + y: -0.09999999999999998, + }, + { + pinId: "U1.7", + x: -1.7999999999999998, + y: -0.10000000000000003, + }, + { + pinId: "U1.8", + x: -1.7999999999999998, + y: 0.30000000000000004, + }, + ], + }, + { + chipId: "schematic_component_1", + center: { + x: 3, + y: 0, + }, + width: 2.2, + height: 0.8, + pins: [ + { + pinId: "J1.1", + x: 1.9, + y: 0.2, + }, + { + pinId: "J1.2", + x: 1.9, + y: 0, + }, + { + pinId: "J1.3", + x: 1.9, + y: -0.2, + }, + ], + }, + { + chipId: "schematic_component_2", + center: { + x: 0, + y: -2, + }, + width: 1.2, + height: 3, + pins: [ + { + pinId: "U2.1", + x: -0.4, + y: -0.5, + }, + { + pinId: "U2.2", + x: 0.4, + y: -0.5, + }, + { + pinId: "U2.3", + x: -0.6, + y: -2.8, + }, + { + pinId: "U2.4", + x: 0.6, + y: -2.8, + }, + ], + }, + ], + directConnections: [ + { + pinIds: ["U1.1", "J1.3"], + }, + { + pinIds: ["U1.8", "J1.1"], + }, + ], + netConnections: [ + { + netId: "SIGNAL1", + pinIds: ["U2.2"], + }, + { + netId: "SIGNAL2", + pinIds: ["U2.1"], + }, + ], + availableNetLabelOrientations: {}, + maxMspPairDistance: 6.5, + }, + allTraces: [ + { + mspPairId: "U1.1-J1.3", + dcConnNetId: "connectivity_net0", + globalConnNetId: "connectivity_net0", + pins: [ + { + pinId: "U1.1", + x: -1.7999999999999998, + y: -0.30000000000000004, + _facingDirection: "x+", + chipId: "schematic_component_0", + }, + { + pinId: "J1.3", + x: 1.9, + y: -0.2, + chipId: "schematic_component_1", + _facingDirection: "x-", + }, + ], + tracePath: [ + { + x: -1.7999999999999998, + y: -0.30000000000000004, + }, + { + x: -0.6, + y: -0.30000000000000004, + }, + { + x: -0.6, + y: 0.05099999999999996, + }, + { + x: 0.6, + y: 0.05099999999999996, + }, + { + x: 0.6, + y: -0.2, + }, + { + x: 1.9, + y: -0.2, + }, + ], + mspConnectionPairIds: ["U1.1-J1.3"], + pinIds: ["U1.1", "J1.3"], + }, + { + mspPairId: "U1.8-J1.1", + dcConnNetId: "connectivity_net1", + globalConnNetId: "connectivity_net1", + pins: [ + { + pinId: "U1.8", + x: -1.7999999999999998, + y: 0.30000000000000004, + _facingDirection: "x+", + chipId: "schematic_component_0", + }, + { + pinId: "J1.1", + x: 1.9, + y: 0.2, + chipId: "schematic_component_1", + _facingDirection: "x-", + }, + ], + tracePath: [ + { + x: -1.7999999999999998, + y: 0.30000000000000004, + }, + { + x: -1.5999999999999999, + y: 0.30000000000000004, + }, + { + x: 0.050000000000000044, + y: 0.30000000000000004, + }, + { + x: 0.050000000000000044, + y: 0.2, + }, + { + x: 1.7, + y: 0.2, + }, + { + x: 1.9, + y: 0.2, + }, + ], + mspConnectionPairIds: ["U1.8-J1.1"], + pinIds: ["U1.8", "J1.1"], + }, + ], + targetTraceIds: new Set(["U1.1-J1.3"]), + allLabelPlacements: [ + { + globalConnNetId: "merged-group-U1-y+", + dcConnNetId: "connectivity_net0", + mspConnectionPairIds: ["U1.1-J1.3", "U1.8-J1.1"], + pinIds: ["U1.1", "J1.3", "U1.8", "J1.1"], + orientation: "y+", + anchorPoint: { + x: -1.5999999999999999, + y: -0.30000000000000004, + }, + width: 0.20000000000000018, + height: 1.05, + center: { + x: -1.5999999999999999, + y: 0.22499999999999998, + }, + }, + { + globalConnNetId: "merged-group-U2-y+", + netId: "SIGNAL2", + mspConnectionPairIds: [], + pinIds: ["U2.1", "U2.2"], + orientation: "y+", + anchorPoint: { + x: -0.4, + y: -0.5, + }, + width: 1, + height: 0.44999999999999996, + center: { + x: 0, + y: -0.274, + }, + }, + ], + mergedLabelNetIdMap: { + "merged-group-U1-y+": new Set(["connectivity_net0", "connectivity_net1"]), + "merged-group-U2-y+": new Set(["connectivity_net3", "connectivity_net2"]), + }, + paddingBuffer: 0.01, +} as any + +test("SingleOverlapSolver snapshot", () => { + const solver = new TraceCleanupSolver({ + ...input, + }) + solver.solve() + expect(solver).toMatchSolverSnapshot(import.meta.path) +}) diff --git a/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/MergedNetLabelObstacles.snap.svg b/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/MergedNetLabelObstacles.snap.svg new file mode 100644 index 0000000..6860888 --- /dev/null +++ b/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/MergedNetLabelObstacles.snap.svg @@ -0,0 +1,240 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/OverlapAvoidanceStepSolver.snap.svg b/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/OverlapAvoidanceStepSolver.snap.svg new file mode 100644 index 0000000..e86c89d --- /dev/null +++ b/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/OverlapAvoidanceStepSolver.snap.svg @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/SingleOverlapSolver.snap.svg b/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/SingleOverlapSolver.snap.svg new file mode 100644 index 0000000..952fcab --- /dev/null +++ b/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/SingleOverlapSolver.snap.svg @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/TraceCleanupSolver.snap.svg b/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/TraceCleanupSolver.snap.svg new file mode 100644 index 0000000..0ea3373 --- /dev/null +++ b/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/TraceCleanupSolver.snap.svg @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file