diff --git a/.github/workflows/bun-pver-release.yml b/.github/workflows/bun-pver-release.yml index 86996fe6..61d9a59e 100644 --- a/.github/workflows/bun-pver-release.yml +++ b/.github/workflows/bun-pver-release.yml @@ -5,7 +5,7 @@ on: branches: - main paths: - - 'lib/**' + - "lib/**" workflow_dispatch: jobs: publish: diff --git a/README.md b/README.md index 34205b7a..1f4e7c9e 100644 --- a/README.md +++ b/README.md @@ -38,10 +38,10 @@ If there is crowding at the pin, we look for an available spot along the trace c ## Usage ```tsx -import { SchematicTracePipelineSolver } from "@tscircuit/schematic-trace-solver" +import { SchematicTracePipelineSolver } from "@tscircuit/schematic-trace-solver"; -type ChipId = string -type PinId = string +type ChipId = string; +type PinId = string; const solver = new SchematicTracePipelineSolver({ chips: { @@ -77,9 +77,9 @@ const solver = new SchematicTracePipelineSolver({ VCC: ["y+", "y-"], GND: ["y+", "y-"], }, -}) +}); -solver.solve() +solver.solve(); ``` ## Development diff --git a/cosmos.decorator.tsx b/cosmos.decorator.tsx index 9f9c38db..51195876 100644 --- a/cosmos.decorator.tsx +++ b/cosmos.decorator.tsx @@ -1,21 +1,21 @@ -import React, { useEffect } from "react" +import React, { useEffect } from "react"; export const TailwindDecorator = ({ children, }: { - children: React.ReactNode + children: React.ReactNode; }) => { useEffect(() => { - const script = document.createElement("script") - script.src = "https://cdn.tailwindcss.com" - document.head.appendChild(script) + const script = document.createElement("script"); + script.src = "https://cdn.tailwindcss.com"; + document.head.appendChild(script); return () => { - document.head.removeChild(script) - } - }, []) + document.head.removeChild(script); + }; + }, []); - return <>{children} -} + return <>{children}; +}; -export default TailwindDecorator +export default TailwindDecorator; diff --git a/index.html b/index.html index 07ba55a0..e7c6ddf0 100644 --- a/index.html +++ b/index.html @@ -1,13 +1,13 @@ - + Schematic Trace Solver diff --git a/lib/data-structures/ChipObstacleSpatialIndex.ts b/lib/data-structures/ChipObstacleSpatialIndex.ts index c9132ac3..f88adb75 100644 --- a/lib/data-structures/ChipObstacleSpatialIndex.ts +++ b/lib/data-structures/ChipObstacleSpatialIndex.ts @@ -1,27 +1,27 @@ -import type { InputChip } from "lib/types/InputProblem" -import type { Bounds, Point } from "@tscircuit/math-utils" -import Flatbush from "flatbush" -import { getInputChipBounds } from "lib/solvers/GuidelinesSolver/getInputChipBounds" +import type { InputChip } from "lib/types/InputProblem"; +import type { Bounds, Point } from "@tscircuit/math-utils"; +import Flatbush from "flatbush"; +import { getInputChipBounds } from "lib/solvers/GuidelinesSolver/getInputChipBounds"; export interface SpatiallyIndexedChip extends InputChip { - bounds: Bounds - spatialIndexId: number + bounds: Bounds; + spatialIndexId: number; } export class ChipObstacleSpatialIndex { - chips: Array - spatialIndex: Flatbush - spatialIndexIdToChip: Map + chips: Array; + spatialIndex: Flatbush; + spatialIndexIdToChip: Map; constructor(chips: InputChip[]) { this.chips = chips.map((chip) => ({ ...chip, bounds: getInputChipBounds(chip), spatialIndexId: null as any, - })) + })); - this.spatialIndexIdToChip = new Map() - this.spatialIndex = new Flatbush(chips.length) + this.spatialIndexIdToChip = new Map(); + this.spatialIndex = new Flatbush(chips.length); for (const chip of this.chips) { chip.spatialIndexId = this.spatialIndex.add( @@ -29,11 +29,11 @@ export class ChipObstacleSpatialIndex { chip.bounds.minY, chip.bounds.maxX, chip.bounds.maxY, - ) - this.spatialIndexIdToChip.set(chip.spatialIndexId, chip) + ); + this.spatialIndexIdToChip.set(chip.spatialIndexId, chip); } - this.spatialIndex.finish() + this.spatialIndex.finish(); } getChipsInBounds(bounds: Bounds): Array { @@ -42,29 +42,29 @@ export class ChipObstacleSpatialIndex { bounds.minY, bounds.maxX, bounds.maxY, - ) + ); - return chipSpatialIndexIds.map((id) => this.spatialIndexIdToChip.get(id)!) + return chipSpatialIndexIds.map((id) => this.spatialIndexIdToChip.get(id)!); } doesOrthogonalLineIntersectChip( line: [Point, Point], opts: { - excludeChipIds?: string[] + excludeChipIds?: string[]; } = {}, ): boolean { - const excludeChipIds = opts.excludeChipIds ?? [] - const [p1, p2] = line - const { x: x1, y: y1 } = p1 - const { x: x2, y: y2 } = p2 + const excludeChipIds = opts.excludeChipIds ?? []; + const [p1, p2] = line; + const { x: x1, y: y1 } = p1; + const { x: x2, y: y2 } = p2; const chips = this.getChipsInBounds({ minX: Math.min(x1, x2), minY: Math.min(y1, y2), maxX: Math.max(x1, x2), maxY: Math.max(y1, y2), - }).filter((chip) => !excludeChipIds.includes(chip.chipId)) + }).filter((chip) => !excludeChipIds.includes(chip.chipId)); - return chips.length > 0 + return chips.length > 0; } } diff --git a/lib/index.ts b/lib/index.ts index 3985b32a..a5a33ddb 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -1,3 +1,3 @@ -export * from "./solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver" -export * from "./types/InputProblem" -export { SchematicTraceSingleLineSolver2 } from "./solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2" +export * from "./solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"; +export * from "./types/InputProblem"; +export { SchematicTraceSingleLineSolver2 } from "./solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2"; diff --git a/lib/solvers/BaseSolver/BaseSolver.ts b/lib/solvers/BaseSolver/BaseSolver.ts index 88ae14f9..9ed5189c 100644 --- a/lib/solvers/BaseSolver/BaseSolver.ts +++ b/lib/solvers/BaseSolver/BaseSolver.ts @@ -1,55 +1,55 @@ -import type { GraphicsObject } from "graphics-debug" +import type { GraphicsObject } from "graphics-debug"; export class BaseSolver { - MAX_ITERATIONS = 100e3 - solved = false - failed = false - iterations = 0 - progress = 0 - error: string | null = null - activeSubSolver?: BaseSolver | null - failedSubSolvers?: BaseSolver[] - timeToSolve?: number - stats: Record = {} + MAX_ITERATIONS = 100e3; + solved = false; + failed = false; + iterations = 0; + progress = 0; + error: string | null = null; + activeSubSolver?: BaseSolver | null; + failedSubSolvers?: BaseSolver[]; + timeToSolve?: number; + stats: Record = {}; /** DO NOT OVERRIDE! Override _step() instead */ step() { - if (this.solved) return - if (this.failed) return - this.iterations++ + if (this.solved) return; + if (this.failed) return; + this.iterations++; try { - this._step() + this._step(); } catch (e) { - this.error = `${this.constructor.name} error: ${e}` - this.failed = true - throw e + this.error = `${this.constructor.name} error: ${e}`; + this.failed = true; + throw e; } if (!this.solved && this.iterations > this.MAX_ITERATIONS) { - this.tryFinalAcceptance() + this.tryFinalAcceptance(); } if (!this.solved && this.iterations > this.MAX_ITERATIONS) { - this.error = `${this.constructor.name} ran out of iterations` - this.failed = true + this.error = `${this.constructor.name} ran out of iterations`; + this.failed = true; } if ("computeProgress" in this) { // @ts-ignore - this.progress = this.computeProgress() as number + this.progress = this.computeProgress() as number; } } _step() {} getConstructorParams() { - throw new Error("getConstructorParams not implemented") + throw new Error("getConstructorParams not implemented"); } solve() { - const startTime = Date.now() + const startTime = Date.now(); while (!this.solved && !this.failed) { - this.step() + this.step(); } - const endTime = Date.now() - this.timeToSolve = endTime - startTime + const endTime = Date.now(); + this.timeToSolve = endTime - startTime; } visualize(): GraphicsObject { @@ -58,7 +58,7 @@ export class BaseSolver { points: [], rects: [], circles: [], - } + }; } /** @@ -78,6 +78,6 @@ export class BaseSolver { points: [], rects: [], circles: [], - } + }; } } diff --git a/lib/solvers/GuidelinesSolver/GuidelinesSolver.ts b/lib/solvers/GuidelinesSolver/GuidelinesSolver.ts index 37177f1f..b5a696c8 100644 --- a/lib/solvers/GuidelinesSolver/GuidelinesSolver.ts +++ b/lib/solvers/GuidelinesSolver/GuidelinesSolver.ts @@ -1,42 +1,40 @@ -import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver" -import type { InputChip, InputProblem } from "lib/types/InputProblem" -import { visualizeInputProblem } from "../SchematicTracePipelineSolver/visualizeInputProblem" -import { getBounds, type GraphicsObject } from "graphics-debug" -import { getGeneratorForAllChipPairs } from "./getGeneratorForAllChipPairs" -import { getInputChipBounds } from "./getInputChipBounds" -import { getHorizontalGuidelineY } from "./getHorizontalGuidelineY" -import { getVerticalGuidelineX } from "./getVerticalGuidelineX" -import { getInputProblemBounds } from "./getInputProblemBounds" +import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"; +import type { InputChip, InputProblem } from "lib/types/InputProblem"; +import { visualizeInputProblem } from "../SchematicTracePipelineSolver/visualizeInputProblem"; +import { getBounds, type GraphicsObject } from "graphics-debug"; +import { getGeneratorForAllChipPairs } from "./getGeneratorForAllChipPairs"; +import { getInputChipBounds } from "./getInputChipBounds"; +import { getHorizontalGuidelineY } from "./getHorizontalGuidelineY"; +import { getVerticalGuidelineX } from "./getVerticalGuidelineX"; +import { getInputProblemBounds } from "./getInputProblemBounds"; export type Guideline = | { - orientation: "horizontal" - y: number - x: undefined + orientation: "horizontal"; + y: number; + x: undefined; } | { - orientation: "vertical" - y: undefined - x: number - } + orientation: "vertical"; + y: undefined; + x: number; + }; -const GUIDELINE_PADDING_FROM_PROBLEM_BOUNDS = 0.1 +const GUIDELINE_PADDING_FROM_PROBLEM_BOUNDS = 0.1; export class GuidelinesSolver extends BaseSolver { - inputProblem: InputProblem - guidelines: Guideline[] + inputProblem: InputProblem; + guidelines: Guideline[]; - chipPairsGenerator: Generator + chipPairsGenerator: Generator; - usedXGuidelines: Set - usedYGuidelines: Set + usedXGuidelines: Set; + usedYGuidelines: Set; - constructor(params: { - inputProblem: InputProblem - }) { - super() - this.inputProblem = params.inputProblem - const inputProblemBounds = getInputProblemBounds(this.inputProblem) + constructor(params: { inputProblem: InputProblem }) { + super(); + this.inputProblem = params.inputProblem; + const inputProblemBounds = getInputProblemBounds(this.inputProblem); this.guidelines = [ { orientation: "horizontal", @@ -58,13 +56,13 @@ export class GuidelinesSolver extends BaseSolver { y: undefined, x: inputProblemBounds.maxX + GUIDELINE_PADDING_FROM_PROBLEM_BOUNDS, }, - ] + ]; this.chipPairsGenerator = getGeneratorForAllChipPairs( this.inputProblem.chips, - ) + ); - this.usedXGuidelines = new Set() - this.usedYGuidelines = new Set() + this.usedXGuidelines = new Set(); + this.usedYGuidelines = new Set(); } override getConstructorParams(): ConstructorParameters< @@ -72,56 +70,56 @@ export class GuidelinesSolver extends BaseSolver { >[0] { return { inputProblem: this.inputProblem, - } + }; } override _step() { - const { done, value: chipPair } = this.chipPairsGenerator.next() + const { done, value: chipPair } = this.chipPairsGenerator.next(); if (done) { - this.solved = true - return + this.solved = true; + return; } - const [chip1, chip2] = chipPair + const [chip1, chip2] = chipPair; - const chip1Bounds = getInputChipBounds(chip1) - const chip2Bounds = getInputChipBounds(chip2) + const chip1Bounds = getInputChipBounds(chip1); + const chip2Bounds = getInputChipBounds(chip2); const horizontalGuidelineY = getHorizontalGuidelineY( chip1Bounds, chip2Bounds, - ) - const verticalGuidelineX = getVerticalGuidelineX(chip1Bounds, chip2Bounds) + ); + const verticalGuidelineX = getVerticalGuidelineX(chip1Bounds, chip2Bounds); if (!this.usedYGuidelines.has(horizontalGuidelineY)) { - this.usedYGuidelines.add(horizontalGuidelineY) + this.usedYGuidelines.add(horizontalGuidelineY); this.guidelines.push({ orientation: "horizontal", y: horizontalGuidelineY, x: undefined, - }) + }); } if (!this.usedXGuidelines.has(verticalGuidelineX)) { - this.usedXGuidelines.add(verticalGuidelineX) + this.usedXGuidelines.add(verticalGuidelineX); this.guidelines.push({ orientation: "vertical", y: undefined, x: verticalGuidelineX, - }) + }); } } override visualize(): GraphicsObject { - const graphics = visualizeInputProblem(this.inputProblem) + const graphics = visualizeInputProblem(this.inputProblem); - const bounds = getBounds(graphics) - const boundsWidth = bounds.maxX - bounds.minX - const boundsHeight = bounds.maxY - bounds.minY - bounds.minX -= boundsWidth * 0.3 - bounds.maxX += boundsWidth * 0.3 - bounds.minY -= boundsHeight * 0.3 - bounds.maxY += boundsHeight * 0.3 + const bounds = getBounds(graphics); + const boundsWidth = bounds.maxX - bounds.minX; + const boundsHeight = bounds.maxY - bounds.minY; + bounds.minX -= boundsWidth * 0.3; + bounds.maxX += boundsWidth * 0.3; + bounds.minY -= boundsHeight * 0.3; + bounds.maxY += boundsHeight * 0.3; for (const guideline of this.guidelines) { if (guideline.orientation === "horizontal") { @@ -138,7 +136,7 @@ export class GuidelinesSolver extends BaseSolver { ], strokeColor: "rgba(0, 0, 0, 0.5)", strokeDash: "2 2", - }) + }); } if (guideline.orientation === "vertical") { @@ -155,9 +153,9 @@ export class GuidelinesSolver extends BaseSolver { ], strokeColor: "rgba(0, 0, 0, 0.5)", strokeDash: "2 2", - }) + }); } } - return graphics + return graphics; } } diff --git a/lib/solvers/GuidelinesSolver/getGeneratorForAllChipPairs.ts b/lib/solvers/GuidelinesSolver/getGeneratorForAllChipPairs.ts index a5a6d9aa..4ae5852a 100644 --- a/lib/solvers/GuidelinesSolver/getGeneratorForAllChipPairs.ts +++ b/lib/solvers/GuidelinesSolver/getGeneratorForAllChipPairs.ts @@ -1,4 +1,4 @@ -import type { InputChip, InputProblem } from "lib/types/InputProblem" +import type { InputChip, InputProblem } from "lib/types/InputProblem"; /** * Creates a generator function that yields all possible pairs of chips from the given array. @@ -31,9 +31,9 @@ export const getGeneratorForAllChipPairs = ( for (let i = 0; i < chips.length; i++) { for (let j = 0; j < chips.length; j++) { if (i !== j) { - yield [chips[i], chips[j]] as const + yield [chips[i], chips[j]] as const; } } } - })() -} + })(); +}; diff --git a/lib/solvers/GuidelinesSolver/getHorizontalGuidelineY.ts b/lib/solvers/GuidelinesSolver/getHorizontalGuidelineY.ts index 0a7064e5..11b9263b 100644 --- a/lib/solvers/GuidelinesSolver/getHorizontalGuidelineY.ts +++ b/lib/solvers/GuidelinesSolver/getHorizontalGuidelineY.ts @@ -1,18 +1,18 @@ -import type { ChipBounds } from "./getInputChipBounds" +import type { ChipBounds } from "./getInputChipBounds"; export function getHorizontalGuidelineY( chip1Bounds: ChipBounds, chip2Bounds: ChipBounds, ): number { if (chip1Bounds.maxY <= chip2Bounds.minY) { - return (chip1Bounds.maxY + chip2Bounds.minY) / 2 + return (chip1Bounds.maxY + chip2Bounds.minY) / 2; } if (chip2Bounds.maxY <= chip1Bounds.minY) { - return (chip2Bounds.maxY + chip1Bounds.minY) / 2 + return (chip2Bounds.maxY + chip1Bounds.minY) / 2; } - const overlapMinY = Math.max(chip1Bounds.minY, chip2Bounds.minY) - const overlapMaxY = Math.min(chip1Bounds.maxY, chip2Bounds.maxY) - return (overlapMinY + overlapMaxY) / 2 + const overlapMinY = Math.max(chip1Bounds.minY, chip2Bounds.minY); + const overlapMaxY = Math.min(chip1Bounds.maxY, chip2Bounds.maxY); + return (overlapMinY + overlapMaxY) / 2; } diff --git a/lib/solvers/GuidelinesSolver/getInputChipBounds.ts b/lib/solvers/GuidelinesSolver/getInputChipBounds.ts index 2de67a36..c28c3fed 100644 --- a/lib/solvers/GuidelinesSolver/getInputChipBounds.ts +++ b/lib/solvers/GuidelinesSolver/getInputChipBounds.ts @@ -1,20 +1,20 @@ -import type { InputChip } from "lib/types/InputProblem" +import type { InputChip } from "lib/types/InputProblem"; export interface ChipBounds { - minX: number - maxX: number - minY: number - maxY: number + minX: number; + maxX: number; + minY: number; + maxY: number; } export function getInputChipBounds(chip: InputChip): ChipBounds { - const halfWidth = chip.width / 2 - const halfHeight = chip.height / 2 + const halfWidth = chip.width / 2; + const halfHeight = chip.height / 2; return { minX: chip.center.x - halfWidth, maxX: chip.center.x + halfWidth, minY: chip.center.y - halfHeight, maxY: chip.center.y + halfHeight, - } + }; } diff --git a/lib/solvers/GuidelinesSolver/getInputProblemBounds.ts b/lib/solvers/GuidelinesSolver/getInputProblemBounds.ts index 97bf3e7f..4a629b14 100644 --- a/lib/solvers/GuidelinesSolver/getInputProblemBounds.ts +++ b/lib/solvers/GuidelinesSolver/getInputProblemBounds.ts @@ -1,5 +1,5 @@ -import type { InputProblem } from "lib/types/InputProblem" -import { getInputChipBounds } from "./getInputChipBounds" +import type { InputProblem } from "lib/types/InputProblem"; +import { getInputChipBounds } from "./getInputChipBounds"; export const getInputProblemBounds = (inputProblem: InputProblem) => { const bounds = { @@ -7,13 +7,13 @@ export const getInputProblemBounds = (inputProblem: InputProblem) => { maxX: -Infinity, minY: Infinity, maxY: -Infinity, - } + }; for (const chip of inputProblem.chips) { - const chipBounds = getInputChipBounds(chip) - bounds.minX = Math.min(bounds.minX, chipBounds.minX) - bounds.maxX = Math.max(bounds.maxX, chipBounds.maxX) - bounds.minY = Math.min(bounds.minY, chipBounds.minY) - bounds.maxY = Math.max(bounds.maxY, chipBounds.maxY) + const chipBounds = getInputChipBounds(chip); + bounds.minX = Math.min(bounds.minX, chipBounds.minX); + bounds.maxX = Math.max(bounds.maxX, chipBounds.maxX); + bounds.minY = Math.min(bounds.minY, chipBounds.minY); + bounds.maxY = Math.max(bounds.maxY, chipBounds.maxY); } - return bounds -} + return bounds; +}; diff --git a/lib/solvers/GuidelinesSolver/getVerticalGuidelineX.ts b/lib/solvers/GuidelinesSolver/getVerticalGuidelineX.ts index a2f6bd14..cf2628fa 100644 --- a/lib/solvers/GuidelinesSolver/getVerticalGuidelineX.ts +++ b/lib/solvers/GuidelinesSolver/getVerticalGuidelineX.ts @@ -1,18 +1,18 @@ -import type { ChipBounds } from "./getInputChipBounds" +import type { ChipBounds } from "./getInputChipBounds"; export function getVerticalGuidelineX( chip1Bounds: ChipBounds, chip2Bounds: ChipBounds, ): number { if (chip1Bounds.maxX <= chip2Bounds.minX) { - return (chip1Bounds.maxX + chip2Bounds.minX) / 2 + return (chip1Bounds.maxX + chip2Bounds.minX) / 2; } if (chip2Bounds.maxX <= chip1Bounds.minX) { - return (chip2Bounds.maxX + chip1Bounds.minX) / 2 + return (chip2Bounds.maxX + chip1Bounds.minX) / 2; } - const overlapMinX = Math.max(chip1Bounds.minX, chip2Bounds.minX) - const overlapMaxX = Math.min(chip1Bounds.maxX, chip2Bounds.maxX) - return (overlapMinX + overlapMaxX) / 2 + const overlapMinX = Math.max(chip1Bounds.minX, chip2Bounds.minX); + const overlapMaxX = Math.min(chip1Bounds.maxX, chip2Bounds.maxX); + return (overlapMinX + overlapMaxX) / 2; } diff --git a/lib/solvers/GuidelinesSolver/visualizeGuidelines.ts b/lib/solvers/GuidelinesSolver/visualizeGuidelines.ts index add5ca10..438a83c5 100644 --- a/lib/solvers/GuidelinesSolver/visualizeGuidelines.ts +++ b/lib/solvers/GuidelinesSolver/visualizeGuidelines.ts @@ -1,21 +1,21 @@ -import type { GraphicsObject } from "graphics-debug" -import type { Guideline } from "./GuidelinesSolver" -import { getBounds } from "graphics-debug" +import type { GraphicsObject } from "graphics-debug"; +import type { Guideline } from "./GuidelinesSolver"; +import { getBounds } from "graphics-debug"; export const visualizeGuidelines = ({ guidelines, graphics, }: { - guidelines: Guideline[] - graphics: GraphicsObject + guidelines: Guideline[]; + graphics: GraphicsObject; }) => { - const globalBounds = getBounds(graphics) - const boundsWidth = globalBounds.maxX - globalBounds.minX - const boundsHeight = globalBounds.maxY - globalBounds.minY - globalBounds.minX -= boundsWidth * 0.3 - globalBounds.maxX += boundsWidth * 0.3 - globalBounds.minY -= boundsHeight * 0.3 - globalBounds.maxY += boundsHeight * 0.3 + const globalBounds = getBounds(graphics); + const boundsWidth = globalBounds.maxX - globalBounds.minX; + const boundsHeight = globalBounds.maxY - globalBounds.minY; + globalBounds.minX -= boundsWidth * 0.3; + globalBounds.maxX += boundsWidth * 0.3; + globalBounds.minY -= boundsHeight * 0.3; + globalBounds.maxY += boundsHeight * 0.3; for (const guideline of guidelines) { if (guideline.orientation === "horizontal") { @@ -26,7 +26,7 @@ export const visualizeGuidelines = ({ ], strokeColor: "rgba(0, 0, 0, 0.5)", strokeDash: "2 2", - }) + }); } if (guideline.orientation === "vertical") { @@ -37,9 +37,9 @@ export const visualizeGuidelines = ({ ], strokeColor: "rgba(0, 0, 0, 0.5)", strokeDash: "2 2", - }) + }); } } - return graphics -} + return graphics; +}; diff --git a/lib/solvers/LongDistancePairSolver/LongDistancePairSolver.ts b/lib/solvers/LongDistancePairSolver/LongDistancePairSolver.ts index 31e0df39..0e551a9a 100644 --- a/lib/solvers/LongDistancePairSolver/LongDistancePairSolver.ts +++ b/lib/solvers/LongDistancePairSolver/LongDistancePairSolver.ts @@ -1,142 +1,144 @@ -import { getConnectivityMapsFromInputProblem } from "lib/solvers/MspConnectionPairSolver/getConnectivityMapFromInputProblem" -import type { MspConnectionPair } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver" +import { getConnectivityMapsFromInputProblem } from "lib/solvers/MspConnectionPairSolver/getConnectivityMapFromInputProblem"; +import type { MspConnectionPair } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver"; import type { InputProblem, InputPin, PinId, InputChip, -} from "lib/types/InputProblem" -import { BaseSolver } from "../BaseSolver/BaseSolver" -import { SchematicTraceSingleLineSolver2 } from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2" -import { doesTraceOverlapWithExistingTraces } from "lib/utils/does-trace-overlap-with-existing-traces" -import { visualizeInputProblem } from "../SchematicTracePipelineSolver/visualizeInputProblem" -import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver" -import type { ConnectivityMap } from "connectivity-map" +} from "lib/types/InputProblem"; +import { BaseSolver } from "../BaseSolver/BaseSolver"; +import { SchematicTraceSingleLineSolver2 } from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2"; +import { doesTraceOverlapWithExistingTraces } from "lib/utils/does-trace-overlap-with-existing-traces"; +import { visualizeInputProblem } from "../SchematicTracePipelineSolver/visualizeInputProblem"; +import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver"; +import type { ConnectivityMap } from "connectivity-map"; -const NEAREST_NEIGHBOR_COUNT = 3 +const NEAREST_NEIGHBOR_COUNT = 3; const distance = (p1: InputPin, p2: InputPin) => { - return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)) -} + return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)); +}; export class LongDistancePairSolver extends BaseSolver { - public solvedLongDistanceTraces: SolvedTracePath[] = [] + public solvedLongDistanceTraces: SolvedTracePath[] = []; private queuedCandidatePairs: Array< [InputPin & { chipId: string }, InputPin & { chipId: string }] - > = [] + > = []; private currentCandidatePair: | [InputPin & { chipId: string }, InputPin & { chipId: string }] - | null = null - private subSolver: SchematicTraceSingleLineSolver2 | null = null - private chipMap: Record = {} - private inputProblem: InputProblem - private netConnMap: ConnectivityMap - private newlyConnectedPinIds = new Set() - private allSolvedTraces: SolvedTracePath[] = [] + | null = null; + private subSolver: SchematicTraceSingleLineSolver2 | null = null; + private chipMap: Record = {}; + private inputProblem: InputProblem; + private netConnMap: ConnectivityMap; + private newlyConnectedPinIds = new Set(); + private allSolvedTraces: SolvedTracePath[] = []; constructor( private params: { - inputProblem: InputProblem - alreadySolvedTraces: SolvedTracePath[] - primaryMspConnectionPairs: MspConnectionPair[] + inputProblem: InputProblem; + alreadySolvedTraces: SolvedTracePath[]; + primaryMspConnectionPairs: MspConnectionPair[]; }, ) { - super() + super(); const { inputProblem, primaryMspConnectionPairs, alreadySolvedTraces } = - this.params + this.params; - this.inputProblem = inputProblem - this.allSolvedTraces = [...alreadySolvedTraces] + this.inputProblem = inputProblem; + this.allSolvedTraces = [...alreadySolvedTraces]; // 1. Create initial maps and sets for efficient lookup - const primaryConnectedPinIds = new Set() + const primaryConnectedPinIds = new Set(); for (const pair of primaryMspConnectionPairs) { - primaryConnectedPinIds.add(pair.pins[0].pinId) - primaryConnectedPinIds.add(pair.pins[1].pinId) + primaryConnectedPinIds.add(pair.pins[0].pinId); + primaryConnectedPinIds.add(pair.pins[1].pinId); } - const { netConnMap } = getConnectivityMapsFromInputProblem(inputProblem) - this.netConnMap = netConnMap - const pinMap = new Map() + const { netConnMap } = getConnectivityMapsFromInputProblem(inputProblem); + this.netConnMap = netConnMap; + const pinMap = new Map(); for (const chip of inputProblem.chips) { - this.chipMap[chip.chipId] = chip + this.chipMap[chip.chipId] = chip; for (const pin of chip.pins) { - pinMap.set(pin.pinId, { ...pin, chipId: chip.chipId }) + pinMap.set(pin.pinId, { ...pin, chipId: chip.chipId }); } } // 2. Generate candidate pairs using N-Nearest-Neighbors approach const candidatePairs: Array< [InputPin & { chipId: string }, InputPin & { chipId: string }] - > = [] - const addedPairKeys = new Set() + > = []; + const addedPairKeys = new Set(); for (const netId of Object.keys(netConnMap.netMap)) { - const allPinIdsInNet = netConnMap.getIdsConnectedToNet(netId) - if (allPinIdsInNet.length < 2) continue + const allPinIdsInNet = netConnMap.getIdsConnectedToNet(netId); + if (allPinIdsInNet.length < 2) continue; const unconnectedPinIds = allPinIdsInNet.filter( (pinId) => !primaryConnectedPinIds.has(pinId), - ) + ); for (const unconnectedPinId of unconnectedPinIds) { - const sourcePin = pinMap.get(unconnectedPinId) - if (!sourcePin) continue + const sourcePin = pinMap.get(unconnectedPinId); + if (!sourcePin) continue; const neighbors = allPinIdsInNet .filter((otherPinId) => otherPinId !== unconnectedPinId) .flatMap((otherPinId) => { - const targetPin = pinMap.get(otherPinId) - if (!targetPin) return [] // Gracefully handle missing pins + const targetPin = pinMap.get(otherPinId); + if (!targetPin) return []; // Gracefully handle missing pins return [ { pin: targetPin, distance: distance(sourcePin, targetPin), }, - ] + ]; }) .sort((a, b) => a.distance - b.distance) - .slice(0, NEAREST_NEIGHBOR_COUNT) + .slice(0, NEAREST_NEIGHBOR_COUNT); for (const neighbor of neighbors) { const pair: [ InputPin & { chipId: string }, InputPin & { chipId: string }, - ] = [sourcePin, neighbor.pin] + ] = [sourcePin, neighbor.pin]; const pairKey = pair .map((p) => p.pinId) .sort() - .join("--") + .join("--"); if (!addedPairKeys.has(pairKey)) { - candidatePairs.push(pair) - addedPairKeys.add(pairKey) + candidatePairs.push(pair); + addedPairKeys.add(pairKey); } } } } - this.queuedCandidatePairs = candidatePairs + this.queuedCandidatePairs = candidatePairs; } override getConstructorParams() { - return this.params + return this.params; } override _step() { // 1. Check if a sub-solver has finished and process its result if (this.subSolver?.solved) { - const newTracePath = this.subSolver.solvedTracePath + const newTracePath = this.subSolver.solvedTracePath; if (newTracePath && this.currentCandidatePair) { const isTraceClear = !doesTraceOverlapWithExistingTraces( newTracePath, this.allSolvedTraces, - ) + ); if (isTraceClear) { - const [p1, p2] = this.currentCandidatePair - const globalConnNetId = this.netConnMap.getNetConnectedToId(p1.pinId)! - const mspPairId = `${p1.pinId}-${p2.pinId}` + const [p1, p2] = this.currentCandidatePair; + const globalConnNetId = this.netConnMap.getNetConnectedToId( + p1.pinId, + )!; + const mspPairId = `${p1.pinId}-${p2.pinId}`; const newSolvedTrace: SolvedTracePath = { mspPairId, @@ -146,66 +148,66 @@ export class LongDistancePairSolver extends BaseSolver { tracePath: newTracePath, mspConnectionPairIds: [mspPairId], pinIds: [p1.pinId, p2.pinId], - } + }; - this.solvedLongDistanceTraces.push(newSolvedTrace) - this.allSolvedTraces.push(newSolvedTrace) + this.solvedLongDistanceTraces.push(newSolvedTrace); + this.allSolvedTraces.push(newSolvedTrace); - this.newlyConnectedPinIds.add(p1.pinId) - this.newlyConnectedPinIds.add(p2.pinId) + this.newlyConnectedPinIds.add(p1.pinId); + this.newlyConnectedPinIds.add(p2.pinId); } } - this.subSolver = null - this.currentCandidatePair = null + this.subSolver = null; + this.currentCandidatePair = null; } else if (this.subSolver?.failed) { - this.subSolver = null - this.currentCandidatePair = null + this.subSolver = null; + this.currentCandidatePair = null; } // 2. If a sub-solver is already running, let it continue if (this.subSolver) { - this.subSolver.step() - return + this.subSolver.step(); + return; } // 3. Find the next valid candidate pair and start a new sub-solver while (this.queuedCandidatePairs.length > 0) { - const nextPair = this.queuedCandidatePairs.shift()! - const [p1, p2] = nextPair + const nextPair = this.queuedCandidatePairs.shift()!; + const [p1, p2] = nextPair; if ( this.newlyConnectedPinIds.has(p1.pinId) || this.newlyConnectedPinIds.has(p2.pinId) ) { - continue + continue; } - this.currentCandidatePair = nextPair + this.currentCandidatePair = nextPair; this.subSolver = new SchematicTraceSingleLineSolver2({ inputProblem: this.params.inputProblem, pins: this.currentCandidatePair, chipMap: this.chipMap, - }) - return + }); + return; } // 4. If we've exited the loop, there are no more valid pairs to process - this.solved = true + this.solved = true; } override visualize() { if (this.subSolver) { - return this.subSolver.visualize() + return this.subSolver.visualize(); } - const graphics = visualizeInputProblem(this.inputProblem) + const graphics = visualizeInputProblem(this.inputProblem); // Draw solved long-distance traces for (const trace of this.solvedLongDistanceTraces) { graphics.lines!.push({ points: trace.tracePath, strokeColor: "purple", - }) + }); } // Draw queued candidate pairs @@ -214,18 +216,21 @@ export class LongDistancePairSolver extends BaseSolver { points: [p1, p2], strokeColor: "gray", strokeDash: "4 4", - }) + }); } - return graphics + return graphics; } public getOutput(): { - newTraces: SolvedTracePath[] - allTracesMerged: SolvedTracePath[] + newTraces: SolvedTracePath[]; + allTracesMerged: SolvedTracePath[]; } { if (!this.solved) { - return { newTraces: [], allTracesMerged: this.params.alreadySolvedTraces } + return { + newTraces: [], + allTracesMerged: this.params.alreadySolvedTraces, + }; } return { newTraces: this.solvedLongDistanceTraces, @@ -233,6 +238,6 @@ export class LongDistancePairSolver extends BaseSolver { ...this.params.alreadySolvedTraces, ...this.solvedLongDistanceTraces, ], - } + }; } } diff --git a/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts b/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts index 48b46c90..fef59282 100644 --- a/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts +++ b/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts @@ -1,80 +1,80 @@ -import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver" +import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"; import type { InputChip, InputPin, InputProblem, PinId, -} from "lib/types/InputProblem" -import { ConnectivityMap } from "connectivity-map" -import { getConnectivityMapsFromInputProblem } from "./getConnectivityMapFromInputProblem" -import { getOrthogonalMinimumSpanningTree } from "./getMspConnectionPairsFromPins" -import { doesPairCrossRestrictedCenterLines } from "./doesPairCrossRestrictedCenterLines" -import type { GraphicsObject } from "graphics-debug" -import { getColorFromString } from "lib/utils/getColorFromString" -import { visualizeInputProblem } from "../SchematicTracePipelineSolver/visualizeInputProblem" +} from "lib/types/InputProblem"; +import { ConnectivityMap } from "connectivity-map"; +import { getConnectivityMapsFromInputProblem } from "./getConnectivityMapFromInputProblem"; +import { getOrthogonalMinimumSpanningTree } from "./getMspConnectionPairsFromPins"; +import { doesPairCrossRestrictedCenterLines } from "./doesPairCrossRestrictedCenterLines"; +import type { GraphicsObject } from "graphics-debug"; +import { getColorFromString } from "lib/utils/getColorFromString"; +import { visualizeInputProblem } from "../SchematicTracePipelineSolver/visualizeInputProblem"; -export type MspConnectionPairId = string +export type MspConnectionPairId = string; export type MspConnectionPair = { - mspPairId: MspConnectionPairId - dcConnNetId: string - globalConnNetId: string - userNetId?: string - pins: [InputPin & { chipId: string }, InputPin & { chipId: string }] -} + mspPairId: MspConnectionPairId; + dcConnNetId: string; + globalConnNetId: string; + userNetId?: string; + pins: [InputPin & { chipId: string }, InputPin & { chipId: string }]; +}; export class MspConnectionPairSolver extends BaseSolver { - inputProblem: InputProblem + inputProblem: InputProblem; - mspConnectionPairs: MspConnectionPair[] = [] - dcConnMap: ConnectivityMap - globalConnMap: ConnectivityMap - queuedDcNetIds: string[] - chipMap: Record - maxMspPairDistance: number + mspConnectionPairs: MspConnectionPair[] = []; + dcConnMap: ConnectivityMap; + globalConnMap: ConnectivityMap; + queuedDcNetIds: string[]; + chipMap: Record; + maxMspPairDistance: number; - pinMap: Record - userNetIdByPinId: Record + pinMap: Record; + userNetIdByPinId: Record; constructor({ inputProblem }: { inputProblem: InputProblem }) { - super() + super(); - this.inputProblem = inputProblem - this.maxMspPairDistance = inputProblem.maxMspPairDistance ?? 1 + this.inputProblem = inputProblem; + this.maxMspPairDistance = inputProblem.maxMspPairDistance ?? 1; const { directConnMap, netConnMap } = - getConnectivityMapsFromInputProblem(inputProblem) - this.dcConnMap = directConnMap - this.globalConnMap = netConnMap + getConnectivityMapsFromInputProblem(inputProblem); + this.dcConnMap = directConnMap; + this.globalConnMap = netConnMap; - this.pinMap = {} + this.pinMap = {}; for (const chip of inputProblem.chips) { for (const pin of chip.pins) { - this.pinMap[pin.pinId] = { ...pin, chipId: chip.chipId } + this.pinMap[pin.pinId] = { ...pin, chipId: chip.chipId }; } } - this.chipMap = {} + this.chipMap = {}; for (const chip of inputProblem.chips) { - this.chipMap[chip.chipId] = chip + this.chipMap[chip.chipId] = chip; } // Build a mapping from PinId to user-provided netId (if any) - this.userNetIdByPinId = {} + this.userNetIdByPinId = {}; for (const dc of inputProblem.directConnections) { if (dc.netId) { - const [a, b] = dc.pinIds - this.userNetIdByPinId[a] = dc.netId - this.userNetIdByPinId[b] = dc.netId + const [a, b] = dc.pinIds; + this.userNetIdByPinId[a] = dc.netId; + this.userNetIdByPinId[b] = dc.netId; } } for (const nc of inputProblem.netConnections) { for (const pid of nc.pinIds) { - this.userNetIdByPinId[pid] = nc.netId + this.userNetIdByPinId[pid] = nc.netId; } } - this.queuedDcNetIds = Object.keys(netConnMap.netMap) + this.queuedDcNetIds = Object.keys(netConnMap.netMap); } override getConstructorParams(): ConstructorParameters< @@ -82,39 +82,39 @@ export class MspConnectionPairSolver extends BaseSolver { >[0] { return { inputProblem: this.inputProblem, - } + }; } override _step() { if (this.queuedDcNetIds.length === 0) { - this.solved = true - return + this.solved = true; + return; } - const dcNetId = this.queuedDcNetIds.shift()! + const dcNetId = this.queuedDcNetIds.shift()!; - const allIds = this.globalConnMap.getIdsConnectedToNet(dcNetId) as string[] - const directlyConnectedPins = allIds.filter((id) => !!this.pinMap[id]) + const allIds = this.globalConnMap.getIdsConnectedToNet(dcNetId) as string[]; + const directlyConnectedPins = allIds.filter((id) => !!this.pinMap[id]); if (directlyConnectedPins.length <= 1) { - return + return; } if (directlyConnectedPins.length === 2) { - const [pin1, pin2] = directlyConnectedPins - const p1 = this.pinMap[pin1!]! - const p2 = this.pinMap[pin2!]! + const [pin1, pin2] = directlyConnectedPins; + const p1 = this.pinMap[pin1!]!; + const p2 = this.pinMap[pin2!]!; // Enforce max pair distance (use Manhattan to match orthogonal routing metric) - const manhattanDist = Math.abs(p1.x - p2.x) + Math.abs(p1.y - p2.y) + const manhattanDist = Math.abs(p1.x - p2.x) + Math.abs(p1.y - p2.y); if (manhattanDist > this.maxMspPairDistance) { // Too far apart; skip creating an MSP pair for this net - return + return; } // Avoid pairs that would cross restricted center lines const pinIdMap = new Map(Object.entries(this.pinMap)) as Map< PinId, InputPin & { chipId: string } - > + >; if ( doesPairCrossRestrictedCenterLines({ inputProblem: this.inputProblem, @@ -124,12 +124,12 @@ export class MspConnectionPairSolver extends BaseSolver { p2, }) ) { - return + return; } - const globalConnNetId = this.globalConnMap.getNetConnectedToId(pin1!)! + const globalConnNetId = this.globalConnMap.getNetConnectedToId(pin1!)!; const userNetId = - this.userNetIdByPinId[pin1!] ?? this.userNetIdByPinId[pin2!] + this.userNetIdByPinId[pin1!] ?? this.userNetIdByPinId[pin2!]; this.mspConnectionPairs.push({ mspPairId: `${pin1}-${pin2}`, @@ -137,9 +137,9 @@ export class MspConnectionPairSolver extends BaseSolver { globalConnNetId, userNetId, pins: [p1, p2], - }) + }); - return + return; } // There are more than 3 pins, so we need to run MSP to find the best pairs @@ -147,7 +147,7 @@ export class MspConnectionPairSolver extends BaseSolver { const pinIdMap = new Map(Object.entries(this.pinMap)) as Map< PinId, InputPin & { chipId: string } - > + >; const msp = getOrthogonalMinimumSpanningTree( directlyConnectedPins.map((p) => this.pinMap[p]!).filter(Boolean), { @@ -161,11 +161,11 @@ export class MspConnectionPairSolver extends BaseSolver { p2: b as InputPin & { chipId: string }, }), }, - ) + ); for (const [pin1, pin2] of msp) { - const p1Obj = this.pinMap[pin1!]! - const p2Obj = this.pinMap[pin2!]! + const p1Obj = this.pinMap[pin1!]!; + const p2Obj = this.pinMap[pin2!]!; // Skip any edge that would cross a restricted center line (safety filter) if ( doesPairCrossRestrictedCenterLines({ @@ -176,19 +176,19 @@ export class MspConnectionPairSolver extends BaseSolver { p2: p2Obj, }) ) { - continue + continue; } - const globalConnNetId = this.globalConnMap.getNetConnectedToId(pin1!)! + const globalConnNetId = this.globalConnMap.getNetConnectedToId(pin1!)!; const userNetId = - this.userNetIdByPinId[pin1!] ?? this.userNetIdByPinId[pin2!] + this.userNetIdByPinId[pin1!] ?? this.userNetIdByPinId[pin2!]; this.mspConnectionPairs.push({ mspPairId: `${pin1}-${pin2}`, dcConnNetId: dcNetId, globalConnNetId, userNetId, pins: [p1Obj, p2Obj], - }) + }); } } @@ -196,7 +196,7 @@ export class MspConnectionPairSolver extends BaseSolver { const graphics = visualizeInputProblem(this.inputProblem, { chipAlpha: 0.1, connectionAlpha: 0.1, - }) + }); // Draw all the solved MSP with lines, and the next-to-be-solved points with points for (const pair of this.mspConnectionPairs) { @@ -212,9 +212,9 @@ export class MspConnectionPairSolver extends BaseSolver { }, ], strokeColor: getColorFromString(pair.mspPairId, 0.75), - }) + }); } - return graphics + return graphics; } } diff --git a/lib/solvers/MspConnectionPairSolver/doesPairCrossRestrictedCenterLines.ts b/lib/solvers/MspConnectionPairSolver/doesPairCrossRestrictedCenterLines.ts index 01d742a8..957f05c7 100644 --- a/lib/solvers/MspConnectionPairSolver/doesPairCrossRestrictedCenterLines.ts +++ b/lib/solvers/MspConnectionPairSolver/doesPairCrossRestrictedCenterLines.ts @@ -3,28 +3,28 @@ import type { InputPin, InputProblem, PinId, -} from "lib/types/InputProblem" -import { getRestrictedCenterLines } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getRestrictedCenterLines" +} from "lib/types/InputProblem"; +import { getRestrictedCenterLines } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getRestrictedCenterLines"; export const doesPairCrossRestrictedCenterLines = (params: { - inputProblem: InputProblem - chipMap: Record - pinIdMap: Map - p1: InputPin & { chipId: string } - p2: InputPin & { chipId: string } + inputProblem: InputProblem; + chipMap: Record; + pinIdMap: Map; + p1: InputPin & { chipId: string }; + p2: InputPin & { chipId: string }; }): boolean => { - const { inputProblem, chipMap, pinIdMap, p1, p2 } = params + const { inputProblem, chipMap, pinIdMap, p1, p2 } = params; const restrictedCenterLines = getRestrictedCenterLines({ pins: [p1, p2], inputProblem, pinIdMap, chipMap, - }) + }); - if (restrictedCenterLines.size === 0) return false + if (restrictedCenterLines.size === 0) return false; - const EPS = 1e-9 + const EPS = 1e-9; const crossesSegment = ( a: { x: number; y: number }, @@ -32,31 +32,31 @@ export const doesPairCrossRestrictedCenterLines = (params: { ): boolean => { for (const [, rcl] of restrictedCenterLines) { if (rcl.axes.has("x") && typeof rcl.x === "number") { - if ((a.x - rcl.x) * (b.x - rcl.x) < -EPS) return true + if ((a.x - rcl.x) * (b.x - rcl.x) < -EPS) return true; } if (rcl.axes.has("y") && typeof rcl.y === "number") { - if ((a.y - rcl.y) * (b.y - rcl.y) < -EPS) return true + if ((a.y - rcl.y) * (b.y - rcl.y) < -EPS) return true; } } - return false - } + return false; + }; // If already aligned on one axis, just check that single segment if (Math.abs(p1.x - p2.x) < EPS || Math.abs(p1.y - p2.y) < EPS) { - return crossesSegment({ x: p1.x, y: p1.y }, { x: p2.x, y: p2.y }) + return crossesSegment({ x: p1.x, y: p1.y }, { x: p2.x, y: p2.y }); } // Two L-shape possibilities: horizontal-then-vertical or vertical-then-horizontal - const elbowHV = { x: p2.x, y: p1.y } - const elbowVH = { x: p1.x, y: p2.y } + const elbowHV = { x: p2.x, y: p1.y }; + const elbowVH = { x: p1.x, y: p2.y }; const hvCrosses = crossesSegment({ x: p1.x, y: p1.y }, elbowHV) || - crossesSegment(elbowHV, { x: p2.x, y: p2.y }) + crossesSegment(elbowHV, { x: p2.x, y: p2.y }); const vhCrosses = crossesSegment({ x: p1.x, y: p1.y }, elbowVH) || - crossesSegment(elbowVH, { x: p2.x, y: p2.y }) + crossesSegment(elbowVH, { x: p2.x, y: p2.y }); // Forbid the pair only if both L-shape routes would cross a restricted center line - return hvCrosses && vhCrosses -} + return hvCrosses && vhCrosses; +}; diff --git a/lib/solvers/MspConnectionPairSolver/getConnectivityMapFromInputProblem.ts b/lib/solvers/MspConnectionPairSolver/getConnectivityMapFromInputProblem.ts index d3a098c3..7ca65c27 100644 --- a/lib/solvers/MspConnectionPairSolver/getConnectivityMapFromInputProblem.ts +++ b/lib/solvers/MspConnectionPairSolver/getConnectivityMapFromInputProblem.ts @@ -1,24 +1,24 @@ -import { ConnectivityMap } from "connectivity-map" -import type { InputProblem } from "lib/types/InputProblem" +import { ConnectivityMap } from "connectivity-map"; +import type { InputProblem } from "lib/types/InputProblem"; export const getConnectivityMapsFromInputProblem = ( inputProblem: InputProblem, ): { directConnMap: ConnectivityMap; netConnMap: ConnectivityMap } => { - const directConnMap = new ConnectivityMap({}) + const directConnMap = new ConnectivityMap({}); for (const directConn of inputProblem.directConnections) { directConnMap.addConnections([ directConn.netId ? [directConn.netId, ...directConn.pinIds] : directConn.pinIds, - ]) + ]); } - const netConnMap = new ConnectivityMap(directConnMap.netMap) + const netConnMap = new ConnectivityMap(directConnMap.netMap); for (const netConn of inputProblem.netConnections) { - netConnMap.addConnections([[netConn.netId, ...netConn.pinIds]]) + netConnMap.addConnections([[netConn.netId, ...netConn.pinIds]]); } - return { directConnMap, netConnMap } -} + return { directConnMap, netConnMap }; +}; diff --git a/lib/solvers/MspConnectionPairSolver/getMspConnectionPairsFromPins.ts b/lib/solvers/MspConnectionPairSolver/getMspConnectionPairsFromPins.ts index b3a95955..43f94506 100644 --- a/lib/solvers/MspConnectionPairSolver/getMspConnectionPairsFromPins.ts +++ b/lib/solvers/MspConnectionPairSolver/getMspConnectionPairsFromPins.ts @@ -1,4 +1,4 @@ -import type { InputPin, PinId } from "lib/types/InputProblem" +import type { InputPin, PinId } from "lib/types/InputProblem"; /** * Compute the Orthogonal (Manhattan/L1) Minimum Spanning Tree (MST) @@ -19,86 +19,86 @@ import type { InputPin, PinId } from "lib/types/InputProblem" export function getOrthogonalMinimumSpanningTree( pins: InputPin[], opts: { - maxDistance?: number - forbidEdge?: (a: InputPin, b: InputPin) => boolean + maxDistance?: number; + forbidEdge?: (a: InputPin, b: InputPin) => boolean; } = {}, ): Array<[PinId, PinId]> { - const n = pins.length - const maxDistance = opts?.maxDistance ?? Number.POSITIVE_INFINITY - if (n <= 1) return [] + const n = pins.length; + const maxDistance = opts?.maxDistance ?? Number.POSITIVE_INFINITY; + if (n <= 1) return []; // Quick validation (optional; remove if hot path) // Ensure pinIds are unique to avoid ambiguous output edges. { - const seen = new Set() + const seen = new Set(); for (const p of pins) { if (seen.has(p.pinId)) { - throw new Error(`Duplicate pinId detected: "${p.pinId}"`) + throw new Error(`Duplicate pinId detected: "${p.pinId}"`); } - seen.add(p.pinId) + seen.add(p.pinId); } } // Helper: Manhattan distance const manhattan = (a: InputPin, b: InputPin) => - Math.abs(a.x - b.x) + Math.abs(a.y - b.y) + Math.abs(a.x - b.x) + Math.abs(a.y - b.y); // Prim's data structures - const inTree = new Array(n).fill(false) - const bestDist = new Array(n).fill(Number.POSITIVE_INFINITY) - const parent = new Array(n).fill(-1) + const inTree = new Array(n).fill(false); + const bestDist = new Array(n).fill(Number.POSITIVE_INFINITY); + const parent = new Array(n).fill(-1); // Start from the point with lexicographically smallest pinId (stable) - let startIndex = 0 + let startIndex = 0; for (let i = 1; i < n; i++) { - if (pins[i].pinId < pins[startIndex].pinId) startIndex = i + if (pins[i].pinId < pins[startIndex].pinId) startIndex = i; } - bestDist[startIndex] = 0 + bestDist[startIndex] = 0; - const edges: Array<[PinId, PinId]> = [] + const edges: Array<[PinId, PinId]> = []; for (let iter = 0; iter < n; iter++) { // Pick the next vertex u with minimal bestDist[u] not yet in the tree - let u = -1 - let best = Number.POSITIVE_INFINITY - let bestId = "" // for tie-breaking + let u = -1; + let best = Number.POSITIVE_INFINITY; + let bestId = ""; // for tie-breaking for (let i = 0; i < n; i++) { if (!inTree[i]) { - const d = bestDist[i] + const d = bestDist[i]; if ( d < best || (d === best && (bestId === "" || pins[i].pinId < bestId)) ) { - best = d - bestId = pins[i].pinId - u = i + best = d; + bestId = pins[i].pinId; + u = i; } } } // Add u to the tree - inTree[u] = true + inTree[u] = true; if (parent[u] !== -1) { - edges.push([pins[u].pinId, pins[parent[u]].pinId]) + edges.push([pins[u].pinId, pins[parent[u]].pinId]); } // Relax edges from u to all v not yet in the tree for (let v = 0; v < n; v++) { if (!inTree[v]) { - const d0 = manhattan(pins[u], pins[v]) - const isForbidden = opts?.forbidEdge?.(pins[u], pins[v]) ?? false + const d0 = manhattan(pins[u], pins[v]); + const isForbidden = opts?.forbidEdge?.(pins[u], pins[v]) ?? false; const d = - d0 > maxDistance || isForbidden ? Number.POSITIVE_INFINITY : d0 + d0 > maxDistance || isForbidden ? Number.POSITIVE_INFINITY : d0; if ( d < bestDist[v] || (d === bestDist[v] && pins[u].pinId < pins[parent[v]]?.pinId) ) { - bestDist[v] = d - parent[v] = u + bestDist[v] = d; + parent[v] = u; } } } } // edges.length === n-1 when n>0 - return edges + return edges; } diff --git a/lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver.ts b/lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver.ts index 0ded4d0b..10fb1cd8 100644 --- a/lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver.ts +++ b/lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver.ts @@ -1,58 +1,58 @@ -import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver" -import type { InputProblem, PinId } from "lib/types/InputProblem" -import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver" -import type { MspConnectionPairId } from "../MspConnectionPairSolver/MspConnectionPairSolver" -import { SingleNetLabelPlacementSolver } from "./SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver" -import type { FacingDirection } from "lib/utils/dir" -import type { Point } from "@tscircuit/math-utils" -import type { GraphicsObject } from "graphics-debug" -import { visualizeInputProblem } from "../SchematicTracePipelineSolver/visualizeInputProblem" -import { getColorFromString } from "lib/utils/getColorFromString" -import { getConnectivityMapsFromInputProblem } from "../MspConnectionPairSolver/getConnectivityMapFromInputProblem" +import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"; +import type { InputProblem, PinId } from "lib/types/InputProblem"; +import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver"; +import type { MspConnectionPairId } from "../MspConnectionPairSolver/MspConnectionPairSolver"; +import { SingleNetLabelPlacementSolver } from "./SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver"; +import type { FacingDirection } from "lib/utils/dir"; +import type { Point } from "@tscircuit/math-utils"; +import type { GraphicsObject } from "graphics-debug"; +import { visualizeInputProblem } from "../SchematicTracePipelineSolver/visualizeInputProblem"; +import { getColorFromString } from "lib/utils/getColorFromString"; +import { getConnectivityMapsFromInputProblem } from "../MspConnectionPairSolver/getConnectivityMapFromInputProblem"; /** * A group of traces that have at least one overlapping segment and * are part of the same global connectivity net */ export type OverlappingSameNetTraceGroup = { - globalConnNetId: string - netId?: string - overlappingTraces?: SolvedTracePath - portOnlyPinId?: string - mspConnectionPairIds?: MspConnectionPairId[] -} + globalConnNetId: string; + netId?: string; + overlappingTraces?: SolvedTracePath; + portOnlyPinId?: string; + mspConnectionPairIds?: MspConnectionPairId[]; +}; export interface NetLabelPlacement { - globalConnNetId: string - dcConnNetId?: string + globalConnNetId: string; + dcConnNetId?: string; /** * Optional user-provided net identifier (if present in the input problem). */ - netId?: string + netId?: string; /** * MSP pair ids that the label is associated with. Port-only labels use []. */ - mspConnectionPairIds: MspConnectionPairId[] + mspConnectionPairIds: MspConnectionPairId[]; /** * Pin ids relevant to this label. For a host trace, the two pins of that pair; * for a port-only label, the single port pin id. */ - pinIds: PinId[] - orientation: FacingDirection + pinIds: PinId[]; + orientation: FacingDirection; /** * The anchor point is the point on the trace where the net label connects */ - anchorPoint: Point + anchorPoint: Point; - width: number - height: number + width: number; + height: number; /** * The center point is computed from the anchor point, the width and height * and the orientation. */ - center: Point + center: Point; } /** @@ -64,82 +64,82 @@ export interface NetLabelPlacement { * The specific placement of the net label is solved for using the */ export class NetLabelPlacementSolver extends BaseSolver { - inputProblem: InputProblem - inputTraceMap: Record + inputProblem: InputProblem; + inputTraceMap: Record; - overlappingSameNetTraceGroups: Array + overlappingSameNetTraceGroups: Array; - queuedOverlappingSameNetTraceGroups: Array + queuedOverlappingSameNetTraceGroups: Array; - declare activeSubSolver: SingleNetLabelPlacementSolver | null + declare activeSubSolver: SingleNetLabelPlacementSolver | null; - netLabelPlacements: Array = [] - currentGroup: OverlappingSameNetTraceGroup | null = null - triedAnyOrientationFallbackForCurrentGroup = false + netLabelPlacements: Array = []; + currentGroup: OverlappingSameNetTraceGroup | null = null; + triedAnyOrientationFallbackForCurrentGroup = false; constructor(params: { - inputProblem: InputProblem - inputTraceMap: Record + inputProblem: InputProblem; + inputTraceMap: Record; }) { - super() - this.inputProblem = params.inputProblem - this.inputTraceMap = params.inputTraceMap + super(); + this.inputProblem = params.inputProblem; + this.inputTraceMap = params.inputTraceMap; this.overlappingSameNetTraceGroups = - this.computeOverlappingSameNetTraceGroups() + this.computeOverlappingSameNetTraceGroups(); this.queuedOverlappingSameNetTraceGroups = [ ...this.overlappingSameNetTraceGroups, - ] + ]; } computeOverlappingSameNetTraceGroups(): Array { // Group existing traces by their global connectivity net id. - const byGlobal: Record> = {} + const byGlobal: Record> = {}; for (const trace of Object.values(this.inputTraceMap)) { - const key = trace.globalConnNetId - if (!byGlobal[key]) byGlobal[key] = [] - byGlobal[key].push(trace) + const key = trace.globalConnNetId; + if (!byGlobal[key]) byGlobal[key] = []; + byGlobal[key].push(trace); } // Build global connectivity from input so we also consider pins with no traces const { netConnMap } = getConnectivityMapsFromInputProblem( this.inputProblem, - ) + ); - const pinIdToPinMap = new Map() + const pinIdToPinMap = new Map(); for (const chip of this.inputProblem.chips) { for (const pin of chip.pins) { - pinIdToPinMap.set(pin.pinId, pin) + pinIdToPinMap.set(pin.pinId, pin); } } // Map pins to user-provided netIds (if any) - const userNetIdByPinId: Record = {} + const userNetIdByPinId: Record = {}; for (const dc of this.inputProblem.directConnections) { if (dc.netId) { - const [a, b] = dc.pinIds - userNetIdByPinId[a] = dc.netId - userNetIdByPinId[b] = dc.netId + const [a, b] = dc.pinIds; + userNetIdByPinId[a] = dc.netId; + userNetIdByPinId[b] = dc.netId; } } for (const nc of this.inputProblem.netConnections) { for (const pid of nc.pinIds) { - userNetIdByPinId[pid] = nc.netId + userNetIdByPinId[pid] = nc.netId; } } - const groups: Array = [] + const groups: Array = []; const allPinIds = this.inputProblem.chips.flatMap((c) => c.pins.map((p) => p.pinId), - ) + ); - const allGlobalConnNetIds = new Set() + const allGlobalConnNetIds = new Set(); for (const pinId of allPinIds) { - const netId = netConnMap.getNetConnectedToId(pinId) + const netId = netConnMap.getNetConnectedToId(pinId); if (netId) { - allGlobalConnNetIds.add(netId) + allGlobalConnNetIds.add(netId); } } @@ -147,35 +147,35 @@ export class NetLabelPlacementSolver extends BaseSolver { for (const globalConnNetId of allGlobalConnNetIds) { const allIdsInNet = netConnMap.getIdsConnectedToNet( globalConnNetId, - ) as string[] - const pinsInNet = allIdsInNet.filter((id) => pinIdToPinMap.has(id)) + ) as string[]; + const pinsInNet = allIdsInNet.filter((id) => pinIdToPinMap.has(id)); // Build adjacency from solved traces (edges) - const adj: Record> = {} - for (const pid of pinsInNet) adj[pid] = new Set() + const adj: Record> = {}; + for (const pid of pinsInNet) adj[pid] = new Set(); for (const t of byGlobal[globalConnNetId] ?? []) { - const a = t.pins[0].pinId - const b = t.pins[1].pinId + const a = t.pins[0].pinId; + const b = t.pins[1].pinId; if (adj[a] && adj[b]) { - adj[a].add(b) - adj[b].add(a) + adj[a].add(b); + adj[b].add(a); } } // Find connected components based on trace edges - const visited = new Set() + const visited = new Set(); for (const pid of pinsInNet) { - if (visited.has(pid)) continue - const stack = [pid] - const component = new Set() - visited.add(pid) + if (visited.has(pid)) continue; + const stack = [pid]; + const component = new Set(); + visited.add(pid); while (stack.length > 0) { - const u = stack.pop()! - component.add(u) + const u = stack.pop()!; + component.add(u); for (const v of adj[u] ?? []) { if (!visited.has(v)) { - visited.add(v) - stack.push(v) + visited.add(v); + stack.push(v); } } } @@ -184,36 +184,38 @@ export class NetLabelPlacementSolver extends BaseSolver { const compTraces = (byGlobal[globalConnNetId] ?? []).filter( (t) => component.has(t.pins[0].pinId) && component.has(t.pins[1].pinId), - ) + ); if (compTraces.length > 0) { // Choose a representative trace (longest by L1 length) const lengthOf = (path: SolvedTracePath) => { - let sum = 0 - const pts = path.tracePath + let sum = 0; + const pts = path.tracePath; for (let i = 0; i < pts.length - 1; i++) { sum += Math.abs(pts[i + 1]!.x - pts[i]!.x) + - Math.abs(pts[i + 1]!.y - pts[i]!.y) + Math.abs(pts[i + 1]!.y - pts[i]!.y); } - return sum - } - let rep = compTraces[0]! - let repLen = lengthOf(rep) + return sum; + }; + let rep = compTraces[0]!; + let repLen = lengthOf(rep); for (let i = 1; i < compTraces.length; i++) { - const len = lengthOf(compTraces[i]!) + const len = lengthOf(compTraces[i]!); if (len > repLen) { - rep = compTraces[i]! - repLen = len + rep = compTraces[i]!; + repLen = len; } } - let userNetId = compTraces.find((t) => t.userNetId != null)?.userNetId + let userNetId = compTraces.find( + (t) => t.userNetId != null, + )?.userNetId; if (!userNetId) { for (const p of component) { if (userNetIdByPinId[p]) { - userNetId = userNetIdByPinId[p] - break + userNetId = userNetIdByPinId[p]; + break; } } } @@ -223,101 +225,101 @@ export class NetLabelPlacementSolver extends BaseSolver { (t) => t.mspConnectionPairIds ?? [t.mspPairId], ), ), - ) + ); const group = { globalConnNetId, netId: userNetId, overlappingTraces: rep, mspConnectionPairIds, - } - groups.push(group) + }; + groups.push(group); } else { // No traces in this component: place label at each pin that has a user net id for (const p of component) { - const userNetId = userNetIdByPinId[p] - if (!userNetId) continue + const userNetId = userNetIdByPinId[p]; + if (!userNetId) continue; groups.push({ globalConnNetId, netId: userNetId, portOnlyPinId: p, - }) + }); } } } } - return groups + return groups; } override _step() { if (this.activeSubSolver?.solved) { - this.netLabelPlacements.push(this.activeSubSolver.netLabelPlacement!) - this.activeSubSolver = null - this.currentGroup = null - this.triedAnyOrientationFallbackForCurrentGroup = false - return + this.netLabelPlacements.push(this.activeSubSolver.netLabelPlacement!); + this.activeSubSolver = null; + this.currentGroup = null; + this.triedAnyOrientationFallbackForCurrentGroup = false; + return; } if (this.activeSubSolver?.failed) { // Retry once with all orientations as a fallback before failing - const fullOrients: FacingDirection[] = ["x+", "x-", "y+", "y-"] - const currOrients = this.activeSubSolver.availableOrientations + const fullOrients: FacingDirection[] = ["x+", "x-", "y+", "y-"]; + const currOrients = this.activeSubSolver.availableOrientations; const isAlreadyFull = currOrients.length === 4 && - fullOrients.every((o) => currOrients.includes(o)) + fullOrients.every((o) => currOrients.includes(o)); if ( !this.triedAnyOrientationFallbackForCurrentGroup && !isAlreadyFull && this.currentGroup ) { - this.triedAnyOrientationFallbackForCurrentGroup = true + this.triedAnyOrientationFallbackForCurrentGroup = true; const netLabelWidth = this.currentGroup.netId ? this.inputProblem.netConnections.find( (nc) => nc.netId === this.currentGroup!.netId, )?.netLabelWidth - : undefined + : undefined; this.activeSubSolver = new SingleNetLabelPlacementSolver({ inputProblem: this.inputProblem, inputTraceMap: this.inputTraceMap, overlappingSameNetTraceGroup: this.currentGroup, availableOrientations: fullOrients, netLabelWidth, - }) - return + }); + return; } - this.failed = true - this.error = this.activeSubSolver.error - return + this.failed = true; + this.error = this.activeSubSolver.error; + return; } if (this.activeSubSolver) { - this.activeSubSolver.step() - return + this.activeSubSolver.step(); + return; } const nextOverlappingSameNetTraceGroup = - this.queuedOverlappingSameNetTraceGroups.shift() + this.queuedOverlappingSameNetTraceGroups.shift(); if (!nextOverlappingSameNetTraceGroup) { - this.solved = true - return + this.solved = true; + return; } const netId = nextOverlappingSameNetTraceGroup.netId ?? - nextOverlappingSameNetTraceGroup.globalConnNetId + nextOverlappingSameNetTraceGroup.globalConnNetId; - this.currentGroup = nextOverlappingSameNetTraceGroup - this.triedAnyOrientationFallbackForCurrentGroup = false + this.currentGroup = nextOverlappingSameNetTraceGroup; + this.triedAnyOrientationFallbackForCurrentGroup = false; const netLabelWidth = this.currentGroup.netId ? this.inputProblem.netConnections.find( (nc) => nc.netId === this.currentGroup!.netId, )?.netLabelWidth - : undefined + : undefined; this.activeSubSolver = new SingleNetLabelPlacementSolver({ inputProblem: this.inputProblem, @@ -327,20 +329,20 @@ export class NetLabelPlacementSolver extends BaseSolver { netId ] ?? ["x+", "x-", "y+", "y-"], netLabelWidth, - }) + }); } override visualize(): GraphicsObject { if (this.activeSubSolver) { - return this.activeSubSolver.visualize() + return this.activeSubSolver.visualize(); } - const graphics = visualizeInputProblem(this.inputProblem) + const graphics = visualizeInputProblem(this.inputProblem); for (const trace of Object.values(this.inputTraceMap)) { graphics.lines!.push({ points: trace.tracePath, strokeColor: "purple", - }) + }); } for (const p of this.netLabelPlacements) { @@ -350,14 +352,14 @@ export class NetLabelPlacementSolver extends BaseSolver { height: p.height, fill: getColorFromString(p.globalConnNetId, 0.35), strokeColor: getColorFromString(p.globalConnNetId, 0.9), - } as any) + } as any); graphics.points!.push({ x: p.anchorPoint.x, y: p.anchorPoint.y, color: getColorFromString(p.globalConnNetId, 0.9), - } as any) + } as any); } - return graphics + return graphics; } } diff --git a/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver.ts b/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver.ts index 02925891..f8fbb087 100644 --- a/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver.ts +++ b/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver.ts @@ -1,29 +1,29 @@ -import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver" +import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"; import type { NetLabelPlacement, OverlappingSameNetTraceGroup, -} from "../NetLabelPlacementSolver" -import type { InputProblem, PinId } from "lib/types/InputProblem" -import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver" -import type { MspConnectionPairId } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver" -import type { FacingDirection } from "lib/utils/dir" -import type { GraphicsObject } from "graphics-debug" -import { ChipObstacleSpatialIndex } from "lib/data-structures/ChipObstacleSpatialIndex" +} from "../NetLabelPlacementSolver"; +import type { InputProblem, PinId } from "lib/types/InputProblem"; +import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"; +import type { MspConnectionPairId } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver"; +import type { FacingDirection } from "lib/utils/dir"; +import type { GraphicsObject } from "graphics-debug"; +import { ChipObstacleSpatialIndex } from "lib/data-structures/ChipObstacleSpatialIndex"; import { getDimsForOrientation, getCenterFromAnchor, getRectBounds, -} from "./geometry" -import { rectIntersectsAnyTrace } from "./collisions" -import { chooseHostTraceForGroup } from "./host" -import { anchorsForSegment } from "./anchors" -import { solveNetLabelPlacementForPortOnlyPin } from "./solvePortOnlyPin" -import { visualizeSingleNetLabelPlacementSolver } from "./SingleNetLabelPlacementSolver_visualize" +} from "./geometry"; +import { rectIntersectsAnyTrace } from "./collisions"; +import { chooseHostTraceForGroup } from "./host"; +import { anchorsForSegment } from "./anchors"; +import { solveNetLabelPlacementForPortOnlyPin } from "./solvePortOnlyPin"; +import { visualizeSingleNetLabelPlacementSolver } from "./SingleNetLabelPlacementSolver_visualize"; export { NET_LABEL_HORIZONTAL_WIDTH, NET_LABEL_HORIZONTAL_HEIGHT, -} from "./geometry" +} from "./geometry"; // NOTE: net labels, when in the y+/y- orientation, are rotated and therefore // the width/height are swapped @@ -51,45 +51,45 @@ export { * order from the largest chip, is the location we return in netLabelPlacement */ export class SingleNetLabelPlacementSolver extends BaseSolver { - inputProblem: InputProblem - inputTraceMap: Record - overlappingSameNetTraceGroup: OverlappingSameNetTraceGroup - availableOrientations: Array + inputProblem: InputProblem; + inputTraceMap: Record; + overlappingSameNetTraceGroup: OverlappingSameNetTraceGroup; + availableOrientations: Array; - chipObstacleSpatialIndex: ChipObstacleSpatialIndex + chipObstacleSpatialIndex: ChipObstacleSpatialIndex; // Optional override for the width of the net label (per netId) - netLabelWidth?: number + netLabelWidth?: number; - netLabelPlacement: NetLabelPlacement | null = null + netLabelPlacement: NetLabelPlacement | null = null; testedCandidates: Array<{ - center: { x: number; y: number } - width: number - height: number - bounds: { minX: number; minY: number; maxX: number; maxY: number } - anchor: { x: number; y: number } - orientation: FacingDirection - status: "ok" | "chip-collision" | "trace-collision" | "parallel-to-segment" - hostSegIndex: number - }> = [] + center: { x: number; y: number }; + width: number; + height: number; + bounds: { minX: number; minY: number; maxX: number; maxY: number }; + anchor: { x: number; y: number }; + orientation: FacingDirection; + status: "ok" | "chip-collision" | "trace-collision" | "parallel-to-segment"; + hostSegIndex: number; + }> = []; constructor(params: { - inputProblem: InputProblem - inputTraceMap: Record - overlappingSameNetTraceGroup: OverlappingSameNetTraceGroup - availableOrientations: FacingDirection[] - netLabelWidth?: number + inputProblem: InputProblem; + inputTraceMap: Record; + overlappingSameNetTraceGroup: OverlappingSameNetTraceGroup; + availableOrientations: FacingDirection[]; + netLabelWidth?: number; }) { - super() - this.inputProblem = params.inputProblem - this.inputTraceMap = params.inputTraceMap - this.overlappingSameNetTraceGroup = params.overlappingSameNetTraceGroup - this.availableOrientations = params.availableOrientations - this.netLabelWidth = params.netLabelWidth + super(); + this.inputProblem = params.inputProblem; + this.inputTraceMap = params.inputTraceMap; + this.overlappingSameNetTraceGroup = params.overlappingSameNetTraceGroup; + this.availableOrientations = params.availableOrientations; + this.netLabelWidth = params.netLabelWidth; this.chipObstacleSpatialIndex = params.inputProblem._chipObstacleSpatialIndex ?? - new ChipObstacleSpatialIndex(params.inputProblem.chips) + new ChipObstacleSpatialIndex(params.inputProblem.chips); } override getConstructorParams(): ConstructorParameters< @@ -101,13 +101,13 @@ export class SingleNetLabelPlacementSolver extends BaseSolver { overlappingSameNetTraceGroup: this.overlappingSameNetTraceGroup, availableOrientations: this.availableOrientations, netLabelWidth: this.netLabelWidth, - } + }; } override _step() { if (this.netLabelPlacement) { - this.solved = true - return + this.solved = true; + return; } // Handle port-only island (no traces) by placing a label at the port @@ -119,21 +119,21 @@ export class SingleNetLabelPlacementSolver extends BaseSolver { overlappingSameNetTraceGroup: this.overlappingSameNetTraceGroup, availableOrientations: this.availableOrientations, netLabelWidth: this.netLabelWidth, - }) - this.testedCandidates.push(...res.testedCandidates) + }); + this.testedCandidates.push(...res.testedCandidates); if (res.placement) { - this.netLabelPlacement = res.placement - this.solved = true - return + this.netLabelPlacement = res.placement; + this.solved = true; + return; } - this.failed = true + this.failed = true; this.error = - res.error ?? "Could not place net label at port without collisions" - return + res.error ?? "Could not place net label at port without collisions"; + return; } // Prefer starting from the trace connected to the "largest" chip (most pins) - const groupId = this.overlappingSameNetTraceGroup.globalConnNetId + const groupId = this.overlappingSameNetTraceGroup.globalConnNetId; let host = chooseHostTraceForGroup({ inputProblem: this.inputProblem, inputTraceMap: this.inputTraceMap, @@ -141,38 +141,38 @@ export class SingleNetLabelPlacementSolver extends BaseSolver { fallbackTrace: this.overlappingSameNetTraceGroup.overlappingTraces, mspConnectionPairIds: this.overlappingSameNetTraceGroup.mspConnectionPairIds, - }) + }); if (!host) { - this.failed = true - this.error = "No host trace found for net label placement" - return + this.failed = true; + this.error = "No host trace found for net label placement"; + return; } // Ensure we traverse the host path starting at the segment attached to the largest chip's pin const traceIdSet = new Set( this.overlappingSameNetTraceGroup.mspConnectionPairIds ?? [], - ) + ); const tracesToScanBase = Object.values(this.inputTraceMap).filter( (t) => t.globalConnNetId === groupId && (traceIdSet.size === 0 || t.mspConnectionPairIds.some((id) => traceIdSet.has(id))), - ) + ); const tracesToScan = this.availableOrientations.length === 1 ? [ host, ...tracesToScanBase.filter((t) => t.mspPairId !== host!.mspPairId), ] - : [host] + : [host]; const orientations = this.availableOrientations.length > 0 ? this.availableOrientations - : (["x+", "x-", "y+", "y-"] as FacingDirection[]) + : (["x+", "x-", "y+", "y-"] as FacingDirection[]); - const singleOrientationMode = this.availableOrientations.length === 1 + const singleOrientationMode = this.availableOrientations.length === 1; // For axis-aligned comparisons (furthest-point selection) const scoreFor = ( @@ -181,62 +181,62 @@ export class SingleNetLabelPlacementSolver extends BaseSolver { ) => { switch (orientation) { case "y+": - return anchor.y + return anchor.y; case "y-": - return -anchor.y + return -anchor.y; case "x+": - return anchor.x + return anchor.x; case "x-": - return -anchor.x + return -anchor.x; } - } + }; let bestCandidate: { - anchor: { x: number; y: number } - orientation: FacingDirection - width: number - height: number - center: { x: number; y: number } - hostSegIndex: number - dcConnNetId: string - mspPairId: MspConnectionPairId - pinIds: PinId[] - } | null = null - let bestScore = -Infinity + anchor: { x: number; y: number }; + orientation: FacingDirection; + width: number; + height: number; + center: { x: number; y: number }; + hostSegIndex: number; + dcConnNetId: string; + mspPairId: MspConnectionPairId; + pinIds: PinId[]; + } | null = null; + let bestScore = -Infinity; - const EPS = 1e-6 + const EPS = 1e-6; for (const curr of tracesToScan) { - const pts = curr.tracePath.slice() + const pts = curr.tracePath.slice(); for (let si = 0; si < pts.length - 1; si++) { - const a = pts[si]! - const b = pts[si + 1]! - const isH = Math.abs(a.y - b.y) < EPS - const isV = Math.abs(a.x - b.x) < EPS - if (!isH && !isV) continue + const a = pts[si]!; + const b = pts[si + 1]!; + const isH = Math.abs(a.y - b.y) < EPS; + const isV = Math.abs(a.x - b.x) < EPS; + if (!isH && !isV) continue; // Only consider orientations perpendicular to the segment to avoid // self-overlap with the host segment. const segmentAllowed: FacingDirection[] = isH ? (["y+", "y-"] as FacingDirection[]) - : (["x+", "x-"] as FacingDirection[]) + : (["x+", "x-"] as FacingDirection[]); const candidateOrients = orientations.filter((o) => segmentAllowed.includes(o), - ) - if (candidateOrients.length === 0) continue + ); + if (candidateOrients.length === 0) continue; - const anchors = anchorsForSegment(a, b) + const anchors = anchorsForSegment(a, b); for (const anchor of anchors) { for (const orientation of candidateOrients) { const { width, height } = getDimsForOrientation({ orientation, netLabelWidth: this.netLabelWidth, - }) + }); const center = getCenterFromAnchor( anchor, orientation, width, height, - ) + ); // Small outward offset to avoid counting the touching trace as a collision const outward = @@ -246,16 +246,17 @@ export class SingleNetLabelPlacementSolver extends BaseSolver { ? { x: -1, y: 0 } : orientation === "y+" ? { x: 0, y: 1 } - : { x: 0, y: -1 } - const offset = 1e-4 + : { x: 0, y: -1 }; + const offset = 1e-4; const testCenter = { x: center.x + outward.x * offset, y: center.y + outward.y * offset, - } - const bounds = getRectBounds(testCenter, width, height) + }; + const bounds = getRectBounds(testCenter, width, height); // Chip collision check - const chips = this.chipObstacleSpatialIndex.getChipsInBounds(bounds) + const chips = + this.chipObstacleSpatialIndex.getChipsInBounds(bounds); if (chips.length > 0) { this.testedCandidates.push({ center: testCenter, @@ -266,8 +267,8 @@ export class SingleNetLabelPlacementSolver extends BaseSolver { orientation, status: "chip-collision", hostSegIndex: si, - }) - continue + }); + continue; } // Trace collision check (ignore the host segment) @@ -276,7 +277,7 @@ export class SingleNetLabelPlacementSolver extends BaseSolver { this.inputTraceMap, curr.mspPairId, si, - ) + ); if (traceIntersectionResult.hasIntersection) { this.testedCandidates.push({ center: testCenter, @@ -287,8 +288,8 @@ export class SingleNetLabelPlacementSolver extends BaseSolver { orientation, status: "trace-collision", hostSegIndex: si, - }) - continue + }); + continue; } // Found a valid placement @@ -301,12 +302,12 @@ export class SingleNetLabelPlacementSolver extends BaseSolver { orientation, status: "ok", hostSegIndex: si, - }) + }); if (singleOrientationMode) { - const s = scoreFor(orientation, anchor) + const s = scoreFor(orientation, anchor); if (s > bestScore + 1e-9) { - bestScore = s + bestScore = s; bestCandidate = { anchor, orientation, @@ -317,10 +318,10 @@ export class SingleNetLabelPlacementSolver extends BaseSolver { dcConnNetId: curr.dcConnNetId, mspPairId: curr.mspPairId, pinIds: [curr.pins[0].pinId, curr.pins[1].pinId], - } + }; } // Continue traversing to prioritize the furthest valid point - continue + continue; } this.netLabelPlacement = { @@ -335,9 +336,9 @@ export class SingleNetLabelPlacementSolver extends BaseSolver { width, height, center, - } - this.solved = true - return + }; + this.solved = true; + return; } } } @@ -355,16 +356,16 @@ export class SingleNetLabelPlacementSolver extends BaseSolver { width: bestCandidate.width, height: bestCandidate.height, center: bestCandidate.center, - } - this.solved = true - return + }; + this.solved = true; + return; } - this.failed = true - this.error = "Could not place net label without collisions" + this.failed = true; + this.error = "Could not place net label without collisions"; } override visualize(): GraphicsObject { - return visualizeSingleNetLabelPlacementSolver(this) + return visualizeSingleNetLabelPlacementSolver(this); } } diff --git a/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver_visualize.ts b/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver_visualize.ts index 220c3b71..7d90129e 100644 --- a/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver_visualize.ts +++ b/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver_visualize.ts @@ -1,32 +1,32 @@ -import type { GraphicsObject } from "graphics-debug" -import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem" -import { getColorFromString } from "lib/utils/getColorFromString" -import { chooseHostTraceForGroup } from "./host" -import type { SingleNetLabelPlacementSolver } from "./SingleNetLabelPlacementSolver" +import type { GraphicsObject } from "graphics-debug"; +import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem"; +import { getColorFromString } from "lib/utils/getColorFromString"; +import { chooseHostTraceForGroup } from "./host"; +import type { SingleNetLabelPlacementSolver } from "./SingleNetLabelPlacementSolver"; export function visualizeSingleNetLabelPlacementSolver( solver: SingleNetLabelPlacementSolver, ): GraphicsObject { - const graphics = visualizeInputProblem(solver.inputProblem) + const graphics = visualizeInputProblem(solver.inputProblem); // Visualize the entire trace group for this net id - const groupId = solver.overlappingSameNetTraceGroup.globalConnNetId + const groupId = solver.overlappingSameNetTraceGroup.globalConnNetId; const host = chooseHostTraceForGroup({ inputProblem: solver.inputProblem, inputTraceMap: solver.inputTraceMap, globalConnNetId: groupId, fallbackTrace: solver.overlappingSameNetTraceGroup.overlappingTraces, - }) - const groupStroke = getColorFromString(groupId, 0.9) - const groupFill = getColorFromString(groupId, 0.5) + }); + const groupStroke = getColorFromString(groupId, 0.9); + const groupFill = getColorFromString(groupId, 0.5); for (const trace of Object.values(solver.inputTraceMap)) { - const isHost = host ? trace.mspPairId === host.mspPairId : false + const isHost = host ? trace.mspPairId === host.mspPairId : false; graphics.lines!.push({ points: trace.tracePath, // strokeColor: isHost ? groupStroke : groupFill, // strokeDash: isHost ? undefined : "4 2", - } as any) + } as any); } // Visualize all tested candidate rectangles with reason coloring @@ -38,7 +38,7 @@ export function visualizeSingleNetLabelPlacementSolver( ? "rgba(220, 0, 0, 0.25)" : c.status === "trace-collision" ? "rgba(220, 140, 0, 0.25)" - : "rgba(120, 120, 120, 0.15)" + : "rgba(120, 120, 120, 0.15)"; const stroke = c.status === "ok" ? "green" @@ -46,7 +46,7 @@ export function visualizeSingleNetLabelPlacementSolver( ? "red" : c.status === "trace-collision" ? "orange" - : "gray" + : "gray"; graphics.rects!.push({ center: { @@ -57,31 +57,31 @@ export function visualizeSingleNetLabelPlacementSolver( height: c.height, fill, strokeColor: stroke, - } as any) + } as any); graphics.points!.push({ x: c.anchor.x, y: c.anchor.y, color: stroke, - } as any) + } as any); } // Visualize the final accepted label (if any) if (solver.netLabelPlacement) { - const p = solver.netLabelPlacement + const p = solver.netLabelPlacement; graphics.rects!.push({ center: p.center, width: p.width, height: p.height, fill: "rgba(0, 128, 255, 0.35)", strokeColor: "blue", - } as any) + } as any); graphics.points!.push({ x: p.anchorPoint.x, y: p.anchorPoint.y, color: "blue", - } as any) + } as any); } - return graphics + return graphics; } diff --git a/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/anchors.ts b/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/anchors.ts index 10a16875..b17ee543 100644 --- a/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/anchors.ts +++ b/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/anchors.ts @@ -7,5 +7,5 @@ export function anchorsForSegment( { x: a.x, y: a.y }, { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 }, { x: b.x, y: b.y }, - ] + ]; } diff --git a/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions.ts b/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions.ts index 9989277a..c3581889 100644 --- a/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions.ts +++ b/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions.ts @@ -1,5 +1,5 @@ -import type { MspConnectionPairId } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver" -import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver" +import type { MspConnectionPairId } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver"; +import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"; export function segmentIntersectsRect( p1: { x: number; y: number }, @@ -7,24 +7,24 @@ export function segmentIntersectsRect( rect: { minX: number; minY: number; maxX: number; maxY: number }, EPS = 1e-9, ): boolean { - const isVert = Math.abs(p1.x - p2.x) < EPS - const isHorz = Math.abs(p1.y - p2.y) < EPS - if (!isVert && !isHorz) return false + const isVert = Math.abs(p1.x - p2.x) < EPS; + const isHorz = Math.abs(p1.y - p2.y) < EPS; + if (!isVert && !isHorz) return false; if (isVert) { - const x = p1.x - if (x < rect.minX - EPS || x > rect.maxX + EPS) return false - const segMinY = Math.min(p1.y, p2.y) - const segMaxY = Math.max(p1.y, p2.y) - const overlap = Math.min(segMaxY, rect.maxY) - Math.max(segMinY, rect.minY) - return overlap > EPS + const x = p1.x; + if (x < rect.minX - EPS || x > rect.maxX + EPS) return false; + const segMinY = Math.min(p1.y, p2.y); + const segMaxY = Math.max(p1.y, p2.y); + const overlap = Math.min(segMaxY, rect.maxY) - Math.max(segMinY, rect.minY); + return overlap > EPS; } else { - const y = p1.y - if (y < rect.minY - EPS || y > rect.maxY + EPS) return false - const segMinX = Math.min(p1.x, p2.x) - const segMaxX = Math.max(p1.x, p2.x) - const overlap = Math.min(segMaxX, rect.maxX) - Math.max(segMinX, rect.minX) - return overlap > EPS + const y = p1.y; + if (y < rect.minY - EPS || y > rect.maxY + EPS) return false; + const segMinX = Math.min(p1.x, p2.x); + const segMaxX = Math.max(p1.x, p2.x); + const overlap = Math.min(segMaxX, rect.maxX) - Math.max(segMinX, rect.minX); + return overlap > EPS; } } @@ -35,18 +35,18 @@ export function rectIntersectsAnyTrace( hostSegIndex?: number, ): | { - hasIntersection: true - mspPairId: MspConnectionPairId - segIndex: number + hasIntersection: true; + mspPairId: MspConnectionPairId; + segIndex: number; } | { hasIntersection: false } { for (const [pairId, solved] of Object.entries(inputTraceMap)) { - const pts = solved.tracePath + const pts = solved.tracePath; for (let i = 0; i < pts.length - 1; i++) { - if (pairId === hostPathId && i === hostSegIndex) continue + if (pairId === hostPathId && i === hostSegIndex) continue; if (segmentIntersectsRect(pts[i]!, pts[i + 1]!, bounds)) - return { hasIntersection: true, mspPairId: pairId, segIndex: i } + return { hasIntersection: true, mspPairId: pairId, segIndex: i }; } } - return { hasIntersection: false } + return { hasIntersection: false }; } diff --git a/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry.ts b/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry.ts index 269b8718..65d922be 100644 --- a/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry.ts +++ b/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry.ts @@ -1,29 +1,29 @@ -import type { FacingDirection } from "lib/utils/dir" +import type { FacingDirection } from "lib/utils/dir"; -export const NET_LABEL_HORIZONTAL_WIDTH = 0.45 -export const NET_LABEL_HORIZONTAL_HEIGHT = 0.2 +export const NET_LABEL_HORIZONTAL_WIDTH = 0.45; +export const NET_LABEL_HORIZONTAL_HEIGHT = 0.2; export function getDimsForOrientation(params: { - orientation: FacingDirection - netLabelWidth?: number + orientation: FacingDirection; + netLabelWidth?: number; }) { - const { orientation, netLabelWidth } = params + const { orientation, netLabelWidth } = params; const horizWidth = typeof netLabelWidth === "number" ? netLabelWidth - : NET_LABEL_HORIZONTAL_WIDTH + : NET_LABEL_HORIZONTAL_WIDTH; if (orientation === "y+" || orientation === "y-") { return { // Rotated, so width/height swap width: NET_LABEL_HORIZONTAL_HEIGHT, height: horizWidth, - } + }; } return { width: horizWidth, height: NET_LABEL_HORIZONTAL_HEIGHT, - } + }; } export function getCenterFromAnchor( @@ -34,13 +34,13 @@ export function getCenterFromAnchor( ) { switch (orientation) { case "x+": - return { x: anchor.x + width / 2, y: anchor.y } + return { x: anchor.x + width / 2, y: anchor.y }; case "x-": - return { x: anchor.x - width / 2, y: anchor.y } + return { x: anchor.x - width / 2, y: anchor.y }; case "y+": - return { x: anchor.x, y: anchor.y + height / 2 } + return { x: anchor.x, y: anchor.y + height / 2 }; case "y-": - return { x: anchor.x, y: anchor.y - height / 2 } + return { x: anchor.x, y: anchor.y - height / 2 }; } } @@ -54,5 +54,5 @@ export function getRectBounds( minY: center.y - h / 2, maxX: center.x + w / 2, maxY: center.y + h / 2, - } + }; } diff --git a/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/host.ts b/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/host.ts index eb980fd2..93dc7cb4 100644 --- a/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/host.ts +++ b/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/host.ts @@ -1,23 +1,23 @@ -import type { InputChip, InputProblem } from "lib/types/InputProblem" -import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver" -import type { MspConnectionPairId } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver" +import type { InputChip, InputProblem } from "lib/types/InputProblem"; +import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"; +import type { MspConnectionPairId } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver"; export function lengthOfTrace(path: SolvedTracePath): number { - let sum = 0 - const pts = path.tracePath + let sum = 0; + const pts = path.tracePath; for (let i = 0; i < pts.length - 1; i++) { sum += - Math.abs(pts[i + 1]!.x - pts[i]!.x) + Math.abs(pts[i + 1]!.y - pts[i]!.y) + Math.abs(pts[i + 1]!.x - pts[i]!.x) + Math.abs(pts[i + 1]!.y - pts[i]!.y); } - return sum + return sum; } export function chooseHostTraceForGroup(params: { - inputProblem: InputProblem - inputTraceMap: Record - globalConnNetId: string - fallbackTrace?: SolvedTracePath - mspConnectionPairIds?: MspConnectionPairId[] + inputProblem: InputProblem; + inputTraceMap: Record; + globalConnNetId: string; + fallbackTrace?: SolvedTracePath; + mspConnectionPairIds?: MspConnectionPairId[]; }): SolvedTracePath | undefined { const { inputProblem, @@ -25,35 +25,35 @@ export function chooseHostTraceForGroup(params: { globalConnNetId, fallbackTrace, mspConnectionPairIds, - } = params + } = params; const chipsById: Record = Object.fromEntries( inputProblem.chips.map((c) => [c.chipId, c]), - ) + ); let groupTraces = Object.values(inputTraceMap).filter( (t) => t.globalConnNetId === globalConnNetId, - ) + ); if (mspConnectionPairIds && mspConnectionPairIds.length > 0) { - const idSet = new Set(mspConnectionPairIds) + const idSet = new Set(mspConnectionPairIds); groupTraces = groupTraces.filter((t) => t.mspConnectionPairIds.some((id) => idSet.has(id)), - ) + ); } - const chipIdsInGroup = new Set() + const chipIdsInGroup = new Set(); for (const t of groupTraces) { - chipIdsInGroup.add(t.pins[0].chipId) - chipIdsInGroup.add(t.pins[1].chipId) + chipIdsInGroup.add(t.pins[0].chipId); + chipIdsInGroup.add(t.pins[1].chipId); } - let largestChipId: string | null = null - let largestPinCount = -1 + let largestChipId: string | null = null; + let largestPinCount = -1; for (const id of chipIdsInGroup) { - const chip = chipsById[id] - const count = chip?.pins?.length ?? 0 + const chip = chipsById[id]; + const count = chip?.pins?.length ?? 0; if (count > largestPinCount) { - largestPinCount = count - largestChipId = id + largestPinCount = count; + largestChipId = id; } } @@ -64,13 +64,13 @@ export function chooseHostTraceForGroup(params: { (t) => t.pins[0].chipId === largestChipId || t.pins[1].chipId === largestChipId, - ) + ); if (hostCandidates.length > 0) { return hostCandidates.reduce((a, b) => lengthOfTrace(a) >= lengthOfTrace(b) ? a : b, - ) + ); } - return fallbackTrace + return fallbackTrace; } diff --git a/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/solvePortOnlyPin.ts b/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/solvePortOnlyPin.ts index ca86e3d1..53cdea45 100644 --- a/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/solvePortOnlyPin.ts +++ b/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/solvePortOnlyPin.ts @@ -1,40 +1,40 @@ -import type { InputProblem } from "lib/types/InputProblem" -import type { MspConnectionPairId } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver" -import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver" -import type { FacingDirection } from "lib/utils/dir" -import { ChipObstacleSpatialIndex } from "lib/data-structures/ChipObstacleSpatialIndex" -import { getPinDirection } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getPinDirection" +import type { InputProblem } from "lib/types/InputProblem"; +import type { MspConnectionPairId } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver"; +import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"; +import type { FacingDirection } from "lib/utils/dir"; +import { ChipObstacleSpatialIndex } from "lib/data-structures/ChipObstacleSpatialIndex"; +import { getPinDirection } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getPinDirection"; import type { NetLabelPlacement, OverlappingSameNetTraceGroup, -} from "../NetLabelPlacementSolver" +} from "../NetLabelPlacementSolver"; import { getDimsForOrientation, getCenterFromAnchor, getRectBounds, -} from "./geometry" -import { rectIntersectsAnyTrace } from "./collisions" +} from "./geometry"; +import { rectIntersectsAnyTrace } from "./collisions"; export function solveNetLabelPlacementForPortOnlyPin(params: { - inputProblem: InputProblem - inputTraceMap: Record - chipObstacleSpatialIndex: ChipObstacleSpatialIndex - overlappingSameNetTraceGroup: OverlappingSameNetTraceGroup - availableOrientations: FacingDirection[] - netLabelWidth?: number + inputProblem: InputProblem; + inputTraceMap: Record; + chipObstacleSpatialIndex: ChipObstacleSpatialIndex; + overlappingSameNetTraceGroup: OverlappingSameNetTraceGroup; + availableOrientations: FacingDirection[]; + netLabelWidth?: number; }): { - placement: NetLabelPlacement | null + placement: NetLabelPlacement | null; testedCandidates: Array<{ - center: { x: number; y: number } - width: number - height: number - bounds: { minX: number; minY: number; maxX: number; maxY: number } - anchor: { x: number; y: number } - orientation: FacingDirection - status: "ok" | "chip-collision" | "trace-collision" | "parallel-to-segment" - hostSegIndex: number - }> - error?: string + center: { x: number; y: number }; + width: number; + height: number; + bounds: { minX: number; minY: number; maxX: number; maxY: number }; + anchor: { x: number; y: number }; + orientation: FacingDirection; + status: "ok" | "chip-collision" | "trace-collision" | "parallel-to-segment"; + hostSegIndex: number; + }>; + error?: string; } { const { inputProblem, @@ -43,26 +43,26 @@ export function solveNetLabelPlacementForPortOnlyPin(params: { overlappingSameNetTraceGroup, availableOrientations, netLabelWidth, - } = params + } = params; - const pinId = overlappingSameNetTraceGroup.portOnlyPinId + const pinId = overlappingSameNetTraceGroup.portOnlyPinId; if (!pinId) { return { placement: null, testedCandidates: [], error: "No portOnlyPinId provided", - } + }; } // Find pin coordinates and facing direction - let pin: { x: number; y: number } | null = null - let pinFacingDirection: FacingDirection | null = null + let pin: { x: number; y: number } | null = null; + let pinFacingDirection: FacingDirection | null = null; for (const chip of inputProblem.chips) { - const p = chip.pins.find((pp) => pp.pinId === pinId) + const p = chip.pins.find((pp) => pp.pinId === pinId); if (p) { - pin = { x: p.x, y: p.y } - pinFacingDirection = p._facingDirection || getPinDirection(p, chip) - break + pin = { x: p.x, y: p.y }; + pinFacingDirection = p._facingDirection || getPinDirection(p, chip); + break; } } if (!pin || !pinFacingDirection) { @@ -70,15 +70,15 @@ export function solveNetLabelPlacementForPortOnlyPin(params: { placement: null, testedCandidates: [], error: `Port-only pin not found: ${pinId}`, - } + }; } const orientations = availableOrientations.length > 0 ? availableOrientations - : (["x+", "x-", "y+", "y-"] as FacingDirection[]) + : (["x+", "x-", "y+", "y-"] as FacingDirection[]); - const anchor = { x: pin.x, y: pin.y } + const anchor = { x: pin.x, y: pin.y }; const outwardOf = (o: FacingDirection) => o === "x+" ? { x: 1, y: 0 } @@ -86,36 +86,36 @@ export function solveNetLabelPlacementForPortOnlyPin(params: { ? { x: -1, y: 0 } : o === "y+" ? { x: 0, y: 1 } - : { x: 0, y: -1 } + : { x: 0, y: -1 }; const testedCandidates: Array<{ - center: { x: number; y: number } - width: number - height: number - bounds: { minX: number; minY: number; maxX: number; maxY: number } - anchor: { x: number; y: number } - orientation: FacingDirection - status: "ok" | "chip-collision" | "trace-collision" | "parallel-to-segment" - hostSegIndex: number - }> = [] + center: { x: number; y: number }; + width: number; + height: number; + bounds: { minX: number; minY: number; maxX: number; maxY: number }; + anchor: { x: number; y: number }; + orientation: FacingDirection; + status: "ok" | "chip-collision" | "trace-collision" | "parallel-to-segment"; + hostSegIndex: number; + }> = []; for (const orientation of orientations) { const { width, height } = getDimsForOrientation({ orientation, netLabelWidth, - }) + }); // Place label fully outside the chip: shift center slightly outward - const baseCenter = getCenterFromAnchor(anchor, orientation, width, height) - const outward = outwardOf(orientation) - const offset = 1e-3 + const baseCenter = getCenterFromAnchor(anchor, orientation, width, height); + const outward = outwardOf(orientation); + const offset = 1e-3; const center = { x: baseCenter.x + outward.x * offset, y: baseCenter.y + outward.y * offset, - } - const bounds = getRectBounds(center, width, height) + }; + const bounds = getRectBounds(center, width, height); // Chip collision check - const chips = chipObstacleSpatialIndex.getChipsInBounds(bounds) + const chips = chipObstacleSpatialIndex.getChipsInBounds(bounds); if (chips.length > 0) { testedCandidates.push({ center, @@ -126,8 +126,8 @@ export function solveNetLabelPlacementForPortOnlyPin(params: { orientation, status: "chip-collision", hostSegIndex: -1, - }) - continue + }); + continue; } // Trace collision check @@ -136,7 +136,7 @@ export function solveNetLabelPlacementForPortOnlyPin(params: { inputTraceMap, "" as MspConnectionPairId, -1, - ) + ); if (traceIntersectionResult.hasIntersection) { testedCandidates.push({ center, @@ -147,8 +147,8 @@ export function solveNetLabelPlacementForPortOnlyPin(params: { orientation, status: "trace-collision", hostSegIndex: -1, - }) - continue + }); + continue; } // Found a valid placement @@ -161,7 +161,7 @@ export function solveNetLabelPlacementForPortOnlyPin(params: { orientation, status: "ok", hostSegIndex: -1, - }) + }); const placement: NetLabelPlacement = { globalConnNetId: overlappingSameNetTraceGroup.globalConnNetId, @@ -174,29 +174,29 @@ export function solveNetLabelPlacementForPortOnlyPin(params: { width, height, center, - } + }; - return { placement, testedCandidates } + return { placement, testedCandidates }; } // If no valid placements found, return placement using pin's facing direction - const fallbackOrientation = pinFacingDirection + const fallbackOrientation = pinFacingDirection; const { width, height } = getDimsForOrientation({ orientation: fallbackOrientation, netLabelWidth, - }) + }); const baseCenter = getCenterFromAnchor( anchor, fallbackOrientation, width, height, - ) - const outward = outwardOf(fallbackOrientation) - const offset = 1e-3 + ); + const outward = outwardOf(fallbackOrientation); + const offset = 1e-3; const fallbackCenter = { x: baseCenter.x + outward.x * offset, y: baseCenter.y + outward.y * offset, - } + }; const fallbackPlacement: NetLabelPlacement = { globalConnNetId: overlappingSameNetTraceGroup.globalConnNetId, @@ -209,7 +209,7 @@ export function solveNetLabelPlacementForPortOnlyPin(params: { width, height, center: fallbackCenter, - } + }; - return { placement: fallbackPlacement, testedCandidates } + return { placement: fallbackPlacement, testedCandidates }; } diff --git a/lib/solvers/SameNetSegmentMergingSolver/SameNetSegmentMergingSolver.ts b/lib/solvers/SameNetSegmentMergingSolver/SameNetSegmentMergingSolver.ts new file mode 100644 index 00000000..be6ec0da --- /dev/null +++ b/lib/solvers/SameNetSegmentMergingSolver/SameNetSegmentMergingSolver.ts @@ -0,0 +1,287 @@ +import { BaseSolver } from "../BaseSolver/BaseSolver" +import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver" +import type { Point } from "@tscircuit/math-utils" + +export interface MergedTracePath extends SolvedTracePath { + originalTracePaths: SolvedTracePath[] +} + +export class SameNetSegmentMergingSolver extends BaseSolver { + solvedTracePaths: SolvedTracePath[] + mergedTracePaths: MergedTracePath[] = [] + mergeThreshold: number + + constructor(params: { + solvedTracePaths: SolvedTracePath[] + mergeThreshold?: number + }) { + super() + this.solvedTracePaths = params.solvedTracePaths + this.mergeThreshold = params.mergeThreshold ?? 0.5 + } + + override getConstructorParams(): ConstructorParameters< + typeof SameNetSegmentMergingSolver + >[0] { + return { + solvedTracePaths: this.solvedTracePaths, + mergeThreshold: this.mergeThreshold, + } + } + + override _step() { + if (!this.solvedTracePaths || this.solvedTracePaths.length === 0) { + this.mergedTracePaths = [] + this.solved = true + return + } + + // Group traces by netId (using netId from the trace's netId property or from pins) + const tracesByNet = new Map() + + for (const trace of this.solvedTracePaths) { + // Try to get netId from different possible locations + let netId: string | undefined = (trace as any).netId + + if (!netId && (trace as any).pins && (trace as any).pins.length > 0) { + // Try to infer netId from the first pin's connections + netId = (trace as any).pins[0]?.netId + } + + if (!netId) { + // Use a default group for traces without netId + netId = "__ungrouped__" + } + + if (!tracesByNet.has(netId)) { + tracesByNet.set(netId, []) + } + tracesByNet.get(netId)!.push(trace) + } + + // Process each net's traces + const allMerged: MergedTracePath[] = [] + for (const [netId, traces] of tracesByNet) { + const merged = this.mergeTracesForNet(traces, netId) + allMerged.push(...merged) + } + + this.mergedTracePaths = allMerged + this.solved = true + } + + private mergeTracesForNet( + traces: SolvedTracePath[], + netId: string, + ): MergedTracePath[] { + if (traces.length <= 1) { + return traces.map((t) => ({ ...t, originalTracePaths: [t] })) + } + + // Extract all segments from all traces + interface Segment { + trace: SolvedTracePath + start: Point + end: Point + index: number + isHorizontal: boolean + } + + const allSegments: Segment[] = [] + + for (const trace of traces) { + if (!trace.tracePath || trace.tracePath.length < 2) continue + for (let i = 0; i < trace.tracePath.length - 1; i++) { + const start = trace.tracePath[i] + const end = trace.tracePath[i + 1] + const isHorizontal = Math.abs(start.y - end.y) < 1e-9 + allSegments.push({ + trace, + start, + end, + index: i, + isHorizontal, + }) + } + } + + if (allSegments.length === 0) { + return traces.map((t) => ({ ...t, originalTracePaths: [t] })) + } + + // Separate horizontal and vertical segments + const horizontalSegments = allSegments.filter((s) => s.isHorizontal) + const verticalSegments = allSegments.filter((s) => !s.isHorizontal) + + // Merge collinear segments + const mergedHorizontal = this.mergeCollinearSegments( + horizontalSegments, + "horizontal", + ) + const mergedVertical = this.mergeCollinearSegments( + verticalSegments, + "vertical", + ) + + // Combine merged segments + const mergedSegments = [...mergedHorizontal, ...mergedVertical] + + // If no merging happened, return original traces + if (mergedSegments.length === allSegments.length) { + return traces.map((t) => ({ ...t, originalTracePaths: [t] })) + } + + // Group merged segments back by original trace + const traceMap = new Map() + for (const seg of mergedSegments) { + if (!traceMap.has(seg.trace)) { + traceMap.set(seg.trace, []) + } + const points = traceMap.get(seg.trace)! + if (points.length === 0) { + points.push(seg.start, seg.end) + } else { + // Check if we need to append or prepend + const firstPoint = points[0] + const lastPoint = points[points.length - 1] + + if (this.pointsEqual(lastPoint, seg.start)) { + points.push(seg.end) + } else if (this.pointsEqual(lastPoint, seg.end)) { + // Already have the end, skip + } else if (this.pointsEqual(firstPoint, seg.end)) { + points.unshift(seg.start) + } else if (this.pointsEqual(firstPoint, seg.start)) { + points.unshift(seg.end) + } else { + // Disconnected segment, add as new + points.push(seg.start, seg.end) + } + } + } + + // Deduplicate consecutive points + for (const [trace, points] of traceMap) { + const uniquePoints: Point[] = [] + for (const point of points) { + if ( + uniquePoints.length === 0 || + !this.pointsEqual(uniquePoints[uniquePoints.length - 1], point) + ) { + uniquePoints.push(point) + } + } + traceMap.set(trace, uniquePoints) + } + + // Create merged trace paths + const result: MergedTracePath[] = [] + for (const [originalTrace, mergedPoints] of traceMap) { + if (mergedPoints.length >= 2) { + result.push({ + ...originalTrace, + tracePath: mergedPoints, + originalTracePaths: [originalTrace], + }) + } else { + result.push({ + ...originalTrace, + originalTracePaths: [originalTrace], + }) + } + } + + return result + } + + private mergeCollinearSegments( + segments: { + trace: SolvedTracePath + start: Point + end: Point + index: number + isHorizontal: boolean + }[], + orientation: "horizontal" | "vertical", + ): typeof segments { + if (segments.length <= 1) return segments + + // Group by the fixed coordinate (Y for horizontal, X for vertical) + const groups = new Map() + + for (const seg of segments) { + const key = orientation === "horizontal" ? seg.start.y : seg.start.x + // Round to avoid floating point issues + const roundedKey = Math.round(key * 1000) / 1000 + if (!groups.has(roundedKey)) { + groups.set(roundedKey, []) + } + groups.get(roundedKey)!.push(seg) + } + + const merged: typeof segments = [] + + for (const [_, group] of groups) { + // Sort by the varying coordinate + const sorted = [...group].sort((a, b) => { + const aStart = orientation === "horizontal" ? a.start.x : a.start.y + const bStart = orientation === "horizontal" ? b.start.x : b.start.y + return aStart - bStart + }) + + // Merge overlapping or close segments + let current = { ...sorted[0] } + + for (let i = 1; i < sorted.length; i++) { + const next = sorted[i] + const currentEnd = + orientation === "horizontal" ? current.end.x : current.end.y + const nextStart = + orientation === "horizontal" ? next.start.x : next.start.y + + if (nextStart - currentEnd <= this.mergeThreshold) { + // Merge: extend current to next's end + if (orientation === "horizontal") { + current.end = { + ...current.end, + x: Math.max(current.end.x, next.end.x), + } + } else { + current.end = { + ...current.end, + y: Math.max(current.end.y, next.end.y), + } + } + // Also extend start if needed + const currentStart = + orientation === "horizontal" ? current.start.x : current.start.y + const nextStartVal = + orientation === "horizontal" ? next.start.x : next.start.y + if (nextStartVal < currentStart) { + if (orientation === "horizontal") { + current.start = { ...current.start, x: nextStartVal } + } else { + current.start = { ...current.start, y: nextStartVal } + } + } + } else { + merged.push(current) + current = { ...next } + } + } + merged.push(current) + } + + return merged + } + + private pointsEqual(p1: Point, p2: Point, tolerance: number = 1e-9): boolean { + return ( + Math.abs(p1.x - p2.x) < tolerance && Math.abs(p1.y - p2.y) < tolerance + ) + } + + override visualize() { + return { lines: [], circles: [], rects: [], points: [], texts: [] } + } +} diff --git a/lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver.ts b/lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver.ts index b9837429..f73f4e89 100644 --- a/lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver.ts +++ b/lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver.ts @@ -1,55 +1,55 @@ -import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver" -import { visualizeInputProblem } from "../SchematicTracePipelineSolver/visualizeInputProblem" -import { getBounds, type GraphicsObject } from "graphics-debug" -import type { InputChip, InputProblem, PinId } from "lib/types/InputProblem" +import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"; +import { visualizeInputProblem } from "../SchematicTracePipelineSolver/visualizeInputProblem"; +import { getBounds, type GraphicsObject } from "graphics-debug"; +import type { InputChip, InputProblem, PinId } from "lib/types/InputProblem"; import type { MspConnectionPair, MspConnectionPairId, -} from "../MspConnectionPairSolver/MspConnectionPairSolver" -import type { ConnectivityMap } from "connectivity-map" -import { SchematicTraceSingleLineSolver2 } from "./SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2" -import type { Guideline } from "../GuidelinesSolver/GuidelinesSolver" -import { visualizeGuidelines } from "../GuidelinesSolver/visualizeGuidelines" -import type { Point } from "@tscircuit/math-utils" +} from "../MspConnectionPairSolver/MspConnectionPairSolver"; +import type { ConnectivityMap } from "connectivity-map"; +import { SchematicTraceSingleLineSolver2 } from "./SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2"; +import type { Guideline } from "../GuidelinesSolver/GuidelinesSolver"; +import { visualizeGuidelines } from "../GuidelinesSolver/visualizeGuidelines"; +import type { Point } from "@tscircuit/math-utils"; export interface SolvedTracePath extends MspConnectionPair { - tracePath: Point[] - mspConnectionPairIds: MspConnectionPairId[] - pinIds: PinId[] + tracePath: Point[]; + mspConnectionPairIds: MspConnectionPairId[]; + pinIds: PinId[]; } export class SchematicTraceLinesSolver extends BaseSolver { - inputProblem: InputProblem - mspConnectionPairs: MspConnectionPair[] + inputProblem: InputProblem; + mspConnectionPairs: MspConnectionPair[]; - dcConnMap: ConnectivityMap - globalConnMap: ConnectivityMap + dcConnMap: ConnectivityMap; + globalConnMap: ConnectivityMap; - queuedConnectionPairs: MspConnectionPair[] - chipMap: Record + queuedConnectionPairs: MspConnectionPair[]; + chipMap: Record; - currentConnectionPair: MspConnectionPair | null = null + currentConnectionPair: MspConnectionPair | null = null; - solvedTracePaths: Array = [] - failedConnectionPairs: Array = [] + solvedTracePaths: Array = []; + failedConnectionPairs: Array = []; - declare activeSubSolver: SchematicTraceSingleLineSolver2 | null + declare activeSubSolver: SchematicTraceSingleLineSolver2 | null; constructor(params: { - mspConnectionPairs: MspConnectionPair[] - chipMap: Record - dcConnMap: ConnectivityMap - globalConnMap: ConnectivityMap - inputProblem: InputProblem + mspConnectionPairs: MspConnectionPair[]; + chipMap: Record; + dcConnMap: ConnectivityMap; + globalConnMap: ConnectivityMap; + inputProblem: InputProblem; }) { - super() - this.inputProblem = params.inputProblem - this.mspConnectionPairs = params.mspConnectionPairs - this.dcConnMap = params.dcConnMap - this.globalConnMap = params.globalConnMap - this.chipMap = params.chipMap - - this.queuedConnectionPairs = [...this.mspConnectionPairs] + super(); + this.inputProblem = params.inputProblem; + this.mspConnectionPairs = params.mspConnectionPairs; + this.dcConnMap = params.dcConnMap; + this.globalConnMap = params.globalConnMap; + this.chipMap = params.chipMap; + + this.queuedConnectionPairs = [...this.mspConnectionPairs]; } override getConstructorParams(): ConstructorParameters< @@ -61,7 +61,7 @@ export class SchematicTraceLinesSolver extends BaseSolver { mspConnectionPairs: this.mspConnectionPairs, dcConnMap: this.dcConnMap, globalConnMap: this.globalConnMap, - } + }; } override _step() { @@ -74,9 +74,9 @@ export class SchematicTraceLinesSolver extends BaseSolver { this.currentConnectionPair!.pins[0].pinId, this.currentConnectionPair!.pins[1].pinId, ], - }) - this.activeSubSolver = null - this.currentConnectionPair = null + }); + this.activeSubSolver = null; + this.currentConnectionPair = null; } if (this.activeSubSolver?.failed) { // Record the failure for this connection and continue to the next pair @@ -84,50 +84,50 @@ export class SchematicTraceLinesSolver extends BaseSolver { this.failedConnectionPairs.push({ ...this.currentConnectionPair, error: this.activeSubSolver.error || undefined, - }) + }); } - this.activeSubSolver = null - this.currentConnectionPair = null + this.activeSubSolver = null; + this.currentConnectionPair = null; // Do not fail the whole solver; proceed to schedule the next pair } if (this.activeSubSolver) { - this.activeSubSolver.step() - return + this.activeSubSolver.step(); + return; } - const connectionPair = this.queuedConnectionPairs.shift() + const connectionPair = this.queuedConnectionPairs.shift(); if (!connectionPair) { - this.solved = true - return + this.solved = true; + return; } - this.currentConnectionPair = connectionPair + this.currentConnectionPair = connectionPair; - const { pins } = connectionPair + const { pins } = connectionPair; this.activeSubSolver = new SchematicTraceSingleLineSolver2({ inputProblem: this.inputProblem, pins, chipMap: this.chipMap, - }) + }); } override visualize(): GraphicsObject { if (this.activeSubSolver) { - return this.activeSubSolver.visualize() + return this.activeSubSolver.visualize(); } const graphics = visualizeInputProblem(this.inputProblem, { chipAlpha: 0.1, connectionAlpha: 0.1, - }) + }); for (const { mspPairId, tracePath } of this.solvedTracePaths) { graphics.lines!.push({ points: tracePath, strokeColor: "green", - }) + }); } // Indicate failed connection pairs with dashed red lines between their pins @@ -139,9 +139,9 @@ export class SchematicTraceLinesSolver extends BaseSolver { ], strokeColor: "red", strokeDash: "4 2", - }) + }); } - return graphics + return graphics; } } diff --git a/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver.ts b/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver.ts index 305e4e06..70d3b191 100644 --- a/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver.ts +++ b/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver.ts @@ -1,83 +1,83 @@ -import { getBounds, type GraphicsObject } from "graphics-debug" -import { ChipObstacleSpatialIndex } from "lib/data-structures/ChipObstacleSpatialIndex" -import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver" -import type { Guideline } from "lib/solvers/GuidelinesSolver/GuidelinesSolver" -import type { MspConnectionPair } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver" -import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem" +import { getBounds, type GraphicsObject } from "graphics-debug"; +import { ChipObstacleSpatialIndex } from "lib/data-structures/ChipObstacleSpatialIndex"; +import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"; +import type { Guideline } from "lib/solvers/GuidelinesSolver/GuidelinesSolver"; +import type { MspConnectionPair } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver"; +import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem"; import type { ChipId, InputChip, InputPin, InputProblem, PinId, -} from "lib/types/InputProblem" -import { calculateElbow } from "calculate-elbow" -import { getPinDirection } from "./getPinDirection" +} from "lib/types/InputProblem"; +import { calculateElbow } from "calculate-elbow"; +import { getPinDirection } from "./getPinDirection"; import { generateElbowVariants, type MovableSegment, -} from "./generateElbowVariants" -import type { Point } from "@tscircuit/math-utils" -import { visualizeGuidelines } from "lib/solvers/GuidelinesSolver/visualizeGuidelines" -import { getInputChipBounds } from "lib/solvers/GuidelinesSolver/getInputChipBounds" -import { getColorFromString } from "lib/utils/getColorFromString" -import { getRestrictedCenterLines } from "./getRestrictedCenterLines" +} from "./generateElbowVariants"; +import type { Point } from "@tscircuit/math-utils"; +import { visualizeGuidelines } from "lib/solvers/GuidelinesSolver/visualizeGuidelines"; +import { getInputChipBounds } from "lib/solvers/GuidelinesSolver/getInputChipBounds"; +import { getColorFromString } from "lib/utils/getColorFromString"; +import { getRestrictedCenterLines } from "./getRestrictedCenterLines"; -type ChipPin = InputPin & { chipId: ChipId } +type ChipPin = InputPin & { chipId: ChipId }; export class SchematicTraceSingleLineSolver extends BaseSolver { - pins: MspConnectionPair["pins"] - inputProblem: InputProblem - guidelines: Guideline[] - chipMap: Record - movableSegments: Array - baseElbow: Point[] + pins: MspConnectionPair["pins"]; + inputProblem: InputProblem; + guidelines: Guideline[]; + chipMap: Record; + movableSegments: Array; + baseElbow: Point[]; - allCandidatePaths: Array - queuedCandidatePaths: Array + allCandidatePaths: Array; + queuedCandidatePaths: Array; - chipObstacleSpatialIndex: ChipObstacleSpatialIndex + chipObstacleSpatialIndex: ChipObstacleSpatialIndex; - solvedTracePath: { x: number; y: number }[] | null = null + solvedTracePath: { x: number; y: number }[] | null = null; // map of pinId -> pin (with chipId attached) - pinIdMap: Map = new Map() + pinIdMap: Map = new Map(); constructor(params: { - pins: MspConnectionPair["pins"] - guidelines: Guideline[] - inputProblem: InputProblem - chipMap: Record + pins: MspConnectionPair["pins"]; + guidelines: Guideline[]; + inputProblem: InputProblem; + chipMap: Record; }) { - super() - this.pins = params.pins - this.inputProblem = params.inputProblem - this.guidelines = params.guidelines - this.chipMap = params.chipMap + super(); + this.pins = params.pins; + this.inputProblem = params.inputProblem; + this.guidelines = params.guidelines; + this.chipMap = params.chipMap; this.chipObstacleSpatialIndex = this.inputProblem._chipObstacleSpatialIndex || - new ChipObstacleSpatialIndex(this.inputProblem.chips) + new ChipObstacleSpatialIndex(this.inputProblem.chips); if (!this.inputProblem._chipObstacleSpatialIndex) { this.inputProblem._chipObstacleSpatialIndex = - this.chipObstacleSpatialIndex + this.chipObstacleSpatialIndex; } // Build a lookup of all pins by id and attach chipId to each pin entry for (const chip of this.inputProblem.chips) { for (const pin of chip.pins) { - this.pinIdMap.set(pin.pinId, { ...pin, chipId: chip.chipId }) + this.pinIdMap.set(pin.pinId, { ...pin, chipId: chip.chipId }); } } for (const pin of this.pins) { if (!pin._facingDirection) { - const chip = this.chipMap[pin.chipId] - pin._facingDirection = getPinDirection(pin, chip) + const chip = this.chipMap[pin.chipId]; + pin._facingDirection = getPinDirection(pin, chip); } } - const [pin1, pin2] = this.pins + const [pin1, pin2] = this.pins; this.baseElbow = calculateElbow( { x: pin1.x, @@ -92,30 +92,30 @@ export class SchematicTraceSingleLineSolver extends BaseSolver { { overshoot: 0.2, }, - ) + ); const { elbowVariants, movableSegments } = generateElbowVariants({ baseElbow: this.baseElbow, guidelines: this.guidelines, maxVariants: 1000, - }) + }); - this.movableSegments = movableSegments + this.movableSegments = movableSegments; const getPathLength = (pts: Point[]) => { - let len = 0 + 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) + 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 - } + return len; + }; - this.allCandidatePaths = [this.baseElbow, ...elbowVariants] + this.allCandidatePaths = [this.baseElbow, ...elbowVariants]; this.queuedCandidatePaths = [...this.allCandidatePaths].sort( (a, b) => getPathLength(a) - getPathLength(b), - ) + ); } override getConstructorParams(): ConstructorParameters< @@ -126,55 +126,55 @@ export class SchematicTraceSingleLineSolver extends BaseSolver { pins: this.pins, guidelines: this.guidelines, inputProblem: this.inputProblem, - } + }; } override _step() { if (this.queuedCandidatePaths.length === 0) { - this.failed = true - this.error = "No more candidate elbows, everything had collisions" - return + this.failed = true; + this.error = "No more candidate elbows, everything had collisions"; + return; } - const nextCandidatePath = this.queuedCandidatePaths.shift()! + const nextCandidatePath = this.queuedCandidatePaths.shift()!; const restrictedCenterLines = getRestrictedCenterLines({ pins: this.pins, inputProblem: this.inputProblem, pinIdMap: this.pinIdMap, chipMap: this.chipMap, - }) + }); // Check if this candidate path is valid - let pathIsValid = true + let pathIsValid = true; for (let i = 0; i < nextCandidatePath.length - 1; i++) { - const start = nextCandidatePath[i] - const end = nextCandidatePath[i + 1] + const start = nextCandidatePath[i]; + const end = nextCandidatePath[i + 1]; // Determine which chips to exclude for this specific segment - let excludeChipIds: string[] = [] + let excludeChipIds: string[] = []; // If this segment would cross any restricted center line, reject the candidate path. - const EPS = 1e-9 + const EPS = 1e-9; for (const [, rcl] of restrictedCenterLines) { if (rcl.axes.has("x") && typeof rcl.x === "number") { // segment strictly crosses vertical center line if ((start.x - rcl.x) * (end.x - rcl.x) < -EPS) { - pathIsValid = false - break + pathIsValid = false; + break; } } if (rcl.axes.has("y") && typeof rcl.y === "number") { // segment strictly crosses horizontal center line if ((start.y - rcl.y) * (end.y - rcl.y) < -EPS) { - pathIsValid = false - break + pathIsValid = false; + break; } } } - if (!pathIsValid) break + if (!pathIsValid) break; // Always exclude chips that contain the start or end points of this segment // if those points are actually pin locations @@ -182,93 +182,93 @@ export class SchematicTraceSingleLineSolver extends BaseSolver { (pin) => Math.abs(pin.x - start.x) < 1e-10 && Math.abs(pin.y - start.y) < 1e-10, - ) + ); const isEndPin = this.pins.some( (pin) => Math.abs(pin.x - end.x) < 1e-10 && Math.abs(pin.y - end.y) < 1e-10, - ) + ); if (isStartPin) { const startPin = this.pins.find( (pin) => Math.abs(pin.x - start.x) < 1e-10 && Math.abs(pin.y - start.y) < 1e-10, - ) + ); if (startPin) { // Enforce that the first segment does not enter the chip interior. - const bounds = getInputChipBounds(this.chipMap[startPin.chipId]) - const dx = end.x - start.x - const dy = end.y - start.y - const EPS = 1e-9 - const onLeft = Math.abs(start.x - bounds.minX) < 1e-9 - const onRight = Math.abs(start.x - bounds.maxX) < 1e-9 - const onBottom = Math.abs(start.y - bounds.minY) < 1e-9 - const onTop = Math.abs(start.y - bounds.maxY) < 1e-9 + const bounds = getInputChipBounds(this.chipMap[startPin.chipId]); + const dx = end.x - start.x; + const dy = end.y - start.y; + const EPS = 1e-9; + const onLeft = Math.abs(start.x - bounds.minX) < 1e-9; + const onRight = Math.abs(start.x - bounds.maxX) < 1e-9; + const onBottom = Math.abs(start.y - bounds.minY) < 1e-9; + const onTop = Math.abs(start.y - bounds.maxY) < 1e-9; const entersInterior = (onLeft && dx > EPS) || (onRight && dx < -EPS) || (onBottom && dy > EPS) || - (onTop && dy < -EPS) + (onTop && dy < -EPS); if (entersInterior) { - pathIsValid = false - break + pathIsValid = false; + break; } if (!excludeChipIds.includes(startPin.chipId)) { - excludeChipIds.push(startPin.chipId) + excludeChipIds.push(startPin.chipId); } } } - if (!pathIsValid) break + if (!pathIsValid) break; if (isEndPin) { const endPin = this.pins.find( (pin) => Math.abs(pin.x - end.x) < 1e-10 && Math.abs(pin.y - end.y) < 1e-10, - ) + ); if (endPin) { // Enforce that the last segment approaches the pin from outside the chip. - const bounds = getInputChipBounds(this.chipMap[endPin.chipId]) - const dx = start.x - end.x // vector from pin outward toward previous point - const dy = start.y - end.y - const EPS = 1e-9 - const onLeft = Math.abs(end.x - bounds.minX) < 1e-9 - const onRight = Math.abs(end.x - bounds.maxX) < 1e-9 - const onBottom = Math.abs(end.y - bounds.minY) < 1e-9 - const onTop = Math.abs(end.y - bounds.maxY) < 1e-9 + const bounds = getInputChipBounds(this.chipMap[endPin.chipId]); + const dx = start.x - end.x; // vector from pin outward toward previous point + const dy = start.y - end.y; + const EPS = 1e-9; + const onLeft = Math.abs(end.x - bounds.minX) < 1e-9; + const onRight = Math.abs(end.x - bounds.maxX) < 1e-9; + const onBottom = Math.abs(end.y - bounds.minY) < 1e-9; + const onTop = Math.abs(end.y - bounds.maxY) < 1e-9; const entersInterior = (onLeft && dx > EPS) || (onRight && dx < -EPS) || (onBottom && dy > EPS) || - (onTop && dy < -EPS) + (onTop && dy < -EPS); if (entersInterior) { - pathIsValid = false - break + pathIsValid = false; + break; } if (!excludeChipIds.includes(endPin.chipId)) { - excludeChipIds.push(endPin.chipId) + excludeChipIds.push(endPin.chipId); } } } - if (!pathIsValid) break + if (!pathIsValid) break; - const obstacleOps = { excludeChipIds } + const obstacleOps = { excludeChipIds }; const intersects = this.chipObstacleSpatialIndex.doesOrthogonalLineIntersectChip( [start, end], obstacleOps, - ) + ); if (intersects) { - pathIsValid = false - break + pathIsValid = false; + break; } } // If this path is valid, use it as the solution if (pathIsValid) { - this.solvedTracePath = nextCandidatePath - this.solved = true + this.solvedTracePath = nextCandidatePath; + this.solved = true; } // If this path is invalid, continue to next step to try the next candidate // The next _step() call will try the next candidate path @@ -278,22 +278,22 @@ export class SchematicTraceSingleLineSolver extends BaseSolver { const graphics = visualizeInputProblem(this.inputProblem, { chipAlpha: 0.1, connectionAlpha: 0.1, - }) + }); - const bounds = getBounds(graphics) - const boundsWidth = bounds.maxX - bounds.minX - const boundsHeight = bounds.maxY - bounds.minY - visualizeGuidelines({ guidelines: this.guidelines, graphics }) + const bounds = getBounds(graphics); + const boundsWidth = bounds.maxX - bounds.minX; + const boundsHeight = bounds.maxY - bounds.minY; + visualizeGuidelines({ guidelines: this.guidelines, graphics }); // Visualize movable segments for (const { start, end, dir } of this.movableSegments) { - const mid = { x: (start.x + end.x) / 2, y: (start.y + end.y) / 2 } - const dist = Math.sqrt((start.x - end.x) ** 2 + (start.y - end.y) ** 2) + const mid = { x: (start.x + end.x) / 2, y: (start.y + end.y) / 2 }; + const dist = Math.sqrt((start.x - end.x) ** 2 + (start.y - end.y) ** 2); graphics.lines!.push({ points: [start, mid, end], strokeColor: "rgba(0,0,255,0.5)", strokeDash: "2 2", - }) + }); graphics.lines!.push({ points: [ mid, @@ -301,7 +301,7 @@ export class SchematicTraceSingleLineSolver extends BaseSolver { ], strokeColor: "rgba(0,0,255,0.5)", strokeDash: "2 2", - }) + }); } // Draw the next candidate path in orange @@ -310,13 +310,13 @@ export class SchematicTraceSingleLineSolver extends BaseSolver { graphics.lines!.push({ points: this.queuedCandidatePaths[0], strokeColor: "orange", - }) + }); } // Visualize all the other queued candidates in faded yellow for (let i = 1; i < this.queuedCandidatePaths.length; i++) { - const candidatePath = this.queuedCandidatePaths[i] - const pi = i / this.queuedCandidatePaths.length + const candidatePath = this.queuedCandidatePaths[i]; + const pi = i / this.queuedCandidatePaths.length; graphics.lines!.push({ points: candidatePath.map((p) => ({ x: p.x + pi * boundsWidth * 0.005, @@ -327,7 +327,7 @@ export class SchematicTraceSingleLineSolver extends BaseSolver { 0.5, ), strokeDash: "8 8", - }) + }); } } @@ -335,9 +335,9 @@ export class SchematicTraceSingleLineSolver extends BaseSolver { graphics.lines!.push({ points: this.solvedTracePath, strokeColor: "green", - }) + }); } - return graphics + return graphics; } } diff --git a/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/generateElbowVariants.ts b/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/generateElbowVariants.ts index 7c5eda74..15b18e19 100644 --- a/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/generateElbowVariants.ts +++ b/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/generateElbowVariants.ts @@ -1,26 +1,26 @@ -import type { Point } from "@tscircuit/math-utils" -import type { Guideline } from "lib/solvers/GuidelinesSolver/GuidelinesSolver" -import { dir, type FacingDirection } from "lib/utils/dir" +import type { Point } from "@tscircuit/math-utils"; +import type { Guideline } from "lib/solvers/GuidelinesSolver/GuidelinesSolver"; +import { dir, type FacingDirection } from "lib/utils/dir"; export interface MovableSegment { - start: Point - end: Point - freedom: "x+" | "x-" | "y+" | "y-" - dir: { x: number; y: number } + start: Point; + end: Point; + freedom: "x+" | "x-" | "y+" | "y-"; + dir: { x: number; y: number }; } // ----- constants & small helpers ------------------------------------------------ -const EPS = 1e-6 // numeric tolerance for equality -const MIN_LEN = 1e-6 // forbid near-zero length segments (adjacent collapses) +const EPS = 1e-6; // numeric tolerance for equality +const MIN_LEN = 1e-6; // forbid near-zero length segments (adjacent collapses) const checkIfUTurnNeeded = (elbow: Point[]): boolean => { - if (elbow.length !== 4) return false + if (elbow.length !== 4) return false; - const [start, p1, p2, end] = elbow + const [start, p1, p2, end] = elbow; // Determine facing directions from the path segments - const startOrient = orientationOf(start, p1) + const startOrient = orientationOf(start, p1); const startFacing: FacingDirection = startOrient === "horizontal" ? p1.x > start.x @@ -28,9 +28,9 @@ const checkIfUTurnNeeded = (elbow: Point[]): boolean => { : "x-" : p1.y > start.y ? "y+" - : "y-" + : "y-"; - const endOrient = orientationOf(p2, end) + const endOrient = orientationOf(p2, end); const endFacing: FacingDirection = endOrient === "horizontal" ? end.x > p2.x @@ -38,159 +38,160 @@ const checkIfUTurnNeeded = (elbow: Point[]): boolean => { : "x-" : end.y > p2.y ? "y+" - : "y-" + : "y-"; // Check if path overshoots and needs to turn back - if (startFacing === "x+" && end.x < p1.x - EPS) return true - if (startFacing === "x-" && end.x > p1.x + EPS) return true - if (startFacing === "y+" && end.y < p1.y - EPS) return true - if (startFacing === "y-" && end.y > p1.y + EPS) return true + if (startFacing === "x+" && end.x < p1.x - EPS) return true; + if (startFacing === "x-" && end.x > p1.x + EPS) return true; + if (startFacing === "y+" && end.y < p1.y - EPS) return true; + if (startFacing === "y-" && end.y > p1.y + EPS) return true; // Check if path segments conflict (p2 is past the end in the facing direction) if (endFacing === "x-" && p2.x > end.x + EPS && startFacing === "x+") - return true + return true; if (endFacing === "x+" && p2.x < end.x - EPS && startFacing === "x-") - return true + return true; if (endFacing === "y-" && p2.y > end.y + EPS && startFacing === "y+") - return true + return true; if (endFacing === "y+" && p2.y < end.y - EPS && startFacing === "y-") - return true + return true; - return false -} + return false; +}; const expandToUTurn = (elbow: Point[]): Point[] => { - if (elbow.length !== 4) return elbow + if (elbow.length !== 4) return elbow; - const [start, p1, p2, end] = elbow + const [start, p1, p2, end] = elbow; // Calculate overshoot distance from existing segments const overshoot = Math.max( Math.abs(start.x - p1.x), Math.abs(start.y - p1.y), 0.2, // minimum overshoot - ) + ); - const startOrient = orientationOf(start, p1) - const expanded: Point[] = [{ ...start }] + const startOrient = orientationOf(start, p1); + const expanded: Point[] = [{ ...start }]; if (startOrient === "horizontal") { - const startDir = p1.x > start.x ? 1 : -1 - expanded.push({ x: start.x + startDir * overshoot, y: start.y }) + const startDir = p1.x > start.x ? 1 : -1; + expanded.push({ x: start.x + startDir * overshoot, y: start.y }); // Move away vertically to create space for U-turn const verticalSpace = Math.max( overshoot * 2, Math.abs(end.y - start.y) + overshoot, - ) + ); const midY = - end.y > start.y ? start.y - verticalSpace : start.y + verticalSpace + end.y > start.y ? start.y - verticalSpace : start.y + verticalSpace; - expanded.push({ x: start.x + startDir * overshoot, y: midY }) + expanded.push({ x: start.x + startDir * overshoot, y: midY }); // Approach end from its required direction - const endOrient = orientationOf(p2, end) + const endOrient = orientationOf(p2, end); const endApproachX = endOrient === "horizontal" ? end.x > p2.x ? end.x - overshoot : end.x + overshoot - : end.x + : end.x; - expanded.push({ x: endApproachX, y: midY }) - expanded.push({ x: endApproachX, y: end.y }) + expanded.push({ x: endApproachX, y: midY }); + expanded.push({ x: endApproachX, y: end.y }); } else { // Vertical start - const startDir = p1.y > start.y ? 1 : -1 - expanded.push({ x: start.x, y: start.y + startDir * overshoot }) + const startDir = p1.y > start.y ? 1 : -1; + expanded.push({ x: start.x, y: start.y + startDir * overshoot }); // Move away horizontally to create space for U-turn const horizontalSpace = Math.max( overshoot * 2, Math.abs(end.x - start.x) + overshoot, - ) + ); const midX = - end.x > start.x ? start.x - horizontalSpace : start.x + horizontalSpace + end.x > start.x ? start.x - horizontalSpace : start.x + horizontalSpace; - expanded.push({ x: midX, y: start.y + startDir * overshoot }) + expanded.push({ x: midX, y: start.y + startDir * overshoot }); // Approach end from its required direction - const endOrient = orientationOf(p2, end) + const endOrient = orientationOf(p2, end); const endApproachY = endOrient === "vertical" ? end.y > p2.y ? end.y - overshoot : end.y + overshoot - : end.y + : end.y; - expanded.push({ x: midX, y: endApproachY }) - expanded.push({ x: end.x, y: endApproachY }) + expanded.push({ x: midX, y: endApproachY }); + expanded.push({ x: end.x, y: endApproachY }); } - expanded.push({ ...end }) - return expanded -} + expanded.push({ ...end }); + return expanded; +}; /** True if segment [a,b] is axis-aligned (horizontal or vertical). */ const isAxisAligned = (a: Point, b: Point): boolean => - Math.abs(a.x - b.x) < EPS || Math.abs(a.y - b.y) < EPS + Math.abs(a.x - b.x) < EPS || Math.abs(a.y - b.y) < EPS; /** Returns 'horizontal' | 'vertical' for a valid orth segment, throws otherwise. */ const orientationOf = (a: Point, b: Point): "horizontal" | "vertical" => { - const dx = Math.abs(a.x - b.x) - const dy = Math.abs(a.y - b.y) - if (dx < EPS && dy >= EPS) return "vertical" - if (dy < EPS && dx >= EPS) return "horizontal" + const dx = Math.abs(a.x - b.x); + const dy = Math.abs(a.y - b.y); + if (dx < EPS && dy >= EPS) return "vertical"; + if (dy < EPS && dx >= EPS) return "horizontal"; // Either both ~0 (degenerate) or both non-zero (non-orthogonal). - throw new Error("Non-orthogonal or degenerate segment detected.") -} + throw new Error("Non-orthogonal or degenerate segment detected."); +}; /** Assert the whole polyline is orthogonal and non-degenerate. */ const assertOrthogonalPolyline = (pts: Point[]): void => { if (pts.length < 2) { - throw new Error("Polyline must have at least two points.") + throw new Error("Polyline must have at least two points."); } for (let i = 0; i < pts.length - 1; i++) { - const a = pts[i] - const b = pts[i + 1] + const a = pts[i]; + const b = pts[i + 1]; if (!isAxisAligned(a, b)) { - throw new Error("Polyline contains a non-orthogonal segment.") + throw new Error("Polyline contains a non-orthogonal segment."); } - const manhattan = Math.abs(a.x - b.x) + Math.abs(a.y - b.y) + const manhattan = Math.abs(a.x - b.x) + Math.abs(a.y - b.y); if (manhattan < MIN_LEN) { - throw new Error("Polyline contains a near-zero length segment.") + throw new Error("Polyline contains a near-zero length segment."); } } -} +}; -type Axis = "x" | "y" +type Axis = "x" | "y"; /** Remove near-duplicates and sort numerically (tolerance aware). */ const uniqSortedWithTol = (vals: number[], tol = EPS): number[] => { - const sorted = vals.slice().sort((a, b) => a - b) - const out: number[] = [] + const sorted = vals.slice().sort((a, b) => a - b); + const out: number[] = []; for (const v of sorted) { - if (out.length === 0 || Math.abs(v - out[out.length - 1]) > tol) out.push(v) + if (out.length === 0 || Math.abs(v - out[out.length - 1]) > tol) + out.push(v); } - return out -} + return out; +}; /** Stringify a polyline with rounding to dedupe variants robustly. */ const keyForPolyline = (pts: Point[], decimals = 9): string => - pts.map((p) => `${p.x.toFixed(decimals)},${p.y.toFixed(decimals)}`).join("|") + pts.map((p) => `${p.x.toFixed(decimals)},${p.y.toFixed(decimals)}`).join("|"); /** Remove consecutive duplicate points (within tolerance) to eliminate zero-length segments. */ const preprocessElbow = (pts: Point[], tol = MIN_LEN): Point[] => { - if (pts.length === 0) return [] - const out: Point[] = [{ ...pts[0] }] + if (pts.length === 0) return []; + const out: Point[] = [{ ...pts[0] }]; for (let i = 1; i < pts.length; i++) { - const p = pts[i] - const last = out[out.length - 1] - const manhattan = Math.abs(p.x - last.x) + Math.abs(p.y - last.y) - if (manhattan > tol) out.push({ ...p }) + const p = pts[i]; + const last = out[out.length - 1]; + const manhattan = Math.abs(p.x - last.x) + Math.abs(p.y - last.y); + if (manhattan > tol) out.push({ ...p }); } - return out -} + return out; +}; /** Collect guideline coordinate candidates for a given movement axis. */ const collectAxisCandidates = ( @@ -198,24 +199,24 @@ const collectAxisCandidates = ( guidelines: Guideline[], currentCoord: number, ): number[] => { - const positions: number[] = [] + const positions: number[] = []; for (const g of guidelines) { if (axis === "x") { // moving horizontally: we can snap to vertical guidelines (fixed x) if (g.orientation === "vertical" && typeof (g as any).x === "number") { - positions.push((g as any).x as number) + positions.push((g as any).x as number); } } else { // moving vertically: we can snap to horizontal guidelines (fixed y) if (g.orientation === "horizontal" && typeof (g as any).y === "number") { - positions.push((g as any).y as number) + positions.push((g as any).y as number); } } } // Include current coordinate to allow "no-move" option. - positions.push(currentCoord) - return uniqSortedWithTol(positions) -} + positions.push(currentCoord); + return uniqSortedWithTol(positions); +}; /** Compute the axis and facing direction used for a sliding move. */ const computeFreedom = ( @@ -223,17 +224,17 @@ const computeFreedom = ( start: Point, end: Point, ): { axis: Axis; facing: FacingDirection } => { - const orient = orientationOf(start, end) + const orient = orientationOf(start, end); if (orient === "horizontal") { // slide vertically; choose facing away from previous vertex - const facing: FacingDirection = prev.y <= start.y ? "y+" : "y-" - return { axis: "y", facing } + const facing: FacingDirection = prev.y <= start.y ? "y+" : "y-"; + return { axis: "y", facing }; } else { // vertical: slide horizontally; choose facing away from previous vertex - const facing: FacingDirection = prev.x <= start.x ? "x+" : "x-" - return { axis: "x", facing } + const facing: FacingDirection = prev.x <= start.x ? "x+" : "x-"; + return { axis: "x", facing }; } -} +}; /** Safe Cartesian product for small arrays. Returns [[]] for empty input. */ const cartesian = (arrays: T[][]): T[][] => @@ -242,7 +243,7 @@ const cartesian = (arrays: T[][]): T[][] => : arrays.reduce( (acc, curr) => acc.flatMap((a) => curr.map((c) => [...a, c])), [[]], - ) + ); // ----- main implementation ------------------------------------------------------ @@ -262,54 +263,54 @@ export const generateElbowVariants = ({ guidelines, maxVariants = 10000, }: { - baseElbow: Point[] - guidelines: Guideline[] - maxVariants?: number + baseElbow: Point[]; + guidelines: Guideline[]; + maxVariants?: number; }): { - elbowVariants: Array - movableSegments: Array + elbowVariants: Array; + movableSegments: Array; } => { // 1) Preprocess to remove zero-length segments, then validate the input path. - const elbow = preprocessElbow(baseElbow) - assertOrthogonalPolyline(elbow) + const elbow = preprocessElbow(baseElbow); + assertOrthogonalPolyline(elbow); - const nPts = elbow.length + const nPts = elbow.length; // No interior segments to move if path is too short. if (nPts < 4) { return { elbowVariants: [elbow.map((p) => ({ ...p }))], movableSegments: [], - } + }; } else if (nPts === 4) { - const needsUTurn = checkIfUTurnNeeded(elbow) + const needsUTurn = checkIfUTurnNeeded(elbow); if (needsUTurn) { - const expandedElbow = expandToUTurn(elbow) + const expandedElbow = expandToUTurn(elbow); // Recursively call with the expanded path - return generateElbowVariants({ baseElbow: expandedElbow, guidelines }) + return generateElbowVariants({ baseElbow: expandedElbow, guidelines }); } } // 2) Choose which segments are allowed to move. // We avoid moving the very first and last segments of the: // movable indices i in [1 .. (nPts - 3)] inclusive (segment i connects P[i] -> P[i+1]). - const firstMovableIndex = 1 - const lastMovableIndex = nPts - 3 + const firstMovableIndex = 1; + const lastMovableIndex = nPts - 3; - const movableSegments: MovableSegment[] = [] - const movableIdx: number[] = [] - const axes: Axis[] = [] - const optionsPerSegment: number[][] = [] + const movableSegments: MovableSegment[] = []; + const movableIdx: number[] = []; + const axes: Axis[] = []; + const optionsPerSegment: number[][] = []; for (let i = firstMovableIndex; i <= lastMovableIndex; i++) { - const prev = elbow[i - 1] - const start = elbow[i] - const end = elbow[i + 1] - const next2 = elbow[i + 2] + const prev = elbow[i - 1]; + const start = elbow[i]; + const end = elbow[i + 1]; + const next2 = elbow[i + 2]; // Ensure the three segments around this joint are orth and valid. // (orientationOf throws if non-orth.) - const { axis, facing } = computeFreedom(prev, start, end) + const { axis, facing } = computeFreedom(prev, start, end); // Build the MovableSegment descriptor (purely informational). movableSegments.push({ @@ -317,93 +318,93 @@ export const generateElbowVariants = ({ end: { ...end }, freedom: facing, dir: dir(facing), - }) - movableIdx.push(i) - axes.push(axis) + }); + movableIdx.push(i); + axes.push(axis); // Collect guideline candidates on the move axis and // filter out positions that would collapse the adjacent vertical/horizontal segments. - const currentCoord = axis === "x" ? start.x : start.y - const rawCandidates = collectAxisCandidates(axis, guidelines, currentCoord) + const currentCoord = axis === "x" ? start.x : start.y; + const rawCandidates = collectAxisCandidates(axis, guidelines, currentCoord); const filtered = rawCandidates.filter((pos) => { if (axis === "y") { // Moving horizontal segment vertically: check neighbors (which are vertical). - const lenPrev = Math.abs(prev.y - pos) - const lenNext = Math.abs(next2.y - pos) - return lenPrev > MIN_LEN && lenNext > MIN_LEN + const lenPrev = Math.abs(prev.y - pos); + const lenNext = Math.abs(next2.y - pos); + return lenPrev > MIN_LEN && lenNext > MIN_LEN; } else { // Moving vertical segment horizontally: check neighbors (which are horizontal). - const lenPrev = Math.abs(prev.x - pos) - const lenNext = Math.abs(next2.x - pos) - return lenPrev > MIN_LEN && lenNext > MIN_LEN + const lenPrev = Math.abs(prev.x - pos); + const lenNext = Math.abs(next2.x - pos); + return lenPrev > MIN_LEN && lenNext > MIN_LEN; } - }) + }); // If all candidates would collapse something, keep none (this segment effectively fixed). // We still include current position if it was valid (it should be, due to base validation). - optionsPerSegment.push(filtered) + optionsPerSegment.push(filtered); } // 3) Generate combinations (Cartesian product of positions for each movable segment). // If there are no movable segments or no valid options, this returns [[]], // which yields the base polyline variant only. - const combos = cartesian(optionsPerSegment) + const combos = cartesian(optionsPerSegment); // 4) Apply each combination to produce variants, ensure orthogonality, dedupe. - const seen = new Set() - const elbowVariants: Point[][] = [] + const seen = new Set(); + const elbowVariants: Point[][] = []; // Always include the base elbow first. { - const key = keyForPolyline(elbow) - seen.add(key) - elbowVariants.push(elbow.map((p) => ({ ...p }))) + const key = keyForPolyline(elbow); + seen.add(key); + elbowVariants.push(elbow.map((p) => ({ ...p }))); } for (const combo of combos) { // Skip the "do nothing" combo if it matches the base (we already added it). - if (combo.length === 0) continue + if (combo.length === 0) continue; - const variant = elbow.map((p) => ({ ...p })) + const variant = elbow.map((p) => ({ ...p })); // Slide each selected segment perpendicular to its orientation. for (let k = 0; k < movableIdx.length; k++) { - const segIndex = movableIdx[k] // segment connects [i] -> [i+1] - const axis = axes[k] - const newPos = combo[k] + const segIndex = movableIdx[k]; // segment connects [i] -> [i+1] + const axis = axes[k]; + const newPos = combo[k]; - if (typeof newPos !== "number" || Number.isNaN(newPos)) continue + if (typeof newPos !== "number" || Number.isNaN(newPos)) continue; if (axis === "y") { - variant[segIndex] = { ...variant[segIndex], y: newPos } - variant[segIndex + 1] = { ...variant[segIndex + 1], y: newPos } + variant[segIndex] = { ...variant[segIndex], y: newPos }; + variant[segIndex + 1] = { ...variant[segIndex + 1], y: newPos }; } else { - variant[segIndex] = { ...variant[segIndex], x: newPos } - variant[segIndex + 1] = { ...variant[segIndex + 1], x: newPos } + variant[segIndex] = { ...variant[segIndex], x: newPos }; + variant[segIndex + 1] = { ...variant[segIndex + 1], x: newPos }; } } // Validate (belt-and-suspenders): reject anything non-orth or collapsed. try { - assertOrthogonalPolyline(variant) + assertOrthogonalPolyline(variant); } catch { // Should never happen with this construction, but we refuse to emit it. - continue + continue; } // Dedupe. - const key = keyForPolyline(variant) + const key = keyForPolyline(variant); if (!seen.has(key)) { - seen.add(key) - elbowVariants.push(variant) + seen.add(key); + elbowVariants.push(variant); // Stop if we've hit the maximum number of variants if (elbowVariants.length >= maxVariants) { - break + break; } } } - return { elbowVariants, movableSegments } -} + return { elbowVariants, movableSegments }; +}; diff --git a/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getPinDirection.ts b/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getPinDirection.ts index f7c29ea4..69b223d2 100644 --- a/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getPinDirection.ts +++ b/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getPinDirection.ts @@ -1,42 +1,42 @@ -import type { InputChip, InputPin } from "lib/types/InputProblem" +import type { InputChip, InputPin } from "lib/types/InputProblem"; export const getPinDirection = ( pin: InputPin, chip: InputChip, ): "x+" | "x-" | "y+" | "y-" => { // Determine what edge the pin lies on - const { x, y } = pin - const { center, width, height } = chip + const { x, y } = pin; + const { center, width, height } = chip; - const yPlusEdge = center.y + height / 2 - const yMinusEdge = center.y - height / 2 - const xPlusEdge = center.x + width / 2 - const xMinusEdge = center.x - width / 2 + const yPlusEdge = center.y + height / 2; + const yMinusEdge = center.y - height / 2; + const xPlusEdge = center.x + width / 2; + const xMinusEdge = center.x - width / 2; // Which edge is the pin closest to? - const yPlusDistance = yPlusEdge - y - const yMinusDistance = y - yMinusEdge - const xPlusDistance = xPlusEdge - x - const xMinusDistance = x - xMinusEdge + const yPlusDistance = yPlusEdge - y; + const yMinusDistance = y - yMinusEdge; + const xPlusDistance = xPlusEdge - x; + const xMinusDistance = x - xMinusEdge; const minDistance = Math.min( yPlusDistance, yMinusDistance, xPlusDistance, xMinusDistance, - ) + ); if (minDistance === yPlusDistance) { - return "y+" + return "y+"; } if (minDistance === yMinusDistance) { - return "y-" + return "y-"; } if (minDistance === xPlusDistance) { - return "x+" + return "x+"; } - return "x-" -} + return "x-"; +}; diff --git a/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getRestrictedCenterLines.ts b/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getRestrictedCenterLines.ts index 50d9ddad..46555295 100644 --- a/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getRestrictedCenterLines.ts +++ b/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getRestrictedCenterLines.ts @@ -4,10 +4,10 @@ import type { InputPin, InputProblem, PinId, -} from "lib/types/InputProblem" -import { getPinDirection } from "./getPinDirection" +} from "lib/types/InputProblem"; +import { getPinDirection } from "./getPinDirection"; -type ChipPin = InputPin & { chipId: ChipId } +type ChipPin = InputPin & { chipId: ChipId }; /** * A line that should not be crossed by a trace segment. @@ -16,124 +16,124 @@ type ChipPin = InputPin & { chipId: ChipId } * has multiple pins on at least one side. */ export type RestrictedCenterLine = { - x?: number - y?: number - axes: Set<"x" | "y"> -} + x?: number; + y?: number; + axes: Set<"x" | "y">; +}; export const getRestrictedCenterLines = (params: { - pins: Array - inputProblem: InputProblem - pinIdMap: Map - chipMap: Record + pins: Array; + inputProblem: InputProblem; + pinIdMap: Map; + chipMap: Record; }): Map => { - const { pins, inputProblem, pinIdMap, chipMap } = params + const { pins, inputProblem, pinIdMap, chipMap } = params; // Determine set of related pin IDs (closure over directConnections) for both endpoints const findAllDirectlyConnectedPins = (startPinId: string) => { - const visited = new Set() - const queue: string[] = [startPinId] - visited.add(startPinId) - const directConns = inputProblem.directConnections || [] + const visited = new Set(); + const queue: string[] = [startPinId]; + visited.add(startPinId); + const directConns = inputProblem.directConnections || []; while (queue.length) { - const cur = queue.shift()! + const cur = queue.shift()!; for (const dc of directConns) { if (dc.pinIds.includes(cur)) { for (const p of dc.pinIds) { if (!visited.has(p)) { - visited.add(p) - queue.push(p) + visited.add(p); + queue.push(p); } } } } } - return visited - } + return visited; + }; - const p0 = pins[0].pinId - const p1 = pins[1].pinId + const p0 = pins[0].pinId; + const p1 = pins[1].pinId; const relatedPinIds = new Set([ ...findAllDirectlyConnectedPins(p0), ...findAllDirectlyConnectedPins(p1), - ]) + ]); - const restrictedCenterLines = new Map() + const restrictedCenterLines = new Map(); // Collect facing-signs per chip const chipFacingMap = new Map< string, { - hasXPos?: boolean - hasXNeg?: boolean - hasYPos?: boolean - hasYNeg?: boolean - center: { x: number; y: number } - counts?: { xPos: number; xNeg: number; yPos: number; yNeg: number } + hasXPos?: boolean; + hasXNeg?: boolean; + hasYPos?: boolean; + hasYNeg?: boolean; + center: { x: number; y: number }; + counts?: { xPos: number; xNeg: number; yPos: number; yNeg: number }; } - >() + >(); - const chipsOfFacingPins = new Set(pins.map((p) => p.chipId)) + const chipsOfFacingPins = new Set(pins.map((p) => p.chipId)); for (const pinId of relatedPinIds) { - const pin = pinIdMap.get(pinId) - if (!pin) continue - const chip = chipMap[pin.chipId] - if (!chip) continue - const facing = pin._facingDirection ?? getPinDirection(pin, chip) - let entry = chipFacingMap.get(chip.chipId) + const pin = pinIdMap.get(pinId); + if (!pin) continue; + const chip = chipMap[pin.chipId]; + if (!chip) continue; + const facing = pin._facingDirection ?? getPinDirection(pin, chip); + let entry = chipFacingMap.get(chip.chipId); if (!entry) { - entry = { center: chip.center } + entry = { center: chip.center }; - const counts = { xPos: 0, xNeg: 0, yPos: 0, yNeg: 0 } + const counts = { xPos: 0, xNeg: 0, yPos: 0, yNeg: 0 }; for (const cp of chip.pins) { - const cpFacing = cp._facingDirection ?? getPinDirection(cp, chip) - if (cpFacing === "x+") counts.xPos++ - if (cpFacing === "x-") counts.xNeg++ - if (cpFacing === "y+") counts.yPos++ - if (cpFacing === "y-") counts.yNeg++ + const cpFacing = cp._facingDirection ?? getPinDirection(cp, chip); + if (cpFacing === "x+") counts.xPos++; + if (cpFacing === "x-") counts.xNeg++; + if (cpFacing === "y+") counts.yPos++; + if (cpFacing === "y-") counts.yNeg++; } - entry.counts = counts + entry.counts = counts; - chipFacingMap.set(chip.chipId, entry) + chipFacingMap.set(chip.chipId, entry); } - if (facing === "x+") entry.hasXPos = true - if (facing === "x-") entry.hasXNeg = true - if (facing === "y+") entry.hasYPos = true - if (facing === "y-") entry.hasYNeg = true + if (facing === "x+") entry.hasXPos = true; + if (facing === "x-") entry.hasXNeg = true; + if (facing === "y+") entry.hasYPos = true; + if (facing === "y-") entry.hasYNeg = true; } // Only mark a center as restricted on an axis if both signs for that axis // are present among related pins on the chip. for (const [chipId, faces] of chipFacingMap) { - const axes = new Set<"x" | "y">() - const rcl: RestrictedCenterLine = { axes } + const axes = new Set<"x" | "y">(); + const rcl: RestrictedCenterLine = { axes }; // determine whether any side on this chip has more than one pin - const counts = faces.counts + const counts = faces.counts; const anySideHasMultiplePins = !!( counts && (counts.xPos > 1 || counts.xNeg > 1 || counts.yPos > 1 || counts.yNeg > 1) - ) + ); const skipCenterRestriction = - !anySideHasMultiplePins && chipsOfFacingPins.has(chipId) + !anySideHasMultiplePins && chipsOfFacingPins.has(chipId); if (!skipCenterRestriction) { if (faces.hasXPos && faces.hasXNeg) { - rcl.x = faces.center.x - axes.add("x") + rcl.x = faces.center.x; + axes.add("x"); } if (faces.hasYPos && faces.hasYNeg) { - rcl.y = faces.center.y - axes.add("y") + rcl.y = faces.center.y; + axes.add("y"); } } if (axes.size > 0) { - restrictedCenterLines.set(chipId, rcl) + restrictedCenterLines.set(chipId, rcl); } } - return restrictedCenterLines -} + return restrictedCenterLines; +}; diff --git a/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts b/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts index 6795ff79..f87260f7 100644 --- a/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts +++ b/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts @@ -1,68 +1,68 @@ -import type { GraphicsObject } from "graphics-debug" -import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem" -import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver" -import type { MspConnectionPair } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver" +import type { GraphicsObject } from "graphics-debug"; +import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem"; +import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"; +import type { MspConnectionPair } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver"; import type { ChipId, InputChip, InputProblem, PinId, -} from "lib/types/InputProblem" -import type { Point } from "@tscircuit/math-utils" -import { calculateElbow } from "calculate-elbow" -import { getPinDirection } from "../SchematicTraceSingleLineSolver/getPinDirection" -import { getObstacleRects, type ChipWithBounds } from "./rect" -import { findFirstCollision, isHorizontal, isVertical } from "./collisions" +} from "lib/types/InputProblem"; +import type { Point } from "@tscircuit/math-utils"; +import { calculateElbow } from "calculate-elbow"; +import { getPinDirection } from "../SchematicTraceSingleLineSolver/getPinDirection"; +import { getObstacleRects, type ChipWithBounds } from "./rect"; +import { findFirstCollision, isHorizontal, isVertical } from "./collisions"; import { aabbFromPoints, candidateMidsFromSet, midBetweenPointAndRect, type Axis, -} from "./mid" -import { pathKey, shiftSegmentOrth } from "./pathOps" +} from "./mid"; +import { pathKey, shiftSegmentOrth } from "./pathOps"; -type PathKey = string +type PathKey = string; export class SchematicTraceSingleLineSolver2 extends BaseSolver { - pins: MspConnectionPair["pins"] - inputProblem: InputProblem - chipMap: Record + pins: MspConnectionPair["pins"]; + inputProblem: InputProblem; + chipMap: Record; - obstacles: ChipWithBounds[] - rectById: Map - aabb: { minX: number; maxX: number; minY: number; maxY: number } + obstacles: ChipWithBounds[]; + rectById: Map; + aabb: { minX: number; maxX: number; minY: number; maxY: number }; - baseElbow: Point[] + baseElbow: Point[]; - solvedTracePath: Point[] | null = null + solvedTracePath: Point[] | null = null; - private queue: Array<{ path: Point[]; collisionChipIds: Set }> = [] - private visited: Set = new Set() + private queue: Array<{ path: Point[]; collisionChipIds: Set }> = []; + private visited: Set = new Set(); constructor(params: { - pins: MspConnectionPair["pins"] - inputProblem: InputProblem - chipMap: Record + pins: MspConnectionPair["pins"]; + inputProblem: InputProblem; + chipMap: Record; }) { - super() - this.pins = params.pins - this.inputProblem = params.inputProblem - this.chipMap = params.chipMap + super(); + this.pins = params.pins; + this.inputProblem = params.inputProblem; + this.chipMap = params.chipMap; // Ensure facing directions are present for (const pin of this.pins) { if (!pin._facingDirection) { - const chip = this.chipMap[pin.chipId] - pin._facingDirection = getPinDirection(pin, chip) + const chip = this.chipMap[pin.chipId]; + pin._facingDirection = getPinDirection(pin, chip); } } // Build obstacle rects from chips - this.obstacles = getObstacleRects(this.inputProblem) - this.rectById = new Map(this.obstacles.map((r) => [r.chipId, r])) + this.obstacles = getObstacleRects(this.inputProblem); + this.rectById = new Map(this.obstacles.map((r) => [r.chipId, r])); // Build initial elbow path - const [pin1, pin2] = this.pins + const [pin1, pin2] = this.pins; this.baseElbow = calculateElbow( { x: pin1.x, @@ -75,17 +75,17 @@ export class SchematicTraceSingleLineSolver2 extends BaseSolver { facingDirection: pin2._facingDirection!, }, { overshoot: 0.2 }, - ) + ); // Bounds defined by PA and PB this.aabb = aabbFromPoints( { x: pin1.x, y: pin1.y }, { x: pin2.x, y: pin2.y }, - ) + ); // Seed search - this.queue.push({ path: this.baseElbow, collisionChipIds: new Set() }) - this.visited.add(pathKey(this.baseElbow)) + this.queue.push({ path: this.baseElbow, collisionChipIds: new Set() }); + this.visited.add(pathKey(this.baseElbow)); } override getConstructorParams(): ConstructorParameters< @@ -95,102 +95,102 @@ export class SchematicTraceSingleLineSolver2 extends BaseSolver { chipMap: this.chipMap, pins: this.pins, inputProblem: this.inputProblem, - } + }; } private axisOfSegment(a: Point, b: Point): Axis | null { - if (isVertical(a, b)) return "x" - if (isHorizontal(a, b)) return "y" - return null + if (isVertical(a, b)) return "x"; + if (isHorizontal(a, b)) return "y"; + return null; } private pathLength(pts: Point[]): number { - let sum = 0 + let sum = 0; for (let i = 0; i < pts.length - 1; i++) { sum += Math.abs(pts[i + 1]!.x - pts[i]!.x) + - Math.abs(pts[i + 1]!.y - pts[i]!.y) + Math.abs(pts[i + 1]!.y - pts[i]!.y); } - return sum + return sum; } override _step() { if (this.solvedTracePath) { - this.solved = true - return + this.solved = true; + return; } - const state = this.queue.shift() + const state = this.queue.shift(); if (!state) { - this.failed = true - this.error = "No collision-free path found" - return + this.failed = true; + this.error = "No collision-free path found"; + return; } - const { path, collisionChipIds } = state + const { path, collisionChipIds } = state; - const [PA, PB] = this.pins - const collision = findFirstCollision(path, this.obstacles) + const [PA, PB] = this.pins; + const collision = findFirstCollision(path, this.obstacles); if (!collision) { // Sanity check: ensure path still connects PA -> PB - const first = path[0]! - const last = path[path.length - 1]! - const EPS = 1e-9 + const first = path[0]!; + const last = path[path.length - 1]!; + const EPS = 1e-9; const samePoint = (p: Point, q: Point) => - Math.abs(p.x - q.x) < EPS && Math.abs(p.y - q.y) < EPS + Math.abs(p.x - q.x) < EPS && Math.abs(p.y - q.y) < EPS; if ( samePoint(first, { x: PA.x, y: PA.y }) && samePoint(last, { x: PB.x, y: PB.y }) ) { - this.solvedTracePath = path - this.solved = true + this.solvedTracePath = path; + this.solved = true; } - return + return; } - let { segIndex, rect } = collision + let { segIndex, rect } = collision; // Never move the first or last segments - move adjacent segment instead - const isFirstSegment = segIndex === 0 - const isLastSegment = segIndex === path.length - 2 + const isFirstSegment = segIndex === 0; + const isLastSegment = segIndex === path.length - 2; if (isFirstSegment) { // If first segment collides, move the second segment instead if (path.length < 3) { // Path too short to have an adjacent segment to move - return + return; } - segIndex = 1 + segIndex = 1; } else if (isLastSegment) { // If last segment collides, move the second-to-last segment instead if (path.length < 3) { // Path too short to have an adjacent segment to move - return + return; } - segIndex = path.length - 3 + segIndex = path.length - 3; } - const a = path[segIndex]! - const b = path[segIndex + 1]! - const axis = this.axisOfSegment(a, b) + const a = path[segIndex]!; + const b = path[segIndex + 1]!; + const axis = this.axisOfSegment(a, b); if (!axis) { // Should not happen for elbow paths - return + return; } // Note: PA and PB are already defined above - const candidates: number[] = [] + const candidates: number[] = []; if (collisionChipIds.size === 0) { // First collision on this search branch: use mid(PA, C) and mid(PB, C) - const m1 = midBetweenPointAndRect(axis, { x: PA.x, y: PA.y }, rect) - const m2 = midBetweenPointAndRect(axis, { x: PB.x, y: PB.y }, rect) + const m1 = midBetweenPointAndRect(axis, { x: PA.x, y: PA.y }, rect); + const m2 = midBetweenPointAndRect(axis, { x: PB.x, y: PB.y }, rect); // Combine and deduplicate candidates - const allCandidates = [...m1, ...m2] - const uniqueCandidates = [...new Set(allCandidates)] - candidates.push(...uniqueCandidates) + const allCandidates = [...m1, ...m2]; + const uniqueCandidates = [...new Set(allCandidates)]; + candidates.push(...uniqueCandidates); } else { // Subsequent collisions: mid between C and nearest rect/bounds from the set const mids = candidateMidsFromSet( @@ -199,32 +199,32 @@ export class SchematicTraceSingleLineSolver2 extends BaseSolver { this.rectById, collisionChipIds, this.aabb, - ) - candidates.push(...mids) + ); + candidates.push(...mids); } // Generate new shifted paths, order by total path length (shorter first) const newStates: Array<{ - path: Point[] - collisionRectIds: Set - len: number - }> = [] + path: Point[]; + collisionRectIds: Set; + len: number; + }> = []; for (const coord of candidates) { - const newPath = shiftSegmentOrth(path, segIndex, axis, coord) - if (!newPath) continue - const key = pathKey(newPath) - if (this.visited.has(key)) continue - this.visited.add(key) - const nextSet = new Set(collisionChipIds) - nextSet.add(rect.chipId) - const len = this.pathLength(newPath) - newStates.push({ path: newPath, collisionRectIds: nextSet, len }) + const newPath = shiftSegmentOrth(path, segIndex, axis, coord); + if (!newPath) continue; + const key = pathKey(newPath); + if (this.visited.has(key)) continue; + this.visited.add(key); + const nextSet = new Set(collisionChipIds); + nextSet.add(rect.chipId); + const len = this.pathLength(newPath); + newStates.push({ path: newPath, collisionRectIds: nextSet, len }); } - newStates.sort((a, b) => a.len - b.len) + newStates.sort((a, b) => a.len - b.len); for (const st of newStates) { - this.queue.push({ path: st.path, collisionChipIds: st.collisionRectIds }) + this.queue.push({ path: st.path, collisionChipIds: st.collisionRectIds }); } } @@ -232,7 +232,7 @@ export class SchematicTraceSingleLineSolver2 extends BaseSolver { const g = visualizeInputProblem(this.inputProblem, { chipAlpha: 0.1, connectionAlpha: 0.1, - }) + }); // Draw the base elbow @@ -240,10 +240,10 @@ export class SchematicTraceSingleLineSolver2 extends BaseSolver { points: this.baseElbow, strokeColor: "red", strokeDash: "4 4", - }) + }); // Draw the MSP pair connection with a dashed line - const [pin1, pin2] = this.pins + const [pin1, pin2] = this.pins; g.lines!.push({ points: [ { x: pin1.x, y: pin1.y }, @@ -251,20 +251,20 @@ export class SchematicTraceSingleLineSolver2 extends BaseSolver { ], strokeColor: "blue", strokeDash: "5 5", - }) + }); // Draw all the new candidates for (const { path, collisionChipIds: collisionRectIds } of this.queue) { - g.lines!.push({ points: path, strokeColor: "teal", strokeDash: "2 2" }) + g.lines!.push({ points: path, strokeColor: "teal", strokeDash: "2 2" }); } if (this.solvedTracePath) { - g.lines!.push({ points: this.solvedTracePath, strokeColor: "green" }) + g.lines!.push({ points: this.solvedTracePath, strokeColor: "green" }); } else if (this.queue.length > 0) { // Show next candidate to be explored - g.lines!.push({ points: this.queue[0]!.path, strokeColor: "orange" }) + g.lines!.push({ points: this.queue[0]!.path, strokeColor: "orange" }); } - return g + return g; } } diff --git a/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions.ts b/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions.ts index 7aa58722..41aa45df 100644 --- a/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions.ts +++ b/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions.ts @@ -1,12 +1,12 @@ -import type { Point } from "@tscircuit/math-utils" -import type { ChipWithBounds } from "./rect" +import type { Point } from "@tscircuit/math-utils"; +import type { ChipWithBounds } from "./rect"; -const EPS = 1e-9 +const EPS = 1e-9; export const isVertical = (a: Point, b: Point, eps = EPS) => - Math.abs(a.x - b.x) < eps + Math.abs(a.x - b.x) < eps; export const isHorizontal = (a: Point, b: Point, eps = EPS) => - Math.abs(a.y - b.y) < eps + Math.abs(a.y - b.y) < eps; export const segmentIntersectsRect = ( a: Point, @@ -14,47 +14,47 @@ export const segmentIntersectsRect = ( r: ChipWithBounds, eps = EPS, ): boolean => { - const vert = isVertical(a, b, eps) - const horz = isHorizontal(a, b, eps) - if (!vert && !horz) return false + const vert = isVertical(a, b, eps); + const horz = isHorizontal(a, b, eps); + if (!vert && !horz) return false; if (vert) { - const x = a.x - if (x < r.minX - eps || x > r.maxX + eps) return false - const segMinY = Math.min(a.y, b.y) - const segMaxY = Math.max(a.y, b.y) - const overlap = Math.min(segMaxY, r.maxY) - Math.max(segMinY, r.minY) - return overlap > eps + const x = a.x; + if (x < r.minX - eps || x > r.maxX + eps) return false; + const segMinY = Math.min(a.y, b.y); + const segMaxY = Math.max(a.y, b.y); + const overlap = Math.min(segMaxY, r.maxY) - Math.max(segMinY, r.minY); + return overlap > eps; } else { - const y = a.y - if (y < r.minY - eps || y > r.maxY + eps) return false - const segMinX = Math.min(a.x, b.x) - const segMaxX = Math.max(a.x, b.x) - const overlap = Math.min(segMaxX, r.maxX) - Math.max(segMinX, r.minX) - return overlap > eps + const y = a.y; + if (y < r.minY - eps || y > r.maxY + eps) return false; + const segMinX = Math.min(a.x, b.x); + const segMaxX = Math.max(a.x, b.x); + const overlap = Math.min(segMaxX, r.maxX) - Math.max(segMinX, r.minX); + return overlap > eps; } -} +}; export const findFirstCollision = ( pts: Point[], rects: ChipWithBounds[], opts: { - excludeRectIdsForSegment?: (segIndex: number) => Set + excludeRectIdsForSegment?: (segIndex: number) => Set; } = {}, ): { segIndex: number; rect: ChipWithBounds } | null => { for (let i = 0; i < pts.length - 1; i++) { - const a = pts[i]! - const b = pts[i + 1]! - const excluded = opts.excludeRectIdsForSegment?.(i) ?? new Set() + const a = pts[i]!; + const b = pts[i + 1]!; + const excluded = opts.excludeRectIdsForSegment?.(i) ?? new Set(); for (const r of rects) { - if (excluded.has(r.chipId)) continue + if (excluded.has(r.chipId)) continue; if (segmentIntersectsRect(a, b, r)) { - return { segIndex: i, rect: r } + return { segIndex: i, rect: r }; } } } - return null -} + return null; +}; /** * Checks if a given path has any intersections with a set of chip obstacles. @@ -66,9 +66,9 @@ export const isPathCollidingWithObstacles = ( for (let i = 0; i < path.length - 1; i++) { for (const obstacle of obstacles) { if (segmentIntersectsRect(path[i], path[i + 1], obstacle)) { - return true // Found a collision + return true; // Found a collision } } } - return false // No collisions found -} + return false; // No collisions found +}; diff --git a/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/mid.ts b/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/mid.ts index 238522a5..625ed483 100644 --- a/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/mid.ts +++ b/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/mid.ts @@ -1,16 +1,16 @@ -import type { Point } from "@tscircuit/math-utils" -import type { ChipWithBounds } from "./rect" +import type { Point } from "@tscircuit/math-utils"; +import type { ChipWithBounds } from "./rect"; -const EPS = 1e-9 +const EPS = 1e-9; -export type Axis = "x" | "y" +export type Axis = "x" | "y"; export const aabbFromPoints = (a: Point, b: Point) => ({ minX: Math.min(a.x, b.x), maxX: Math.max(a.x, b.x), minY: Math.min(a.y, b.y), maxY: Math.max(a.y, b.y), -}) +}); export const midBetweenPointAndRect = ( axis: Axis, @@ -20,24 +20,24 @@ export const midBetweenPointAndRect = ( ): number[] => { if (axis === "x") { if (p.x < r.minX - eps) { - return [(p.x + r.minX) / 2] + return [(p.x + r.minX) / 2]; } if (p.x > r.maxX + eps) { - return [(p.x + r.maxX) / 2] + return [(p.x + r.maxX) / 2]; } // Point is within rect bounds on this axis - generate candidates on both sides - return [r.minX - 0.2, r.maxX + 0.2] + return [r.minX - 0.2, r.maxX + 0.2]; } else { if (p.y < r.minY - eps) { - return [(p.y + r.minY) / 2] + return [(p.y + r.minY) / 2]; } if (p.y > r.maxY + eps) { - return [(p.y + r.maxY) / 2] + return [(p.y + r.maxY) / 2]; } // Point is within rect bounds on this axis - generate candidates on both sides - return [r.minY - 0.2, r.maxY + 0.2] + return [r.minY - 0.2, r.maxY + 0.2]; } -} +}; export const candidateMidsFromSet = ( axis: Axis, @@ -49,49 +49,49 @@ export const candidateMidsFromSet = ( ): number[] => { const setRects = [...collisionRectIds] .map((id) => rectsById.get(id)) - .filter((r): r is ChipWithBounds => !!r) + .filter((r): r is ChipWithBounds => !!r); if (axis === "x") { const leftBoundaries = [aabb.minX, ...setRects.map((r) => r.maxX)].filter( (v) => v < colliding.minX - eps, - ) + ); const rightBoundaries = [aabb.maxX, ...setRects.map((r) => r.minX)].filter( (v) => v > colliding.maxX + eps, - ) + ); const leftNeighbor = - leftBoundaries.length > 0 ? Math.max(...leftBoundaries) : undefined + leftBoundaries.length > 0 ? Math.max(...leftBoundaries) : undefined; const rightNeighbor = - rightBoundaries.length > 0 ? Math.min(...rightBoundaries) : undefined + rightBoundaries.length > 0 ? Math.min(...rightBoundaries) : undefined; - const out: number[] = [] + const out: number[] = []; if (leftNeighbor !== undefined) { - out.push((leftNeighbor + colliding.minX) / 2) + out.push((leftNeighbor + colliding.minX) / 2); } if (rightNeighbor !== undefined) { - out.push((colliding.maxX + rightNeighbor) / 2) + out.push((colliding.maxX + rightNeighbor) / 2); } - return out + return out; } else { const bottomBoundaries = [aabb.minY, ...setRects.map((r) => r.maxY)].filter( (v) => v < colliding.minY - eps, - ) + ); const topBoundaries = [aabb.maxY, ...setRects.map((r) => r.minY)].filter( (v) => v > colliding.maxY + eps, - ) + ); const bottomNeighbor = - bottomBoundaries.length > 0 ? Math.max(...bottomBoundaries) : undefined + bottomBoundaries.length > 0 ? Math.max(...bottomBoundaries) : undefined; const topNeighbor = - topBoundaries.length > 0 ? Math.min(...topBoundaries) : undefined + topBoundaries.length > 0 ? Math.min(...topBoundaries) : undefined; - const out: number[] = [] + const out: number[] = []; if (bottomNeighbor !== undefined) { - out.push((bottomNeighbor + colliding.minY) / 2) + out.push((bottomNeighbor + colliding.minY) / 2); } if (topNeighbor !== undefined) { - out.push((colliding.maxY + topNeighbor) / 2) + out.push((colliding.maxY + topNeighbor) / 2); } - return out + return out; } -} +}; diff --git a/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/pathOps.ts b/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/pathOps.ts index e9d78d03..554ff90b 100644 --- a/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/pathOps.ts +++ b/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/pathOps.ts @@ -1,9 +1,9 @@ -import type { Point } from "@tscircuit/math-utils" -import { isHorizontal, isVertical } from "./collisions" +import type { Point } from "@tscircuit/math-utils"; +import { isHorizontal, isVertical } from "./collisions"; -const EPS = 1e-9 +const EPS = 1e-9; -export type Axis = "x" | "y" +export type Axis = "x" | "y"; export const shiftSegmentOrth = ( pts: Point[], @@ -13,72 +13,72 @@ export const shiftSegmentOrth = ( eps = EPS, ): Point[] | null => { if (segIndex < 0 || segIndex >= pts.length - 1) { - return null + return null; } - const a = pts[segIndex]! - const b = pts[segIndex + 1]! - const vert = isVertical(a, b, eps) - const horz = isHorizontal(a, b, eps) + const a = pts[segIndex]!; + const b = pts[segIndex + 1]!; + const vert = isVertical(a, b, eps); + const horz = isHorizontal(a, b, eps); if (!vert && !horz) { - return null + return null; } // Ensure axis matches orthogonal shift if (vert && axis !== "x") { - return null + return null; } if (horz && axis !== "y") { - return null + return null; } - const out = pts.map((p) => ({ ...p })) + const out = pts.map((p) => ({ ...p })); if (axis === "x") { if (Math.abs(a.x - newCoord) < eps && Math.abs(b.x - newCoord) < eps) { - return null + return null; } - out[segIndex] = { ...out[segIndex], x: newCoord } - out[segIndex + 1] = { ...out[segIndex + 1], x: newCoord } + out[segIndex] = { ...out[segIndex], x: newCoord }; + out[segIndex + 1] = { ...out[segIndex + 1], x: newCoord }; } else { if (Math.abs(a.y - newCoord) < eps && Math.abs(b.y - newCoord) < eps) { - return null + return null; } - out[segIndex] = { ...out[segIndex], y: newCoord } - out[segIndex + 1] = { ...out[segIndex + 1], y: newCoord } + out[segIndex] = { ...out[segIndex], y: newCoord }; + out[segIndex + 1] = { ...out[segIndex + 1], y: newCoord }; } // Prevent collapsing adjacent segments if (segIndex - 1 >= 0) { - const p = out[segIndex - 1]! - const q = out[segIndex]! - const manhattan = Math.abs(p.x - q.x) + Math.abs(p.y - q.y) + const p = out[segIndex - 1]!; + const q = out[segIndex]!; + const manhattan = Math.abs(p.x - q.x) + Math.abs(p.y - q.y); if (manhattan < eps) { - return null + return null; } } if (segIndex + 2 <= out.length - 1) { - const p = out[segIndex + 1]! - const q = out[segIndex + 2]! - const manhattan = Math.abs(p.x - q.x) + Math.abs(p.y - q.y) + const p = out[segIndex + 1]!; + const q = out[segIndex + 2]!; + const manhattan = Math.abs(p.x - q.x) + Math.abs(p.y - q.y); if (manhattan < eps) { - return null + return null; } } // Sanity: still orthogonal for (let i = 0; i < out.length - 1; i++) { - const u = out[i]! - const v = out[i + 1]! + const u = out[i]!; + const v = out[i + 1]!; if (!isHorizontal(u, v, eps) && !isVertical(u, v, eps)) { - return null + return null; } } - return out -} + return out; +}; export const pathKey = (pts: Point[], decimals = 6) => { const key = pts .map((p) => `${p.x.toFixed(decimals)},${p.y.toFixed(decimals)}`) - .join("|") - return key -} + .join("|"); + return key; +}; diff --git a/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect.ts b/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect.ts index f0fa6f17..85b7e71f 100644 --- a/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect.ts +++ b/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect.ts @@ -1,19 +1,19 @@ -import type { InputChip, InputProblem } from "lib/types/InputProblem" -import { getInputChipBounds } from "lib/solvers/GuidelinesSolver/getInputChipBounds" +import type { InputChip, InputProblem } from "lib/types/InputProblem"; +import { getInputChipBounds } from "lib/solvers/GuidelinesSolver/getInputChipBounds"; export type ChipWithBounds = { - chipId: string - minX: number - minY: number - maxX: number - maxY: number -} + chipId: string; + minX: number; + minY: number; + maxX: number; + maxY: number; +}; export const chipToRect = (chip: InputChip): ChipWithBounds => { - const b = getInputChipBounds(chip) - return { chipId: chip.chipId, ...b } -} + const b = getInputChipBounds(chip); + return { chipId: chip.chipId, ...b }; +}; export const getObstacleRects = (problem: InputProblem): ChipWithBounds[] => { - return problem.chips.map(chipToRect) -} + return problem.chips.map(chipToRect); +}; diff --git a/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts b/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts index c9d5a995..4bacace4 100644 --- a/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +++ b/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts @@ -20,7 +20,7 @@ import { expandChipsToFitPins } from "./expandChipsToFitPins" import { LongDistancePairSolver } from "../LongDistancePairSolver/LongDistancePairSolver" import { MergedNetLabelObstacleSolver } from "../TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver" import { TraceCleanupSolver } from "../TraceCleanupSolver/TraceCleanupSolver" - +import { SameNetSegmentMergingSolver } from "lib/solvers/SameNetSegmentMergingSolver/SameNetSegmentMergingSolver" type PipelineStep BaseSolver> = { solverName: string solverClass: T @@ -63,6 +63,7 @@ export class SchematicTracePipelineSolver extends BaseSolver { mspConnectionPairSolver?: MspConnectionPairSolver // guidelinesSolver?: GuidelinesSolver schematicTraceLinesSolver?: SchematicTraceLinesSolver + sameNetSegmentMergingSolver?: SameNetSegmentMergingSolver longDistancePairSolver?: LongDistancePairSolver traceOverlapShiftSolver?: TraceOverlapShiftSolver netLabelPlacementSolver?: NetLabelPlacementSolver @@ -112,6 +113,27 @@ export class SchematicTracePipelineSolver extends BaseSolver { }, ], ), + // ========== SAME NET SEGMENT MERGING SOLVER ========== + definePipelineStep( + "sameNetSegmentMergingSolver", + SameNetSegmentMergingSolver, + () => [ + { + solvedTracePaths: this.schematicTraceLinesSolver!.solvedTracePaths, + mergeThreshold: 0.5, + }, + ], + { + onSolved: (instance) => { + // Replace the solved trace paths with merged ones for subsequent solvers + if (instance.sameNetSegmentMergingSolver?.mergedTracePaths) { + instance.schematicTraceLinesSolver!.solvedTracePaths = instance + .sameNetSegmentMergingSolver.mergedTracePaths as any + } + }, + }, + ), + // ========== END OF SAME NET SEGMENT MERGING SOLVER ========== definePipelineStep( "longDistancePairSolver", LongDistancePairSolver, diff --git a/lib/solvers/SchematicTracePipelineSolver/correctPinsInsideChip.ts b/lib/solvers/SchematicTracePipelineSolver/correctPinsInsideChip.ts index ec61db45..0f010960 100644 --- a/lib/solvers/SchematicTracePipelineSolver/correctPinsInsideChip.ts +++ b/lib/solvers/SchematicTracePipelineSolver/correctPinsInsideChip.ts @@ -1,37 +1,37 @@ -import type { InputProblem } from "lib/types/InputProblem" -import { getInputChipBounds } from "../GuidelinesSolver/getInputChipBounds" +import type { InputProblem } from "lib/types/InputProblem"; +import { getInputChipBounds } from "../GuidelinesSolver/getInputChipBounds"; export const correctPinsInsideChips = (problem: InputProblem) => { for (const chip of problem.chips) { - const bounds = getInputChipBounds(chip) + const bounds = getInputChipBounds(chip); for (const pin of chip.pins) { const isInside = pin.x > bounds.minX && pin.x < bounds.maxX && pin.y > bounds.minY && - pin.y < bounds.maxY + pin.y < bounds.maxY; - if (!isInside) continue + if (!isInside) continue; - const distLeft = pin.x - bounds.minX - const distRight = bounds.maxX - pin.x - const distBottom = pin.y - bounds.minY - const distTop = bounds.maxY - pin.y + const distLeft = pin.x - bounds.minX; + const distRight = bounds.maxX - pin.x; + const distBottom = pin.y - bounds.minY; + const distTop = bounds.maxY - pin.y; - const minDist = Math.min(distLeft, distRight, distBottom, distTop) + const minDist = Math.min(distLeft, distRight, distBottom, distTop); if (minDist === distLeft) { - pin.x = bounds.minX + pin.x = bounds.minX; } else if (minDist === distRight) { - pin.x = bounds.maxX + pin.x = bounds.maxX; } else if (minDist === distBottom) { - pin.y = bounds.minY + pin.y = bounds.minY; } else { - pin.y = bounds.maxY + pin.y = bounds.maxY; } // Clear any cached facing direction since geometry changed. - pin._facingDirection = undefined + pin._facingDirection = undefined; } } -} +}; diff --git a/lib/solvers/SchematicTracePipelineSolver/expandChipsToFitPins.ts b/lib/solvers/SchematicTracePipelineSolver/expandChipsToFitPins.ts index 6d5a7ba2..39d0fd41 100644 --- a/lib/solvers/SchematicTracePipelineSolver/expandChipsToFitPins.ts +++ b/lib/solvers/SchematicTracePipelineSolver/expandChipsToFitPins.ts @@ -1,4 +1,4 @@ -import type { InputProblem } from "lib/types/InputProblem" +import type { InputProblem } from "lib/types/InputProblem"; /** * Expands chip width/height (never shrinks) so that all existing pin coordinates @@ -12,30 +12,30 @@ import type { InputProblem } from "lib/types/InputProblem" */ export const expandChipsToFitPins = (problem: InputProblem) => { for (const chip of problem.chips) { - const halfWidth = chip.width / 2 - const halfHeight = chip.height / 2 + const halfWidth = chip.width / 2; + const halfHeight = chip.height / 2; - let maxDx = 0 - let maxDy = 0 + let maxDx = 0; + let maxDy = 0; for (const pin of chip.pins) { - const dx = Math.abs(pin.x - chip.center.x) - const dy = Math.abs(pin.y - chip.center.y) - if (dx > maxDx) maxDx = dx - if (dy > maxDy) maxDy = dy + const dx = Math.abs(pin.x - chip.center.x); + const dy = Math.abs(pin.y - chip.center.y); + if (dx > maxDx) maxDx = dx; + if (dy > maxDy) maxDy = dy; } - const newHalfWidth = Math.max(halfWidth, maxDx) - const newHalfHeight = Math.max(halfHeight, maxDy) + const newHalfWidth = Math.max(halfWidth, maxDx); + const newHalfHeight = Math.max(halfHeight, maxDy); if (newHalfWidth > halfWidth || newHalfHeight > halfHeight) { - chip.width = newHalfWidth * 2 - chip.height = newHalfHeight * 2 + chip.width = newHalfWidth * 2; + chip.height = newHalfHeight * 2; // Clear any cached facing direction since geometry changed. for (const pin of chip.pins) { - pin._facingDirection = undefined + pin._facingDirection = undefined; } } } -} +}; diff --git a/lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem.ts b/lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem.ts index ba25d8e0..663831be 100644 --- a/lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem.ts +++ b/lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem.ts @@ -1,16 +1,16 @@ -import type { GraphicsObject } from "graphics-debug" -import type { PinId, InputPin, InputProblem } from "lib/types/InputProblem" -import { getColorFromString } from "lib/utils/getColorFromString" -import { getPinDirection } from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getPinDirection" +import type { GraphicsObject } from "graphics-debug"; +import type { PinId, InputPin, InputProblem } from "lib/types/InputProblem"; +import { getColorFromString } from "lib/utils/getColorFromString"; +import { getPinDirection } from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getPinDirection"; export const visualizeInputProblem = ( inputProblem: InputProblem, opts: { - chipAlpha?: number - connectionAlpha?: number + chipAlpha?: number; + connectionAlpha?: number; } = {}, ): GraphicsObject => { - const { connectionAlpha = 0.8, chipAlpha = 0.8 } = opts + const { connectionAlpha = 0.8, chipAlpha = 0.8 } = opts; const graphics: Pick< Required, "lines" | "points" | "rects" @@ -18,12 +18,12 @@ export const visualizeInputProblem = ( lines: [], points: [], rects: [], - } + }; - const pinIdMap = new Map() + const pinIdMap = new Map(); for (const chip of inputProblem.chips) { for (const pin of chip.pins) { - pinIdMap.set(pin.pinId, pin) + pinIdMap.set(pin.pinId, pin); } } @@ -34,7 +34,7 @@ export const visualizeInputProblem = ( width: chip.width, height: chip.height, fill: getColorFromString(chip.chipId, chipAlpha), - }) + }); for (const pin of chip.pins) { graphics.points.push({ @@ -42,14 +42,14 @@ export const visualizeInputProblem = ( x: pin.x, y: pin.y, color: getColorFromString(pin.pinId, 0.8), - }) + }); } } for (const directConn of inputProblem.directConnections) { - const [pinId1, pinId2] = directConn.pinIds - const pin1 = pinIdMap.get(pinId1)! - const pin2 = pinIdMap.get(pinId2)! + const [pinId1, pinId2] = directConn.pinIds; + const pin1 = pinIdMap.get(pinId1)!; + const pin2 = pinIdMap.get(pinId2)!; graphics.lines.push({ points: [ { @@ -65,15 +65,15 @@ export const visualizeInputProblem = ( directConn.netId ?? `${pinId1}-${pinId2}`, connectionAlpha, ), - }) + }); } for (const netConn of inputProblem.netConnections) { - const pins = netConn.pinIds.map((pinId) => pinIdMap.get(pinId)!) + const pins = netConn.pinIds.map((pinId) => pinIdMap.get(pinId)!); for (let i = 0; i < pins.length - 1; i++) { for (let j = i + 1; j < pins.length; j++) { - const pin1 = pins[i]! - const pin2 = pins[j]! + const pin1 = pins[i]!; + const pin2 = pins[j]!; graphics.lines.push({ points: [ { x: pin1.x, y: pin1.y }, @@ -81,10 +81,10 @@ export const visualizeInputProblem = ( ], strokeColor: getColorFromString(netConn.netId, connectionAlpha), strokeDash: "4 2", - }) + }); } } } - return graphics -} + return graphics; +}; diff --git a/lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts b/lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts index e9bac7ca..e1bbbd18 100644 --- a/lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts +++ b/lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts @@ -1,25 +1,25 @@ -import type { InputProblem } from "lib/types/InputProblem" -import type { GraphicsObject, Line } from "graphics-debug" -import { minimizeTurnsWithFilteredLabels } from "./minimizeTurnsWithFilteredLabels" -import { balanceZShapes } from "./balanceZShapes" -import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver" -import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver" -import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem" -import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver" +import type { InputProblem } from "lib/types/InputProblem"; +import type { GraphicsObject, Line } from "graphics-debug"; +import { minimizeTurnsWithFilteredLabels } from "./minimizeTurnsWithFilteredLabels"; +import { balanceZShapes } from "./balanceZShapes"; +import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"; +import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"; +import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem"; +import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver"; /** * Defines the input structure for the TraceCleanupSolver. */ interface TraceCleanupSolverInput { - inputProblem: InputProblem - allTraces: SolvedTracePath[] - allLabelPlacements: NetLabelPlacement[] - mergedLabelNetIdMap: Record> - paddingBuffer: number + inputProblem: InputProblem; + allTraces: SolvedTracePath[]; + allLabelPlacements: NetLabelPlacement[]; + mergedLabelNetIdMap: Record>; + paddingBuffer: number; } -import { UntangleTraceSubsolver } from "./sub-solver/UntangleTraceSubsolver" -import { is4PointRectangle } from "./is4PointRectangle" +import { UntangleTraceSubsolver } from "./sub-solver/UntangleTraceSubsolver"; +import { is4PointRectangle } from "./is4PointRectangle"; /** * Represents the different stages or steps within the trace cleanup pipeline. @@ -27,7 +27,7 @@ import { is4PointRectangle } from "./is4PointRectangle" type PipelineStep = | "minimizing_turns" | "balancing_l_shapes" - | "untangling_traces" + | "untangling_traces"; /** * The TraceCleanupSolver is responsible for improving the aesthetics and readability of schematic traces. @@ -38,52 +38,54 @@ type PipelineStep = * The solver processes traces one by one, applying these cleanup steps sequentially to refine the overall trace layout. */ export class TraceCleanupSolver extends BaseSolver { - private input: TraceCleanupSolverInput - private outputTraces: SolvedTracePath[] - private traceIdQueue: string[] - private tracesMap: Map - private pipelineStep: PipelineStep = "untangling_traces" - private activeTraceId: string | null = null // New property - override activeSubSolver: BaseSolver | null = null + private input: TraceCleanupSolverInput; + private outputTraces: SolvedTracePath[]; + private traceIdQueue: string[]; + private tracesMap: Map; + private pipelineStep: PipelineStep = "untangling_traces"; + private activeTraceId: string | null = null; // New property + override activeSubSolver: BaseSolver | null = null; constructor(solverInput: TraceCleanupSolverInput) { - super() - this.input = solverInput - this.outputTraces = [...solverInput.allTraces] - this.tracesMap = new Map(this.outputTraces.map((t) => [t.mspPairId, t])) + super(); + this.input = solverInput; + this.outputTraces = [...solverInput.allTraces]; + this.tracesMap = new Map(this.outputTraces.map((t) => [t.mspPairId, t])); this.traceIdQueue = Array.from( solverInput.allTraces.map((e) => e.mspPairId), - ) + ); } override _step() { if (this.activeSubSolver) { - this.activeSubSolver.step() + this.activeSubSolver.step(); if (this.activeSubSolver.solved) { const output = ( this.activeSubSolver as UntangleTraceSubsolver - ).getOutput() - this.outputTraces = output.traces - this.tracesMap = new Map(this.outputTraces.map((t) => [t.mspPairId, t])) - this.activeSubSolver = null - this.pipelineStep = "minimizing_turns" + ).getOutput(); + this.outputTraces = output.traces; + this.tracesMap = new Map( + this.outputTraces.map((t) => [t.mspPairId, t]), + ); + this.activeSubSolver = null; + this.pipelineStep = "minimizing_turns"; } else if (this.activeSubSolver.failed) { - this.activeSubSolver = null - this.pipelineStep = "minimizing_turns" + this.activeSubSolver = null; + this.pipelineStep = "minimizing_turns"; } - return + return; } switch (this.pipelineStep) { case "untangling_traces": - this._runUntangleTracesStep() - break + this._runUntangleTracesStep(); + break; case "minimizing_turns": - this._runMinimizeTurnsStep() - break + this._runMinimizeTurnsStep(); + break; case "balancing_l_shapes": - this._runBalanceLShapesStep() - break + this._runBalanceLShapesStep(); + break; } } @@ -91,90 +93,90 @@ export class TraceCleanupSolver extends BaseSolver { this.activeSubSolver = new UntangleTraceSubsolver({ ...this.input, allTraces: Array.from(this.tracesMap.values()), - }) + }); } private _runMinimizeTurnsStep() { if (this.traceIdQueue.length === 0) { - this.pipelineStep = "balancing_l_shapes" + this.pipelineStep = "balancing_l_shapes"; this.traceIdQueue = Array.from( this.input.allTraces.map((e) => e.mspPairId), - ) - return + ); + return; } - this._processTrace("minimizing_turns") + this._processTrace("minimizing_turns"); } private _runBalanceLShapesStep() { if (this.traceIdQueue.length === 0) { - this.solved = true - return + this.solved = true; + return; } - this._processTrace("balancing_l_shapes") + this._processTrace("balancing_l_shapes"); } private _processTrace(step: "minimizing_turns" | "balancing_l_shapes") { - const targetMspConnectionPairId = this.traceIdQueue.shift()! - this.activeTraceId = targetMspConnectionPairId - const originalTrace = this.tracesMap.get(targetMspConnectionPairId)! + const targetMspConnectionPairId = this.traceIdQueue.shift()!; + this.activeTraceId = targetMspConnectionPairId; + const originalTrace = this.tracesMap.get(targetMspConnectionPairId)!; if (is4PointRectangle(originalTrace.tracePath)) { - return + return; } - const allTraces = Array.from(this.tracesMap.values()) + const allTraces = Array.from(this.tracesMap.values()); - let updatedTrace: SolvedTracePath + let updatedTrace: SolvedTracePath; if (step === "minimizing_turns") { updatedTrace = minimizeTurnsWithFilteredLabels({ ...this.input, targetMspConnectionPairId, traces: allTraces, - }) + }); } else { updatedTrace = balanceZShapes({ ...this.input, targetMspConnectionPairId, traces: allTraces, - }) + }); } - this.tracesMap.set(targetMspConnectionPairId, updatedTrace) - this.outputTraces = Array.from(this.tracesMap.values()) + this.tracesMap.set(targetMspConnectionPairId, updatedTrace); + this.outputTraces = Array.from(this.tracesMap.values()); } getOutput() { return { traces: this.outputTraces, - } + }; } override visualize(): GraphicsObject { if (this.activeSubSolver) { - return this.activeSubSolver.visualize() + return this.activeSubSolver.visualize(); } const graphics = visualizeInputProblem(this.input.inputProblem, { 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 = [] + 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: trace.mspPairId === this.activeTraceId ? "red" : "blue", // Highlight active trace - } - graphics.lines!.push(line) + }; + graphics.lines!.push(line); } - return graphics + return graphics; } } diff --git a/lib/solvers/TraceCleanupSolver/balanceZShapes.ts b/lib/solvers/TraceCleanupSolver/balanceZShapes.ts index 7b0b6714..3ebe7c5a 100644 --- a/lib/solvers/TraceCleanupSolver/balanceZShapes.ts +++ b/lib/solvers/TraceCleanupSolver/balanceZShapes.ts @@ -1,10 +1,10 @@ -import type { Point } from "graphics-debug" -import type { InputProblem } from "lib/types/InputProblem" -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" -import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver" +import type { Point } from "graphics-debug"; +import type { InputProblem } from "lib/types/InputProblem"; +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"; +import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver"; export const balanceZShapes = ({ targetMspConnectionPairId, @@ -14,40 +14,40 @@ export const balanceZShapes = ({ mergedLabelNetIdMap, paddingBuffer, }: { - targetMspConnectionPairId: string - traces: SolvedTracePath[] - inputProblem: InputProblem - allLabelPlacements: NetLabelPlacement[] - mergedLabelNetIdMap: Record> - paddingBuffer: number + targetMspConnectionPairId: string; + traces: SolvedTracePath[]; + inputProblem: InputProblem; + allLabelPlacements: NetLabelPlacement[]; + mergedLabelNetIdMap: Record>; + paddingBuffer: number; }): SolvedTracePath => { const targetTrace = traces.find( (t) => t.mspPairId === targetMspConnectionPairId, - ) + ); if (!targetTrace) { - throw new Error(`Target trace ${targetMspConnectionPairId} not found`) + throw new Error(`Target trace ${targetMspConnectionPairId} not found`); } - const TOLERANCE = 1e-5 + const TOLERANCE = 1e-5; const obstacleTraces = traces.filter( (t) => t.mspPairId !== targetMspConnectionPairId, - ) + ); - const TRACE_WIDTH = 0.01 + const TRACE_WIDTH = 0.01; const traceObstacles = obstacleTraces.flatMap((trace, i) => trace.tracePath.slice(0, -1).map((p1, pi) => { - const p2 = trace.tracePath[pi + 1]! + const p2 = trace.tracePath[pi + 1]!; return { chipId: `trace-obstacle-${i}-${pi}`, minX: Math.min(p1.x, p2.x) - TRACE_WIDTH / 2, minY: Math.min(p1.y, p2.y) - TRACE_WIDTH / 2, maxX: Math.max(p1.x, p2.x) + TRACE_WIDTH / 2, maxY: Math.max(p1.y, p2.y) + TRACE_WIDTH / 2, - } + }; }), - ) + ); const staticObstacles = getObstacleRects(inputProblem).map((obs) => ({ ...obs, @@ -55,9 +55,9 @@ export const balanceZShapes = ({ maxX: obs.maxX - TOLERANCE, minY: obs.minY + TOLERANCE, maxY: obs.maxY - TOLERANCE, - })) + })); - const combinedObstacles = [...staticObstacles, ...traceObstacles] + const combinedObstacles = [...staticObstacles, ...traceObstacles]; const segmentIntersectsAnyRect = ( p1: Point, @@ -66,48 +66,48 @@ export const balanceZShapes = ({ ): boolean => { for (const rect of rects) { if (segmentIntersectsRect(p1, p2, rect)) { - return true + return true; } } - return false - } + return false; + }; const filteredLabels = allLabelPlacements.filter((label) => { - const originalNetIds = mergedLabelNetIdMap[label.globalConnNetId] + const originalNetIds = mergedLabelNetIdMap[label.globalConnNetId]; if (originalNetIds) { - return !originalNetIds.has(targetTrace.globalConnNetId) + return !originalNetIds.has(targetTrace.globalConnNetId); } - return label.globalConnNetId !== targetTrace.globalConnNetId - }) + return label.globalConnNetId !== targetTrace.globalConnNetId; + }); const labelBounds = filteredLabels.map((nl) => ({ minX: nl.center.x - nl.width / 2 + TOLERANCE, maxX: nl.center.x + nl.width / 2 - TOLERANCE, minY: nl.center.y - nl.height / 2 + TOLERANCE, maxY: nl.center.y + nl.height / 2 - TOLERANCE, - })) + })); - const newPath = [...targetTrace.tracePath] + const newPath = [...targetTrace.tracePath]; if (newPath.length < 4) { - return { ...targetTrace } + return { ...targetTrace }; } if (newPath.length === 4) { - const [p0, p1, p2, p3] = newPath - let p1New: Point - let p2New: Point + const [p0, p1, p2, p3] = newPath; + let p1New: Point; + let p2New: Point; - const isHVHShape = p0.y === p1.y && p1.x === p2.x && p2.y === p3.y + const isHVHShape = p0.y === p1.y && p1.x === p2.x && p2.y === p3.y; if (isHVHShape) { - const idealX = (p0.x + p3.x) / 2 - p1New = { x: idealX, y: p1.y } - p2New = { x: idealX, y: p2.y } + const idealX = (p0.x + p3.x) / 2; + p1New = { x: idealX, y: p1.y }; + p2New = { x: idealX, y: p2.y }; } else { - const idealY = (p0.y + p3.y) / 2 - p1New = { x: p1.x, y: idealY } - p2New = { x: p2.x, y: idealY } + const idealY = (p0.y + p3.y) / 2; + p1New = { x: p1.x, y: idealY }; + p2New = { x: p2.x, y: idealY }; } const collides = @@ -116,65 +116,65 @@ export const balanceZShapes = ({ segmentIntersectsAnyRect(p2New, p3, combinedObstacles) || segmentIntersectsAnyRect(p0, p1New, labelBounds) || segmentIntersectsAnyRect(p1New, p2New, labelBounds) || - segmentIntersectsAnyRect(p2New, p3, labelBounds) + segmentIntersectsAnyRect(p2New, p3, labelBounds); if (!collides) { - newPath[1] = p1New - newPath[2] = p2New + newPath[1] = p1New; + newPath[2] = p2New; } - return { ...targetTrace, tracePath: simplifyPath(newPath) } + return { ...targetTrace, tracePath: simplifyPath(newPath) }; } for (let i = 1; i < newPath.length - 4; i++) { - const p1 = newPath[i]! - const p2 = newPath[i + 1]! - const p3 = newPath[i + 2]! - const p4 = newPath[i + 3]! + const p1 = newPath[i]!; + const p2 = newPath[i + 1]!; + const p3 = newPath[i + 2]!; + const p4 = newPath[i + 3]!; - const isHVHZShape = p1.y === p2.y && p2.x === p3.x && p3.y === p4.y - const isVHVZShape = p1.x === p2.x && p2.y === p3.y && p3.x === p4.x + const isHVHZShape = p1.y === p2.y && p2.x === p3.x && p3.y === p4.y; + const isVHVZShape = p1.x === p2.x && p2.y === p3.y && p3.x === p4.x; const isCollinearHorizontal = - p1.y === p2.y && p2.y === p3.y && p3.y === p4.y - const isCollinearVertical = p1.x === p2.x && p2.x === p3.x && p3.x === p4.x - const isCollinear = isCollinearHorizontal || isCollinearVertical + p1.y === p2.y && p2.y === p3.y && p3.y === p4.y; + const isCollinearVertical = p1.x === p2.x && p2.x === p3.x && p3.x === p4.x; + const isCollinear = isCollinearHorizontal || isCollinearVertical; - let isSameDirection = false + let isSameDirection = false; if (isHVHZShape) { - isSameDirection = Math.sign(p2.x - p1.x) === Math.sign(p4.x - p3.x) + isSameDirection = Math.sign(p2.x - p1.x) === Math.sign(p4.x - p3.x); } else if (isVHVZShape) { - isSameDirection = Math.sign(p2.y - p1.y) === Math.sign(p4.y - p3.y) + isSameDirection = Math.sign(p2.y - p1.y) === Math.sign(p4.y - p3.y); } const isValidZShape = - (isHVHZShape || isVHVZShape) && !isCollinear && isSameDirection + (isHVHZShape || isVHVZShape) && !isCollinear && isSameDirection; if (!isValidZShape) { - continue + continue; } - let p2New: Point - let p3New: Point + let p2New: Point; + let p3New: Point; const len1Original = isHVHZShape ? Math.abs(p1.x - p2.x) - : Math.abs(p1.y - p2.y) + : Math.abs(p1.y - p2.y); const len2Original = isHVHZShape ? Math.abs(p3.x - p4.x) - : Math.abs(p3.y - p4.y) + : Math.abs(p3.y - p4.y); if (Math.abs(len1Original - len2Original) < 0.001) { - continue + continue; } if (isHVHZShape) { - const idealX = (p1.x + p4.x) / 2 - p2New = { x: idealX, y: p2.y } - p3New = { x: idealX, y: p3.y } + const idealX = (p1.x + p4.x) / 2; + p2New = { x: idealX, y: p2.y }; + p3New = { x: idealX, y: p3.y }; } else { - const idealY = (p1.y + p4.y) / 2 - p2New = { x: p2.x, y: idealY } - p3New = { x: p3.x, y: idealY } + const idealY = (p1.y + p4.y) / 2; + p2New = { x: p2.x, y: idealY }; + p3New = { x: p3.x, y: idealY }; } const collides = @@ -183,18 +183,18 @@ export const balanceZShapes = ({ segmentIntersectsAnyRect(p3New, p4, combinedObstacles) || segmentIntersectsAnyRect(p1, p2New, labelBounds) || segmentIntersectsAnyRect(p2New, p3New, labelBounds) || - segmentIntersectsAnyRect(p3New, p4, labelBounds) + segmentIntersectsAnyRect(p3New, p4, labelBounds); if (!collides) { - newPath[i + 1] = p2New - newPath[i + 2] = p3New - i = 0 + newPath[i + 1] = p2New; + newPath[i + 2] = p3New; + i = 0; } } - const finalSimplifiedPath = simplifyPath(newPath) + const finalSimplifiedPath = simplifyPath(newPath); return { ...targetTrace, tracePath: finalSimplifiedPath, - } -} + }; +}; diff --git a/lib/solvers/TraceCleanupSolver/countTurns.ts b/lib/solvers/TraceCleanupSolver/countTurns.ts index beaeb4f2..700b5cdc 100644 --- a/lib/solvers/TraceCleanupSolver/countTurns.ts +++ b/lib/solvers/TraceCleanupSolver/countTurns.ts @@ -1,18 +1,18 @@ -import type { Point } from "@tscircuit/math-utils" +import type { Point } from "@tscircuit/math-utils"; export const countTurns = (points: Point[]): number => { - let turns = 0 + let turns = 0; for (let i = 1; i < points.length - 1; i++) { - const prev = points[i - 1] - const curr = points[i] - const next = points[i + 1] + const prev = points[i - 1]; + const curr = points[i]; + const next = points[i + 1]; - const prevVertical = prev.x === curr.x - const nextVertical = curr.x === next.x + const prevVertical = prev.x === curr.x; + const nextVertical = curr.x === next.x; if (prevVertical !== nextVertical) { - turns++ + turns++; } } - return turns -} + return turns; +}; diff --git a/lib/solvers/TraceCleanupSolver/hasCollisions.ts b/lib/solvers/TraceCleanupSolver/hasCollisions.ts index 0bcca564..102b86e5 100644 --- a/lib/solvers/TraceCleanupSolver/hasCollisions.ts +++ b/lib/solvers/TraceCleanupSolver/hasCollisions.ts @@ -1,5 +1,5 @@ -import type { Point } from "@tscircuit/math-utils" -import { segmentToBoxMinDistance } from "@tscircuit/math-utils" +import type { Point } from "@tscircuit/math-utils"; +import { segmentToBoxMinDistance } from "@tscircuit/math-utils"; /** * Checks if a given path (series of segments) collides with any of the provided obstacles. @@ -11,8 +11,8 @@ export const hasCollisions = ( ): boolean => { // Check each segment of the path for (let i = 0; i < pathSegments.length - 1; i++) { - const p1 = pathSegments[i] - const p2 = pathSegments[i + 1] + const p1 = pathSegments[i]; + const p2 = pathSegments[i + 1]; // Check collision with each obstacle for (const obstacle of obstacles) { @@ -23,12 +23,12 @@ export const hasCollisions = ( }, width: obstacle.maxX - obstacle.minX, height: obstacle.maxY - obstacle.minY, - } + }; if (segmentToBoxMinDistance(p1, p2, box) <= 0) { - return true + return true; } } } - return false -} + return false; +}; diff --git a/lib/solvers/TraceCleanupSolver/hasCollisionsWithLabels.ts b/lib/solvers/TraceCleanupSolver/hasCollisionsWithLabels.ts index 26fe68e6..fae5a7c4 100644 --- a/lib/solvers/TraceCleanupSolver/hasCollisionsWithLabels.ts +++ b/lib/solvers/TraceCleanupSolver/hasCollisionsWithLabels.ts @@ -1,19 +1,19 @@ -import type { Point } from "@tscircuit/math-utils" -import { segmentIntersectsRect } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions" +import type { Point } from "@tscircuit/math-utils"; +import { segmentIntersectsRect } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions"; export const hasCollisionsWithLabels = ( pathSegments: Point[], labels: any[], ): boolean => { for (let i = 0; i < pathSegments.length - 1; i++) { - const p1 = pathSegments[i] - const p2 = pathSegments[i + 1] + const p1 = pathSegments[i]; + const p2 = pathSegments[i + 1]; for (const label of labels) { if (segmentIntersectsRect(p1, p2, label)) { - return true + return true; } } } - return false -} + return false; +}; diff --git a/lib/solvers/TraceCleanupSolver/is4PointRectangle.ts b/lib/solvers/TraceCleanupSolver/is4PointRectangle.ts index e8635450..1aa03e95 100644 --- a/lib/solvers/TraceCleanupSolver/is4PointRectangle.ts +++ b/lib/solvers/TraceCleanupSolver/is4PointRectangle.ts @@ -1,17 +1,17 @@ -import type { Point } from "@tscircuit/math-utils" +import type { Point } from "@tscircuit/math-utils"; /** * Checks if a given path of four points forms a rectangle with horizontal and vertical segments. * It verifies if the path forms either an H-V-H "C" shape or a V-H-V "C" shape. */ export const is4PointRectangle = (path: Point[]): boolean => { - if (path.length !== 4) return false - const [p0, p1, p2, p3] = path + if (path.length !== 4) return false; + const [p0, p1, p2, p3] = path; // H-V-H "C" shape const isHVHC = - p0.y === p1.y && p1.x === p2.x && p2.y === p3.y && p0.x === p3.x + p0.y === p1.y && p1.x === p2.x && p2.y === p3.y && p0.x === p3.x; // V-H-V "C" shape const isVHVC = - p0.x === p1.x && p1.y === p2.y && p2.x === p3.x && p0.y === p3.y - return isHVHC || isVHVC -} + p0.x === p1.x && p1.y === p2.y && p2.x === p3.x && p0.y === p3.y; + return isHVHC || isVHVC; +}; diff --git a/lib/solvers/TraceCleanupSolver/isSegmentAnEndpointSegment.ts b/lib/solvers/TraceCleanupSolver/isSegmentAnEndpointSegment.ts index d51d7878..098026f0 100644 --- a/lib/solvers/TraceCleanupSolver/isSegmentAnEndpointSegment.ts +++ b/lib/solvers/TraceCleanupSolver/isSegmentAnEndpointSegment.ts @@ -1,4 +1,4 @@ -import type { Point } from "graphics-debug" +import type { Point } from "graphics-debug"; /** * Determines if a given segment (p1-p2) is either the first or the last segment of an original path. @@ -9,10 +9,10 @@ export const isSegmentAnEndpointSegment = ( p2: Point, originalPath: Point[], ): boolean => { - if (originalPath.length < 2) return false + if (originalPath.length < 2) return false; - const originalStart = originalPath[0] - const originalEnd = originalPath[originalPath.length - 1] + const originalStart = originalPath[0]; + const originalEnd = originalPath[originalPath.length - 1]; // Check if p1-p2 is the first segment of the original path if ( @@ -21,7 +21,7 @@ export const isSegmentAnEndpointSegment = ( p2.x === originalPath[1].x && p2.y === originalPath[1].y ) { - return true + return true; } // Check if p1-p2 is the last segment of the original path if ( @@ -30,7 +30,7 @@ export const isSegmentAnEndpointSegment = ( p2.x === originalEnd.x && p2.y === originalEnd.y ) { - return true + return true; } - return false -} + return false; +}; diff --git a/lib/solvers/TraceCleanupSolver/mergeGraphicsObjects.ts b/lib/solvers/TraceCleanupSolver/mergeGraphicsObjects.ts index 99902d3c..f2317f18 100644 --- a/lib/solvers/TraceCleanupSolver/mergeGraphicsObjects.ts +++ b/lib/solvers/TraceCleanupSolver/mergeGraphicsObjects.ts @@ -1,4 +1,4 @@ -import type { GraphicsObject } from "graphics-debug" +import type { GraphicsObject } from "graphics-debug"; /** * Merges multiple GraphicsObject instances into a single GraphicsObject. @@ -13,16 +13,16 @@ export const mergeGraphicsObjects = ( rects: [], circles: [], texts: [], - } + }; for (const obj of objects) { - if (!obj) continue - if (obj.lines) merged.lines!.push(...obj.lines) - if (obj.points) merged.points!.push(...obj.points) - if (obj.rects) merged.rects!.push(...obj.rects) - if (obj.circles) merged.circles!.push(...obj.circles) - if (obj.texts) merged.texts!.push(...obj.texts) + if (!obj) continue; + if (obj.lines) merged.lines!.push(...obj.lines); + if (obj.points) merged.points!.push(...obj.points); + if (obj.rects) merged.rects!.push(...obj.rects); + if (obj.circles) merged.circles!.push(...obj.circles); + if (obj.texts) merged.texts!.push(...obj.texts); } - return merged -} + return merged; +}; diff --git a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts index be3e3868..ac7f0e7a 100644 --- a/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +++ b/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts @@ -1,8 +1,8 @@ -import type { InputProblem } from "lib/types/InputProblem" -import { minimizeTurns } from "./turnMinimization" -import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver" -import { getObstacleRects } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect" -import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver" +import type { InputProblem } from "lib/types/InputProblem"; +import { minimizeTurns } from "./turnMinimization"; +import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"; +import { getObstacleRects } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect"; +import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver"; /** * Minimizes the turns of a target trace while considering other traces and labels as obstacles. @@ -19,75 +19,75 @@ export const minimizeTurnsWithFilteredLabels = ({ mergedLabelNetIdMap, paddingBuffer, }: { - targetMspConnectionPairId: string - traces: SolvedTracePath[] - inputProblem: InputProblem - allLabelPlacements: NetLabelPlacement[] - mergedLabelNetIdMap: Record> - paddingBuffer: number + targetMspConnectionPairId: string; + traces: SolvedTracePath[]; + inputProblem: InputProblem; + allLabelPlacements: NetLabelPlacement[]; + mergedLabelNetIdMap: Record>; + paddingBuffer: number; }): SolvedTracePath => { const targetTrace = traces.find( (t) => t.mspPairId === targetMspConnectionPairId, - ) + ); if (!targetTrace) { - throw new Error(`Target trace ${targetMspConnectionPairId} not found`) + throw new Error(`Target trace ${targetMspConnectionPairId} not found`); } const obstacleTraces = traces.filter( (t) => t.mspPairId !== targetMspConnectionPairId, - ) + ); - const TRACE_WIDTH = 0.01 + const TRACE_WIDTH = 0.01; const traceObstacles = obstacleTraces.flatMap((trace, i) => trace.tracePath.slice(0, -1).map((p1, pi) => { - const p2 = trace.tracePath[pi + 1]! + const p2 = trace.tracePath[pi + 1]!; return { chipId: `trace-obstacle-${i}-${pi}`, minX: Math.min(p1.x, p2.x) - TRACE_WIDTH / 2, minY: Math.min(p1.y, p2.y) - TRACE_WIDTH / 2, maxX: Math.max(p1.x, p2.x) + TRACE_WIDTH / 2, maxY: Math.max(p1.y, p2.y) + TRACE_WIDTH / 2, - } + }; }), - ) + ); - const staticObstaclesRaw = getObstacleRects(inputProblem) - const PADDING = 0.01 + const staticObstaclesRaw = getObstacleRects(inputProblem); + const PADDING = 0.01; const staticObstacles = staticObstaclesRaw.map((obs) => ({ ...obs, minX: obs.minX - PADDING, minY: obs.minY - PADDING, maxX: obs.maxX + PADDING, maxY: obs.maxY + PADDING, - })) + })); - const combinedObstacles = [...staticObstacles, ...traceObstacles] + const combinedObstacles = [...staticObstacles, ...traceObstacles]; - const originalPath = targetTrace.tracePath + const originalPath = targetTrace.tracePath; const filteredLabels = allLabelPlacements.filter((label) => { - const originalNetIds = mergedLabelNetIdMap[label.globalConnNetId] + const originalNetIds = mergedLabelNetIdMap[label.globalConnNetId]; if (originalNetIds) { - return !originalNetIds.has(targetTrace.globalConnNetId) + return !originalNetIds.has(targetTrace.globalConnNetId); } - return label.globalConnNetId !== targetTrace.globalConnNetId - }) + return label.globalConnNetId !== targetTrace.globalConnNetId; + }); const labelBounds = filteredLabels.map((nl) => ({ minX: nl.center.x - nl.width / 2 - paddingBuffer, maxX: nl.center.x + nl.width / 2 + paddingBuffer, minY: nl.center.y - nl.height / 2 - paddingBuffer, maxY: nl.center.y + nl.height / 2 + paddingBuffer, - })) + })); const newPath = minimizeTurns({ path: originalPath, obstacles: combinedObstacles, labelBounds, originalPath: originalPath, - }) + }); return { ...targetTrace, tracePath: newPath, - } -} + }; +}; diff --git a/lib/solvers/TraceCleanupSolver/recognizeStairStepPattern.ts b/lib/solvers/TraceCleanupSolver/recognizeStairStepPattern.ts index cefaa398..9e030f65 100644 --- a/lib/solvers/TraceCleanupSolver/recognizeStairStepPattern.ts +++ b/lib/solvers/TraceCleanupSolver/recognizeStairStepPattern.ts @@ -1,4 +1,4 @@ -import type { Point } from "graphics-debug" +import type { Point } from "graphics-debug"; /** * Recognizes a "stair-step" pattern within a given path of points starting from a specified index. @@ -10,47 +10,47 @@ export const recognizeStairStepPattern = ( pathToCheck: Point[], startIdx: number, ): number => { - if (startIdx >= pathToCheck.length - 3) return -1 + if (startIdx >= pathToCheck.length - 3) return -1; - let endIdx = startIdx - let isStairStep = true + let endIdx = startIdx; + let isStairStep = true; for (let i = startIdx; i < pathToCheck.length - 2 && i < startIdx + 10; i++) { - if (i + 2 >= pathToCheck.length) break + if (i + 2 >= pathToCheck.length) break; - const p1 = pathToCheck[i] - const p2 = pathToCheck[i + 1] - const p3 = pathToCheck[i + 2] + const p1 = pathToCheck[i]; + const p2 = pathToCheck[i + 1]; + const p3 = pathToCheck[i + 2]; - const seg1Vertical = p1.x === p2.x - const seg2Vertical = p2.x === p3.x + const seg1Vertical = p1.x === p2.x; + const seg2Vertical = p2.x === p3.x; if (seg1Vertical === seg2Vertical) { - break + break; } const seg1Direction = seg1Vertical ? Math.sign(p2.y - p1.y) - : Math.sign(p2.x - p1.x) + : Math.sign(p2.x - p1.x); if (i > startIdx) { - const prevP = pathToCheck[i - 1] - const prevSegVertical = prevP.x === p1.x + const prevP = pathToCheck[i - 1]; + const prevSegVertical = prevP.x === p1.x; const prevDirection = prevSegVertical ? Math.sign(p1.y - prevP.y) - : Math.sign(p1.x - prevP.x) + : Math.sign(p1.x - prevP.x); if ( (seg1Vertical && prevSegVertical && seg1Direction !== prevDirection) || (!seg1Vertical && !prevSegVertical && seg1Direction !== prevDirection) ) { - isStairStep = false - break + isStairStep = false; + break; } } - endIdx = i + 2 + endIdx = i + 2; } - return isStairStep && endIdx - startIdx >= 3 ? endIdx : -1 -} + return isStairStep && endIdx - startIdx >= 3 ? endIdx : -1; +}; diff --git a/lib/solvers/TraceCleanupSolver/simplifyPath.ts b/lib/solvers/TraceCleanupSolver/simplifyPath.ts index e17bfb52..ffb83f9c 100644 --- a/lib/solvers/TraceCleanupSolver/simplifyPath.ts +++ b/lib/solvers/TraceCleanupSolver/simplifyPath.ts @@ -1,41 +1,41 @@ -import type { Point } from "graphics-debug" +import type { Point } from "graphics-debug"; import { isHorizontal, isVertical, -} from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions" +} from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions"; export const simplifyPath = (path: Point[]): Point[] => { - if (path.length < 3) return path - const newPath: Point[] = [path[0]] + if (path.length < 3) return path; + const newPath: Point[] = [path[0]]; for (let i = 1; i < path.length - 1; i++) { - const p1 = newPath[newPath.length - 1] - const p2 = path[i] - const p3 = path[i + 1] + const p1 = newPath[newPath.length - 1]; + const p2 = path[i]; + const p3 = path[i + 1]; if ( (isVertical(p1, p2) && isVertical(p2, p3)) || (isHorizontal(p1, p2) && isHorizontal(p2, p3)) ) { - continue + continue; } - newPath.push(p2) + newPath.push(p2); } - newPath.push(path[path.length - 1]) + newPath.push(path[path.length - 1]); - if (newPath.length < 3) return newPath - const finalPath: Point[] = [newPath[0]] + if (newPath.length < 3) return newPath; + const finalPath: Point[] = [newPath[0]]; for (let i = 1; i < newPath.length - 1; i++) { - const p1 = finalPath[finalPath.length - 1] - const p2 = newPath[i] - const p3 = newPath[i + 1] + const p1 = finalPath[finalPath.length - 1]; + const p2 = newPath[i]; + const p3 = newPath[i + 1]; if ( (isVertical(p1, p2) && isVertical(p2, p3)) || (isHorizontal(p1, p2) && isHorizontal(p2, p3)) ) { - continue + continue; } - finalPath.push(p2) + finalPath.push(p2); } - finalPath.push(newPath[newPath.length - 1]) + finalPath.push(newPath[newPath.length - 1]); - return finalPath -} + return finalPath; +}; diff --git a/lib/solvers/TraceCleanupSolver/sub-solver/UntangleTraceSubsolver.ts b/lib/solvers/TraceCleanupSolver/sub-solver/UntangleTraceSubsolver.ts index 3519aa90..b65834a5 100644 --- a/lib/solvers/TraceCleanupSolver/sub-solver/UntangleTraceSubsolver.ts +++ b/lib/solvers/TraceCleanupSolver/sub-solver/UntangleTraceSubsolver.ts @@ -1,38 +1,38 @@ -import { BaseSolver } from "../../BaseSolver/BaseSolver" -import type { InputProblem } from "../../../types/InputProblem" -import type { SolvedTracePath } from "../../SchematicTraceLinesSolver/SchematicTraceLinesSolver" -import type { NetLabelPlacement } from "../../NetLabelPlacementSolver/NetLabelPlacementSolver" - -import { findAllLShapedTurns, type LShape } from "./findAllLShapedTurns" -import { getTraceObstacles } from "./getTraceObstacles" -import { findIntersectionsWithObstacles } from "./findIntersectionsWithObstacles" -import { generateLShapeRerouteCandidates } from "./generateLShapeRerouteCandidates" -import { isPathColliding, type CollisionInfo } from "./isPathColliding" +import { BaseSolver } from "../../BaseSolver/BaseSolver"; +import type { InputProblem } from "../../../types/InputProblem"; +import type { SolvedTracePath } from "../../SchematicTraceLinesSolver/SchematicTraceLinesSolver"; +import type { NetLabelPlacement } from "../../NetLabelPlacementSolver/NetLabelPlacementSolver"; + +import { findAllLShapedTurns, type LShape } from "./findAllLShapedTurns"; +import { getTraceObstacles } from "./getTraceObstacles"; +import { findIntersectionsWithObstacles } from "./findIntersectionsWithObstacles"; +import { generateLShapeRerouteCandidates } from "./generateLShapeRerouteCandidates"; +import { isPathColliding, type CollisionInfo } from "./isPathColliding"; import { generateRectangleCandidates, type Rectangle, type RectangleCandidate, -} from "./generateRectangleCandidates" +} from "./generateRectangleCandidates"; -import type { GraphicsObject } from "graphics-debug" -import type { Point } from "@tscircuit/math-utils" +import type { GraphicsObject } from "graphics-debug"; +import type { Point } from "@tscircuit/math-utils"; -import { visualizeLSapes } from "./visualizeLSapes" -import { visualizeIntersectionPoints } from "./visualizeIntersectionPoints" -import { visualizeTightRectangle } from "../visualizeTightRectangle" -import { visualizeCandidates } from "./visualizeCandidates" -import { mergeGraphicsObjects } from "../mergeGraphicsObjects" -import { visualizeCollision } from "./visualizeCollision" +import { visualizeLSapes } from "./visualizeLSapes"; +import { visualizeIntersectionPoints } from "./visualizeIntersectionPoints"; +import { visualizeTightRectangle } from "../visualizeTightRectangle"; +import { visualizeCandidates } from "./visualizeCandidates"; +import { mergeGraphicsObjects } from "../mergeGraphicsObjects"; +import { visualizeCollision } from "./visualizeCollision"; /** * Defines the input structure for the UntangleTraceSubsolver. */ export interface UntangleTraceSubsolverInput { - inputProblem: InputProblem - allTraces: SolvedTracePath[] - allLabelPlacements: NetLabelPlacement[] - mergedLabelNetIdMap: Record> - paddingBuffer: number + inputProblem: InputProblem; + allTraces: SolvedTracePath[]; + allLabelPlacements: NetLabelPlacement[]; + mergedLabelNetIdMap: Record>; + paddingBuffer: number; } /** @@ -42,7 +42,7 @@ type VisualizationMode = | "l_shapes" | "intersection_points" | "tight_rectangle" - | "candidates" + | "candidates"; /** * The UntangleTraceSubsolver is designed to resolve complex overlaps and improve the routing of traces, @@ -56,144 +56,144 @@ type VisualizationMode = * This iterative process aims to untangle traces and create a cleaner, more efficient layout. */ export class UntangleTraceSubsolver extends BaseSolver { - private input: UntangleTraceSubsolverInput - private lShapesToProcess: LShape[] = [] - private visualizationMode: VisualizationMode = "l_shapes" - - private currentLShape: LShape | null = null - private intersectionPoints: Point[] = [] - private tightRectangle: Rectangle | null = null - private candidates: Point[][] = [] - private bestRoute: Point[] | null = null - private lastCollision: CollisionInfo | null = null - private collidingCandidate: Point[] | null = null - - private rectangleCandidates: RectangleCandidate[] = [] - private currentRectangleIndex = 0 - - private isInitialStep = true - private currentCandidateIndex = 0 + private input: UntangleTraceSubsolverInput; + private lShapesToProcess: LShape[] = []; + private visualizationMode: VisualizationMode = "l_shapes"; + + private currentLShape: LShape | null = null; + private intersectionPoints: Point[] = []; + private tightRectangle: Rectangle | null = null; + private candidates: Point[][] = []; + private bestRoute: Point[] | null = null; + private lastCollision: CollisionInfo | null = null; + private collidingCandidate: Point[] | null = null; + + private rectangleCandidates: RectangleCandidate[] = []; + private currentRectangleIndex = 0; + + private isInitialStep = true; + private currentCandidateIndex = 0; private lShapeProcessingStep: | "idle" | "intersections" | "rectangle_selection" - | "candidate_evaluation" = "idle" - private lShapeJustProcessed = false - private bestRouteFound: Point[] | null = null + | "candidate_evaluation" = "idle"; + private lShapeJustProcessed = false; + private bestRouteFound: Point[] | null = null; constructor(solverInput: UntangleTraceSubsolverInput) { - super() - this.input = solverInput - this.visualizationMode = "l_shapes" + super(); + this.input = solverInput; + this.visualizationMode = "l_shapes"; for (const trace of this.input.allTraces) { - const lShapes = findAllLShapedTurns(trace.tracePath) + const lShapes = findAllLShapedTurns(trace.tracePath); this.lShapesToProcess.push( ...lShapes.map((l) => ({ ...l, traceId: trace.mspPairId as string })), - ) + ); } } override _step(): void { if (this.isInitialStep) { - this.isInitialStep = false - return + this.isInitialStep = false; + return; } if (this.lShapeJustProcessed) { - this._resetAfterLShapProcessing() - return + this._resetAfterLShapProcessing(); + return; } if (this.lShapesToProcess.length === 0 && this.currentLShape === null) { - this.solved = true - return + this.solved = true; + return; } switch (this.lShapeProcessingStep) { case "idle": - this._handleIdleStep() - break + this._handleIdleStep(); + break; case "intersections": - this._handleIntersectionsStep() - break + this._handleIntersectionsStep(); + break; case "rectangle_selection": - this._handleRectangleSelectionStep() - break + this._handleRectangleSelectionStep(); + break; case "candidate_evaluation": - this._handleCandidateEvaluationStep() - break + this._handleCandidateEvaluationStep(); + break; } } private _resetAfterLShapProcessing() { - this.lShapeProcessingStep = "idle" - this.currentLShape = null - this.currentCandidateIndex = 0 - this.lShapeJustProcessed = false - this.visualizationMode = "l_shapes" // Reset visualization mode - this.intersectionPoints = [] // Clear temporary data - this.tightRectangle = null - this.candidates = [] - this.bestRoute = null - this.lastCollision = null - this.collidingCandidate = null + this.lShapeProcessingStep = "idle"; + this.currentLShape = null; + this.currentCandidateIndex = 0; + this.lShapeJustProcessed = false; + this.visualizationMode = "l_shapes"; // Reset visualization mode + this.intersectionPoints = []; // Clear temporary data + this.tightRectangle = null; + this.candidates = []; + this.bestRoute = null; + this.lastCollision = null; + this.collidingCandidate = null; } private _handleIdleStep() { - this.currentLShape = this.lShapesToProcess.shift()! + this.currentLShape = this.lShapesToProcess.shift()!; if (!this.currentLShape) { - this.solved = true - return + this.solved = true; + return; } - this.lShapeProcessingStep = "intersections" - this.visualizationMode = "l_shapes" + this.lShapeProcessingStep = "intersections"; + this.visualizationMode = "l_shapes"; } private _handleIntersectionsStep() { if (!this.currentLShape!.traceId) { - this.lShapeProcessingStep = "idle" - return + this.lShapeProcessingStep = "idle"; + return; } const allObstacles = getTraceObstacles( this.input.allTraces, this.currentLShape!.traceId, - ) + ); const intersections1 = findIntersectionsWithObstacles( this.currentLShape!.p1, this.currentLShape!.p2, allObstacles, - ) + ); const intersections2 = findIntersectionsWithObstacles( this.currentLShape!.p2, this.currentLShape!.p3, allObstacles, - ) + ); - this.intersectionPoints = [...intersections1, ...intersections2] + this.intersectionPoints = [...intersections1, ...intersections2]; if (intersections1.length === 0 || intersections2.length === 0) { - this.lShapeProcessingStep = "idle" - return + this.lShapeProcessingStep = "idle"; + return; } this.rectangleCandidates = generateRectangleCandidates( intersections1, intersections2, - ) - this.currentRectangleIndex = 0 - this.lShapeProcessingStep = "rectangle_selection" + ); + this.currentRectangleIndex = 0; + this.lShapeProcessingStep = "rectangle_selection"; } private _handleRectangleSelectionStep() { if (this.currentRectangleIndex >= this.rectangleCandidates.length) { - this.lShapeProcessingStep = "idle" - return + this.lShapeProcessingStep = "idle"; + return; } const { rect, i1, i2 } = - this.rectangleCandidates[this.currentRectangleIndex] - this.tightRectangle = rect + this.rectangleCandidates[this.currentRectangleIndex]; + this.tightRectangle = rect; this.candidates = generateLShapeRerouteCandidates({ lShape: this.currentLShape!, @@ -201,82 +201,82 @@ export class UntangleTraceSubsolver extends BaseSolver { padding: 2 * this.input.paddingBuffer, interactionPoint1: i1, interactionPoint2: i2, - }) - this.currentCandidateIndex = 0 - this.lastCollision = null - this.collidingCandidate = null + }); + this.currentCandidateIndex = 0; + this.lastCollision = null; + this.collidingCandidate = null; - this.visualizationMode = "candidates" - this.lShapeProcessingStep = "candidate_evaluation" + this.visualizationMode = "candidates"; + this.lShapeProcessingStep = "candidate_evaluation"; } private _handleCandidateEvaluationStep() { - this.visualizationMode = "candidates" + this.visualizationMode = "candidates"; if (this.bestRouteFound) { - this._applyBestRoute(this.bestRouteFound) - this.bestRouteFound = null - return + this._applyBestRoute(this.bestRouteFound); + this.bestRouteFound = null; + return; } if (this.currentCandidateIndex >= this.candidates.length) { - this.currentRectangleIndex++ - this.lShapeProcessingStep = "rectangle_selection" - return + this.currentRectangleIndex++; + this.lShapeProcessingStep = "rectangle_selection"; + return; } - const currentCandidate = this.candidates[this.currentCandidateIndex] + const currentCandidate = this.candidates[this.currentCandidateIndex]; const collisionResult = isPathColliding( currentCandidate, this.input.allTraces, this.currentLShape!.traceId, - ) + ); if (!collisionResult?.isColliding) { - this.bestRouteFound = currentCandidate - this.lastCollision = null - this.collidingCandidate = null + this.bestRouteFound = currentCandidate; + this.lastCollision = null; + this.collidingCandidate = null; } else { - this.lastCollision = collisionResult - this.collidingCandidate = currentCandidate - this.currentCandidateIndex++ + this.lastCollision = collisionResult; + this.collidingCandidate = currentCandidate; + this.currentCandidateIndex++; } } private _applyBestRoute(bestRoute: Point[]) { - this.bestRoute = bestRoute - this.collidingCandidate = null - this.lastCollision = null + this.bestRoute = bestRoute; + this.collidingCandidate = null; + this.lastCollision = null; const traceIndex = this.input.allTraces.findIndex( (trace) => trace.mspPairId === this.currentLShape!.traceId, - ) + ); if (traceIndex !== -1) { - const originalTrace = this.input.allTraces[traceIndex] + const originalTrace = this.input.allTraces[traceIndex]; const p2Index = originalTrace.tracePath.findIndex( (p) => p.x === this.currentLShape!.p2.x && p.y === this.currentLShape!.p2.y, - ) + ); if (p2Index !== -1) { const newTracePath = [ ...originalTrace.tracePath.slice(0, p2Index), ...bestRoute, ...originalTrace.tracePath.slice(p2Index + 1), - ] + ]; this.input.allTraces[traceIndex] = { ...originalTrace, tracePath: newTracePath, - } + }; this.lShapesToProcess = this.lShapesToProcess.filter( (l) => l.traceId !== this.currentLShape!.traceId, - ) + ); } } - this.lShapeJustProcessed = true + this.lShapeJustProcessed = true; } getOutput(): { traces: SolvedTracePath[] } { - return { traces: this.input.allTraces } + return { traces: this.input.allTraces }; } override visualize(): GraphicsObject { @@ -294,12 +294,12 @@ export class UntangleTraceSubsolver extends BaseSolver { switch (this.visualizationMode) { case "l_shapes": - return visualizeLSapes(this.lShapesToProcess) + return visualizeLSapes(this.lShapesToProcess); case "intersection_points": return mergeGraphicsObjects([ this.currentLShape ? visualizeLSapes(this.currentLShape) : undefined, visualizeIntersectionPoints(this.intersectionPoints), - ]) + ]); case "tight_rectangle": return mergeGraphicsObjects([ this.currentLShape ? visualizeLSapes(this.currentLShape) : undefined, @@ -307,41 +307,41 @@ export class UntangleTraceSubsolver extends BaseSolver { this.tightRectangle ? visualizeTightRectangle(this.tightRectangle) : undefined, - ]) + ]); case "candidates": { if (this.lShapeJustProcessed) { - const allTracesGraphics: GraphicsObject = { lines: [] } + const allTracesGraphics: GraphicsObject = { lines: [] }; for (const trace of this.input.allTraces) { const isUpdatedTrace = - trace.mspPairId === this.currentLShape?.traceId + trace.mspPairId === this.currentLShape?.traceId; for (let i = 0; i < trace.tracePath.length - 1; i++) { allTracesGraphics.lines!.push({ points: [trace.tracePath[i], trace.tracePath[i + 1]], strokeColor: isUpdatedTrace ? "green" : "#ccc", - }) + }); } } - return allTracesGraphics + return allTracesGraphics; } - const allTracesGraphics: GraphicsObject = { lines: [] } + const allTracesGraphics: GraphicsObject = { lines: [] }; for (const trace of this.input.allTraces) { for (let i = 0; i < trace.tracePath.length - 1; i++) { allTracesGraphics.lines!.push({ points: [trace.tracePath[i], trace.tracePath[i + 1]], strokeColor: "#ccc", // Light gray for other traces - }) + }); } } - let candidateToDraw: Point[] | undefined + let candidateToDraw: Point[] | undefined; if (this.bestRouteFound) { - candidateToDraw = this.bestRouteFound + candidateToDraw = this.bestRouteFound; } else if (this.lastCollision?.isColliding) { - candidateToDraw = this.collidingCandidate ?? undefined + candidateToDraw = this.collidingCandidate ?? undefined; } else { if (this.currentCandidateIndex < this.candidates.length) { - candidateToDraw = this.candidates[this.currentCandidateIndex] + candidateToDraw = this.candidates[this.currentCandidateIndex]; } } @@ -361,10 +361,10 @@ export class UntangleTraceSubsolver extends BaseSolver { this.lastCollision ? visualizeCollision(this.lastCollision) : undefined, - ]) + ]); } default: - return {} + return {}; } } } diff --git a/lib/solvers/TraceCleanupSolver/sub-solver/findAllLShapedTurns.ts b/lib/solvers/TraceCleanupSolver/sub-solver/findAllLShapedTurns.ts index bdebcd35..f57a8a4a 100644 --- a/lib/solvers/TraceCleanupSolver/sub-solver/findAllLShapedTurns.ts +++ b/lib/solvers/TraceCleanupSolver/sub-solver/findAllLShapedTurns.ts @@ -1,14 +1,14 @@ -import type { Point } from "@tscircuit/math-utils" +import type { Point } from "@tscircuit/math-utils"; /** * Represents an L-shaped turn in a trace path, defined by three consecutive points. * p1 and p3 are the endpoints of the L-shape, and p2 is the corner point. */ export interface LShape { - p1: Point - p2: Point // The corner - p3: Point - traceId?: string + p1: Point; + p2: Point; // The corner + p3: Point; + traceId?: string; } /** @@ -18,20 +18,20 @@ export interface LShape { * checking every sequence of three points to see if they form an L-shape. */ export const findAllLShapedTurns = (tracePath: Point[]): LShape[] => { - const lShapes: LShape[] = [] + const lShapes: LShape[] = []; if (tracePath.length < 3) { - return lShapes + return lShapes; } for (let i = 0; i < tracePath.length - 2; i++) { - const p1 = tracePath[i] - const p2 = tracePath[i + 1] - const p3 = tracePath[i + 2] + const p1 = tracePath[i]; + const p2 = tracePath[i + 1]; + const p3 = tracePath[i + 2]; - const dx1 = p2.x - p1.x - const dy1 = p2.y - p1.y - const dx2 = p3.x - p2.x - const dy2 = p3.y - p2.y + const dx1 = p2.x - p1.x; + const dy1 = p2.y - p1.y; + const dx2 = p3.x - p2.x; + const dy2 = p3.y - p2.y; // Check for a 90-degree turn (orthogonal segments) if ( @@ -40,9 +40,9 @@ export const findAllLShapedTurns = (tracePath: Point[]): LShape[] => { dx1 * dx1 + dy1 * dy1 >= 0.25 && // p1-p2 arm length >= 0.5 dx2 * dx2 + dy2 * dy2 >= 0.25 // p2-p3 arm length >= 0.5 ) { - lShapes.push({ p1, p2, p3 }) + lShapes.push({ p1, p2, p3 }); } } - return lShapes -} + return lShapes; +}; diff --git a/lib/solvers/TraceCleanupSolver/sub-solver/findIntersectionsWithObstacles.ts b/lib/solvers/TraceCleanupSolver/sub-solver/findIntersectionsWithObstacles.ts index 84ea62e1..16c5e3dc 100644 --- a/lib/solvers/TraceCleanupSolver/sub-solver/findIntersectionsWithObstacles.ts +++ b/lib/solvers/TraceCleanupSolver/sub-solver/findIntersectionsWithObstacles.ts @@ -1,6 +1,6 @@ -import type { Point } from "@tscircuit/math-utils" -import { getSegmentIntersection } from "@tscircuit/math-utils/line-intersections" -import type { TraceObstacle } from "./getTraceObstacles" +import type { Point } from "@tscircuit/math-utils"; +import { getSegmentIntersection } from "@tscircuit/math-utils/line-intersections"; +import type { TraceObstacle } from "./getTraceObstacles"; /** * Finds all intersection points between a given line segment (p1-p2) and a list of trace obstacles. @@ -11,26 +11,26 @@ export const findIntersectionsWithObstacles = ( p2: Point, obstacles: TraceObstacle[], ): Point[] => { - const intersections: Point[] = [] + const intersections: Point[] = []; for (const obstacle of obstacles) { - const obstaclePath = obstacle.points + const obstaclePath = obstacle.points; for (let i = 0; i < obstaclePath.length - 1; i++) { - const o1 = obstaclePath[i] - const o2 = obstaclePath[i + 1] + const o1 = obstaclePath[i]; + const o2 = obstaclePath[i + 1]; // Ensure both points are defined before proceeding if (!o1 || !o2) { // console.warn("Skipping obstacle segment due to undefined point:", { o1, o2, obstaclePath }); - continue + continue; } - const intersection = getSegmentIntersection(p1, p2, o1, o2) + const intersection = getSegmentIntersection(p1, p2, o1, o2); if (intersection) { - intersections.push(intersection) + intersections.push(intersection); } } } - return intersections -} + return intersections; +}; diff --git a/lib/solvers/TraceCleanupSolver/sub-solver/generateLShapeRerouteCandidates.ts b/lib/solvers/TraceCleanupSolver/sub-solver/generateLShapeRerouteCandidates.ts index c2b012dc..1356378d 100644 --- a/lib/solvers/TraceCleanupSolver/sub-solver/generateLShapeRerouteCandidates.ts +++ b/lib/solvers/TraceCleanupSolver/sub-solver/generateLShapeRerouteCandidates.ts @@ -1,14 +1,14 @@ -import type { Point } from "@tscircuit/math-utils" -import type { LShape } from "./findAllLShapedTurns" -import type { Rectangle } from "./generateRectangleCandidates" +import type { Point } from "@tscircuit/math-utils"; +import type { LShape } from "./findAllLShapedTurns"; +import type { Rectangle } from "./generateRectangleCandidates"; -const EPS = 1e-6 +const EPS = 1e-6; /** * Checks if a segment defined by two points is vertical. * It considers a segment vertical if the absolute difference between their x-coordinates is less than a small epsilon. */ -const isVertical = (a: Point, b: Point, eps = EPS) => Math.abs(a.x - b.x) < eps +const isVertical = (a: Point, b: Point, eps = EPS) => Math.abs(a.x - b.x) < eps; /** * Generates candidate reroutes for an L-shaped turn within a given rectangular area. @@ -23,85 +23,85 @@ export const generateLShapeRerouteCandidates = ({ interactionPoint1, interactionPoint2, }: { - lShape: LShape - rectangle: Rectangle - padding: number - interactionPoint1: Point - interactionPoint2: Point + lShape: LShape; + rectangle: Rectangle; + padding: number; + interactionPoint1: Point; + interactionPoint2: Point; }): Point[][] => { - const { p1, p2, p3 } = lShape - const { x, y, width, height } = rectangle + const { p1, p2, p3 } = lShape; + const { x, y, width, height } = rectangle; - let c2: Point - let i1_padded: Point = interactionPoint1 - let i2_padded: Point = interactionPoint2 + let c2: Point; + let i1_padded: Point = interactionPoint1; + let i2_padded: Point = interactionPoint2; if (Math.abs(p2.x - x) < EPS && Math.abs(p2.y - (y + height)) < EPS) { - c2 = { x: x + width + padding, y: y - padding } + c2 = { x: x + width + padding, y: y - padding }; if (isVertical(p1, p2)) { - i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y - padding } + i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y - padding }; } else { // isHorizontal(p1, p2) - i1_padded = { x: interactionPoint1.x + padding, y: interactionPoint1.y } + i1_padded = { x: interactionPoint1.x + padding, y: interactionPoint1.y }; } if (isVertical(p2, p3)) { - i2_padded = { x: interactionPoint2.x, y: interactionPoint2.y - padding } + i2_padded = { x: interactionPoint2.x, y: interactionPoint2.y - padding }; } else { // isHorizontal(p2, p3) - i2_padded = { x: interactionPoint2.x + padding, y: interactionPoint2.y } + i2_padded = { x: interactionPoint2.x + padding, y: interactionPoint2.y }; } } else if ( Math.abs(p2.x - (x + width)) < EPS && Math.abs(p2.y - (y + height)) < EPS ) { - c2 = { x: x - padding, y: y - padding } + c2 = { x: x - padding, y: y - padding }; if (isVertical(p1, p2)) { - i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y - padding } + i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y - padding }; } else { // isHorizontal(p1, p2) - i1_padded = { x: interactionPoint1.x - padding, y: interactionPoint1.y } + i1_padded = { x: interactionPoint1.x - padding, y: interactionPoint1.y }; } if (isVertical(p2, p3)) { - i2_padded = { x: interactionPoint2.x, y: interactionPoint2.y - padding } + i2_padded = { x: interactionPoint2.x, y: interactionPoint2.y - padding }; } else { // isHorizontal(p2, p3) - i2_padded = { x: interactionPoint2.x - padding, y: interactionPoint2.y } + i2_padded = { x: interactionPoint2.x - padding, y: interactionPoint2.y }; } } else if (Math.abs(p2.x - x) < EPS && Math.abs(p2.y - y) < EPS) { - c2 = { x: x + width + padding, y: y + height + padding } + c2 = { x: x + width + padding, y: y + height + padding }; if (isVertical(p1, p2)) { - i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y + padding } + i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y + padding }; } else { // isHorizontal(p1, p2) - i1_padded = { x: interactionPoint1.x + padding, y: interactionPoint1.y } + i1_padded = { x: interactionPoint1.x + padding, y: interactionPoint1.y }; } if (isVertical(p2, p3)) { - i2_padded = { x: interactionPoint2.x, y: interactionPoint2.y + padding } + i2_padded = { x: interactionPoint2.x, y: interactionPoint2.y + padding }; } else { // isHorizontal(p2, p3) - i2_padded = { x: interactionPoint2.x + padding, y: interactionPoint2.y } + i2_padded = { x: interactionPoint2.x + padding, y: interactionPoint2.y }; } } else if (Math.abs(p2.x - (x + width)) < EPS && Math.abs(p2.y - y) < EPS) { - c2 = { x: x - padding, y: y + height + padding } + c2 = { x: x - padding, y: y + height + padding }; if (isVertical(p1, p2)) { - i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y + padding } + i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y + padding }; } else { // isHorizontal(p1, p2) - i1_padded = { x: interactionPoint1.x - padding, y: interactionPoint1.y } + i1_padded = { x: interactionPoint1.x - padding, y: interactionPoint1.y }; } if (isVertical(p2, p3)) { - i2_padded = { x: interactionPoint2.x, y: interactionPoint2.y + padding } + i2_padded = { x: interactionPoint2.x, y: interactionPoint2.y + padding }; } else { // isHorizontal(p2, p3) - i2_padded = { x: interactionPoint2.x - padding, y: interactionPoint2.y } + i2_padded = { x: interactionPoint2.x - padding, y: interactionPoint2.y }; } } else { - return [] + return []; } - return [[i1_padded, c2, i2_padded]] -} + return [[i1_padded, c2, i2_padded]]; +}; diff --git a/lib/solvers/TraceCleanupSolver/sub-solver/generateRectangleCandidates.ts b/lib/solvers/TraceCleanupSolver/sub-solver/generateRectangleCandidates.ts index 09b09df7..ca015b39 100644 --- a/lib/solvers/TraceCleanupSolver/sub-solver/generateRectangleCandidates.ts +++ b/lib/solvers/TraceCleanupSolver/sub-solver/generateRectangleCandidates.ts @@ -1,16 +1,16 @@ -import type { Point } from "@tscircuit/math-utils" +import type { Point } from "@tscircuit/math-utils"; export interface Rectangle { - x: number - y: number - width: number - height: number + x: number; + y: number; + width: number; + height: number; } export interface RectangleCandidate { - rect: Rectangle - i1: Point - i2: Point + rect: Rectangle; + i1: Point; + i2: Point; } /** @@ -23,17 +23,17 @@ export const generateRectangleCandidates = ( intersections1: Point[], intersections2: Point[], ): RectangleCandidate[] => { - const rectangleCandidates: RectangleCandidate[] = [] + const rectangleCandidates: RectangleCandidate[] = []; for (const p1 of intersections1) { for (const p2 of intersections2) { - const minX = Math.min(p1.x, p2.x) - const minY = Math.min(p1.y, p2.y) - const maxX = Math.max(p1.x, p2.x) - const maxY = Math.max(p1.y, p2.y) + const minX = Math.min(p1.x, p2.x); + const minY = Math.min(p1.y, p2.y); + const maxX = Math.max(p1.x, p2.x); + const maxY = Math.max(p1.y, p2.y); - const width = maxX - minX - const height = maxY - minY + const width = maxX - minX; + const height = maxY - minY; // Ensure the rectangle has a non-zero area if (width > 1e-6 && height > 1e-6) { @@ -46,10 +46,10 @@ export const generateRectangleCandidates = ( }, i1: p1, i2: p2, - }) + }); } } } - return rectangleCandidates -} + return rectangleCandidates; +}; diff --git a/lib/solvers/TraceCleanupSolver/sub-solver/getTraceObstacles.ts b/lib/solvers/TraceCleanupSolver/sub-solver/getTraceObstacles.ts index bbd5eabb..912607cb 100644 --- a/lib/solvers/TraceCleanupSolver/sub-solver/getTraceObstacles.ts +++ b/lib/solvers/TraceCleanupSolver/sub-solver/getTraceObstacles.ts @@ -1,7 +1,7 @@ -import type { SolvedTracePath } from "../../SchematicTraceLinesSolver/SchematicTraceLinesSolver" +import type { SolvedTracePath } from "../../SchematicTraceLinesSolver/SchematicTraceLinesSolver"; export interface TraceObstacle { - points: Array<{ x: number; y: number }> + points: Array<{ x: number; y: number }>; } /** @@ -13,13 +13,13 @@ export const getTraceObstacles = ( allTraces: SolvedTracePath[], excludeTraceId: string, ): TraceObstacle[] => { - const obstacles: TraceObstacle[] = [] + const obstacles: TraceObstacle[] = []; for (const trace of allTraces) { if (trace.mspPairId !== excludeTraceId) { - obstacles.push({ points: trace.tracePath }) + obstacles.push({ points: trace.tracePath }); } } - return obstacles -} + return obstacles; +}; diff --git a/lib/solvers/TraceCleanupSolver/sub-solver/isPathColliding.ts b/lib/solvers/TraceCleanupSolver/sub-solver/isPathColliding.ts index b1002870..10f6e024 100644 --- a/lib/solvers/TraceCleanupSolver/sub-solver/isPathColliding.ts +++ b/lib/solvers/TraceCleanupSolver/sub-solver/isPathColliding.ts @@ -1,12 +1,12 @@ -import type { Point } from "@tscircuit/math-utils" -import type { SolvedTracePath } from "../../SchematicTraceLinesSolver/SchematicTraceLinesSolver" -import { getSegmentIntersection } from "@tscircuit/math-utils/line-intersections" +import type { Point } from "@tscircuit/math-utils"; +import type { SolvedTracePath } from "../../SchematicTraceLinesSolver/SchematicTraceLinesSolver"; +import { getSegmentIntersection } from "@tscircuit/math-utils/line-intersections"; export type CollisionInfo = { - isColliding: boolean - collidingTraceId?: string - collisionPoint?: Point -} + isColliding: boolean; + collidingTraceId?: string; + collisionPoint?: Point; +}; /** * Checks if a given path collides with any other traces in a list of solved trace paths. @@ -20,39 +20,39 @@ export const isPathColliding = ( traceIdToExclude?: string, ): CollisionInfo => { if (path.length < 2) { - return { isColliding: false } + return { isColliding: false }; } for (let i = 0; i < path.length - 1; i++) { - const pathSegP1 = path[i] - const pathSegQ1 = path[i + 1] + const pathSegP1 = path[i]; + const pathSegQ1 = path[i + 1]; for (const existingTrace of allTraces) { if (existingTrace.mspPairId === traceIdToExclude) { - continue // Skip self-collision check + continue; // Skip self-collision check } for (let j = 0; j < existingTrace.tracePath.length - 1; j++) { - const existingSegP2 = existingTrace.tracePath[j] - const existingSegQ2 = existingTrace.tracePath[j + 1] + const existingSegP2 = existingTrace.tracePath[j]; + const existingSegQ2 = existingTrace.tracePath[j + 1]; const intersectionPoint = getSegmentIntersection( pathSegP1, pathSegQ1, existingSegP2, existingSegQ2, - ) + ); if (intersectionPoint) { return { isColliding: true, collidingTraceId: existingTrace.mspPairId as string, collisionPoint: intersectionPoint, - } + }; } } } } - return { isColliding: false } // No collision found -} + return { isColliding: false }; // No collision found +}; diff --git a/lib/solvers/TraceCleanupSolver/sub-solver/visualizeCandidates.ts b/lib/solvers/TraceCleanupSolver/sub-solver/visualizeCandidates.ts index 74a11bec..6dde5902 100644 --- a/lib/solvers/TraceCleanupSolver/sub-solver/visualizeCandidates.ts +++ b/lib/solvers/TraceCleanupSolver/sub-solver/visualizeCandidates.ts @@ -1,5 +1,5 @@ -import type { GraphicsObject } from "graphics-debug" -import type { Point } from "@tscircuit/math-utils" +import type { GraphicsObject } from "graphics-debug"; +import type { Point } from "@tscircuit/math-utils"; /** * Visualizes a set of candidate paths and optional intersection points. @@ -11,13 +11,13 @@ export const visualizeCandidates = ( color = "gray", intersectionPoints: Point[] = [], ): GraphicsObject => { - const graphics: GraphicsObject = { lines: [], circles: [] } + const graphics: GraphicsObject = { lines: [], circles: [] }; for (const candidate of candidates) { graphics.lines!.push({ points: candidate, strokeColor: color, - }) + }); } // Draw intersection points @@ -26,8 +26,8 @@ export const visualizeCandidates = ( center: point, radius: 0.01, // Larger radius for intersection points fill: "green", - }) + }); } - return graphics -} + return graphics; +}; diff --git a/lib/solvers/TraceCleanupSolver/sub-solver/visualizeCollision.ts b/lib/solvers/TraceCleanupSolver/sub-solver/visualizeCollision.ts index a57b03a4..9279bed1 100644 --- a/lib/solvers/TraceCleanupSolver/sub-solver/visualizeCollision.ts +++ b/lib/solvers/TraceCleanupSolver/sub-solver/visualizeCollision.ts @@ -1,5 +1,5 @@ -import type { GraphicsObject } from "graphics-debug" -import type { CollisionInfo } from "./isPathColliding" +import type { GraphicsObject } from "graphics-debug"; +import type { CollisionInfo } from "./isPathColliding"; /** * Visualizes a collision point if collision information is provided and a collision occurred. @@ -8,13 +8,13 @@ import type { CollisionInfo } from "./isPathColliding" export const visualizeCollision = ( collisionInfo: CollisionInfo | null, ): GraphicsObject => { - const collisionGraphics: GraphicsObject = { circles: [] } + const collisionGraphics: GraphicsObject = { circles: [] }; if (collisionInfo?.isColliding && collisionInfo.collisionPoint) { collisionGraphics.circles!.push({ center: collisionInfo.collisionPoint, radius: 0.01, fill: "red", - }) + }); } - return collisionGraphics -} + return collisionGraphics; +}; diff --git a/lib/solvers/TraceCleanupSolver/sub-solver/visualizeIntersectionPoints.ts b/lib/solvers/TraceCleanupSolver/sub-solver/visualizeIntersectionPoints.ts index 61ec2563..1ad3d204 100644 --- a/lib/solvers/TraceCleanupSolver/sub-solver/visualizeIntersectionPoints.ts +++ b/lib/solvers/TraceCleanupSolver/sub-solver/visualizeIntersectionPoints.ts @@ -1,5 +1,5 @@ -import type { Point } from "@tscircuit/math-utils" -import type { GraphicsObject } from "graphics-debug" +import type { Point } from "@tscircuit/math-utils"; +import type { GraphicsObject } from "graphics-debug"; /** * Visualizes a set of intersection points by drawing circles at their locations. @@ -9,7 +9,7 @@ export const visualizeIntersectionPoints = ( points: Point[], color = "red", ): GraphicsObject => { - const graphics: GraphicsObject = { circles: [] } + const graphics: GraphicsObject = { circles: [] }; for (const point of points) { graphics.circles!.push({ @@ -19,8 +19,8 @@ export const visualizeIntersectionPoints = ( }, radius: 0.01, fill: color, - }) + }); } - return graphics -} + return graphics; +}; diff --git a/lib/solvers/TraceCleanupSolver/sub-solver/visualizeLSapes.ts b/lib/solvers/TraceCleanupSolver/sub-solver/visualizeLSapes.ts index 6394a274..138912b0 100644 --- a/lib/solvers/TraceCleanupSolver/sub-solver/visualizeLSapes.ts +++ b/lib/solvers/TraceCleanupSolver/sub-solver/visualizeLSapes.ts @@ -1,5 +1,5 @@ -import type { LShape } from "./findAllLShapedTurns" -import type { GraphicsObject } from "graphics-debug" +import type { LShape } from "./findAllLShapedTurns"; +import type { GraphicsObject } from "graphics-debug"; /** * Visualizes L-shaped turns by drawing a blue circle at the corner point (p2) @@ -7,9 +7,9 @@ import type { GraphicsObject } from "graphics-debug" * This function can visualize a single L-shape or an array of L-shapes. */ export const visualizeLSapes = (lShapes: LShape[] | LShape): GraphicsObject => { - const graphics: GraphicsObject = { circles: [], lines: [] } + const graphics: GraphicsObject = { circles: [], lines: [] }; - const lShapesArray = Array.isArray(lShapes) ? lShapes : [lShapes] + const lShapesArray = Array.isArray(lShapes) ? lShapes : [lShapes]; for (const lShape of lShapesArray) { // Draw the center point as a blue ball @@ -20,14 +20,14 @@ export const visualizeLSapes = (lShapes: LShape[] | LShape): GraphicsObject => { }, radius: 0.01, fill: "blue", - }) + }); // Draw the two lines in a light blue color graphics.lines!.push({ points: [lShape.p1, lShape.p2, lShape.p3], strokeColor: "lightblue", - }) + }); } - return graphics -} + return graphics; +}; diff --git a/lib/solvers/TraceCleanupSolver/tryConnectPoints.ts b/lib/solvers/TraceCleanupSolver/tryConnectPoints.ts index d3d71b77..14a62d6d 100644 --- a/lib/solvers/TraceCleanupSolver/tryConnectPoints.ts +++ b/lib/solvers/TraceCleanupSolver/tryConnectPoints.ts @@ -1,14 +1,14 @@ -import type { Point } from "@tscircuit/math-utils" +import type { Point } from "@tscircuit/math-utils"; export const tryConnectPoints = (start: Point, end: Point): Point[][] => { - const candidates: Point[][] = [] + const candidates: Point[][] = []; if (start.x === end.x || start.y === end.y) { - candidates.push([start, end]) + candidates.push([start, end]); } else { - candidates.push([start, { x: end.x, y: start.y }, end]) - candidates.push([start, { x: start.x, y: end.y }, end]) + candidates.push([start, { x: end.x, y: start.y }, end]); + candidates.push([start, { x: start.x, y: end.y }, end]); } - return candidates -} + return candidates; +}; diff --git a/lib/solvers/TraceCleanupSolver/turnMinimization.ts b/lib/solvers/TraceCleanupSolver/turnMinimization.ts index 43b38543..b434e67e 100644 --- a/lib/solvers/TraceCleanupSolver/turnMinimization.ts +++ b/lib/solvers/TraceCleanupSolver/turnMinimization.ts @@ -1,11 +1,11 @@ -import type { Point } from "graphics-debug" -import { hasCollisions } from "./hasCollisions" -import { countTurns } from "./countTurns" -import { simplifyPath } from "./simplifyPath" -import { tryConnectPoints } from "./tryConnectPoints" -import { hasCollisionsWithLabels } from "./hasCollisionsWithLabels" -import { recognizeStairStepPattern } from "./recognizeStairStepPattern" -import { isSegmentAnEndpointSegment } from "./isSegmentAnEndpointSegment" +import type { Point } from "graphics-debug"; +import { hasCollisions } from "./hasCollisions"; +import { countTurns } from "./countTurns"; +import { simplifyPath } from "./simplifyPath"; +import { tryConnectPoints } from "./tryConnectPoints"; +import { hasCollisionsWithLabels } from "./hasCollisionsWithLabels"; +import { recognizeStairStepPattern } from "./recognizeStairStepPattern"; +import { isSegmentAnEndpointSegment } from "./isSegmentAnEndpointSegment"; /** * Minimizes the number of turns in a given path while avoiding collisions with obstacles and labels. @@ -21,25 +21,25 @@ export const minimizeTurns = ({ labelBounds, originalPath, }: { - path: Point[] - obstacles: any[] - labelBounds: any[] - originalPath: Point[] + path: Point[]; + obstacles: any[]; + labelBounds: any[]; + originalPath: Point[]; }): Point[] => { if (path.length <= 2) { - return path + return path; } - let optimizedPath = [...path] - let currentTurns = countTurns(optimizedPath) - let improved = true + let optimizedPath = [...path]; + let currentTurns = countTurns(optimizedPath); + let improved = true; while (improved) { - improved = false + improved = false; // First try to identify and replace stair-step patterns for (let startIdx = 0; startIdx < optimizedPath.length - 3; startIdx++) { - const stairEndIdx = recognizeStairStepPattern(optimizedPath, startIdx) + const stairEndIdx = recognizeStairStepPattern(optimizedPath, startIdx); if (stairEndIdx > 0) { if ( @@ -54,37 +54,37 @@ export const minimizeTurns = ({ originalPath, ) ) { - continue + continue; } - const startPoint = optimizedPath[startIdx] - const endPoint = optimizedPath[stairEndIdx] + const startPoint = optimizedPath[startIdx]; + const endPoint = optimizedPath[stairEndIdx]; - const connectionOptions = tryConnectPoints(startPoint, endPoint) + const connectionOptions = tryConnectPoints(startPoint, endPoint); for (const connection of connectionOptions) { const testPath = [ ...optimizedPath.slice(0, startIdx + 1), ...connection.slice(1, -1), ...optimizedPath.slice(stairEndIdx), - ] + ]; - const collidesWithObstacles = hasCollisions(connection, obstacles) + const collidesWithObstacles = hasCollisions(connection, obstacles); const collidesWithLabels = hasCollisionsWithLabels( connection, labelBounds, - ) + ); if (!collidesWithObstacles && !collidesWithLabels) { - const newTurns = countTurns(testPath) - optimizedPath = testPath - currentTurns = newTurns - improved = true - break + const newTurns = countTurns(testPath); + optimizedPath = testPath; + currentTurns = newTurns; + improved = true; + break; } } - if (improved) break + if (improved) break; } } @@ -94,12 +94,12 @@ export const minimizeTurns = ({ const maxRemove = Math.min( optimizedPath.length - startIdx - 2, optimizedPath.length - 2, - ) + ); for (let removeCount = 1; removeCount <= maxRemove; removeCount++) { - const endIdx = startIdx + removeCount + 1 + const endIdx = startIdx + removeCount + 1; - if (endIdx >= optimizedPath.length) continue + if (endIdx >= optimizedPath.length) continue; if ( isSegmentAnEndpointSegment( @@ -113,91 +113,91 @@ export const minimizeTurns = ({ originalPath, ) ) { - continue + continue; } - const startPoint = optimizedPath[startIdx] - const endPoint = optimizedPath[endIdx] + const startPoint = optimizedPath[startIdx]; + const endPoint = optimizedPath[endIdx]; - const connectionOptions = tryConnectPoints(startPoint, endPoint) + const connectionOptions = tryConnectPoints(startPoint, endPoint); for (const connection of connectionOptions) { const testPath = [ ...optimizedPath.slice(0, startIdx + 1), ...connection.slice(1, -1), ...optimizedPath.slice(endIdx), - ] + ]; - const connectionSegments = connection + const connectionSegments = connection; const collidesWithObstacles = hasCollisions( connectionSegments, obstacles, - ) + ); const collidesWithLabels = hasCollisionsWithLabels( connectionSegments, labelBounds, - ) + ); if (!collidesWithObstacles && !collidesWithLabels) { - const newTurns = countTurns(testPath) + const newTurns = countTurns(testPath); if ( newTurns < currentTurns || (newTurns === currentTurns && testPath.length < optimizedPath.length) ) { - optimizedPath = testPath - currentTurns = newTurns - improved = true - break + optimizedPath = testPath; + currentTurns = newTurns; + improved = true; + break; } } } - if (improved) break + if (improved) break; } - if (improved) break + if (improved) break; } } if (!improved) { for (let i = 0; i < optimizedPath.length - 2; i++) { - const p1 = optimizedPath[i] - const p2 = optimizedPath[i + 1] - const p3 = optimizedPath[i + 2] + const p1 = optimizedPath[i]; + const p2 = optimizedPath[i + 1]; + const p3 = optimizedPath[i + 2]; if ( isSegmentAnEndpointSegment(p1, p2, originalPath) || isSegmentAnEndpointSegment(p2, p3, originalPath) ) { - continue + continue; } - const allVertical = p1.x === p2.x && p2.x === p3.x - const allHorizontal = p1.y === p2.y && p2.y === p3.y + const allVertical = p1.x === p2.x && p2.x === p3.x; + const allHorizontal = p1.y === p2.y && p2.y === p3.y; if (allVertical || allHorizontal) { const testPath = [ ...optimizedPath.slice(0, i + 1), ...optimizedPath.slice(i + 2), - ] + ]; - const collidesWithObstacles = hasCollisions([p1, p3], obstacles) + const collidesWithObstacles = hasCollisions([p1, p3], obstacles); const collidesWithLabels = hasCollisionsWithLabels( [p1, p3], labelBounds, - ) + ); if (!collidesWithObstacles && !collidesWithLabels) { - optimizedPath = testPath - improved = true - break + optimizedPath = testPath; + improved = true; + break; } } } } } - const finalSimplifiedPath = simplifyPath(optimizedPath) - return finalSimplifiedPath -} + const finalSimplifiedPath = simplifyPath(optimizedPath); + return finalSimplifiedPath; +}; diff --git a/lib/solvers/TraceCleanupSolver/visualizeTightRectangle.ts b/lib/solvers/TraceCleanupSolver/visualizeTightRectangle.ts index 156735c5..11721b63 100644 --- a/lib/solvers/TraceCleanupSolver/visualizeTightRectangle.ts +++ b/lib/solvers/TraceCleanupSolver/visualizeTightRectangle.ts @@ -1,5 +1,5 @@ -import type { GraphicsObject } from "graphics-debug" -import type { Rectangle } from "./sub-solver/generateRectangleCandidates" +import type { GraphicsObject } from "graphics-debug"; +import type { Rectangle } from "./sub-solver/generateRectangleCandidates"; /** * Visualizes a given rectangle by drawing it as a green-stroked rectangle. @@ -8,7 +8,7 @@ import type { Rectangle } from "./sub-solver/generateRectangleCandidates" export const visualizeTightRectangle = ( rectangle: Rectangle, ): GraphicsObject => { - const graphics: GraphicsObject = { rects: [] } + const graphics: GraphicsObject = { rects: [] }; graphics.rects!.push({ center: { @@ -18,7 +18,7 @@ export const visualizeTightRectangle = ( width: rectangle.width, height: rectangle.height, stroke: "green", - }) + }); - return graphics -} + return graphics; +}; diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts index 8773f70d..60d5a3a5 100644 --- a/lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts @@ -1,18 +1,18 @@ -import { BaseSolver } from "../BaseSolver/BaseSolver" -import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver" -import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver" -import type { GraphicsObject } from "graphics-debug" -import { visualizeInputProblem } from "../SchematicTracePipelineSolver/visualizeInputProblem" -import type { InputProblem } from "../../types/InputProblem" -import { MergedNetLabelObstacleSolver } from "./sub-solvers/LabelMergingSolver/LabelMergingSolver" -import { getColorFromString } from "lib/utils/getColorFromString" -import { OverlapAvoidanceStepSolver } from "./sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver" -import { detectTraceLabelOverlap } from "./detectTraceLabelOverlap" +import { BaseSolver } from "../BaseSolver/BaseSolver"; +import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver"; +import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver"; +import type { GraphicsObject } from "graphics-debug"; +import { visualizeInputProblem } from "../SchematicTracePipelineSolver/visualizeInputProblem"; +import type { InputProblem } from "../../types/InputProblem"; +import { MergedNetLabelObstacleSolver } from "./sub-solvers/LabelMergingSolver/LabelMergingSolver"; +import { getColorFromString } from "lib/utils/getColorFromString"; +import { OverlapAvoidanceStepSolver } from "./sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver"; +import { detectTraceLabelOverlap } from "./detectTraceLabelOverlap"; interface TraceLabelOverlapAvoidanceSolverInput { - inputProblem: InputProblem - traces: SolvedTracePath[] - netLabelPlacements: NetLabelPlacement[] + inputProblem: InputProblem; + traces: SolvedTracePath[]; + netLabelPlacements: NetLabelPlacement[]; } /** @@ -30,25 +30,25 @@ interface TraceLabelOverlapAvoidanceSolverInput { * `MergedNetLabelObstacleSolver` ensures pipeline compatibility. */ export class TraceLabelOverlapAvoidanceSolver extends BaseSolver { - inputProblem: InputProblem - netLabelPlacements: NetLabelPlacement[] + inputProblem: InputProblem; + netLabelPlacements: NetLabelPlacement[]; - unprocessedTraces: SolvedTracePath[] = [] - cleanTraces: SolvedTracePath[] = [] - subSolvers: OverlapAvoidanceStepSolver[] = [] + unprocessedTraces: SolvedTracePath[] = []; + cleanTraces: SolvedTracePath[] = []; + subSolvers: OverlapAvoidanceStepSolver[] = []; private phase: "searching_for_overlaps" | "fixing_overlaps" = - "searching_for_overlaps" - detourCounts: Map = new Map() + "searching_for_overlaps"; + detourCounts: Map = new Map(); - labelMergingSolver?: MergedNetLabelObstacleSolver + labelMergingSolver?: MergedNetLabelObstacleSolver; constructor(solverInput: TraceLabelOverlapAvoidanceSolverInput) { - super() - this.inputProblem = solverInput.inputProblem - this.unprocessedTraces = [...solverInput.traces] - this.netLabelPlacements = solverInput.netLabelPlacements - this.cleanTraces = [] - this.subSolvers = [] + super(); + this.inputProblem = solverInput.inputProblem; + this.unprocessedTraces = [...solverInput.traces]; + this.netLabelPlacements = solverInput.netLabelPlacements; + this.cleanTraces = []; + this.subSolvers = []; } override _step() { @@ -56,30 +56,30 @@ export class TraceLabelOverlapAvoidanceSolver extends BaseSolver { if (this.unprocessedTraces.length === 0) { console.log( `Dispatch phase complete. Created ${this.subSolvers.length} sub-solvers.`, - ) - this.phase = "fixing_overlaps" - return + ); + this.phase = "fixing_overlaps"; + return; } - const currentTargetTrace = this.unprocessedTraces.shift()! + const currentTargetTrace = this.unprocessedTraces.shift()!; const localOverlaps = detectTraceLabelOverlap({ traces: [currentTargetTrace], netLabels: this.netLabelPlacements, - }) + }); if (localOverlaps.length === 0) { - this.cleanTraces.push(currentTargetTrace) + this.cleanTraces.push(currentTargetTrace); } else { // Dispatch a new sub-solver for this dirty trace - const collidingLabels = localOverlaps.map((o) => o.label) + const collidingLabels = localOverlaps.map((o) => o.label); const labelMerger = new MergedNetLabelObstacleSolver({ netLabelPlacements: collidingLabels, inputProblem: this.inputProblem, traces: [currentTargetTrace], - }) - labelMerger.solve() - const mergingOutput = labelMerger.getOutput() + }); + labelMerger.solve(); + const mergingOutput = labelMerger.getOutput(); const subSolver = new OverlapAvoidanceStepSolver({ inputProblem: this.inputProblem, @@ -88,69 +88,71 @@ export class TraceLabelOverlapAvoidanceSolver extends BaseSolver { mergedNetLabelPlacements: mergingOutput.netLabelPlacements, mergedLabelNetIdMap: mergingOutput.mergedLabelNetIdMap, detourCounts: this.detourCounts, - }) - this.subSolvers.push(subSolver) + }); + this.subSolvers.push(subSolver); } } else if (this.phase === "fixing_overlaps") { if (this.subSolvers.every((s) => s.solved || s.failed)) { - console.log("All sub-solvers finished.") + console.log("All sub-solvers finished."); // Final merge for pipeline compatibility if (!this.labelMergingSolver) { this.labelMergingSolver = new MergedNetLabelObstacleSolver({ netLabelPlacements: this.netLabelPlacements, inputProblem: this.inputProblem, traces: this.getOutput().traces, - }) - this.labelMergingSolver.solve() + }); + this.labelMergingSolver.solve(); } - this.solved = true - return + this.solved = true; + return; } // Step through all active sub-solvers for (const solver of this.subSolvers) { if (!solver.solved && !solver.failed) { - solver.step() + solver.step(); } } } } getOutput() { - const solvedTraces = this.subSolvers.flatMap((s) => s.getOutput().allTraces) + const solvedTraces = this.subSolvers.flatMap( + (s) => s.getOutput().allTraces, + ); return { traces: [...this.cleanTraces, ...solvedTraces], netLabelPlacements: this.labelMergingSolver?.getOutput().netLabelPlacements ?? this.netLabelPlacements, - } + }; } override visualize(): GraphicsObject { - const graphics = visualizeInputProblem(this.inputProblem) - if (!graphics.lines) graphics.lines = [] - if (!graphics.rects) graphics.rects = [] + const graphics = visualizeInputProblem(this.inputProblem); + if (!graphics.lines) graphics.lines = []; + if (!graphics.rects) graphics.rects = []; // Show clean traces in purple for (const trace of this.cleanTraces) { graphics.lines!.push({ points: trace.tracePath, strokeColor: "purple", - }) + }); } // Delegate visualization to sub-solvers for (const solver of this.subSolvers) { - const solverGraphics = solver.visualize() - graphics.lines!.push(...(solverGraphics.lines ?? [])) - graphics.rects!.push(...(solverGraphics.rects ?? [])) + const solverGraphics = solver.visualize(); + graphics.lines!.push(...(solverGraphics.lines ?? [])); + graphics.rects!.push(...(solverGraphics.rects ?? [])); // graphics.texts!.push(...(solverGraphics.texts ?? [])) - graphics.points!.push(...(solverGraphics.points ?? [])) + graphics.points!.push(...(solverGraphics.points ?? [])); } // Also show original labels for (const label of this.netLabelPlacements) { - const color = getColorFromString(label.globalConnNetId, 0.3) // Make fill opaque + const color = getColorFromString(label.globalConnNetId, 0.3); // Make fill opaque graphics.rects!.push({ center: label.center, width: label.width, @@ -158,9 +160,9 @@ export class TraceLabelOverlapAvoidanceSolver extends BaseSolver { fill: color, stroke: color.replace("0.3", "1"), label: label.globalConnNetId, - }) + }); } - return graphics + return graphics; } } diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/detectTraceLabelOverlap.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/detectTraceLabelOverlap.ts index b921a1cd..dc721d03 100644 --- a/lib/solvers/TraceLabelOverlapAvoidanceSolver/detectTraceLabelOverlap.ts +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/detectTraceLabelOverlap.ts @@ -1,11 +1,11 @@ -import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver" -import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver" -import { segmentIntersectsRect } from "../NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions" -import { getRectBounds } from "../NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry" +import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver"; +import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver"; +import { segmentIntersectsRect } from "../NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions"; +import { getRectBounds } from "../NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"; export interface TraceLabelOverlap { - trace: SolvedTracePath - label: NetLabelPlacement + trace: SolvedTracePath; + label: NetLabelPlacement; } /** @@ -23,31 +23,35 @@ export const detectTraceLabelOverlap = ({ traces, netLabels = [], }: { - traces: SolvedTracePath[] - netLabels?: NetLabelPlacement[] + traces: SolvedTracePath[]; + netLabels?: NetLabelPlacement[]; }): TraceLabelOverlap[] => { - const overlaps: TraceLabelOverlap[] = [] + const overlaps: TraceLabelOverlap[] = []; for (const trace of traces) { for (const label of netLabels) { - const labelBounds = getRectBounds(label.center, label.width, label.height) + const labelBounds = getRectBounds( + label.center, + label.width, + label.height, + ); for (let j = 0; j < trace.tracePath.length - 1; j++) { - const p1 = trace.tracePath[j] - const p2 = trace.tracePath[j + 1] + const p1 = trace.tracePath[j]; + const p2 = trace.tracePath[j + 1]; if (segmentIntersectsRect(p1, p2, labelBounds)) { // If the trace and label belong to the same net, it's a legitimate connection, not an overlap to avoid. // This check is now redundant with the new logic in OverlapAvoidanceStepSolver, // but kept here for now as per instruction not to remove anything unnecessary. if (trace.globalConnNetId === label.globalConnNetId) { - break // Break from the inner-most loop (segment loop) + break; // Break from the inner-most loop (segment loop) } - overlaps.push({ trace, label }) - break // Break from the inner-most loop (segment loop) + overlaps.push({ trace, label }); + break; // Break from the inner-most loop (segment loop) } } } } - return overlaps -} + return overlaps; +}; diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/index.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/index.ts index da58042f..c9db9841 100644 --- a/lib/solvers/TraceLabelOverlapAvoidanceSolver/index.ts +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/index.ts @@ -1 +1 @@ -export * from "./TraceLabelOverlapAvoidanceSolver" +export * from "./TraceLabelOverlapAvoidanceSolver"; diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace.ts index 2b7af607..da56c072 100644 --- a/lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace.ts +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace.ts @@ -1,12 +1,12 @@ -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 type { InputProblem } from "lib/types/InputProblem" -import { findTraceViolationZone } from "./violation" -import { generateSnipAndReconnectCandidates } from "./trySnipAndReconnect" -import { generateFourPointDetourCandidates } from "./tryFourPointDetour" -import { simplifyPath } from "../TraceCleanupSolver/simplifyPath" +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 type { InputProblem } from "lib/types/InputProblem"; +import { findTraceViolationZone } from "./violation"; +import { generateSnipAndReconnectCandidates } from "./trySnipAndReconnect"; +import { generateFourPointDetourCandidates } from "./tryFourPointDetour"; +import { simplifyPath } from "../TraceCleanupSolver/simplifyPath"; /** * Generates a list of candidate rerouted paths for a given trace that is @@ -26,26 +26,26 @@ export const generateRerouteCandidates = ({ paddingBuffer, detourCount, }: { - trace: SolvedTracePath - label: NetLabelPlacement - problem: InputProblem - paddingBuffer: number - detourCount: number + trace: SolvedTracePath; + label: NetLabelPlacement; + problem: InputProblem; + paddingBuffer: number; + detourCount: number; }): Point[][] => { - const initialTrace = { ...trace, tracePath: simplifyPath(trace.tracePath) } + const initialTrace = { ...trace, tracePath: simplifyPath(trace.tracePath) }; if (trace.globalConnNetId === label.globalConnNetId) { - return [initialTrace.tracePath] + return [initialTrace.tracePath]; } - const labelBoundsRaw = getRectBounds(label.center, label.width, label.height) + const labelBoundsRaw = getRectBounds(label.center, label.width, label.height); const labelBounds = { minX: labelBoundsRaw.minX, minY: labelBoundsRaw.minY, maxX: labelBoundsRaw.maxX, maxY: labelBoundsRaw.maxY, chipId: `netlabel-${label.netId}`, - } + }; const fourPointCandidates = generateFourPointDetourCandidates({ initialTrace, @@ -53,12 +53,12 @@ export const generateRerouteCandidates = ({ labelBounds, paddingBuffer, detourCount, - }) + }); const { firstInsideIndex, lastInsideIndex } = findTraceViolationZone( initialTrace.tracePath, labelBounds, - ) + ); const snipReconnectCandidates = generateSnipAndReconnectCandidates({ initialTrace, @@ -67,7 +67,7 @@ export const generateRerouteCandidates = ({ labelBounds, paddingBuffer, detourCount, - }) + }); - return [...fourPointCandidates, ...snipReconnectCandidates] -} + return [...fourPointCandidates, ...snipReconnectCandidates]; +}; diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver.ts index ff777b42..40192b62 100644 --- a/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver.ts +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver.ts @@ -1,61 +1,61 @@ -import type { SolvedTracePath } from "../../../SchematicTraceLinesSolver/SchematicTraceLinesSolver" -import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver" -import type { NetLabelPlacement } from "../../../NetLabelPlacementSolver/NetLabelPlacementSolver" -import { getRectBounds } from "../../../NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry" -import type { GraphicsObject, Line } from "graphics-debug" -import { getColorFromString } from "lib/utils/getColorFromString" -import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem" -import type { InputProblem } from "lib/types/InputProblem" -import { groupLabelsByChipAndOrientation } from "./groupLabelsByChipAndOrientation" -import { mergeLabelGroup } from "./mergeLabelGroup" -import { filterLabelsAtTraceEdges } from "./filterLabelsAtTraceEdges" +import type { SolvedTracePath } from "../../../SchematicTraceLinesSolver/SchematicTraceLinesSolver"; +import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"; +import type { NetLabelPlacement } from "../../../NetLabelPlacementSolver/NetLabelPlacementSolver"; +import { getRectBounds } from "../../../NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"; +import type { GraphicsObject, Line } from "graphics-debug"; +import { getColorFromString } from "lib/utils/getColorFromString"; +import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem"; +import type { InputProblem } from "lib/types/InputProblem"; +import { groupLabelsByChipAndOrientation } from "./groupLabelsByChipAndOrientation"; +import { mergeLabelGroup } from "./mergeLabelGroup"; +import { filterLabelsAtTraceEdges } from "./filterLabelsAtTraceEdges"; interface LabelMergingSolverInput { - netLabelPlacements: NetLabelPlacement[] - inputProblem: InputProblem - traces: SolvedTracePath[] + netLabelPlacements: NetLabelPlacement[]; + inputProblem: InputProblem; + traces: SolvedTracePath[]; } interface LabelMergingSolverOutput { - netLabelPlacements: NetLabelPlacement[] - mergedLabelNetIdMap: Record> + netLabelPlacements: NetLabelPlacement[]; + mergedLabelNetIdMap: Record>; } type PipelineStep = | "filtering_labels" | "grouping_labels" | "merging_groups" - | "finalizing" + | "finalizing"; /** * Merges multiple net labels into a single, larger label if they are on the * same side of the same chip and physically adjacent. */ export class MergedNetLabelObstacleSolver extends BaseSolver { - private input: LabelMergingSolverInput - private output!: LabelMergingSolverOutput - private inputProblem: InputProblem - private traces: SolvedTracePath[] + private input: LabelMergingSolverInput; + private output!: LabelMergingSolverOutput; + private inputProblem: InputProblem; + private traces: SolvedTracePath[]; // State for the new pipeline - private pipelineStep: PipelineStep = "filtering_labels" - private filteredLabels: NetLabelPlacement[] = [] - private labelGroups: Record = {} - private groupKeysToProcess: string[] = [] - private finalPlacements: NetLabelPlacement[] = [] - private mergedLabelNetIdMap: Record> = {} - private activeMergingGroupKey: string | null = null + private pipelineStep: PipelineStep = "filtering_labels"; + private filteredLabels: NetLabelPlacement[] = []; + private labelGroups: Record = {}; + private groupKeysToProcess: string[] = []; + private finalPlacements: NetLabelPlacement[] = []; + private mergedLabelNetIdMap: Record> = {}; + private activeMergingGroupKey: string | null = null; constructor(solverInput: LabelMergingSolverInput) { - super() - this.input = solverInput - this.inputProblem = solverInput.inputProblem - this.traces = solverInput.traces + super(); + this.input = solverInput; + this.inputProblem = solverInput.inputProblem; + this.traces = solverInput.traces; this.output = { netLabelPlacements: solverInput.netLabelPlacements, mergedLabelNetIdMap: {}, - } + }; } override _step() { @@ -64,41 +64,42 @@ export class MergedNetLabelObstacleSolver extends BaseSolver { this.filteredLabels = filterLabelsAtTraceEdges({ labels: this.input.netLabelPlacements, traces: this.traces, - }) - this.pipelineStep = "grouping_labels" - break + }); + this.pipelineStep = "grouping_labels"; + break; case "grouping_labels": this.labelGroups = groupLabelsByChipAndOrientation({ labels: this.filteredLabels, chips: this.inputProblem.chips, - }) - this.groupKeysToProcess = Object.keys(this.labelGroups) - this.pipelineStep = "merging_groups" - break + }); + this.groupKeysToProcess = Object.keys(this.labelGroups); + this.pipelineStep = "merging_groups"; + break; case "merging_groups": if (this.groupKeysToProcess.length === 0) { - this.pipelineStep = "finalizing" - this.activeMergingGroupKey = null - break + this.pipelineStep = "finalizing"; + this.activeMergingGroupKey = null; + break; } - const groupKey = this.groupKeysToProcess.pop()! - this.activeMergingGroupKey = groupKey - const group = this.labelGroups[groupKey]! + const groupKey = this.groupKeysToProcess.pop()!; + this.activeMergingGroupKey = groupKey; + const group = this.labelGroups[groupKey]!; if (group.length > 1) { const { mergedLabel, originalNetIds } = mergeLabelGroup( group, groupKey, - ) - this.finalPlacements.push(mergedLabel) - this.mergedLabelNetIdMap[mergedLabel.globalConnNetId] = originalNetIds + ); + this.finalPlacements.push(mergedLabel); + this.mergedLabelNetIdMap[mergedLabel.globalConnNetId] = + originalNetIds; } else { - this.finalPlacements.push(...group) + this.finalPlacements.push(...group); } - break + break; case "finalizing": // Any labels that were filtered out and not part of any group should be added back @@ -108,46 +109,46 @@ export class MergedNetLabelObstacleSolver extends BaseSolver { ? [...this.mergedLabelNetIdMap[p.globalConnNetId]!] : [p.globalConnNetId], ), - ) + ); const unprocessedLabels = this.input.netLabelPlacements.filter( (l) => !processedOriginalIds.has(l.globalConnNetId), - ) + ); this.output = { netLabelPlacements: [...this.finalPlacements, ...unprocessedLabels], mergedLabelNetIdMap: this.mergedLabelNetIdMap, - } - this.solved = true - break + }; + this.solved = true; + break; } } getOutput(): LabelMergingSolverOutput { - return this.output + return this.output; } override visualize(): GraphicsObject { const graphics = visualizeInputProblem(this.inputProblem, { 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 = [] + if (!graphics.rects) graphics.rects = []; + if (!graphics.lines) graphics.lines = []; + if (!graphics.points) graphics.points = []; + if (!graphics.texts) graphics.texts = []; - const originalLabelsById = new Map() + const originalLabelsById = new Map(); for (const label of this.input.netLabelPlacements) { - originalLabelsById.set(label.globalConnNetId, label) + originalLabelsById.set(label.globalConnNetId, label); } for (const trace of this.traces) { const line: Line = { points: trace.tracePath.map((p) => ({ x: p.x, y: p.y })), strokeColor: "blue", - } - graphics.lines!.push(line) + }; + graphics.lines!.push(line); } // Highlight the active sub-group being merged @@ -155,7 +156,7 @@ export class MergedNetLabelObstacleSolver extends BaseSolver { this.activeMergingGroupKey && this.labelGroups[this.activeMergingGroupKey] ) { - const activeGroup = this.labelGroups[this.activeMergingGroupKey]! + const activeGroup = this.labelGroups[this.activeMergingGroupKey]!; for (const label of activeGroup) { graphics.rects.push({ center: label.center, @@ -163,13 +164,13 @@ export class MergedNetLabelObstacleSolver extends BaseSolver { height: label.height, fill: "rgba(255, 165, 0, 0.5)", // Orange highlight stroke: "orange", - }) + }); } } for (const finalLabel of this.output.netLabelPlacements) { - const isMerged = finalLabel.globalConnNetId.startsWith("merged-group-") - const color = getColorFromString(finalLabel.globalConnNetId) + const isMerged = finalLabel.globalConnNetId.startsWith("merged-group-"); + const color = getColorFromString(finalLabel.globalConnNetId); if (isMerged) { graphics.rects.push({ @@ -179,33 +180,33 @@ export class MergedNetLabelObstacleSolver extends BaseSolver { fill: color.replace(/, 1\)/, ", 0.2)"), stroke: color, label: finalLabel.globalConnNetId, - }) + }); const originalNetIds = - this.output.mergedLabelNetIdMap[finalLabel.globalConnNetId] + this.output.mergedLabelNetIdMap[finalLabel.globalConnNetId]; if (originalNetIds) { for (const originalNetId of originalNetIds) { - const originalLabel = originalLabelsById.get(originalNetId) + const originalLabel = originalLabelsById.get(originalNetId); if (originalLabel) { 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 } + ); + 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", - }) + }); graphics.lines.push({ points: [originalLabel.center, finalLabel.center], strokeColor: color, strokeDash: "2 2", - }) + }); } } } @@ -216,10 +217,10 @@ export class MergedNetLabelObstacleSolver extends BaseSolver { height: finalLabel.height, stroke: color, label: finalLabel.globalConnNetId, - }) + }); } } - return graphics + return graphics; } } diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/filterLabelsAtTraceEdges.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/filterLabelsAtTraceEdges.ts index f244b5a3..b572f49a 100644 --- a/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/filterLabelsAtTraceEdges.ts +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/filterLabelsAtTraceEdges.ts @@ -1,7 +1,7 @@ -import type { NetLabelPlacement } from "../../../NetLabelPlacementSolver/NetLabelPlacementSolver" -import type { SolvedTracePath } from "../../../SchematicTraceLinesSolver/SchematicTraceLinesSolver" -import type { Point } from "graphics-debug" -import { distance } from "@tscircuit/math-utils" +import type { NetLabelPlacement } from "../../../NetLabelPlacementSolver/NetLabelPlacementSolver"; +import type { SolvedTracePath } from "../../../SchematicTraceLinesSolver/SchematicTraceLinesSolver"; +import type { Point } from "graphics-debug"; +import { distance } from "@tscircuit/math-utils"; /** * Filters a list of labels, returning only those that are physically located @@ -16,59 +16,59 @@ export const filterLabelsAtTraceEdges = ({ traces, distanceThreshold = 0.5, // Example threshold }: { - labels: NetLabelPlacement[] - traces: SolvedTracePath[] - distanceThreshold?: number + labels: NetLabelPlacement[]; + traces: SolvedTracePath[]; + distanceThreshold?: number; }): NetLabelPlacement[] => { // 1. Group traces by their net ID for efficient lookup - const tracesByNetId = new Map() + const tracesByNetId = new Map(); if (!traces) { - throw new Error("No traces provided to filterLabelsAtTraceEdges") + throw new Error("No traces provided to filterLabelsAtTraceEdges"); } for (const trace of traces) { - if (!trace.globalConnNetId) continue + if (!trace.globalConnNetId) continue; if (!tracesByNetId.has(trace.globalConnNetId)) { - tracesByNetId.set(trace.globalConnNetId, []) + tracesByNetId.set(trace.globalConnNetId, []); } - tracesByNetId.get(trace.globalConnNetId)!.push(trace) + tracesByNetId.get(trace.globalConnNetId)!.push(trace); } - const filteredLabels: NetLabelPlacement[] = [] + const filteredLabels: NetLabelPlacement[] = []; // 2. Iterate through labels and check proximity against only relevant traces for (const label of labels) { // Check if it's a port-only label (no associated MSP connection pairs) if (label.mspConnectionPairIds.length === 0) { - filteredLabels.push(label) - continue // Skip trace-edge checks for port-only labels + filteredLabels.push(label); + continue; // Skip trace-edge checks for port-only labels } - const relevantTraces = tracesByNetId.get(label.globalConnNetId) - let isNearTraceEdge = false + const relevantTraces = tracesByNetId.get(label.globalConnNetId); + let isNearTraceEdge = false; if (!relevantTraces || relevantTraces.length === 0) { - continue + continue; } for (const trace of relevantTraces) { - if (trace.tracePath.length === 0) continue + if (trace.tracePath.length === 0) continue; - const startPoint = trace.tracePath[0] - const endPoint = trace.tracePath[trace.tracePath.length - 1] + const startPoint = trace.tracePath[0]; + const endPoint = trace.tracePath[trace.tracePath.length - 1]; - const startDist = distance(label.center, startPoint) - const endDist = distance(label.center, endPoint) + const startDist = distance(label.center, startPoint); + const endDist = distance(label.center, endPoint); if (startDist <= distanceThreshold || endDist <= distanceThreshold) { - isNearTraceEdge = true - break // Found a nearby edge, no need to check other traces for this label + isNearTraceEdge = true; + break; // Found a nearby edge, no need to check other traces for this label } } if (isNearTraceEdge) { - filteredLabels.push(label) + filteredLabels.push(label); } } - return filteredLabels -} + return filteredLabels; +}; diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/groupLabelsByChipAndOrientation.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/groupLabelsByChipAndOrientation.ts index f3f58392..57204ac9 100644 --- a/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/groupLabelsByChipAndOrientation.ts +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/groupLabelsByChipAndOrientation.ts @@ -1,5 +1,5 @@ -import type { NetLabelPlacement } from "../../../NetLabelPlacementSolver/NetLabelPlacementSolver" -import type { InputProblem } from "lib/types/InputProblem" +import type { NetLabelPlacement } from "../../../NetLabelPlacementSolver/NetLabelPlacementSolver"; +import type { InputProblem } from "lib/types/InputProblem"; /** * Groups NetLabelPlacement objects by their associated chip ID and orientation. @@ -15,31 +15,31 @@ export const groupLabelsByChipAndOrientation = ({ labels, chips, }: { - labels: NetLabelPlacement[] - chips: InputProblem["chips"] + labels: NetLabelPlacement[]; + chips: InputProblem["chips"]; }): Record => { - const groupedLabels: Record = {} + const groupedLabels: Record = {}; for (const label of labels) { if (label.pinIds.length === 0) { // Labels without pinIds cannot be associated with a chip and orientation for merging - continue + continue; } // Extract chipId from the first pinId (e.g., "U1.1" -> "U1") - const chipId = label.pinIds[0].split(".")[0] + const chipId = label.pinIds[0].split(".")[0]; if (!chipId) { // Should not happen if pinIds are well-formed, but good to guard - continue + continue; } - const key = `${chipId}-${label.orientation}` + const key = `${chipId}-${label.orientation}`; if (!groupedLabels[key]) { - groupedLabels[key] = [] + groupedLabels[key] = []; } - groupedLabels[key].push(label) + groupedLabels[key].push(label); } - return groupedLabels -} + return groupedLabels; +}; diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/mergeLabelGroup.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/mergeLabelGroup.ts index 53d14031..4781cd65 100644 --- a/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/mergeLabelGroup.ts +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/mergeLabelGroup.ts @@ -1,6 +1,6 @@ -import type { NetLabelPlacement } from "../../../NetLabelPlacementSolver/NetLabelPlacementSolver" -import { getRectBounds } from "../../../NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry" -import type { Point } from "graphics-debug" // Assuming Point is from graphics-debug or similar +import type { NetLabelPlacement } from "../../../NetLabelPlacementSolver/NetLabelPlacementSolver"; +import { getRectBounds } from "../../../NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"; +import type { Point } from "graphics-debug"; // Assuming Point is from graphics-debug or similar /** * Merges a group of NetLabelPlacement objects into a single, larger NetLabelPlacement. @@ -17,42 +17,42 @@ export const mergeLabelGroup = ( group: NetLabelPlacement[], groupKey: string, ): { - mergedLabel: NetLabelPlacement - originalNetIds: Set + mergedLabel: NetLabelPlacement; + originalNetIds: Set; } => { if (group.length === 0) { - throw new Error("Cannot merge an empty group of labels.") + throw new Error("Cannot merge an empty group of labels."); } - let minX = Infinity - let minY = Infinity - let maxX = -Infinity - let maxY = -Infinity + let minX = Infinity; + let minY = Infinity; + let maxX = -Infinity; + let maxY = -Infinity; - const allPinIds = new Set() - const allMspConnectionPairIds = new Set() - const originalNetIds = new Set() + const allPinIds = new Set(); + const allMspConnectionPairIds = new Set(); + const originalNetIds = new Set(); for (const label of group) { - const bounds = getRectBounds(label.center, label.width, label.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 bounds = getRectBounds(label.center, label.width, label.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); - label.pinIds.forEach((id) => allPinIds.add(id)) - label.mspConnectionPairIds.forEach((id) => allMspConnectionPairIds.add(id)) - originalNetIds.add(label.globalConnNetId) + label.pinIds.forEach((id) => allPinIds.add(id)); + label.mspConnectionPairIds.forEach((id) => allMspConnectionPairIds.add(id)); + originalNetIds.add(label.globalConnNetId); } - const newWidth = maxX - minX - const newHeight = maxY - minY - const newCenter: Point = { x: minX + newWidth / 2, y: minY + newHeight / 2 } + const newWidth = maxX - minX; + const newHeight = maxY - minY; + const newCenter: Point = { x: minX + newWidth / 2, y: minY + newHeight / 2 }; // Use the first label as a template for properties that are consistent across the group // like orientation. - const template = group[0]! - const syntheticId = `merged-group-${groupKey}` + const template = group[0]!; + const syntheticId = `merged-group-${groupKey}`; const mergedLabel: NetLabelPlacement = { ...template, // Copy common properties @@ -62,10 +62,10 @@ export const mergeLabelGroup = ( height: newHeight, pinIds: Array.from(allPinIds), mspConnectionPairIds: Array.from(allMspConnectionPairIds), - } + }; return { mergedLabel, originalNetIds, - } -} + }; +}; diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver.ts index bc43fb7b..62a13439 100644 --- a/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver.ts +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver.ts @@ -1,24 +1,24 @@ -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" -import { doesTraceStartOrEndInLabel } from "./doesTraceStartOrEndInLabel" -import { visualizeDecomposition } from "./visualizeDecomposition" - -type Overlap = ReturnType[0] +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"; +import { doesTraceStartOrEndInLabel } from "./doesTraceStartOrEndInLabel"; +import { visualizeDecomposition } from "./visualizeDecomposition"; + +type Overlap = ReturnType[0]; // Define a type for the input of the internal overlap solver to avoid conflicts export interface OverlapCollectionSolverInput { - inputProblem: InputProblem - traces: SolvedTracePath[] - initialNetLabelPlacements: NetLabelPlacement[] - mergedNetLabelPlacements: NetLabelPlacement[] - mergedLabelNetIdMap: Record> - detourCounts: Map + inputProblem: InputProblem; + traces: SolvedTracePath[]; + initialNetLabelPlacements: NetLabelPlacement[]; + mergedNetLabelPlacements: NetLabelPlacement[]; + mergedLabelNetIdMap: Record>; + detourCounts: Map; } /** @@ -26,123 +26,123 @@ export interface OverlapCollectionSolverInput { * multiple overlaps. It follows the pattern of SchematicTraceLinesSolver. */ export class OverlapAvoidanceStepSolver extends BaseSolver { - inputProblem: InputProblem - initialNetLabelPlacements: NetLabelPlacement[] - mergedNetLabelPlacements: NetLabelPlacement[] - mergedLabelNetIdMap: Record> + inputProblem: InputProblem; + initialNetLabelPlacements: NetLabelPlacement[]; + mergedNetLabelPlacements: NetLabelPlacement[]; + mergedLabelNetIdMap: Record>; - allTraces: SolvedTracePath[] - modifiedTraces: SolvedTracePath[] = [] + allTraces: SolvedTracePath[]; + modifiedTraces: SolvedTracePath[] = []; - private readonly PADDING_BUFFER = 0.1 - private detourCounts: Map = new Map() + private readonly PADDING_BUFFER = 0.1; + private detourCounts: Map = new Map(); - public override activeSubSolver: SingleOverlapSolver | null = null - private overlapQueue: Overlap[] = [] - private recentlyFailed: Set = new Set() + public override activeSubSolver: SingleOverlapSolver | null = null; + private overlapQueue: Overlap[] = []; + private recentlyFailed: Set = new Set(); - private currentlyProcessingOverlap: Overlap | null = null - private decomposedChildLabels: NetLabelPlacement[] | null = null + private currentlyProcessingOverlap: Overlap | null = null; + private decomposedChildLabels: NetLabelPlacement[] | null = null; constructor(solverInput: OverlapCollectionSolverInput) { - super() - this.inputProblem = solverInput.inputProblem - this.initialNetLabelPlacements = solverInput.initialNetLabelPlacements - this.mergedNetLabelPlacements = solverInput.mergedNetLabelPlacements - this.mergedLabelNetIdMap = solverInput.mergedLabelNetIdMap - this.allTraces = [...solverInput.traces] - this.detourCounts = solverInput.detourCounts + super(); + this.inputProblem = solverInput.inputProblem; + this.initialNetLabelPlacements = solverInput.initialNetLabelPlacements; + this.mergedNetLabelPlacements = solverInput.mergedNetLabelPlacements; + this.mergedLabelNetIdMap = solverInput.mergedLabelNetIdMap; + this.allTraces = [...solverInput.traces]; + this.detourCounts = solverInput.detourCounts; } override _step() { - this.currentlyProcessingOverlap = null - this.decomposedChildLabels = null + this.currentlyProcessingOverlap = null; + this.decomposedChildLabels = null; if (this.activeSubSolver) { - this.activeSubSolver.step() + this.activeSubSolver.step(); if (this.activeSubSolver.solved) { - const solvedPath = this.activeSubSolver.solvedTracePath + 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.allTraces[traceIndex].tracePath = solvedPath; + this.modifiedTraces.push(this.allTraces[traceIndex]); } } - this.activeSubSolver = null - this.recentlyFailed.clear() + 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 + const overlapId = `${this.activeSubSolver.initialTrace.mspPairId}-${this.activeSubSolver.label.globalConnNetId}`; + this.recentlyFailed.add(overlapId); + this.activeSubSolver = null; } else { } - return + return; } const overlaps = detectTraceLabelOverlap({ traces: this.allTraces, netLabels: this.mergedNetLabelPlacements, - }) + }); if (overlaps.length === 0) { - this.solved = true - return + this.solved = true; + return; } // Filter out overlaps that have recently failed to avoid infinite loops const nonFailedOverlaps = overlaps.filter((o) => { - const overlapId = `${o.trace.mspPairId}-${o.label.globalConnNetId}` - return !this.recentlyFailed.has(overlapId) - }) + 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.solved = true; // No more progress can be made + return; } - this.overlapQueue = nonFailedOverlaps + this.overlapQueue = nonFailedOverlaps; - const nextOverlap = this.overlapQueue.shift() - this.currentlyProcessingOverlap = nextOverlap ?? null + const nextOverlap = this.overlapQueue.shift(); + this.currentlyProcessingOverlap = nextOverlap ?? null; if (nextOverlap) { const traceToFix = this.allTraces.find( (t) => t.mspPairId === nextOverlap.trace.mspPairId, - )! - const labelToAvoid = nextOverlap.label + )!; + const labelToAvoid = nextOverlap.label; const originalNetIds = - this.mergedLabelNetIdMap[labelToAvoid.globalConnNetId] - const isSelfOverlap = originalNetIds?.has(traceToFix.globalConnNetId) + this.mergedLabelNetIdMap[labelToAvoid.globalConnNetId]; + const isSelfOverlap = originalNetIds?.has(traceToFix.globalConnNetId); if (isSelfOverlap) { const childLabels = this.initialNetLabelPlacements.filter((l) => originalNetIds.has(l.globalConnNetId), - ) - this.decomposedChildLabels = childLabels - let actualOverlapLabel: NetLabelPlacement | null = null + ); + this.decomposedChildLabels = childLabels; + let actualOverlapLabel: NetLabelPlacement | null = null; for (const childLabel of childLabels) { const overlapsWithChild = detectTraceLabelOverlap({ traces: [traceToFix], netLabels: [childLabel], - }) + }); if (overlapsWithChild.length > 0) { - actualOverlapLabel = childLabel - break + actualOverlapLabel = childLabel; + break; } } if (actualOverlapLabel) { const detourCount = - this.detourCounts.get(actualOverlapLabel.globalConnNetId) ?? 0 + this.detourCounts.get(actualOverlapLabel.globalConnNetId) ?? 0; this.detourCounts.set( actualOverlapLabel.globalConnNetId, detourCount + 1, - ) + ); this.activeSubSolver = new SingleOverlapSolver({ trace: traceToFix, @@ -150,12 +150,12 @@ export class OverlapAvoidanceStepSolver extends BaseSolver { problem: this.inputProblem, paddingBuffer: this.PADDING_BUFFER, detourCount: detourCount, - }) + }); } else { - const overlapId = `${traceToFix.mspPairId}-${labelToAvoid.globalConnNetId}` - this.recentlyFailed.add(overlapId) + const overlapId = `${traceToFix.mspPairId}-${labelToAvoid.globalConnNetId}`; + this.recentlyFailed.add(overlapId); } - return + return; } if ( @@ -164,54 +164,54 @@ export class OverlapAvoidanceStepSolver extends BaseSolver { ) { const childLabels = this.initialNetLabelPlacements.filter((l) => originalNetIds.has(l.globalConnNetId), - ) - this.decomposedChildLabels = childLabels - let actualOverlapLabel: NetLabelPlacement | null = null + ); + this.decomposedChildLabels = childLabels; + let actualOverlapLabel: NetLabelPlacement | null = null; for (const childLabel of childLabels) { const overlapsWithChild = detectTraceLabelOverlap({ traces: [traceToFix], netLabels: [childLabel], - }) + }); if (overlapsWithChild.length > 0) { - actualOverlapLabel = childLabel - break + actualOverlapLabel = childLabel; + break; } } if (actualOverlapLabel) { const detourCount = - this.detourCounts.get(actualOverlapLabel.globalConnNetId) ?? 0 + this.detourCounts.get(actualOverlapLabel.globalConnNetId) ?? 0; this.detourCounts.set( actualOverlapLabel.globalConnNetId, detourCount + 1, - ) + ); this.activeSubSolver = new SingleOverlapSolver({ trace: traceToFix, label: actualOverlapLabel, problem: this.inputProblem, paddingBuffer: this.PADDING_BUFFER, detourCount: detourCount, - }) + }); } else { - const overlapId = `${traceToFix.mspPairId}-${labelToAvoid.globalConnNetId}` - this.recentlyFailed.add(overlapId) + const overlapId = `${traceToFix.mspPairId}-${labelToAvoid.globalConnNetId}`; + this.recentlyFailed.add(overlapId); } - return + return; } // STRATEGY 3: Real collision between different nets. // We must reroute around the entire merged label. const detourCount = - this.detourCounts.get(labelToAvoid.globalConnNetId) ?? 0 - this.detourCounts.set(labelToAvoid.globalConnNetId, detourCount + 1) + this.detourCounts.get(labelToAvoid.globalConnNetId) ?? 0; + this.detourCounts.set(labelToAvoid.globalConnNetId, detourCount + 1); this.activeSubSolver = new SingleOverlapSolver({ trace: traceToFix, label: labelToAvoid, problem: this.inputProblem, paddingBuffer: this.PADDING_BUFFER, detourCount: detourCount, - }) + }); } } @@ -220,31 +220,31 @@ export class OverlapAvoidanceStepSolver extends BaseSolver { allTraces: this.allTraces, modifiedTraces: this.modifiedTraces, detourCounts: this.detourCounts, - } + }; } override visualize(): GraphicsObject { if (this.activeSubSolver) { - return this.activeSubSolver.visualize() + return this.activeSubSolver.visualize(); } - const graphics = visualizeInputProblem(this.inputProblem) - if (!graphics.lines) graphics.lines = [] + const graphics = visualizeInputProblem(this.inputProblem); + if (!graphics.lines) graphics.lines = []; for (const trace of this.allTraces) { graphics.lines!.push({ points: trace.tracePath, strokeColor: "purple", - }) + }); } if (this.currentlyProcessingOverlap) { - const { trace, label } = this.currentlyProcessingOverlap + const { trace, label } = this.currentlyProcessingOverlap; // Highlight the colliding trace graphics.lines!.push({ points: trace.tracePath, strokeColor: "red", - }) + }); if (this.decomposedChildLabels) { visualizeDecomposition({ @@ -252,27 +252,27 @@ export class OverlapAvoidanceStepSolver extends BaseSolver { collidingTrace: trace, mergedLabel: label, graphics, - }) + }); } else { // Standard case: highlight the entire label - if (!graphics.rects) graphics.rects = [] + if (!graphics.rects) graphics.rects = []; graphics.rects.push({ center: label.center, width: label.width, height: label.height, fill: "yellow", - }) - if (!graphics.texts) graphics.texts = [] + }); + if (!graphics.texts) graphics.texts = []; graphics.texts.push({ x: label.center.x, y: label.center.y + label.height / 2 + 0.5, text: `COLLISION: Trace ${trace.mspPairId} vs Label ${label.globalConnNetId}`, fontSize: 0.3, color: "red", - }) + }); } } - return graphics + return graphics; } } diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/doesTraceStartOrEndInLabel.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/doesTraceStartOrEndInLabel.ts index a4f17392..1f87be0b 100644 --- a/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/doesTraceStartOrEndInLabel.ts +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/doesTraceStartOrEndInLabel.ts @@ -1,10 +1,10 @@ -import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver" -import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver" -import { detectTraceLabelOverlap } from "../../detectTraceLabelOverlap" +import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"; +import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"; +import { detectTraceLabelOverlap } from "../../detectTraceLabelOverlap"; interface Props { - trace: SolvedTracePath - label: NetLabelPlacement + trace: SolvedTracePath; + label: NetLabelPlacement; } /** @@ -13,46 +13,46 @@ interface Props { */ export const doesTraceStartOrEndInLabel = ({ trace, label }: Props) => { if (trace.tracePath.length < 2) { - return false // Not a valid trace with segments + return false; // Not a valid trace with segments } // Create a mini-trace for the first segment const firstSegmentTrace: SolvedTracePath = { ...trace, tracePath: [trace.tracePath[0], trace.tracePath[1]], - } + }; // Check for overlap with the first segment const firstSegmentOverlap = detectTraceLabelOverlap({ traces: [firstSegmentTrace], netLabels: [label], - }) + }); if (firstSegmentOverlap.length > 0) { - return true + return true; } // If the trace has more than one segment, check the last one if (trace.tracePath.length > 2) { - const lastPoint = trace.tracePath[trace.tracePath.length - 1] - const secondToLastPoint = trace.tracePath[trace.tracePath.length - 2] + const lastPoint = trace.tracePath[trace.tracePath.length - 1]; + const secondToLastPoint = trace.tracePath[trace.tracePath.length - 2]; // Create a mini-trace for the last segment const lastSegmentTrace: SolvedTracePath = { ...trace, tracePath: [secondToLastPoint, lastPoint], - } + }; // Check for overlap with the last segment const lastSegmentOverlap = detectTraceLabelOverlap({ traces: [lastSegmentTrace], netLabels: [label], - }) + }); if (lastSegmentOverlap.length > 0) { - return true + return true; } } - return false -} + return false; +}; diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/isPointInsideLabel.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/isPointInsideLabel.ts index 7b1c1e77..0f451784 100644 --- a/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/isPointInsideLabel.ts +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/isPointInsideLabel.ts @@ -1,6 +1,6 @@ -import type { Point } from "graphics-debug" -import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver" -import { getRectBounds } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry" +import type { Point } from "graphics-debug"; +import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"; +import { getRectBounds } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"; /** * Checks if a point is inside the bounding box of a net label. @@ -9,15 +9,15 @@ export const isPointInsideLabel = ({ point, label, }: { - point: Point - label: NetLabelPlacement + point: Point; + label: NetLabelPlacement; }): boolean => { - const bounds = getRectBounds(label.center, label.width, label.height) + const bounds = getRectBounds(label.center, label.width, label.height); return ( point.x >= bounds.minX && point.x <= bounds.maxX && point.y >= bounds.minY && point.y <= bounds.maxY - ) -} + ); +}; diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/visualizeDecomposition.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/visualizeDecomposition.ts index f33179e0..58a52b21 100644 --- a/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/visualizeDecomposition.ts +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/visualizeDecomposition.ts @@ -1,12 +1,12 @@ -import type { GraphicsObject } from "graphics-debug" -import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver" -import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver" +import type { GraphicsObject } from "graphics-debug"; +import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"; +import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"; interface VisualizeDecompositionParams { - decomposedChildLabels: NetLabelPlacement[] - collidingTrace: SolvedTracePath - mergedLabel: NetLabelPlacement - graphics: GraphicsObject + decomposedChildLabels: NetLabelPlacement[]; + collidingTrace: SolvedTracePath; + mergedLabel: NetLabelPlacement; + graphics: GraphicsObject; } /** @@ -25,21 +25,21 @@ export const visualizeDecomposition = ( params: VisualizeDecompositionParams, ): GraphicsObject => { const { decomposedChildLabels, collidingTrace, mergedLabel, graphics } = - params + params; - if (!graphics.rects) graphics.rects = [] - if (!graphics.texts) graphics.texts = [] + if (!graphics.rects) graphics.rects = []; + if (!graphics.texts) graphics.texts = []; for (const childLabel of decomposedChildLabels) { const isOwnLabel = - childLabel.globalConnNetId === collidingTrace.globalConnNetId + childLabel.globalConnNetId === collidingTrace.globalConnNetId; graphics.rects.push({ center: childLabel.center, width: childLabel.width, height: childLabel.height, fill: isOwnLabel ? "green" : "red", // Green for own label, red for others - }) + }); } graphics.texts.push({ @@ -48,7 +48,7 @@ export const visualizeDecomposition = ( text: `DECOMPOSITION: Trace ${collidingTrace.mspPairId} vs Merged Label ${mergedLabel.globalConnNetId}`, fontSize: 0.3, color: "blue", - }) + }); - return graphics -} + return graphics; +}; diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts index 40211a03..c3ce2723 100644 --- a/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts @@ -1,24 +1,24 @@ -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 "lib/solvers/TraceCleanupSolver/simplifyPath" +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 "lib/solvers/TraceCleanupSolver/simplifyPath"; interface SingleOverlapSolverInput { - trace: SolvedTracePath - label: NetLabelPlacement - problem: InputProblem - paddingBuffer: number - detourCount: number + trace: SolvedTracePath; + label: NetLabelPlacement; + problem: InputProblem; + paddingBuffer: number; + detourCount: number; } -const MAX_TRIES = 5 +const MAX_TRIES = 5; /** * This solver attempts to find a valid rerouting for a single trace that is @@ -26,60 +26,60 @@ const MAX_TRIES = 5 * 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 - _tried: number = 0 + queuedCandidatePaths: Point[][]; + solvedTracePath: Point[] | null = null; + initialTrace: SolvedTracePath; + problem: InputProblem; + obstacles: ReturnType; + label: NetLabelPlacement; + _tried: number = 0; constructor(solverInput: SingleOverlapSolverInput) { - super() - this.initialTrace = solverInput.trace - this.problem = solverInput.problem - this.label = solverInput.label + super(); + this.initialTrace = solverInput.trace; + this.problem = solverInput.problem; + this.label = solverInput.label; // Calculate an effective padding for this specific run based on the detourCount. const effectivePadding = solverInput.paddingBuffer + - solverInput.detourCount * solverInput.paddingBuffer + solverInput.detourCount * solverInput.paddingBuffer; const candidates = generateRerouteCandidates({ ...solverInput, paddingBuffer: effectivePadding, // Use the calculated, larger padding - }) + }); const getPathLength = (pts: Point[]) => { - let len = 0 + 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) + 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 - } + return len; + }; this.queuedCandidatePaths = candidates.sort( (a, b) => getPathLength(a) - getPathLength(b), - ) - this.obstacles = getObstacleRects(this.problem) + ); + this.obstacles = getObstacleRects(this.problem); } override _step() { // Failure conditions: no more candidates or exceeded max tries if (this.queuedCandidatePaths.length === 0 || this._tried >= MAX_TRIES) { - this.failed = true - return + this.failed = true; + return; } - this._tried++ - const nextCandidatePath = this.queuedCandidatePaths.shift()! - const simplifiedPath = simplifyPath(nextCandidatePath) + this._tried++; + const nextCandidatePath = this.queuedCandidatePaths.shift()!; + const simplifiedPath = simplifyPath(nextCandidatePath); if (!isPathCollidingWithObstacles(simplifiedPath, this.obstacles)) { - this.solvedTracePath = simplifiedPath - this.solved = true + this.solvedTracePath = simplifiedPath; + this.solved = true; } // If the path collides, we simply do nothing and let the next step try another candidate. } @@ -88,17 +88,17 @@ export class SingleOverlapSolver extends BaseSolver { const graphics = visualizeInputProblem(this.problem, { chipAlpha: 0.1, connectionAlpha: 0.1, - }) + }); - if (!graphics.lines) graphics.lines = [] - if (!graphics.rects) graphics.rects = [] + 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({ @@ -106,14 +106,14 @@ export class SingleOverlapSolver extends BaseSolver { 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 @@ -121,9 +121,9 @@ export class SingleOverlapSolver extends BaseSolver { graphics.lines.push({ points: this.solvedTracePath, strokeColor: "green", - }) + }); } - return graphics + return graphics; } } diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/tryFourPointDetour.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/tryFourPointDetour.ts index 7a740c9c..c65f7908 100644 --- a/lib/solvers/TraceLabelOverlapAvoidanceSolver/tryFourPointDetour.ts +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/tryFourPointDetour.ts @@ -1,7 +1,7 @@ -import type { Point } from "graphics-debug" -import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver" -import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver" -import { segmentIntersectsRect } from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions" +import type { Point } from "graphics-debug"; +import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver"; +import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver"; +import { segmentIntersectsRect } from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions"; /** * Generates candidate four-point detour paths for a trace colliding with a label obstacle. @@ -16,63 +16,63 @@ export const generateFourPointDetourCandidates = ({ paddingBuffer, detourCount, }: { - initialTrace: SolvedTracePath - label: NetLabelPlacement - labelBounds: { minX: number; maxX: number; minY: number; maxY: number } - paddingBuffer: number - detourCount: number + initialTrace: SolvedTracePath; + label: NetLabelPlacement; + labelBounds: { minX: number; maxX: number; minY: number; maxY: number }; + paddingBuffer: number; + detourCount: number; }): Point[][] => { - const isMergedLabel = label.globalConnNetId.startsWith("merged-group-") - const effectivePadding = paddingBuffer + detourCount * paddingBuffer + const isMergedLabel = label.globalConnNetId.startsWith("merged-group-"); + const effectivePadding = paddingBuffer + detourCount * paddingBuffer; const paddedLabelBounds = { minX: labelBounds.minX - effectivePadding, maxX: labelBounds.maxX + effectivePadding, minY: labelBounds.minY - effectivePadding, maxY: labelBounds.maxY + effectivePadding, - } + }; - let entryPoint: Point, exitPoint: Point - let entryIndex: number, exitIndex: number + let entryPoint: Point, exitPoint: Point; + let entryIndex: number, exitIndex: number; const isPointInRect = (p: Point) => p.x >= paddedLabelBounds.minX && p.x <= paddedLabelBounds.maxX && p.y >= paddedLabelBounds.minY && - p.y <= paddedLabelBounds.maxY + p.y <= paddedLabelBounds.maxY; if (isMergedLabel) { // STRATEGY 2: Merged Label - find first point before and after the entire crossing - let firstInsideIndex = -1 + let firstInsideIndex = -1; for (let i = 0; i < initialTrace.tracePath.length; i++) { if (isPointInRect(initialTrace.tracePath[i])) { - firstInsideIndex = i - break + firstInsideIndex = i; + break; } } - if (firstInsideIndex === -1) return [] // No points inside, shouldn't be called + if (firstInsideIndex === -1) return []; // No points inside, shouldn't be called - entryIndex = Math.max(0, firstInsideIndex - 1) - entryPoint = initialTrace.tracePath[entryIndex] + entryIndex = Math.max(0, firstInsideIndex - 1); + entryPoint = initialTrace.tracePath[entryIndex]; - let firstOutsideIndex = -1 + let firstOutsideIndex = -1; for (let i = firstInsideIndex; i < initialTrace.tracePath.length; i++) { if (!isPointInRect(initialTrace.tracePath[i])) { - firstOutsideIndex = i - break + firstOutsideIndex = i; + break; } } if (firstOutsideIndex === -1) { // Trace ends inside the box, use the last point as exit - exitIndex = initialTrace.tracePath.length - 1 + exitIndex = initialTrace.tracePath.length - 1; } else { - exitIndex = firstOutsideIndex + exitIndex = firstOutsideIndex; } - exitPoint = initialTrace.tracePath[exitIndex] + exitPoint = initialTrace.tracePath[exitIndex]; } else { // STRATEGY 1: Single Label - find the first intersecting segment - let collidingSegIndex = -1 + let collidingSegIndex = -1; for (let i = 0; i < initialTrace.tracePath.length - 1; i++) { if ( segmentIntersectsRect( @@ -81,28 +81,28 @@ export const generateFourPointDetourCandidates = ({ { ...paddedLabelBounds, chipId: "temp-obstacle" }, ) ) { - collidingSegIndex = i - break + collidingSegIndex = i; + break; } } - if (collidingSegIndex === -1) return [] + if (collidingSegIndex === -1) return []; - entryIndex = collidingSegIndex - exitIndex = collidingSegIndex + 1 - entryPoint = initialTrace.tracePath[entryIndex] - exitPoint = initialTrace.tracePath[exitIndex] + entryIndex = collidingSegIndex; + exitIndex = collidingSegIndex + 1; + entryPoint = initialTrace.tracePath[entryIndex]; + exitPoint = initialTrace.tracePath[exitIndex]; } - if (!entryPoint || !exitPoint || entryIndex >= exitIndex) return [] + if (!entryPoint || !exitPoint || entryIndex >= exitIndex) return []; - const candidateDetours: Point[][] = [] - const dx = exitPoint.x - entryPoint.x - const dy = exitPoint.y - entryPoint.y + const candidateDetours: Point[][] = []; + const dx = exitPoint.x - entryPoint.x; + const dy = exitPoint.y - entryPoint.y; if (Math.abs(dx) > Math.abs(dy)) { // More horizontal - const yCandidates = [paddedLabelBounds.maxY, paddedLabelBounds.minY] + const yCandidates = [paddedLabelBounds.maxY, paddedLabelBounds.minY]; for (const newY of yCandidates) { candidateDetours.push( dx > 0 // Left-to-right @@ -119,11 +119,11 @@ export const generateFourPointDetourCandidates = ({ { x: paddedLabelBounds.minX, y: newY }, { x: paddedLabelBounds.minX, y: exitPoint.y }, ], - ) + ); } } else { // More vertical - const xCandidates = [paddedLabelBounds.maxX, paddedLabelBounds.minX] + const xCandidates = [paddedLabelBounds.maxX, paddedLabelBounds.minX]; for (const newX of xCandidates) { candidateDetours.push( dy > 0 // Top-to-bottom @@ -140,7 +140,7 @@ export const generateFourPointDetourCandidates = ({ { x: newX, y: paddedLabelBounds.minY }, { x: exitPoint.x, y: paddedLabelBounds.minY }, ], - ) + ); } } @@ -150,5 +150,5 @@ export const generateFourPointDetourCandidates = ({ ...detourPoints, exitPoint, ...initialTrace.tracePath.slice(exitIndex + 1), - ]) -} + ]); +}; diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/trySnipAndReconnect.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/trySnipAndReconnect.ts index cb95d074..8335c0fb 100644 --- a/lib/solvers/TraceLabelOverlapAvoidanceSolver/trySnipAndReconnect.ts +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/trySnipAndReconnect.ts @@ -1,5 +1,5 @@ -import type { Point } from "@tscircuit/math-utils" -import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver" +import type { Point } from "@tscircuit/math-utils"; +import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver"; export const generateSnipAndReconnectCandidates = ({ initialTrace, @@ -9,43 +9,43 @@ export const generateSnipAndReconnectCandidates = ({ paddingBuffer, detourCount, }: { - initialTrace: SolvedTracePath - firstInsideIndex: number - lastInsideIndex: number - labelBounds: any - paddingBuffer: number - detourCount: number + initialTrace: SolvedTracePath; + firstInsideIndex: number; + lastInsideIndex: number; + labelBounds: any; + paddingBuffer: number; + detourCount: number; }): Point[][] => { if ( firstInsideIndex <= 0 || lastInsideIndex >= initialTrace.tracePath.length - 1 ) { - return [] + return []; } - const entryPoint = initialTrace.tracePath[firstInsideIndex - 1] - const exitPoint = initialTrace.tracePath[lastInsideIndex + 1] + const entryPoint = initialTrace.tracePath[firstInsideIndex - 1]; + const exitPoint = initialTrace.tracePath[lastInsideIndex + 1]; - const pathToEntry = initialTrace.tracePath.slice(0, firstInsideIndex) - const pathFromExit = initialTrace.tracePath.slice(lastInsideIndex + 1) + const pathToEntry = initialTrace.tracePath.slice(0, firstInsideIndex); + const pathFromExit = initialTrace.tracePath.slice(lastInsideIndex + 1); - const allCandidateDetours: Point[][] = [] + const allCandidateDetours: Point[][] = []; // Candidate type 1: simple elbow if (entryPoint.x !== exitPoint.x && entryPoint.y !== exitPoint.y) { - allCandidateDetours.push([{ x: exitPoint.x, y: entryPoint.y }]) - allCandidateDetours.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) { // Candidate type 2: direct connection (if points are aligned) - allCandidateDetours.push([]) // No detour points needed + allCandidateDetours.push([]); // No detour points needed } // 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 - const bottomY = labelBounds.minY - buffer + const buffer = paddingBuffer + detourCount * paddingBuffer; + const leftX = labelBounds.minX - buffer; + const rightX = labelBounds.maxX + buffer; + const topY = labelBounds.maxY + buffer; + const bottomY = labelBounds.minY - buffer; if ( (entryPoint.x <= labelBounds.minX || exitPoint.x <= labelBounds.minX) && @@ -55,7 +55,7 @@ export const generateSnipAndReconnectCandidates = ({ allCandidateDetours.push([ { x: leftX, y: entryPoint.y }, { x: leftX, y: exitPoint.y }, - ]) + ]); } if ( @@ -66,7 +66,7 @@ export const generateSnipAndReconnectCandidates = ({ allCandidateDetours.push([ { x: rightX, y: entryPoint.y }, { x: rightX, y: exitPoint.y }, - ]) + ]); } if ( @@ -77,7 +77,7 @@ export const generateSnipAndReconnectCandidates = ({ allCandidateDetours.push([ { x: entryPoint.x, y: topY }, { x: exitPoint.x, y: topY }, - ]) + ]); } if ( @@ -88,12 +88,12 @@ export const generateSnipAndReconnectCandidates = ({ allCandidateDetours.push([ { x: entryPoint.x, y: bottomY }, { x: exitPoint.x, y: bottomY }, - ]) + ]); } return allCandidateDetours.map((detour) => [ ...pathToEntry, ...detour, ...pathFromExit, - ]) -} + ]); +}; diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/violation.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/violation.ts index 3edf5015..801067e3 100644 --- a/lib/solvers/TraceLabelOverlapAvoidanceSolver/violation.ts +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/violation.ts @@ -1,4 +1,4 @@ -import type { Point } from "@tscircuit/math-utils" +import type { Point } from "@tscircuit/math-utils"; export const findTraceViolationZone = ( path: Point[], @@ -8,18 +8,18 @@ export const findTraceViolationZone = ( p.x > labelBounds.minX && p.x < labelBounds.maxX && p.y > labelBounds.minY && - p.y < labelBounds.maxY + p.y < labelBounds.maxY; - let firstInsideIndex = -1 - let lastInsideIndex = -1 + let firstInsideIndex = -1; + let lastInsideIndex = -1; for (let i = 0; i < path.length; i++) { if (isPointInside(path[i])) { if (firstInsideIndex === -1) { - firstInsideIndex = i + firstInsideIndex = i; } - lastInsideIndex = i + lastInsideIndex = i; } } - return { firstInsideIndex, lastInsideIndex } -} + return { firstInsideIndex, lastInsideIndex }; +}; diff --git a/lib/solvers/TraceOverlapShiftSolver/TraceOverlapIssueSolver/TraceOverlapIssueSolver.ts b/lib/solvers/TraceOverlapShiftSolver/TraceOverlapIssueSolver/TraceOverlapIssueSolver.ts index db56a0d1..2d16fbc0 100644 --- a/lib/solvers/TraceOverlapShiftSolver/TraceOverlapIssueSolver/TraceOverlapIssueSolver.ts +++ b/lib/solvers/TraceOverlapShiftSolver/TraceOverlapIssueSolver/TraceOverlapIssueSolver.ts @@ -1,34 +1,34 @@ -import type { GraphicsObject } from "graphics-debug" -import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver" -import type { MspConnectionPairId } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver" -import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver" -import { applyJogToTerminalSegment } from "./applyJogToTrace" +import type { GraphicsObject } from "graphics-debug"; +import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"; +import type { MspConnectionPairId } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver"; +import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"; +import { applyJogToTerminalSegment } from "./applyJogToTrace"; -type ConnNetId = string +type ConnNetId = string; export interface OverlappingTraceSegmentLocator { - connNetId: string + connNetId: string; pathsWithOverlap: Array<{ - solvedTracePathIndex: number - traceSegmentIndex: number - }> + solvedTracePathIndex: number; + traceSegmentIndex: number; + }>; } export class TraceOverlapIssueSolver extends BaseSolver { - overlappingTraceSegments: OverlappingTraceSegmentLocator[] - traceNetIslands: Record> + overlappingTraceSegments: OverlappingTraceSegmentLocator[]; + traceNetIslands: Record>; - SHIFT_DISTANCE = 0.1 + SHIFT_DISTANCE = 0.1; - correctedTraceMap: Record = {} + correctedTraceMap: Record = {}; constructor(params: { - overlappingTraceSegments: OverlappingTraceSegmentLocator[] - traceNetIslands: Record> + overlappingTraceSegments: OverlappingTraceSegmentLocator[]; + traceNetIslands: Record>; }) { - super() - this.overlappingTraceSegments = params.overlappingTraceSegments - this.traceNetIslands = params.traceNetIslands + super(); + this.overlappingTraceSegments = params.overlappingTraceSegments; + this.traceNetIslands = params.traceNetIslands; // Only add the relevant traces to the correctedTraceMap for (const { connNetId, pathsWithOverlap } of this @@ -38,9 +38,9 @@ export class TraceOverlapIssueSolver extends BaseSolver { traceSegmentIndex, } of pathsWithOverlap) { const mspPairId = - this.traceNetIslands[connNetId][solvedTracePathIndex].mspPairId + this.traceNetIslands[connNetId][solvedTracePathIndex].mspPairId; this.correctedTraceMap[mspPairId] = - this.traceNetIslands[connNetId][solvedTracePathIndex] + this.traceNetIslands[connNetId][solvedTracePathIndex]; } } } @@ -49,50 +49,50 @@ export class TraceOverlapIssueSolver extends BaseSolver { // Shift only the overlapping segments, and move the shared endpoints // (the last point of the previous segment and the first point of the next // segment) so the polyline remains orthogonal without self-overlap. - const EPS = 1e-6 + const EPS = 1e-6; // Compute offsets for each island involved: alternate directions const offsets = this.overlappingTraceSegments.map((_, idx) => { - const n = Math.floor(idx / 2) + 1 - const signed = idx % 2 === 0 ? -n : n - return signed * this.SHIFT_DISTANCE - }) + const n = Math.floor(idx / 2) + 1; + const signed = idx % 2 === 0 ? -n : n; + return signed * this.SHIFT_DISTANCE; + }); - const eq = (a: number, b: number) => Math.abs(a - b) < EPS + const eq = (a: number, b: number) => Math.abs(a - b) < EPS; const samePoint = ( p: { x: number; y: number } | undefined, q: { x: number; y: number } | undefined, - ) => !!p && !!q && eq(p.x, q.x) && eq(p.y, q.y) + ) => !!p && !!q && eq(p.x, q.x) && eq(p.y, q.y); // For each net island group, shift only its overlapping segments and adjust adjacent joints this.overlappingTraceSegments.forEach((group, gidx) => { - const offset = offsets[gidx]! + const offset = offsets[gidx]!; // Gather unique segment indices per path - const byPath: Map> = new Map() + const byPath: Map> = new Map(); for (const loc of group.pathsWithOverlap) { if (!byPath.has(loc.solvedTracePathIndex)) { - byPath.set(loc.solvedTracePathIndex, new Set()) + byPath.set(loc.solvedTracePathIndex, new Set()); } - byPath.get(loc.solvedTracePathIndex)!.add(loc.traceSegmentIndex) + byPath.get(loc.solvedTracePathIndex)!.add(loc.traceSegmentIndex); } for (const [pathIdx, segIdxSet] of byPath) { - const original = this.traceNetIslands[group.connNetId][pathIdx]! - const current = this.correctedTraceMap[original.mspPairId] ?? original - const pts = current.tracePath.map((p) => ({ ...p })) + const original = this.traceNetIslands[group.connNetId][pathIdx]!; + const current = this.correctedTraceMap[original.mspPairId] ?? original; + const pts = current.tracePath.map((p) => ({ ...p })); - const segIdxs = Array.from(segIdxSet).sort((a, b) => a - b) + const segIdxs = Array.from(segIdxSet).sort((a, b) => a - b); const segIdxsRev = Array.from(segIdxSet) .sort((a, b) => a - b) - .reverse() + .reverse(); - const JOG_SIZE = this.SHIFT_DISTANCE + const JOG_SIZE = this.SHIFT_DISTANCE; // Process from end to start to keep indices valid after splicing for (const si of segIdxsRev) { - if (si < 0 || si >= pts.length - 1) continue + if (si < 0 || si >= pts.length - 1) continue; if (si === 0 || si === pts.length - 2) { applyJogToTerminalSegment({ @@ -101,45 +101,45 @@ export class TraceOverlapIssueSolver extends BaseSolver { offset, JOG_SIZE, EPS, - }) + }); } else { // Internal segment - shift both points - const start = pts[si]! - const end = pts[si + 1]! - const isVertical = Math.abs(start.x - end.x) < EPS - const isHorizontal = Math.abs(start.y - end.y) < EPS - if (!isVertical && !isHorizontal) continue + const start = pts[si]!; + const end = pts[si + 1]!; + const isVertical = Math.abs(start.x - end.x) < EPS; + const isHorizontal = Math.abs(start.y - end.y) < EPS; + if (!isVertical && !isHorizontal) continue; if (isVertical) { - start.x += offset - end.x += offset + start.x += offset; + end.x += offset; } else { // Horizontal - start.y += offset - end.y += offset + start.y += offset; + end.y += offset; } } } // Remove consecutive duplicate points that might appear after shifts - const cleaned: typeof pts = [] + const cleaned: typeof pts = []; for (const p of pts) { if ( cleaned.length === 0 || !samePoint(cleaned[cleaned.length - 1], p) ) { - cleaned.push(p) + cleaned.push(p); } } this.correctedTraceMap[original.mspPairId] = { ...current, tracePath: cleaned, - } + }; } - }) + }); - this.solved = true + this.solved = true; } override visualize(): GraphicsObject { @@ -149,7 +149,7 @@ export class TraceOverlapIssueSolver extends BaseSolver { points: [], rects: [], circles: [], - } + }; // Draw overlapped segments in red for (const group of this.overlappingTraceSegments) { @@ -158,13 +158,13 @@ export class TraceOverlapIssueSolver extends BaseSolver { traceSegmentIndex, } of group.pathsWithOverlap) { const path = - this.traceNetIslands[group.connNetId][solvedTracePathIndex]! - const segStart = path.tracePath[traceSegmentIndex]! - const segEnd = path.tracePath[traceSegmentIndex + 1]! + this.traceNetIslands[group.connNetId][solvedTracePathIndex]!; + const segStart = path.tracePath[traceSegmentIndex]!; + const segEnd = path.tracePath[traceSegmentIndex + 1]!; graphics.lines!.push({ points: [segStart, segEnd], strokeColor: "red", - }) + }); } } @@ -174,9 +174,9 @@ export class TraceOverlapIssueSolver extends BaseSolver { points: trace.tracePath, strokeColor: "blue", strokeDash: "4 2", - }) + }); } - return graphics + return graphics; } } diff --git a/lib/solvers/TraceOverlapShiftSolver/TraceOverlapIssueSolver/applyJogToTrace.ts b/lib/solvers/TraceOverlapShiftSolver/TraceOverlapIssueSolver/applyJogToTrace.ts index 08cd6a91..4bb3e297 100644 --- a/lib/solvers/TraceOverlapShiftSolver/TraceOverlapIssueSolver/applyJogToTrace.ts +++ b/lib/solvers/TraceOverlapShiftSolver/TraceOverlapIssueSolver/applyJogToTrace.ts @@ -1,4 +1,4 @@ -import type { Point } from "@tscircuit/math-utils" +import type { Point } from "@tscircuit/math-utils"; export const applyJogToTerminalSegment = ({ pts, @@ -7,19 +7,19 @@ export const applyJogToTerminalSegment = ({ JOG_SIZE, EPS = 1e-6, }: { - pts: Point[] - segmentIndex: number - offset: number - JOG_SIZE: number - EPS?: number + pts: Point[]; + segmentIndex: number; + offset: number; + JOG_SIZE: number; + EPS?: number; }) => { - if (si !== 0 && si !== pts.length - 2) return + if (si !== 0 && si !== pts.length - 2) return; - const start = pts[si]! - const end = pts[si + 1]! - const isVertical = Math.abs(start.x - end.x) < EPS - const isHorizontal = Math.abs(start.y - end.y) < EPS - if (!isVertical && !isHorizontal) return + const start = pts[si]!; + const end = pts[si + 1]!; + const isVertical = Math.abs(start.x - end.x) < EPS; + const isHorizontal = Math.abs(start.y - end.y) < EPS; + if (!isVertical && !isHorizontal) return; const segDir = isVertical ? end.y > start.y @@ -27,50 +27,50 @@ export const applyJogToTerminalSegment = ({ : -1 : end.x > start.x ? 1 - : -1 + : -1; if (si === 0) { if (isVertical) { - const jogY = start.y + segDir * JOG_SIZE + const jogY = start.y + segDir * JOG_SIZE; pts.splice( 1, 1, { x: start.x, y: jogY }, { x: start.x + offset, y: jogY }, { x: end.x + offset, y: end.y }, - ) + ); } else { // Horizontal - const jogX = start.x + segDir * JOG_SIZE + const jogX = start.x + segDir * JOG_SIZE; pts.splice( 1, 1, { x: jogX, y: start.y }, { x: jogX, y: start.y + offset }, { x: end.x, y: end.y + offset }, - ) + ); } } else { // si === pts.length - 2 if (isVertical) { - const jogY = end.y - segDir * JOG_SIZE + const jogY = end.y - segDir * JOG_SIZE; pts.splice( si, 1, { x: start.x + offset, y: start.y }, { x: end.x + offset, y: jogY }, { x: end.x, y: jogY }, - ) + ); } else { // Horizontal - const jogX = end.x - segDir * JOG_SIZE + const jogX = end.x - segDir * JOG_SIZE; pts.splice( si, 1, { x: start.x, y: start.y + offset }, { x: jogX, y: end.y + offset }, { x: jogX, y: end.y }, - ) + ); } } -} +}; diff --git a/lib/solvers/TraceOverlapShiftSolver/TraceOverlapShiftSolver.ts b/lib/solvers/TraceOverlapShiftSolver/TraceOverlapShiftSolver.ts index 5d33ee8b..d1225d5e 100644 --- a/lib/solvers/TraceOverlapShiftSolver/TraceOverlapShiftSolver.ts +++ b/lib/solvers/TraceOverlapShiftSolver/TraceOverlapShiftSolver.ts @@ -1,15 +1,15 @@ -import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver" -import { visualizeInputProblem } from "../SchematicTracePipelineSolver/visualizeInputProblem" -import type { InputProblem } from "lib/types/InputProblem" -import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver" -import type { ConnectivityMap } from "connectivity-map" +import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"; +import { visualizeInputProblem } from "../SchematicTracePipelineSolver/visualizeInputProblem"; +import type { InputProblem } from "lib/types/InputProblem"; +import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver"; +import type { ConnectivityMap } from "connectivity-map"; import { TraceOverlapIssueSolver, type OverlappingTraceSegmentLocator, -} from "./TraceOverlapIssueSolver/TraceOverlapIssueSolver" -import type { MspConnectionPairId } from "../MspConnectionPairSolver/MspConnectionPairSolver" +} from "./TraceOverlapIssueSolver/TraceOverlapIssueSolver"; +import type { MspConnectionPairId } from "../MspConnectionPairSolver/MspConnectionPairSolver"; -type ConnNetId = string +type ConnNetId = string; /** * This solver finds traces that overlap (coincident and parallel) and aren't @@ -29,37 +29,37 @@ type ConnNetId = string * same net island they shift together. */ export class TraceOverlapShiftSolver extends BaseSolver { - inputProblem: InputProblem - inputTracePaths: Array - globalConnMap: ConnectivityMap + inputProblem: InputProblem; + inputTracePaths: Array; + globalConnMap: ConnectivityMap; - declare activeSubSolver: TraceOverlapIssueSolver | null + declare activeSubSolver: TraceOverlapIssueSolver | null; /** * A traceNetIsland is a set of traces that are connected via the globalConnMap */ - traceNetIslands: Record> = {} + traceNetIslands: Record> = {}; - correctedTraceMap: Record = {} + correctedTraceMap: Record = {}; - cleanupPhase: "diagonals" | "done" | null = null + cleanupPhase: "diagonals" | "done" | null = null; constructor(params: { - inputProblem: InputProblem - inputTracePaths: Array - globalConnMap: ConnectivityMap + inputProblem: InputProblem; + inputTracePaths: Array; + globalConnMap: ConnectivityMap; }) { - super() - this.inputProblem = params.inputProblem - this.inputTracePaths = params.inputTracePaths - this.globalConnMap = params.globalConnMap + super(); + this.inputProblem = params.inputProblem; + this.inputTracePaths = params.inputTracePaths; + this.globalConnMap = params.globalConnMap; for (const tracePath of this.inputTracePaths) { - const { mspPairId } = tracePath - this.correctedTraceMap[mspPairId] = tracePath + const { mspPairId } = tracePath; + this.correctedTraceMap[mspPairId] = tracePath; } - this.traceNetIslands = this.computeTraceNetIslands() + this.traceNetIslands = this.computeTraceNetIslands(); } override getConstructorParams(): ConstructorParameters< @@ -69,52 +69,52 @@ export class TraceOverlapShiftSolver extends BaseSolver { inputProblem: this.inputProblem, inputTracePaths: this.inputTracePaths, globalConnMap: this.globalConnMap, - } + }; } computeTraceNetIslands(): Record> { // Build islands keyed by global connection net id. // Preserve stable order by iterating original inputTracePaths array. - const islands: Record> = {} + const islands: Record> = {}; for (const original of this.inputTracePaths) { - const path = this.correctedTraceMap[original.mspPairId] ?? original - const key: ConnNetId = path.globalConnNetId - if (!islands[key]) islands[key] = [] - islands[key].push(path) + const path = this.correctedTraceMap[original.mspPairId] ?? original; + const key: ConnNetId = path.globalConnNetId; + if (!islands[key]) islands[key] = []; + islands[key].push(path); } - return islands + return islands; } findNextOverlapIssue(): { - overlappingTraceSegments: Array + overlappingTraceSegments: Array; } | null { // Detect the next set of overlapping segments between two different net islands. - const EPS = 2e-3 + const EPS = 2e-3; - const netIds = Object.keys(this.traceNetIslands) + const netIds = Object.keys(this.traceNetIslands); // Compare each pair of different nets for (let i = 0; i < netIds.length; i++) { for (let j = i + 1; j < netIds.length; j++) { - const netA = netIds[i]! - const netB = netIds[j]! - const pathsA = this.traceNetIslands[netA] || [] - const pathsB = this.traceNetIslands[netB] || [] + const netA = netIds[i]!; + const netB = netIds[j]!; + const pathsA = this.traceNetIslands[netA] || []; + const pathsB = this.traceNetIslands[netB] || []; // Collect overlaps for this pair const overlapsA: Array<{ - solvedTracePathIndex: number - traceSegmentIndex: number - }> = [] + solvedTracePathIndex: number; + traceSegmentIndex: number; + }> = []; const overlapsB: Array<{ - solvedTracePathIndex: number - traceSegmentIndex: number - }> = [] + solvedTracePathIndex: number; + traceSegmentIndex: number; + }> = []; // Track to avoid duplicates - const seenA = new Set() - const seenB = new Set() + const seenA = new Set(); + const seenB = new Set(); const overlaps1D = ( a1: number, @@ -122,74 +122,74 @@ export class TraceOverlapShiftSolver extends BaseSolver { b1: number, b2: number, ): boolean => { - const minA = Math.min(a1, a2) - const maxA = Math.max(a1, a2) - const minB = Math.min(b1, b2) - const maxB = Math.max(b1, b2) - const overlap = Math.min(maxA, maxB) - Math.max(minA, minB) - return overlap > EPS - } + const minA = Math.min(a1, a2); + const maxA = Math.max(a1, a2); + const minB = Math.min(b1, b2); + const maxB = Math.max(b1, b2); + const overlap = Math.min(maxA, maxB) - Math.max(minA, minB); + return overlap > EPS; + }; for (let pa = 0; pa < pathsA.length; pa++) { - const pathA = pathsA[pa]! - const ptsA = pathA.tracePath + const pathA = pathsA[pa]!; + const ptsA = pathA.tracePath; for (let sa = 0; sa < ptsA.length - 1; sa++) { - const a1 = ptsA[sa]! - const a2 = ptsA[sa + 1]! - const aVert = Math.abs(a1.x - a2.x) < EPS - const aHorz = Math.abs(a1.y - a2.y) < EPS - if (!aVert && !aHorz) continue + const a1 = ptsA[sa]!; + const a2 = ptsA[sa + 1]!; + const aVert = Math.abs(a1.x - a2.x) < EPS; + const aHorz = Math.abs(a1.y - a2.y) < EPS; + if (!aVert && !aHorz) continue; for (let pb = 0; pb < pathsB.length; pb++) { - const pathB = pathsB[pb]! - const ptsB = pathB.tracePath + const pathB = pathsB[pb]!; + const ptsB = pathB.tracePath; for (let sb = 0; sb < ptsB.length - 1; sb++) { - const b1 = ptsB[sb]! - const b2 = ptsB[sb + 1]! - const bVert = Math.abs(b1.x - b2.x) < EPS - const bHorz = Math.abs(b1.y - b2.y) < EPS - if (!bVert && !bHorz) continue + const b1 = ptsB[sb]!; + const b2 = ptsB[sb + 1]!; + const bVert = Math.abs(b1.x - b2.x) < EPS; + const bHorz = Math.abs(b1.y - b2.y) < EPS; + if (!bVert && !bHorz) continue; // Only consider colinear, parallel orientation overlaps if (aVert && bVert) { if (Math.abs(a1.x - b1.x) < EPS) { if (overlaps1D(a1.y, a2.y, b1.y, b2.y)) { - const keyA = `${pa}:${sa}` - const keyB = `${pb}:${sb}` + const keyA = `${pa}:${sa}`; + const keyB = `${pb}:${sb}`; if (!seenA.has(keyA)) { overlapsA.push({ solvedTracePathIndex: pa, traceSegmentIndex: sa, - }) - seenA.add(keyA) + }); + seenA.add(keyA); } if (!seenB.has(keyB)) { overlapsB.push({ solvedTracePathIndex: pb, traceSegmentIndex: sb, - }) - seenB.add(keyB) + }); + seenB.add(keyB); } } } } else if (aHorz && bHorz) { if (Math.abs(a1.y - b1.y) < EPS) { if (overlaps1D(a1.x, a2.x, b1.x, b2.x)) { - const keyA = `${pa}:${sa}` - const keyB = `${pb}:${sb}` + const keyA = `${pa}:${sa}`; + const keyB = `${pb}:${sb}`; if (!seenA.has(keyA)) { overlapsA.push({ solvedTracePathIndex: pa, traceSegmentIndex: sa, - }) - seenA.add(keyA) + }); + seenA.add(keyA); } if (!seenB.has(keyB)) { overlapsB.push({ solvedTracePathIndex: pb, traceSegmentIndex: sb, - }) - seenB.add(keyB) + }); + seenB.add(keyB); } } } @@ -205,68 +205,68 @@ export class TraceOverlapShiftSolver extends BaseSolver { { connNetId: netA, pathsWithOverlap: overlapsA }, { connNetId: netB, pathsWithOverlap: overlapsB }, ], - } + }; } } } - return null + return null; } private findNextDiagonalSegment() { - const EPS = 2e-3 + const EPS = 2e-3; for (const mspPairId in this.correctedTraceMap) { - const tracePath = this.correctedTraceMap[mspPairId]!.tracePath + const tracePath = this.correctedTraceMap[mspPairId]!.tracePath; for (let i = 0; i < tracePath.length - 1; i++) { - const p1 = tracePath[i]! - const p2 = tracePath[i + 1]! + const p1 = tracePath[i]!; + const p2 = tracePath[i + 1]!; - const isHorizontal = Math.abs(p1.y - p2.y) < EPS - const isVertical = Math.abs(p1.x - p2.x) < EPS + const isHorizontal = Math.abs(p1.y - p2.y) < EPS; + const isVertical = Math.abs(p1.x - p2.x) < EPS; if (!isHorizontal && !isVertical) { - return { mspPairId, tracePath, i, p1, p2 } + return { mspPairId, tracePath, i, p1, p2 }; } } } - return null + return null; } private findAndFixNextDiagonalSegment(): boolean { - const diagonalInfo = this.findNextDiagonalSegment() + const diagonalInfo = this.findNextDiagonalSegment(); if (!diagonalInfo) { - return false // No diagonal segments found + return false; // No diagonal segments found } - const { mspPairId, tracePath, i, p1, p2 } = diagonalInfo - const EPS = 2e-3 + const { mspPairId, tracePath, i, p1, p2 } = diagonalInfo; + const EPS = 2e-3; - const p0 = i > 0 ? tracePath[i - 1] : null - const p3 = i + 2 < tracePath.length ? tracePath[i + 2] : null + const p0 = i > 0 ? tracePath[i - 1] : null; + const p3 = i + 2 < tracePath.length ? tracePath[i + 2] : null; - const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) < EPS : false - const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) < EPS : false + const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) < EPS : false; + const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) < EPS : false; - const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) < EPS : false - const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) < EPS : false + const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) < EPS : false; + const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) < EPS : false; - const elbow1 = { x: p1.x, y: p2.y } // vertical from p1 - const elbow2 = { x: p2.x, y: p1.y } // horizontal from p1 + const elbow1 = { x: p1.x, y: p2.y }; // vertical from p1 + const elbow2 = { x: p2.x, y: p1.y }; // horizontal from p1 - let score1 = 0 - if (prevIsVertical) score1++ - if (nextIsHorizontal) score1++ + let score1 = 0; + if (prevIsVertical) score1++; + if (nextIsHorizontal) score1++; - let score2 = 0 - if (prevIsHorizontal) score2++ - if (nextIsVertical) score2++ + let score2 = 0; + if (prevIsHorizontal) score2++; + if (nextIsVertical) score2++; - const elbowPoint = score1 < score2 ? elbow1 : elbow2 + const elbowPoint = score1 < score2 ? elbow1 : elbow2; // Replace [p1, p2] with [p1, elbowPoint, p2] - tracePath.splice(i + 1, 0, elbowPoint) - return true // Fixed one diagonal, return true to re-evaluate in next step + tracePath.splice(i + 1, 0, elbowPoint); + return true; // Fixed one diagonal, return true to re-evaluate in next step } override _step() { @@ -274,73 +274,73 @@ export class TraceOverlapShiftSolver extends BaseSolver { for (const [mspPairId, newTrace] of Object.entries( this.activeSubSolver.correctedTraceMap, )) { - this.correctedTraceMap[mspPairId] = newTrace + this.correctedTraceMap[mspPairId] = newTrace; } - this.activeSubSolver = null - this.traceNetIslands = this.computeTraceNetIslands() + this.activeSubSolver = null; + this.traceNetIslands = this.computeTraceNetIslands(); } if (this.activeSubSolver) { - this.activeSubSolver.step() - return + this.activeSubSolver.step(); + return; } // Find the next overlapping trace segment - const overlapIssue = this.findNextOverlapIssue() + const overlapIssue = this.findNextOverlapIssue(); if (overlapIssue === null) { if (this.cleanupPhase === null) { - this.cleanupPhase = "diagonals" + this.cleanupPhase = "diagonals"; } if (this.cleanupPhase === "diagonals") { - const fixedDiagonal = this.findAndFixNextDiagonalSegment() + const fixedDiagonal = this.findAndFixNextDiagonalSegment(); if (!fixedDiagonal) { - this.cleanupPhase = "done" - this.solved = true + this.cleanupPhase = "done"; + this.solved = true; } - return + return; } // If cleanupPhase is "done", then the solver is truly solved. - this.solved = true - return + this.solved = true; + return; } - const { overlappingTraceSegments } = overlapIssue + const { overlappingTraceSegments } = overlapIssue; this.activeSubSolver = new TraceOverlapIssueSolver({ overlappingTraceSegments, traceNetIslands: this.traceNetIslands, - }) + }); } override visualize() { if (this.activeSubSolver) { - return this.activeSubSolver.visualize() + return this.activeSubSolver.visualize(); } - const graphics = visualizeInputProblem(this.inputProblem) - graphics.circles = graphics.circles || [] + const graphics = visualizeInputProblem(this.inputProblem); + graphics.circles = graphics.circles || []; // Draw current corrected traces for (const trace of Object.values(this.correctedTraceMap)) { graphics.lines!.push({ points: trace.tracePath, strokeColor: "purple", - }) + }); } if (this.cleanupPhase === "diagonals") { - const diagonalInfo = this.findNextDiagonalSegment() + const diagonalInfo = this.findNextDiagonalSegment(); if (diagonalInfo) { graphics.lines!.push({ points: [diagonalInfo.p1, diagonalInfo.p2], strokeColor: "red", strokeWidth: 0.05, - }) + }); } } - return graphics + return graphics; } } diff --git a/lib/types/InputProblem.ts b/lib/types/InputProblem.ts index e5db7fd5..006b2984 100644 --- a/lib/types/InputProblem.ts +++ b/lib/types/InputProblem.ts @@ -1,42 +1,42 @@ -import type { ChipObstacleSpatialIndex } from "lib/data-structures/ChipObstacleSpatialIndex" -import type { FacingDirection } from "lib/utils/dir" +import type { ChipObstacleSpatialIndex } from "lib/data-structures/ChipObstacleSpatialIndex"; +import type { FacingDirection } from "lib/utils/dir"; -export type ChipId = string -export type PinId = string -export type NetId = string +export type ChipId = string; +export type PinId = string; +export type NetId = string; export interface InputPin { - pinId: PinId - x: number - y: number + pinId: PinId; + x: number; + y: number; - _facingDirection?: "x+" | "x-" | "y+" | "y-" + _facingDirection?: "x+" | "x-" | "y+" | "y-"; } export interface InputChip { - chipId: ChipId - center: { x: number; y: number } - width: number - height: number - pins: Array + chipId: ChipId; + center: { x: number; y: number }; + width: number; + height: number; + pins: Array; } export interface InputDirectConnection { - pinIds: [PinId, PinId] - netId?: string + pinIds: [PinId, PinId]; + netId?: string; } export interface InputNetConnection { - netId: string - pinIds: Array - netLabelWidth?: number + netId: string; + pinIds: Array; + netLabelWidth?: number; } export interface InputProblem { - chips: Array - directConnections: Array - netConnections: Array + chips: Array; + directConnections: Array; + netConnections: Array; - availableNetLabelOrientations: Record - maxMspPairDistance?: number + availableNetLabelOrientations: Record; + maxMspPairDistance?: number; - _chipObstacleSpatialIndex?: ChipObstacleSpatialIndex + _chipObstacleSpatialIndex?: ChipObstacleSpatialIndex; } diff --git a/lib/utils/dir.ts b/lib/utils/dir.ts index c50d5811..931f8731 100644 --- a/lib/utils/dir.ts +++ b/lib/utils/dir.ts @@ -1,16 +1,16 @@ -export type FacingDirection = "x+" | "x-" | "y+" | "y-" +export type FacingDirection = "x+" | "x-" | "y+" | "y-"; export const dir = ( facingDirection: FacingDirection, ): { x: number; y: number } => { switch (facingDirection) { case "x+": - return { x: 1, y: 0 } + return { x: 1, y: 0 }; case "x-": - return { x: -1, y: 0 } + return { x: -1, y: 0 }; case "y+": - return { x: 0, y: 1 } + return { x: 0, y: 1 }; case "y-": - return { x: 0, y: -1 } + return { x: 0, y: -1 }; } - throw new Error(`Invalid facing direction: ${facingDirection}`) -} + throw new Error(`Invalid facing direction: ${facingDirection}`); +}; diff --git a/lib/utils/does-trace-overlap-with-existing-traces.ts b/lib/utils/does-trace-overlap-with-existing-traces.ts index 0540c300..b23907ce 100644 --- a/lib/utils/does-trace-overlap-with-existing-traces.ts +++ b/lib/utils/does-trace-overlap-with-existing-traces.ts @@ -1,19 +1,19 @@ -import type { Point } from "@tscircuit/math-utils" -import type { SolvedTracePath } from "../solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver" -import { doSegmentsIntersect } from "@tscircuit/math-utils" +import type { Point } from "@tscircuit/math-utils"; +import type { SolvedTracePath } from "../solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"; +import { doSegmentsIntersect } from "@tscircuit/math-utils"; export function doesTraceOverlapWithExistingTraces( newTracePath: Point[], existingTraces: SolvedTracePath[], ): boolean { for (let i = 0; i < newTracePath.length - 1; i++) { - const newSegmentP1 = newTracePath[i] - const newSegmentP2 = newTracePath[i + 1] + const newSegmentP1 = newTracePath[i]; + const newSegmentP2 = newTracePath[i + 1]; for (const existingTrace of existingTraces) { for (let j = 0; j < existingTrace.tracePath.length - 1; j++) { - const existingSegmentP1 = existingTrace.tracePath[j] - const existingSegmentP2 = existingTrace.tracePath[j + 1] + const existingSegmentP1 = existingTrace.tracePath[j]; + const existingSegmentP2 = existingTrace.tracePath[j + 1]; if ( doSegmentsIntersect( @@ -23,11 +23,11 @@ export function doesTraceOverlapWithExistingTraces( existingSegmentP2, ) ) { - return true // Found an intersection + return true; // Found an intersection } } } } - return false // No intersections found + return false; // No intersections found } diff --git a/lib/utils/getAllPossibleOrderingsGenerator.ts b/lib/utils/getAllPossibleOrderingsGenerator.ts index 65c9ef13..e7bfe066 100644 --- a/lib/utils/getAllPossibleOrderingsGenerator.ts +++ b/lib/utils/getAllPossibleOrderingsGenerator.ts @@ -33,15 +33,15 @@ export function* getAllPossibleOrderingsGenerator( items: T[], ): Generator { if (items.length === 0) { - yield [] - return + yield []; + return; } for (let i = 0; i < items.length; i++) { - const head = items[i] - const rest = items.slice(0, i).concat(items.slice(i + 1)) + const head = items[i]; + const rest = items.slice(0, i).concat(items.slice(i + 1)); for (const tail of getAllPossibleOrderingsGenerator(rest)) { - yield [head, ...tail] + yield [head, ...tail]; } } } diff --git a/lib/utils/getColorFromString.ts b/lib/utils/getColorFromString.ts index 6332e2d4..9a3bf187 100644 --- a/lib/utils/getColorFromString.ts +++ b/lib/utils/getColorFromString.ts @@ -1,7 +1,7 @@ export const getColorFromString = (string: string, alpha = 1) => { // pseudo random number from string const hash = string.split("").reduce((acc, char) => { - return acc * 31 + char.charCodeAt(0) - }, 0) - return `hsl(${hash % 360}, 100%, 50%, ${alpha})` -} + return acc * 31 + char.charCodeAt(0); + }, 0); + return `hsl(${hash % 360}, 100%, 50%, ${alpha})`; +}; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..b5264cdf --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5216 @@ +{ + "name": "@tscircuit/schematic-trace-solver", + "version": "0.0.47", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@tscircuit/schematic-trace-solver", + "version": "0.0.47", + "devDependencies": { + "@biomejs/biome": "^2.2.2", + "@react-hook/resize-observer": "^2.0.2", + "@tscircuit/math-utils": "^0.0.19", + "@types/bun": "^1.2.21", + "bun-match-svg": "^0.0.13", + "calculate-elbow": "^0.0.12", + "connectivity-map": "^1.0.0", + "flatbush": "^4.5.0", + "graphics-debug": "^0.0.62", + "react": "^19.1.1", + "react-cosmos": "^7.0.0", + "react-cosmos-plugin-vite": "^7.0.0", + "react-dom": "^19.1.1", + "tsup": "^8.5.0", + "vite": "^7.1.3" + }, + "peerDependencies": { + "typescript": "^5" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@biomejs/biome": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.10.tgz", + "integrity": "sha512-xxA3AphFQ1geij4JTHXv4EeSTda1IFn22ye9LdyVPoJU19fNVl0uzfEuhsfQ4Yue/0FaLs2/ccVi4UDiE7R30w==", + "dev": true, + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "2.4.10", + "@biomejs/cli-darwin-x64": "2.4.10", + "@biomejs/cli-linux-arm64": "2.4.10", + "@biomejs/cli-linux-arm64-musl": "2.4.10", + "@biomejs/cli-linux-x64": "2.4.10", + "@biomejs/cli-linux-x64-musl": "2.4.10", + "@biomejs/cli-win32-arm64": "2.4.10", + "@biomejs/cli-win32-x64": "2.4.10" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.10.tgz", + "integrity": "sha512-vuzzI1cWqDVzOMIkYyHbKqp+AkQq4K7k+UCXWpkYcY/HDn1UxdsbsfgtVpa40shem8Kax4TLDLlx8kMAecgqiw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.10.tgz", + "integrity": "sha512-14fzASRo+BPotwp7nWULy2W5xeUyFnTaq1V13Etrrxkrih+ez/2QfgFm5Ehtf5vSjtgx/IJycMMpn5kPd5ZNaA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.10.tgz", + "integrity": "sha512-7MH1CMW5uuxQ/s7FLST63qF8B3Hgu2HRdZ7tA1X1+mk+St4JOuIrqdhIBnnyqeyWJNI+Bww7Es5QZ0wIc1Cmkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.10.tgz", + "integrity": "sha512-WrJY6UuiSD/Dh+nwK2qOTu8kdMDlLV3dLMmychIghHPAysWFq1/DGC1pVZx8POE3ZkzKR3PUUnVrtZfMfaJjyQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.10.tgz", + "integrity": "sha512-tZLvEEi2u9Xu1zAqRjTcpIDGVtldigVvzug2fTuPG0ME/g8/mXpRPcNgLB22bGn6FvLJpHHnqLnwliOu8xjYrg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.10.tgz", + "integrity": "sha512-kDTi3pI6PBN6CiczsWYOyP2zk0IJI08EWEQyDMQWW221rPaaEz6FvjLhnU07KMzLv8q3qSuoB93ua6inSQ55Tw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.10.tgz", + "integrity": "sha512-umwQU6qPzH+ISTf/eHyJ/QoQnJs3V9Vpjz2OjZXe9MVBZ7prgGafMy7yYeRGnlmDAn87AKTF3Q6weLoMGpeqdQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.10.tgz", + "integrity": "sha512-aW/JU5GuyH4uxMrNYpoC2kjaHlyJGLgIa3XkhPEZI0uKhZhJZU8BuEyJmvgzSPQNGozBwWjC972RaNdcJ9KyJg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", + "dev": true + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@react-hook/latest": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@react-hook/latest/-/latest-1.0.3.tgz", + "integrity": "sha512-dy6duzl+JnAZcDbNTfmaP3xHiKtbXYOaz3G51MGVljh548Y8MWzTr+PHLOfvpypEVW9zwvl+VyKjbWKEVbV1Rg==", + "dev": true, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/@react-hook/passive-layout-effect": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@react-hook/passive-layout-effect/-/passive-layout-effect-1.2.1.tgz", + "integrity": "sha512-IwEphTD75liO8g+6taS+4oqz+nnroocNfWVHWz7j+N+ZO2vYrc6PV1q7GQhuahL0IOR7JccFTsFKQ/mb6iZWAg==", + "dev": true, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/@react-hook/resize-observer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@react-hook/resize-observer/-/resize-observer-2.0.2.tgz", + "integrity": "sha512-tzKKzxNpfE5TWmxuv+5Ae3IF58n0FQgQaWJmcbYkjXTRZATXxClnTprQ2uuYygYTpu1pqbBskpwMpj6jpT1djA==", + "dev": true, + "dependencies": { + "@react-hook/latest": "^1.0.2", + "@react-hook/passive-layout-effect": "^1.2.0" + }, + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@remix-run/router": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz", + "integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", + "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", + "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", + "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", + "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", + "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", + "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", + "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", + "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", + "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", + "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", + "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", + "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", + "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", + "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", + "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", + "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", + "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", + "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", + "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", + "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", + "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", + "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", + "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", + "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", + "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@skidding/launch-editor": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@skidding/launch-editor/-/launch-editor-2.2.3.tgz", + "integrity": "sha512-0SuGEsWdulnbryUJ6humogFuuDMWMb4VJyhOc3FGVkibxVdECYDDkGx8VjS/NePZSegNONDIVhCEVZLTv4ycTQ==", + "dev": true, + "dependencies": { + "chalk": "^2.3.0", + "shell-quote": "^1.6.1" + } + }, + "node_modules/@tscircuit/math-utils": { + "version": "0.0.19", + "resolved": "https://registry.npmjs.org/@tscircuit/math-utils/-/math-utils-0.0.19.tgz", + "integrity": "sha512-SWNNnp6GtdUVIXDUE25E2A//FlSctRjgDwLDLYl135GhYutuPq4cDdM1KUzdIPWrIoBRwsHTvZvUEhLG8LxW6w==", + "dev": true, + "peerDependencies": { + "typescript": "^5.0.0" + } + }, + "node_modules/@types/bun": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.3.11.tgz", + "integrity": "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg==", + "dev": true, + "dependencies": { + "bun-types": "1.3.11" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true + }, + "node_modules/@types/history": { + "version": "4.7.11", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", + "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==", + "dev": true + }, + "node_modules/@types/http-proxy": { + "version": "1.17.17", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.17.tgz", + "integrity": "sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "25.5.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.2.tgz", + "integrity": "sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==", + "dev": true, + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "dev": true, + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-router": { + "version": "5.1.20", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", + "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==", + "dev": true, + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*" + } + }, + "node_modules/@types/react-router-dom": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", + "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", + "dev": true, + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "*" + } + }, + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-styles/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/ansi-styles/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "node_modules/b4a": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.0.tgz", + "integrity": "sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==", + "dev": true, + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/bare-events": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", + "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", + "dev": true, + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/bare-fs": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.6.0.tgz", + "integrity": "sha512-2YkS7NuiJceSEbyEOdSNLE9tsGd+f4+f7C+Nik/MCk27SYdwIMPT/yRKvg++FZhQXgk0KWJKJyXX9RhVV0RGqA==", + "dev": true, + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-os": { + "version": "3.8.7", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.8.7.tgz", + "integrity": "sha512-G4Gr1UsGeEy2qtDTZwL7JFLo2wapUarz7iTMcYcMFdS89AIQuBoyjgXZz0Utv7uHs3xA9LckhVbeBi8lEQrC+w==", + "dev": true, + "engines": { + "bare": ">=1.14.0" + } + }, + "node_modules/bare-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", + "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", + "dev": true, + "dependencies": { + "bare-os": "^3.0.1" + } + }, + "node_modules/bare-stream": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.12.0.tgz", + "integrity": "sha512-w28i8lkBgREV3rPXGbgK+BO66q+ZpKqRWrZLiCdmmUlLPrQ45CzkvRhN+7lnv00Gpi2zy5naRxnUFAxCECDm9g==", + "dev": true, + "dependencies": { + "streamx": "^2.25.0", + "teex": "^1.0.1" + }, + "peerDependencies": { + "bare-abort-controller": "*", + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + }, + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/bare-url": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.0.tgz", + "integrity": "sha512-NSTU5WN+fy/L0DDenfE8SXQna4voXuW0FHM7wH8i3/q9khUSchfPbPezO4zSFMnDGIf9YE+mt/RWhZgNRKRIXA==", + "dev": true, + "dependencies": { + "bare-path": "^3.0.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "dev": true, + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bun-match-svg": { + "version": "0.0.13", + "resolved": "https://registry.npmjs.org/bun-match-svg/-/bun-match-svg-0.0.13.tgz", + "integrity": "sha512-MyklFz5vrx2++lT2dTJ8HlWPPSCCDYq+67b9kW2kTKVQoyb/Yq+HWuvbgrRt/o+dsOXL4Pf8eZPMyh2qPlgnMg==", + "dev": true, + "dependencies": { + "looks-same": "^9.0.1" + }, + "bin": { + "bun-match-svg": "cli.ts" + }, + "peerDependencies": { + "typescript": "^5.0.0" + } + }, + "node_modules/bun-types": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.3.11.tgz", + "integrity": "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bundle-require": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", + "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==", + "dev": true, + "dependencies": { + "load-tsconfig": "^0.2.3" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "esbuild": ">=0.18" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/calculate-elbow": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/calculate-elbow/-/calculate-elbow-0.0.12.tgz", + "integrity": "sha512-UkGS4EhabJn1WR6+UyoWpcxhKMx6MxM7+rK+3G0JcaPLMiYlvv5pEuc91unC/nH7kLGHV9xsVavhr5jJ50o+HA==", + "dev": true, + "peerDependencies": { + "typescript": "^5" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", + "integrity": "sha512-RwBeO/B/vZR3dfKL1ye/vx8MHZ40ugzpyfeVG5GsiuGnrlMWe2o8wxBbLCpw9CsxV+wHuzYlCiWnybrIA0ling==", + "dev": true + }, + "node_modules/color-diff": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/color-diff/-/color-diff-1.4.0.tgz", + "integrity": "sha512-4oDB/o78lNdppbaqrg0HjOp7pHmUc+dfCxWKWFnQg6AB/1dkjtBDop3RZht5386cq9xBUDRvDvSCA7WUlM9Jqw==", + "dev": true + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/condense-newlines": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/condense-newlines/-/condense-newlines-0.2.1.tgz", + "integrity": "sha512-P7X+QL9Hb9B/c8HI5BFFKmjgBu2XpQuF98WZ9XkO+dBGgk5XgwiQz7o1SmpglNWId3581UcS0SFAWfoIhMHPfg==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-whitespace": "^0.3.0", + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/connectivity-map": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/connectivity-map/-/connectivity-map-1.0.0.tgz", + "integrity": "sha512-AwCFYacp/GaWZE7bkmD95+C/o0jGP+JYT/+v2bLxoITEUHWyLd6HTX7KZQ6clo2h39aLSfIFSlapBVAXGZPXHg==", + "dev": true, + "dependencies": { + "@biomejs/biome": "^2.2.2" + }, + "peerDependencies": { + "typescript": "^5" + } + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-rename-keys": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/deep-rename-keys/-/deep-rename-keys-0.2.1.tgz", + "integrity": "sha512-RHd9ABw4Fvk+gYDWqwOftG849x0bYOySl/RgX0tLI9i27ZIeSO91mLZJEp7oPHOMFqHvpgu21YptmDt0FYD/0A==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2", + "rename-keys": "^1.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-browser": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", + "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", + "dev": true, + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/editorconfig": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.7.tgz", + "integrity": "sha512-e0GOtq/aTQhVdNyDU9e02+wz9oDDM+SIOQxWME2QRjzRX5yyLAuHDE+0aE8vHb9XRC8XD37eO2u57+F09JqFhw==", + "dev": true, + "dependencies": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "^9.0.1", + "semver": "^7.5.3" + }, + "bin": { + "editorconfig": "bin/editorconfig" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es6-promisify": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-7.0.0.tgz", + "integrity": "sha512-ginqzK3J90Rd4/Yz7qRrqUeIpe3TwSXTPPZtPne7tGBPeAaQiU8qt4fpKApnxHcq1AwtUdHVg5P77x/yrggG8Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "dev": true, + "dependencies": { + "bare-events": "^2.7.0" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/express": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fix-dts-default-cjs-exports": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", + "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", + "dev": true, + "dependencies": { + "magic-string": "^0.30.17", + "mlly": "^1.7.4", + "rollup": "^4.34.8" + } + }, + "node_modules/flatbush": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/flatbush/-/flatbush-4.5.1.tgz", + "integrity": "sha512-5tpuZV/26A5gRYfqyof3/ZTWeARKgZgd9aBrJLGNSGeasdO2PRb/2lIwDHBIuvPhKK2Y4ELuHbRGtegOwH3Lqg==", + "dev": true, + "dependencies": { + "flatqueue": "^3.0.0" + } + }, + "node_modules/flatqueue": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/flatqueue/-/flatqueue-3.0.0.tgz", + "integrity": "sha512-y1deYaVt+lIc/d2uIcWDNd0CrdQTO5xoCjeFdhX0kSXvm2Acm0o+3bAOiYklTEoRyzwio3sv3/IiBZdusbAe2Q==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/graphics-debug": { + "version": "0.0.62", + "resolved": "https://registry.npmjs.org/graphics-debug/-/graphics-debug-0.0.62.tgz", + "integrity": "sha512-wFYOS9M0E5lpQjZH6qCMBcncc6zVDJbqmd0j8JVFhopyENt7qCaNJD5yGY3FR+bhmE2720vOSJjQdwnZtzPkcA==", + "dev": true, + "dependencies": { + "@types/react-router-dom": "^5.3.3", + "polished": "^4.3.1", + "pretty": "^2.0.0", + "react-router-dom": "^6.28.0", + "react-supergrid": "^1.0.10", + "svgson": "^5.3.1", + "transformation-matrix": "^3.0.0", + "use-mouse-matrix-transform": "^1.3.0" + }, + "bin": { + "gd": "dist/cli/cli.js", + "graphics-debug": "dist/cli/cli.js" + }, + "peerDependencies": { + "bun-match-svg": "^0.0.9", + "looks-same": "^9.0.1", + "typescript": "^5.0.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dev": true, + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.5.tgz", + "integrity": "sha512-GLZZm1X38BPY4lkXA01jhwxvDoOkkXqjgVyUzVxiEK4iuRu03PZoYHhHRwxnfhQMDuaxi3vVri0YgSro/1oWqg==", + "dev": true, + "dependencies": { + "@types/http-proxy": "^1.17.15", + "debug": "^4.3.6", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.3", + "is-plain-object": "^5.0.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-middleware/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", + "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-whitespace": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-whitespace/-/is-whitespace-0.3.0.tgz", + "integrity": "sha512-RydPhl4S6JwAyj0JJjshWJEFG6hNye3pZFBRZaTUfZFwGHxzppNaNOVgQuS/E/SlhrApuMXrpnK1EEIXfdo3Dg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dev": true, + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/js-base64": { + "version": "3.7.8", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.8.tgz", + "integrity": "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==", + "dev": true + }, + "node_modules/js-beautify": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", + "dev": true, + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.4.2", + "js-cookie": "^3.0.5", + "nopt": "^7.2.1" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-graph-algorithms": { + "version": "1.0.18", + "resolved": "https://registry.npmjs.org/js-graph-algorithms/-/js-graph-algorithms-1.0.18.tgz", + "integrity": "sha512-Gu1wtWzXBzGeye/j9BuyplGHscwqKRZodp/0M1vyBc19RJpblSwKGu099KwwaTx9cRIV+Qupk8xUMfEiGfFqSA==", + "dev": true, + "bin": { + "js-graphs": "src/jsgraphs.js" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true + }, + "node_modules/lodash-es": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz", + "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==", + "dev": true + }, + "node_modules/looks-same": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/looks-same/-/looks-same-9.0.1.tgz", + "integrity": "sha512-V+vsT22nLIUdmvxr6jxsbafpJaZvLFnwZhV7BbmN38+v6gL+/BaHnwK9z5UURhDNSOrj3baOgbwzpjINqoZCpA==", + "dev": true, + "dependencies": { + "color-diff": "^1.1.0", + "fs-extra": "^8.1.0", + "js-graph-algorithms": "1.0.18", + "lodash": "^4.17.3", + "nested-error-stacks": "^2.1.0", + "parse-color": "^1.0.0", + "sharp": "0.32.6" + }, + "engines": { + "node": ">= 18.0.0" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dev": true, + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, + "node_modules/mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "dev": true, + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "dev": true + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/nested-error-stacks": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz", + "integrity": "sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==", + "dev": true + }, + "node_modules/node-abi": { + "version": "3.89.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.89.0.tgz", + "integrity": "sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "dev": true + }, + "node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "dev": true, + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "dev": true, + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "node_modules/parse-color": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-color/-/parse-color-1.0.0.tgz", + "integrity": "sha512-fuDHYgFHJGbpGMgw9skY/bj3HL/Jrn4l/5rSspy00DoT4RyLnDcRvPxdZ+r6OFwIsgAuhDh4I09tAId4mI12bw==", + "dev": true, + "dependencies": { + "color-convert": "~0.5.0" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "dev": true + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true + }, + "node_modules/pem": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/pem/-/pem-1.15.1.tgz", + "integrity": "sha512-kNNaflLX8Cpb3mrDNxSy8qIwpsNFKgBZx9pgFhbj4h+Rid4j2SMYQxcjtIjyhJg8/lwJTL+A3NHdD0M+UwyrCw==", + "deprecated": "this package has been deprecated - published by mistake", + "dev": true, + "dependencies": { + "es6-promisify": "^7.0.0", + "md5": "^2.3.0", + "os-tmpdir": "^1.0.2", + "which": "^2.0.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/polished": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/polished/-/polished-4.3.1.tgz", + "integrity": "sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.17.8" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "dev": true, + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prebuild-install/node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "dev": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/prebuild-install/node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pretty": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pretty/-/pretty-2.0.0.tgz", + "integrity": "sha512-G9xUchgTEiNpormdYBl+Pha50gOUovT18IvAe7EYMZ1/f9W/WWMPRn+xI68yXNMUk3QXHDwo/1wV/4NejVNe1w==", + "dev": true, + "dependencies": { + "condense-newlines": "^0.2.1", + "extend-shallow": "^2.0.1", + "js-beautify": "^1.6.12" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/qs": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "dev": true, + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "dev": true, + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-cosmos": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/react-cosmos/-/react-cosmos-7.2.0.tgz", + "integrity": "sha512-8g3ryjIkkk+cBwvTTd3sBP1ecGLuYtIFEpOgGICKOocDnrOgqrhzHEDCrT/yho0puniBxXfhBv90TAPs7GwUmQ==", + "dev": true, + "dependencies": { + "@skidding/launch-editor": "2.2.3", + "chokidar": "3.6.0", + "express": "4.22.1", + "glob": "10.5.0", + "http-proxy-middleware": "3.0.5", + "lodash-es": "4.17.23", + "micromatch": "4.0.8", + "open": "10.2.0", + "pem": "1.15.1", + "react-cosmos-core": "^7.2.0", + "react-cosmos-renderer": "^7.2.0", + "react-cosmos-ui": "^7.2.0", + "ws": "8.19.0", + "yargs": "17.7.2" + }, + "bin": { + "cosmos": "bin/cosmos.js", + "cosmos-export": "bin/cosmos-export.js", + "cosmos-native": "bin/cosmos-native.js" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/react-cosmos-core": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/react-cosmos-core/-/react-cosmos-core-7.2.0.tgz", + "integrity": "sha512-nRT76yN+uhysfnQurWVGJeVEjQyklt9n1HQUh3zrf8l4L/RDixQYsumS+bpm//3dojZdvzcN2V2IAOfK88sEUw==", + "dev": true, + "dependencies": { + "js-base64": "3.7.8", + "lodash-es": "4.17.23" + }, + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/react-cosmos-dom": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/react-cosmos-dom/-/react-cosmos-dom-7.2.0.tgz", + "integrity": "sha512-kyEjqXfzsn4hnXUj6YKqHn0Ik2bAJ2r1wLvMEd+oITSO3rOCM07JgCRPbVHDnClY93MW9gD4F8glriI32IpH0Q==", + "dev": true, + "dependencies": { + "lodash-es": "4.17.23", + "react-cosmos-core": "^7.2.0", + "react-cosmos-renderer": "^7.2.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/react-cosmos-plugin-vite": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/react-cosmos-plugin-vite/-/react-cosmos-plugin-vite-7.2.0.tgz", + "integrity": "sha512-Wm6RAoOcoTcbOgjPvlwyuIow7bhRvSWXV03fSamXRPdrVimN/GaToQBRm16xv/Nn5B1BCSlx+4Z/Mg2Z86sQzw==", + "dev": true, + "dependencies": { + "parse5": "7.3.0", + "react-cosmos-core": "^7.2.0", + "react-cosmos-dom": "^7.2.0" + }, + "peerDependencies": { + "react": ">=18", + "react-cosmos": ">=7", + "react-dom": ">=18", + "vite": "*" + } + }, + "node_modules/react-cosmos-renderer": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/react-cosmos-renderer/-/react-cosmos-renderer-7.2.0.tgz", + "integrity": "sha512-Cka98RQ7TF8Q1hOWdX/EuW7A7zImVDmQPtuZTDXuP81HCVxk7kIG7RE3lqKEwNtbW5TDKEravPqQxokH+y1U4Q==", + "dev": true, + "dependencies": { + "lodash-es": "4.17.23", + "react-cosmos-core": "^7.2.0" + }, + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/react-cosmos-ui": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/react-cosmos-ui/-/react-cosmos-ui-7.2.0.tgz", + "integrity": "sha512-X2GtLXzx5M//+rah4+fEgOt6Ic4Z+jVAA/S/W8bnGdvfjXCplVZcZLF0pB7ZNCalmenGzhmUh3/7BgKbOO1DGg==", + "dev": true, + "dependencies": { + "lodash-es": "4.17.23", + "react-cosmos-core": "^7.2.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "dev": true, + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/react-router": { + "version": "6.30.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz", + "integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==", + "dev": true, + "dependencies": { + "@remix-run/router": "1.23.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.30.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz", + "integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==", + "dev": true, + "dependencies": { + "@remix-run/router": "1.23.2", + "react-router": "6.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/react-supergrid": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/react-supergrid/-/react-supergrid-1.0.10.tgz", + "integrity": "sha512-dJd9wkH6BJkdfkv62EcRAIBn59e2wj58bJFVXiW/ZHQzxz20qIql63fTU2qFMOujXnBIDaMG0uTod67/mjEGeA==", + "dev": true, + "peerDependencies": { + "react": "*", + "react-dom": "*", + "transformation-matrix": "*" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rename-keys": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/rename-keys/-/rename-keys-1.2.0.tgz", + "integrity": "sha512-U7XpAktpbSgHTRSNRrjKSrjYkZKuhUukfoBlXWXUExCAqhzh1TU3BDRAfJmarcl5voKS+pbKU9MvyLWKZ4UEEg==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/rollup": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", + "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.1", + "@rollup/rollup-android-arm64": "4.60.1", + "@rollup/rollup-darwin-arm64": "4.60.1", + "@rollup/rollup-darwin-x64": "4.60.1", + "@rollup/rollup-freebsd-arm64": "4.60.1", + "@rollup/rollup-freebsd-x64": "4.60.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", + "@rollup/rollup-linux-arm-musleabihf": "4.60.1", + "@rollup/rollup-linux-arm64-gnu": "4.60.1", + "@rollup/rollup-linux-arm64-musl": "4.60.1", + "@rollup/rollup-linux-loong64-gnu": "4.60.1", + "@rollup/rollup-linux-loong64-musl": "4.60.1", + "@rollup/rollup-linux-ppc64-gnu": "4.60.1", + "@rollup/rollup-linux-ppc64-musl": "4.60.1", + "@rollup/rollup-linux-riscv64-gnu": "4.60.1", + "@rollup/rollup-linux-riscv64-musl": "4.60.1", + "@rollup/rollup-linux-s390x-gnu": "4.60.1", + "@rollup/rollup-linux-x64-gnu": "4.60.1", + "@rollup/rollup-linux-x64-musl": "4.60.1", + "@rollup/rollup-openbsd-x64": "4.60.1", + "@rollup/rollup-openharmony-arm64": "4.60.1", + "@rollup/rollup-win32-arm64-msvc": "4.60.1", + "@rollup/rollup-win32-ia32-msvc": "4.60.1", + "@rollup/rollup-win32-x64-gnu": "4.60.1", + "@rollup/rollup-win32-x64-msvc": "4.60.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "dev": true + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "dev": true, + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/sharp": { + "version": "0.32.6", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.6.tgz", + "integrity": "sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.2", + "node-addon-api": "^6.1.0", + "prebuild-install": "^7.1.1", + "semver": "^7.5.4", + "simple-get": "^4.0.1", + "tar-fs": "^3.0.4", + "tunnel-agent": "^0.6.0" + }, + "engines": { + "node": ">=14.15.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", + "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamx": { + "version": "2.25.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.25.0.tgz", + "integrity": "sha512-0nQuG6jf1w+wddNEEXCF4nTg3LtufWINB5eFEN+5TNZW7KWJp6x87+JFL43vaAUPyCfH1wID+mNVyW6OHtFamg==", + "dev": true, + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/svgson": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/svgson/-/svgson-5.3.1.tgz", + "integrity": "sha512-qdPgvUNWb40gWktBJnbJRelWcPzkLed/ShhnRsjbayXz8OtdPOzbil9jtiZdrYvSDumAz/VNQr6JaNfPx/gvPA==", + "dev": true, + "dependencies": { + "deep-rename-keys": "^0.2.1", + "xml-reader": "2.4.3" + } + }, + "node_modules/tar-fs": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.2.tgz", + "integrity": "sha512-QGxxTxxyleAdyM3kpFs14ymbYmNFrfY+pHj7Z8FgtbZ7w2//VAgLMac7sT6nRpIHjppXO2AwwEOg0bPFVRcmXw==", + "dev": true, + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" + } + }, + "node_modules/tar-stream": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.8.tgz", + "integrity": "sha512-U6QpVRyCGHva435KoNWy9PRoi2IFYCgtEhq9nmrPPpbRacPs9IH4aJ3gbrFC8dPcXvdSZ4XXfXT5Fshbp2MtlQ==", + "dev": true, + "dependencies": { + "b4a": "^1.6.4", + "bare-fs": "^4.5.5", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "dev": true, + "dependencies": { + "streamx": "^2.12.5" + } + }, + "node_modules/text-decoder": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", + "dev": true, + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/transformation-matrix": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/transformation-matrix/-/transformation-matrix-3.1.0.tgz", + "integrity": "sha512-oYubRWTi2tYFHAL2J8DLvPIqIYcYZ0fSOi2vmSy042Ho4jBW2ce6VP7QfD44t65WQz6bw5w1Pk22J7lcUpaTKA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/chrvadala" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, + "node_modules/tsup": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.1.tgz", + "integrity": "sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==", + "dev": true, + "dependencies": { + "bundle-require": "^5.1.0", + "cac": "^6.7.14", + "chokidar": "^4.0.3", + "consola": "^3.4.0", + "debug": "^4.4.0", + "esbuild": "^0.27.0", + "fix-dts-default-cjs-exports": "^1.0.0", + "joycon": "^3.1.1", + "picocolors": "^1.1.1", + "postcss-load-config": "^6.0.1", + "resolve-from": "^5.0.0", + "rollup": "^4.34.8", + "source-map": "^0.7.6", + "sucrase": "^3.35.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.11", + "tree-kill": "^1.2.2" + }, + "bin": { + "tsup": "dist/cli-default.js", + "tsup-node": "dist/cli-node.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@microsoft/api-extractor": "^7.36.0", + "@swc/core": "^1", + "postcss": "^8.4.12", + "typescript": ">=4.5.0" + }, + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "postcss": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/tsup/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/tsup/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/tsup/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/tsup/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ufo": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", + "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", + "dev": true + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/use-mouse-matrix-transform": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/use-mouse-matrix-transform/-/use-mouse-matrix-transform-1.3.5.tgz", + "integrity": "sha512-Ng938MFw/1kmxqQYORBIzC50KAekVLLtY3aepGbrzHehoNvbE75afOo3APxGk+kR3cVKKc3H8fW6vjbNY5J5tw==", + "dev": true, + "dependencies": { + "transformation-matrix": "^3.0.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xml-lexer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/xml-lexer/-/xml-lexer-0.2.2.tgz", + "integrity": "sha512-G0i98epIwiUEiKmMcavmVdhtymW+pCAohMRgybyIME9ygfVu8QheIi+YoQh3ngiThsT0SQzJT4R0sKDEv8Ou0w==", + "dev": true, + "dependencies": { + "eventemitter3": "^2.0.0" + } + }, + "node_modules/xml-lexer/node_modules/eventemitter3": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", + "integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==", + "dev": true + }, + "node_modules/xml-reader": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/xml-reader/-/xml-reader-2.4.3.tgz", + "integrity": "sha512-xWldrIxjeAMAu6+HSf9t50ot1uL5M+BtOidRCWHXIeewvSeIpscWCsp4Zxjk8kHHhdqFBrfK8U0EJeCcnyQ/gA==", + "dev": true, + "dependencies": { + "eventemitter3": "^2.0.0", + "xml-lexer": "^0.2.2" + } + }, + "node_modules/xml-reader/node_modules/eventemitter3": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", + "integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + } + } +} diff --git a/site/MspConnectionPairSolver/MspConnectionPairSolver01.page.tsx b/site/MspConnectionPairSolver/MspConnectionPairSolver01.page.tsx index 78bc1f70..a655cf8f 100644 --- a/site/MspConnectionPairSolver/MspConnectionPairSolver01.page.tsx +++ b/site/MspConnectionPairSolver/MspConnectionPairSolver01.page.tsx @@ -1,8 +1,8 @@ -import { MspConnectionPairSolver } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver" -import inputParams from "./MspConnectionPairSolver01_params.json" -import { GenericSolverDebugger } from "site/components/GenericSolverDebugger" -import { useMemo } from "react" -import type { InputProblem } from "lib/types/InputProblem" +import { MspConnectionPairSolver } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver"; +import inputParams from "./MspConnectionPairSolver01_params.json"; +import { GenericSolverDebugger } from "site/components/GenericSolverDebugger"; +import { useMemo } from "react"; +import type { InputProblem } from "lib/types/InputProblem"; export default () => { const solver = useMemo( @@ -11,6 +11,6 @@ export default () => { inputProblem: inputParams.inputProblem as unknown as InputProblem, }), [], - ) - return -} + ); + return ; +}; diff --git a/site/PasteInput.page.tsx b/site/PasteInput.page.tsx index 2234126d..5d5bc1d2 100644 --- a/site/PasteInput.page.tsx +++ b/site/PasteInput.page.tsx @@ -1,32 +1,32 @@ -import type { InputProblem } from "lib/types/InputProblem" -import { PipelineDebugger } from "site/components/PipelineDebugger" -import { useState } from "react" +import type { InputProblem } from "lib/types/InputProblem"; +import { PipelineDebugger } from "site/components/PipelineDebugger"; +import { useState } from "react"; export default () => { - const [inputText, setInputText] = useState("") - const [inputProblem, setInputProblem] = useState(null) - const [error, setError] = useState(null) + const [inputText, setInputText] = useState(""); + const [inputProblem, setInputProblem] = useState(null); + const [error, setError] = useState(null); const handleOpenDebugger = () => { try { - setError(null) - let parsed: InputProblem + setError(null); + let parsed: InputProblem; - const trimmedInput = inputText.trim() + const trimmedInput = inputText.trim(); if (trimmedInput.startsWith("{") || trimmedInput.startsWith("[")) { - parsed = JSON.parse(trimmedInput) + parsed = JSON.parse(trimmedInput); } else { - parsed = eval(`(${trimmedInput})`) + parsed = eval(`(${trimmedInput})`); } - setInputProblem(parsed) + setInputProblem(parsed); } catch (err) { - setError(err instanceof Error ? err.message : "Invalid input format") + setError(err instanceof Error ? err.message : "Invalid input format"); } - } + }; if (inputProblem) { - return + return ; } return ( @@ -79,5 +79,5 @@ export default () => { Open Debugger - ) -} + ); +}; diff --git a/site/SameNetSegmentMergingDemo.page.tsx b/site/SameNetSegmentMergingDemo.page.tsx new file mode 100644 index 00000000..0d16a5da --- /dev/null +++ b/site/SameNetSegmentMergingDemo.page.tsx @@ -0,0 +1,48 @@ +import { GenericSolverDebugger } from "site/components/GenericSolverDebugger" +import { useMemo } from "react" +import type { InputProblem } from "lib/types/InputProblem" +import { SchematicTracePipelineSolver } from "lib/index" + +const inputProblem = { + chips: [ + { + chipId: "chip1", + center: { x: 0, y: 0 }, + width: 2, + height: 2, + pins: [ + { pinId: "pin1", x: -1, y: 0.5 }, + { pinId: "pin2", x: -1, y: -0.5 }, + { pinId: "pin3", x: 1, y: 0.5 }, + { pinId: "pin4", x: 1, y: -0.5 }, + ], + }, + { + chipId: "chip2", + center: { x: 3, y: 0 }, + width: 1, + height: 1, + pins: [ + { pinId: "pin5", x: 2.5, y: 0.3 }, + { pinId: "pin6", x: 2.5, y: -0.3 }, + ], + }, + ], + directConnections: [ + { pinIds: ["pin1", "pin3"], netId: "NET1" }, + { pinIds: ["pin2", "pin4"], netId: "NET2" }, + { pinIds: ["pin3", "pin5"], netId: "NET1" }, + { pinIds: ["pin4", "pin6"], netId: "NET2" }, + ], + netConnections: [], + availableNetLabelOrientations: {}, + maxMspPairDistance: 5, +} as InputProblem + +export default () => { + const solver = useMemo( + () => new SchematicTracePipelineSolver(inputProblem), + [], + ) + return +} diff --git a/site/SchematicTraceLinesSolver/SchematicTraceLinesSolver01.page.tsx b/site/SchematicTraceLinesSolver/SchematicTraceLinesSolver01.page.tsx index 86ce0b39..ba57ca91 100644 --- a/site/SchematicTraceLinesSolver/SchematicTraceLinesSolver01.page.tsx +++ b/site/SchematicTraceLinesSolver/SchematicTraceLinesSolver01.page.tsx @@ -1,6 +1,6 @@ -import { useMemo } from "react" -import { GenericSolverDebugger } from "../components/GenericSolverDebugger" -import { SchematicTraceLinesSolver } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver" +import { useMemo } from "react"; +import { GenericSolverDebugger } from "../components/GenericSolverDebugger"; +import { SchematicTraceLinesSolver } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"; const input = { mspConnectionPairs: [ @@ -112,11 +112,11 @@ const input = { ], }, }, -} +}; export default () => { const solver = useMemo(() => { - return new SchematicTraceLinesSolver(input as any) - }, []) - return -} + return new SchematicTraceLinesSolver(input as any); + }, []); + return ; +}; diff --git a/site/SchematicTraceLinesSolver/SchematicTraceLinesSolver02.page.tsx b/site/SchematicTraceLinesSolver/SchematicTraceLinesSolver02.page.tsx index 0f52bd84..6d18f7b9 100644 --- a/site/SchematicTraceLinesSolver/SchematicTraceLinesSolver02.page.tsx +++ b/site/SchematicTraceLinesSolver/SchematicTraceLinesSolver02.page.tsx @@ -1,11 +1,11 @@ -import { useMemo } from "react" -import { GenericSolverDebugger } from "../components/GenericSolverDebugger" -import { SchematicTraceLinesSolver } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver" -import inputProblem from "./SchematicTraceLinesSolver02_params.json" +import { useMemo } from "react"; +import { GenericSolverDebugger } from "../components/GenericSolverDebugger"; +import { SchematicTraceLinesSolver } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"; +import inputProblem from "./SchematicTraceLinesSolver02_params.json"; export default () => { const solver = useMemo(() => { - return new SchematicTraceLinesSolver(inputProblem as any) - }, []) - return -} + return new SchematicTraceLinesSolver(inputProblem as any); + }, []); + return ; +}; diff --git a/site/SchematicTracePipelineSolver/SchematicTracePipelineSolver01.page.tsx b/site/SchematicTracePipelineSolver/SchematicTracePipelineSolver01.page.tsx index 539ccdd2..8014cbdc 100644 --- a/site/SchematicTracePipelineSolver/SchematicTracePipelineSolver01.page.tsx +++ b/site/SchematicTracePipelineSolver/SchematicTracePipelineSolver01.page.tsx @@ -1,7 +1,7 @@ -import { GenericSolverDebugger } from "site/components/GenericSolverDebugger" -import { useMemo } from "react" -import type { InputProblem } from "lib/types/InputProblem" -import { SchematicTracePipelineSolver } from "lib/index" +import { GenericSolverDebugger } from "site/components/GenericSolverDebugger"; +import { useMemo } from "react"; +import type { InputProblem } from "lib/types/InputProblem"; +import { SchematicTracePipelineSolver } from "lib/index"; const inputProblem = { chips: [ @@ -132,12 +132,12 @@ const inputProblem = { GND: ["y-"], }, maxMspPairDistance: 5, -} as InputProblem +} as InputProblem; export default () => { const solver = useMemo( () => new SchematicTracePipelineSolver(inputProblem), [], - ) - return -} + ); + return ; +}; diff --git a/site/SchematicTracePipelineSolver/SchematicTracePipelineSolver02.page.tsx b/site/SchematicTracePipelineSolver/SchematicTracePipelineSolver02.page.tsx index c6c73d74..2435fa5f 100644 --- a/site/SchematicTracePipelineSolver/SchematicTracePipelineSolver02.page.tsx +++ b/site/SchematicTracePipelineSolver/SchematicTracePipelineSolver02.page.tsx @@ -1,7 +1,7 @@ -import { GenericSolverDebugger } from "site/components/GenericSolverDebugger" -import { useMemo } from "react" -import type { InputProblem } from "lib/types/InputProblem" -import { SchematicTracePipelineSolver } from "lib/index" +import { GenericSolverDebugger } from "site/components/GenericSolverDebugger"; +import { useMemo } from "react"; +import type { InputProblem } from "lib/types/InputProblem"; +import { SchematicTracePipelineSolver } from "lib/index"; const inputProblem = { chips: [ @@ -166,12 +166,12 @@ const inputProblem = { ], availableNetLabelOrientations: {}, maxMspPairDistance: 5, -} as InputProblem +} as InputProblem; export default () => { const solver = useMemo( () => new SchematicTracePipelineSolver(inputProblem), [], - ) - return -} + ); + return ; +}; diff --git a/site/SchematicTracePipelineSolver/SchematicTracePipelineSolver03.page.tsx b/site/SchematicTracePipelineSolver/SchematicTracePipelineSolver03.page.tsx index 12bcc5f3..252dea38 100644 --- a/site/SchematicTracePipelineSolver/SchematicTracePipelineSolver03.page.tsx +++ b/site/SchematicTracePipelineSolver/SchematicTracePipelineSolver03.page.tsx @@ -1,5 +1,5 @@ -import { PipelineDebugger } from "site/components/PipelineDebugger" -import type { InputProblem } from "lib/types/InputProblem" +import { PipelineDebugger } from "site/components/PipelineDebugger"; +import type { InputProblem } from "lib/types/InputProblem"; export const inputProblem: InputProblem = { chips: [ @@ -481,6 +481,6 @@ export const inputProblem: InputProblem = { ROW2: ["x-", "x+"], }, maxMspPairDistance: 5, -} +}; -export default () => +export default () => ; diff --git a/site/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver01.page.tsx b/site/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver01.page.tsx index 290bd65b..79718d87 100644 --- a/site/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver01.page.tsx +++ b/site/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver01.page.tsx @@ -1,6 +1,6 @@ -import { useMemo } from "react" -import { GenericSolverDebugger } from "../components/GenericSolverDebugger" -import { SchematicTraceSingleLineSolver } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver" +import { useMemo } from "react"; +import { GenericSolverDebugger } from "../components/GenericSolverDebugger"; +import { SchematicTraceSingleLineSolver } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver"; const input = { chipMap: { @@ -884,11 +884,11 @@ const input = { availableNetLabelOrientations: {}, maxMspPairDistance: 2, }, -} +}; export default () => { const solver = useMemo(() => { - return new SchematicTraceSingleLineSolver(input as any) - }, []) - return -} + return new SchematicTraceSingleLineSolver(input as any); + }, []); + return ; +}; diff --git a/site/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2_01-example17.page.tsx b/site/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2_01-example17.page.tsx index da4478dd..b59e3212 100644 --- a/site/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2_01-example17.page.tsx +++ b/site/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2_01-example17.page.tsx @@ -1,6 +1,6 @@ -import { useMemo } from "react" -import { GenericSolverDebugger } from "../components/GenericSolverDebugger" -import { SchematicTraceSingleLineSolver2 } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2" +import { useMemo } from "react"; +import { GenericSolverDebugger } from "../components/GenericSolverDebugger"; +import { SchematicTraceSingleLineSolver2 } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2"; export const inputProblem = { chipMap: { @@ -314,11 +314,11 @@ export const inputProblem = { availableNetLabelOrientations: {}, maxMspPairDistance: 2.4, }, -} +}; export default () => { const solver = useMemo(() => { - return new SchematicTraceSingleLineSolver2(inputProblem as any) - }, []) - return -} + return new SchematicTraceSingleLineSolver2(inputProblem as any); + }, []); + return ; +}; diff --git a/site/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver01.page.tsx b/site/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver01.page.tsx index b0e15be3..17398b49 100644 --- a/site/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver01.page.tsx +++ b/site/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver01.page.tsx @@ -1,6 +1,6 @@ -import { useMemo } from "react" -import { GenericSolverDebugger } from "../components/GenericSolverDebugger" -import { SingleNetLabelPlacementSolver } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver" +import { useMemo } from "react"; +import { GenericSolverDebugger } from "../components/GenericSolverDebugger"; +import { SingleNetLabelPlacementSolver } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver"; export const input = { inputProblem: { @@ -564,11 +564,11 @@ export const input = { portOnlyPinId: "U1.1", }, availableOrientations: ["x+", "x-", "y+", "y-"], -} +}; export default () => { const solver = useMemo(() => { - return new SingleNetLabelPlacementSolver(input as any) - }, []) - return -} + return new SingleNetLabelPlacementSolver(input as any); + }, []); + return ; +}; diff --git a/site/components/DownloadDropdown.tsx b/site/components/DownloadDropdown.tsx index 566d3095..b0ebe95d 100644 --- a/site/components/DownloadDropdown.tsx +++ b/site/components/DownloadDropdown.tsx @@ -1,35 +1,35 @@ -import { useState, useRef, useEffect } from "react" -import type { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver" +import { useState, useRef, useEffect } from "react"; +import type { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"; interface DownloadDropdownProps { - solver: BaseSolver - className?: string + solver: BaseSolver; + className?: string; } const deepRemoveUnderscoreProperties = (obj: any): any => { if (obj === null || typeof obj !== "object") { - return obj + return obj; } if (Array.isArray(obj)) { - return obj.map(deepRemoveUnderscoreProperties) + return obj.map(deepRemoveUnderscoreProperties); } - const result: any = {} + const result: any = {}; for (const [key, value] of Object.entries(obj)) { if (!key.startsWith("_")) { - result[key] = deepRemoveUnderscoreProperties(value) + result[key] = deepRemoveUnderscoreProperties(value); } } - return result -} + return result; +}; export const DownloadDropdown = ({ solver, className = "", }: DownloadDropdownProps) => { - const [isOpen, setIsOpen] = useState(false) - const dropdownRef = useRef(null) + const [isOpen, setIsOpen] = useState(false); + const dropdownRef = useRef(null); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { @@ -37,53 +37,53 @@ export const DownloadDropdown = ({ dropdownRef.current && !dropdownRef.current.contains(event.target as Node) ) { - setIsOpen(false) + setIsOpen(false); } - } + }; - document.addEventListener("mousedown", handleClickOutside) - return () => document.removeEventListener("mousedown", handleClickOutside) - }, []) + document.addEventListener("mousedown", handleClickOutside); + return () => document.removeEventListener("mousedown", handleClickOutside); + }, []); const downloadJSON = () => { try { if (typeof solver.getConstructorParams !== "function") { alert( `getConstructorParams() is not implemented for ${solver.constructor.name}`, - ) - return + ); + return; } const params = deepRemoveUnderscoreProperties( solver.getConstructorParams(), - ) + ); const blob = new Blob([JSON.stringify(params, null, 2)], { type: "application/json", - }) - const url = URL.createObjectURL(blob) - const a = document.createElement("a") - a.href = url - a.download = `${solver.constructor.name}_params.json` - a.click() - URL.revokeObjectURL(url) + }); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = `${solver.constructor.name}_params.json`; + a.click(); + URL.revokeObjectURL(url); } catch (error) { alert( `Error downloading params for ${solver.constructor.name}: ${error instanceof Error ? error.message : String(error)}`, - ) + ); } - setIsOpen(false) - } + setIsOpen(false); + }; const downloadPageTsx = () => { try { const params = deepRemoveUnderscoreProperties( solver.getConstructorParams(), - ) - const solverName = solver.constructor.name + ); + const solverName = solver.constructor.name; const isSchematicTracePipelineSolver = - solverName === "SchematicTracePipelineSolver" + solverName === "SchematicTracePipelineSolver"; - let content: string + let content: string; if (isSchematicTracePipelineSolver) { content = `import { PipelineDebugger } from "site/components/PipelineDebugger" @@ -92,7 +92,7 @@ import type { InputProblem } from "lib/types/InputProblem" const inputProblem: InputProblem = ${JSON.stringify(params, null, 2)} export default () => -` +`; } else { content = `import { useMemo } from "react" import { GenericSolverDebugger } from "../components/GenericSolverDebugger" @@ -106,30 +106,30 @@ export default () => { }, []) return } -` +`; } - const blob = new Blob([content], { type: "text/plain" }) - const url = URL.createObjectURL(blob) - const a = document.createElement("a") - a.href = url - a.download = `${solverName}.page.tsx` - a.click() - URL.revokeObjectURL(url) + const blob = new Blob([content], { type: "text/plain" }); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = `${solverName}.page.tsx`; + a.click(); + URL.revokeObjectURL(url); } catch (error) { alert( `Error generating page.tsx for ${solver.constructor.name}: ${error instanceof Error ? error.message : String(error)}`, - ) + ); } - setIsOpen(false) - } + setIsOpen(false); + }; const downloadTestTs = () => { try { const params = deepRemoveUnderscoreProperties( solver.getConstructorParams(), - ) - const solverName = solver.constructor.name + ); + const solverName = solver.constructor.name; const content = `import { ${solverName} } from "lib/solvers/${solverName}/${solverName}\nimport { test, expect } from "bun:test" @@ -144,22 +144,22 @@ test("${solverName} should solve problem correctly", () => { // Add more specific assertions based on expected output // expect(solver.netLabelPlacementSolver!.netLabelPlacements).toMatchInlineSnapshot() }) -` - - const blob = new Blob([content], { type: "text/plain" }) - const url = URL.createObjectURL(blob) - const a = document.createElement("a") - a.href = url - a.download = `${solverName}.test.ts` - a.click() - URL.revokeObjectURL(url) +`; + + const blob = new Blob([content], { type: "text/plain" }); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = `${solverName}.test.ts`; + a.click(); + URL.revokeObjectURL(url); } catch (error) { alert( `Error generating test.ts for ${solver.constructor.name}: ${error instanceof Error ? error.message : String(error)}`, - ) + ); } - setIsOpen(false) - } + setIsOpen(false); + }; return (
@@ -194,5 +194,5 @@ test("${solverName} should solve problem correctly", () => {
)} - ) -} + ); +}; diff --git a/site/components/GenericSolverDebugger.tsx b/site/components/GenericSolverDebugger.tsx index 3c507a85..4c71b48d 100644 --- a/site/components/GenericSolverDebugger.tsx +++ b/site/components/GenericSolverDebugger.tsx @@ -1,16 +1,16 @@ -import type { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver" -import type { InputProblem } from "lib/types/InputProblem" -import { useMemo, useReducer } from "react" -import { InteractiveGraphics } from "graphics-debug/react" -import { SolverToolbar } from "./SolverToolbar" +import type { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"; +import type { InputProblem } from "lib/types/InputProblem"; +import { useMemo, useReducer } from "react"; +import { InteractiveGraphics } from "graphics-debug/react"; +import { SolverToolbar } from "./SolverToolbar"; export const GenericSolverDebugger = ({ solver }: { solver: BaseSolver }) => { - const [, incRenderCount] = useReducer((x) => x + 1, 0) + const [, incRenderCount] = useReducer((x) => x + 1, 0); return (
incRenderCount()} solver={solver} />
- ) -} + ); +}; diff --git a/site/components/PipelineDebugger.tsx b/site/components/PipelineDebugger.tsx index 27725eba..d2f8862e 100644 --- a/site/components/PipelineDebugger.tsx +++ b/site/components/PipelineDebugger.tsx @@ -1,20 +1,20 @@ -import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver" -import type { InputProblem } from "lib/types/InputProblem" -import { useMemo, useReducer } from "react" -import { InteractiveGraphics } from "graphics-debug/react" -import { SolverToolbar } from "./SolverToolbar" -import { PipelineStageTable } from "./PipelineStageTable" +import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"; +import type { InputProblem } from "lib/types/InputProblem"; +import { useMemo, useReducer } from "react"; +import { InteractiveGraphics } from "graphics-debug/react"; +import { SolverToolbar } from "./SolverToolbar"; +import { PipelineStageTable } from "./PipelineStageTable"; export const PipelineDebugger = ({ inputProblem, }: { - inputProblem: InputProblem + inputProblem: InputProblem; }) => { - const [, incRenderCount] = useReducer((x) => x + 1, 0) + const [, incRenderCount] = useReducer((x) => x + 1, 0); const solver = useMemo( () => new SchematicTracePipelineSolver(inputProblem), [], - ) + ); return (
@@ -25,5 +25,5 @@ export const PipelineDebugger = ({ pipelineSolver={solver} />
- ) -} + ); +}; diff --git a/site/components/PipelineStageTable.tsx b/site/components/PipelineStageTable.tsx index 297e5e5a..71f90b5a 100644 --- a/site/components/PipelineStageTable.tsx +++ b/site/components/PipelineStageTable.tsx @@ -1,6 +1,6 @@ -import type { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver" +import type { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"; -type StageStatus = "Solved" | "Failed" | "Running" | "Not Started" +type StageStatus = "Solved" | "Failed" | "Running" | "Not Started"; /** * Displays every stage of the pipeline with the status ("Solved", "Failed", "Running" or "Not Started"), @@ -13,43 +13,43 @@ export const PipelineStageTable = ({ pipelineSolver, triggerRender, }: { - pipelineSolver: SchematicTracePipelineSolver - triggerRender: () => void + pipelineSolver: SchematicTracePipelineSolver; + triggerRender: () => void; }) => { const getStageStatus = (stageIndex: number): StageStatus => { if (pipelineSolver.currentPipelineStepIndex > stageIndex) { - return "Solved" + return "Solved"; } if (pipelineSolver.currentPipelineStepIndex === stageIndex) { - if (pipelineSolver.failed) return "Failed" - if (pipelineSolver.activeSubSolver) return "Running" + if (pipelineSolver.failed) return "Failed"; + if (pipelineSolver.activeSubSolver) return "Running"; } - return "Not Started" - } + return "Not Started"; + }; const downloadParams = (stageName: string) => { const stage = pipelineSolver.pipelineDef.find( (s) => s.solverName === stageName, - ) - if (!stage) return + ); + if (!stage) return; try { - const params = stage.getConstructorParams(pipelineSolver) + const params = stage.getConstructorParams(pipelineSolver); const blob = new Blob([JSON.stringify(params, null, 2)], { type: "application/json", - }) - const url = URL.createObjectURL(blob) - const a = document.createElement("a") - a.href = url - a.download = `${stageName}_params.json` - a.click() - URL.revokeObjectURL(url) + }); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = `${stageName}_params.json`; + a.click(); + URL.revokeObjectURL(url); } catch (error) { alert( `Error downloading params for ${stageName}: ${error instanceof Error ? error.message : String(error)}`, - ) + ); } - } + }; return (
@@ -78,16 +78,16 @@ export const PipelineStageTable = ({ {pipelineSolver.pipelineDef.map((stage, index) => { - const status = getStageStatus(index) + const status = getStageStatus(index); const startIteration = - pipelineSolver.firstIterationOfPhase[stage.solverName] + pipelineSolver.firstIterationOfPhase[stage.solverName]; const endIteration = status === "Solved" ? (pipelineSolver.firstIterationOfPhase[stage.solverName] || 0) + ((pipelineSolver as any)[stage.solverName]?.iterations || 0) - : undefined - const timeSpent = pipelineSolver.timeSpentOnPhase[stage.solverName] + : undefined; + const timeSpent = pipelineSolver.timeSpentOnPhase[stage.solverName]; return ( @@ -111,8 +111,8 @@ export const PipelineStageTable = ({ - ) + ); })}
- ) -} + ); +}; diff --git a/site/components/SolverBreadcrumbInputDownloader.tsx b/site/components/SolverBreadcrumbInputDownloader.tsx index ab46a78a..7625c14d 100644 --- a/site/components/SolverBreadcrumbInputDownloader.tsx +++ b/site/components/SolverBreadcrumbInputDownloader.tsx @@ -1,12 +1,12 @@ -import type { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver" -import { DownloadDropdown } from "./DownloadDropdown" +import type { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"; +import { DownloadDropdown } from "./DownloadDropdown"; export const getSolverChain = (solver: BaseSolver): BaseSolver[] => { if (!solver.activeSubSolver) { - return [solver] + return [solver]; } - return [solver, ...getSolverChain(solver.activeSubSolver)] -} + return [solver, ...getSolverChain(solver.activeSubSolver)]; +}; /** * Displays each solver in the chain as a breadcrumb with download functionality @@ -14,9 +14,9 @@ export const getSolverChain = (solver: BaseSolver): BaseSolver[] => { export const SolverBreadcrumbInputDownloader = ({ solver, }: { - solver: BaseSolver + solver: BaseSolver; }) => { - const solverChain = getSolverChain(solver) + const solverChain = getSolverChain(solver); return (
@@ -27,5 +27,5 @@ export const SolverBreadcrumbInputDownloader = ({
))} - ) -} + ); +}; diff --git a/site/components/SolverToolbar.tsx b/site/components/SolverToolbar.tsx index 0ad50190..8ce412c4 100644 --- a/site/components/SolverToolbar.tsx +++ b/site/components/SolverToolbar.tsx @@ -1,6 +1,6 @@ -import type { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver" -import { useReducer, useRef, useEffect } from "react" -import { SolverBreadcrumbInputDownloader } from "./SolverBreadcrumbInputDownloader" +import type { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"; +import { useReducer, useRef, useEffect } from "react"; +import { SolverBreadcrumbInputDownloader } from "./SolverBreadcrumbInputDownloader"; /** * The solver toolbar offers various actions for progressing the solver @@ -17,64 +17,64 @@ export const SolverToolbar = ({ solver, triggerRender, }: { - solver: BaseSolver - triggerRender: Function + solver: BaseSolver; + triggerRender: Function; }) => { - const [isAnimating, setIsAnimating] = useReducer((x) => !x, false) - const animationRef = useRef(undefined) + const [isAnimating, setIsAnimating] = useReducer((x) => !x, false); + const animationRef = useRef(undefined); const handleStep = () => { - solver.step() - triggerRender() - } + solver.step(); + triggerRender(); + }; const handleSolve = () => { - solver.solve() - triggerRender() - } + solver.solve(); + triggerRender(); + }; const handleAnimate = () => { if (isAnimating) { if (animationRef.current) { - clearInterval(animationRef.current) - animationRef.current = undefined + clearInterval(animationRef.current); + animationRef.current = undefined; } - setIsAnimating() + setIsAnimating(); } else { - setIsAnimating() + setIsAnimating(); animationRef.current = window.setInterval(() => { if (solver.solved || solver.failed) { if (animationRef.current) { - clearInterval(animationRef.current) - animationRef.current = undefined + clearInterval(animationRef.current); + animationRef.current = undefined; } - setIsAnimating() - triggerRender() - return + setIsAnimating(); + triggerRender(); + return; } - solver.step() - triggerRender() - }, 25) // 40 iterations/second = 25ms interval + solver.step(); + triggerRender(); + }, 25); // 40 iterations/second = 25ms interval } - } + }; useEffect(() => { return () => { if (animationRef.current) { - clearInterval(animationRef.current) + clearInterval(animationRef.current); } - } - }, []) + }; + }, []); useEffect(() => { if ((solver.solved || solver.failed) && isAnimating) { if (animationRef.current) { - clearInterval(animationRef.current) - animationRef.current = undefined + clearInterval(animationRef.current); + animationRef.current = undefined; } - setIsAnimating() + setIsAnimating(); } - }, [solver.solved, solver.failed, isAnimating]) + }, [solver.solved, solver.failed, isAnimating]); return (
@@ -124,5 +124,5 @@ export const SolverToolbar = ({
Failed: {solver.error}
)}
- ) -} + ); +}; diff --git a/site/examples/example01-basic.page.tsx b/site/examples/example01-basic.page.tsx index 1b0fdcd4..1feaf78a 100644 --- a/site/examples/example01-basic.page.tsx +++ b/site/examples/example01-basic.page.tsx @@ -1,5 +1,5 @@ -import type { InputProblem } from "lib/types/InputProblem" -import { PipelineDebugger } from "site/components/PipelineDebugger" +import type { InputProblem } from "lib/types/InputProblem"; +import { PipelineDebugger } from "site/components/PipelineDebugger"; export const inputProblem: InputProblem = { chips: [ @@ -100,6 +100,6 @@ export const inputProblem: InputProblem = { GND: ["y-"], }, maxMspPairDistance: 5, -} +}; -export default () => +export default () => ; diff --git a/site/examples/example02.page.tsx b/site/examples/example02.page.tsx index 79f66868..2321bfb3 100644 --- a/site/examples/example02.page.tsx +++ b/site/examples/example02.page.tsx @@ -1,5 +1,5 @@ -import { PipelineDebugger } from "site/components/PipelineDebugger" -import type { InputProblem } from "lib/types/InputProblem" +import { PipelineDebugger } from "site/components/PipelineDebugger"; +import type { InputProblem } from "lib/types/InputProblem"; export const inputProblem: InputProblem = { chips: [ { @@ -177,6 +177,6 @@ export const inputProblem: InputProblem = { GND: ["y-"], V3_3: ["y+"], }, -} +}; -export default () => +export default () => ; diff --git a/site/examples/example03.page.tsx b/site/examples/example03.page.tsx index b5ad5538..cec09887 100644 --- a/site/examples/example03.page.tsx +++ b/site/examples/example03.page.tsx @@ -1,5 +1,5 @@ -import type { InputProblem } from "lib/index" -import { PipelineDebugger } from "site/components/PipelineDebugger" +import type { InputProblem } from "lib/index"; +import { PipelineDebugger } from "site/components/PipelineDebugger"; export const inputProblem: InputProblem = { chips: [ @@ -210,6 +210,6 @@ export const inputProblem: InputProblem = { }, ], availableNetLabelOrientations: {}, -} +}; -export default () => +export default () => ; diff --git a/site/examples/example04-single-symbol.page.tsx b/site/examples/example04-single-symbol.page.tsx index 8d749d35..93ee00ac 100644 --- a/site/examples/example04-single-symbol.page.tsx +++ b/site/examples/example04-single-symbol.page.tsx @@ -1,5 +1,5 @@ -import { PipelineDebugger } from "../components/PipelineDebugger" -import type { InputProblem } from "lib/types/InputProblem" +import { PipelineDebugger } from "../components/PipelineDebugger"; +import type { InputProblem } from "lib/types/InputProblem"; export const inputProblem: InputProblem = { chips: [ @@ -41,6 +41,6 @@ export const inputProblem: InputProblem = { V3_3: ["y+"], }, maxMspPairDistance: 2, -} +}; -export default () => +export default () => ; diff --git a/site/examples/example05.page.tsx b/site/examples/example05.page.tsx index b0881982..71a29a76 100644 --- a/site/examples/example05.page.tsx +++ b/site/examples/example05.page.tsx @@ -1,5 +1,5 @@ -import type { InputProblem } from "lib/index" -import { PipelineDebugger } from "site/components/PipelineDebugger" +import type { InputProblem } from "lib/index"; +import { PipelineDebugger } from "site/components/PipelineDebugger"; export const inputProblem: InputProblem = { chips: [ @@ -175,8 +175,8 @@ export const inputProblem: InputProblem = { netConnections: [], availableNetLabelOrientations: {}, maxMspPairDistance: 10, -} +}; export default function Example05Page() { - return + return ; } diff --git a/site/examples/example06.page.tsx b/site/examples/example06.page.tsx index 0204b1ce..15165218 100644 --- a/site/examples/example06.page.tsx +++ b/site/examples/example06.page.tsx @@ -1,5 +1,5 @@ -import { PipelineDebugger } from "site/components/PipelineDebugger" -import type { InputProblem } from "lib/types/InputProblem" +import { PipelineDebugger } from "site/components/PipelineDebugger"; +import type { InputProblem } from "lib/types/InputProblem"; export const inputProblem: InputProblem = { chips: [ @@ -55,6 +55,6 @@ export const inputProblem: InputProblem = { netConnections: [], availableNetLabelOrientations: {}, maxMspPairDistance: 10, -} +}; -export default () => +export default () => ; diff --git a/site/examples/example07.page.tsx b/site/examples/example07.page.tsx index b7721ed3..db6b81d6 100644 --- a/site/examples/example07.page.tsx +++ b/site/examples/example07.page.tsx @@ -1,5 +1,5 @@ -import { PipelineDebugger } from "site/components/PipelineDebugger" -import type { InputProblem } from "lib/types/InputProblem" +import { PipelineDebugger } from "site/components/PipelineDebugger"; +import type { InputProblem } from "lib/types/InputProblem"; export const inputProblem: InputProblem = { chips: [ @@ -130,6 +130,6 @@ export const inputProblem: InputProblem = { GND: ["y-"], }, maxMspPairDistance: 2, -} +}; -export default () => +export default () => ; diff --git a/site/examples/example08.page.tsx b/site/examples/example08.page.tsx index 678cd63f..2dcf06c5 100644 --- a/site/examples/example08.page.tsx +++ b/site/examples/example08.page.tsx @@ -1,5 +1,5 @@ -import { PipelineDebugger } from "site/components/PipelineDebugger" -import type { InputProblem } from "lib/types/InputProblem" +import { PipelineDebugger } from "site/components/PipelineDebugger"; +import type { InputProblem } from "lib/types/InputProblem"; export const inputProblem: InputProblem = { chips: [ @@ -89,6 +89,6 @@ export const inputProblem: InputProblem = { netConnections: [], availableNetLabelOrientations: {}, maxMspPairDistance: 5, -} +}; -export default () => +export default () => ; diff --git a/site/examples/example09.page.tsx b/site/examples/example09.page.tsx index 138e6e28..df63d6e8 100644 --- a/site/examples/example09.page.tsx +++ b/site/examples/example09.page.tsx @@ -1,5 +1,5 @@ -import { PipelineDebugger } from "site/components/PipelineDebugger" -import type { InputProblem } from "lib/types/InputProblem" +import { PipelineDebugger } from "site/components/PipelineDebugger"; +import type { InputProblem } from "lib/types/InputProblem"; export const inputProblem: InputProblem = { chips: [ @@ -412,6 +412,6 @@ export const inputProblem: InputProblem = { netConnections: [], availableNetLabelOrientations: {}, maxMspPairDistance: 5, -} +}; -export default () => +export default () => ; diff --git a/site/examples/example10.page.tsx b/site/examples/example10.page.tsx index 6a8d3d4e..b5a03d07 100644 --- a/site/examples/example10.page.tsx +++ b/site/examples/example10.page.tsx @@ -1,5 +1,5 @@ -import { PipelineDebugger } from "site/components/PipelineDebugger" -import type { InputProblem } from "lib/types/InputProblem" +import { PipelineDebugger } from "site/components/PipelineDebugger"; +import type { InputProblem } from "lib/types/InputProblem"; export const inputProblem: InputProblem = { chips: [ @@ -73,6 +73,6 @@ export const inputProblem: InputProblem = { ], availableNetLabelOrientations: {}, maxMspPairDistance: 2, -} +}; -export default () => +export default () => ; diff --git a/site/examples/example11.page.tsx b/site/examples/example11.page.tsx index eaef9cce..fd0031f4 100644 --- a/site/examples/example11.page.tsx +++ b/site/examples/example11.page.tsx @@ -1,5 +1,5 @@ -import { PipelineDebugger } from "site/components/PipelineDebugger" -import type { InputProblem } from "lib/types/InputProblem" +import { PipelineDebugger } from "site/components/PipelineDebugger"; +import type { InputProblem } from "lib/types/InputProblem"; export const inputProblem: InputProblem = { chips: [ @@ -114,6 +114,6 @@ export const inputProblem: InputProblem = { netConnections: [], availableNetLabelOrientations: {}, maxMspPairDistance: 5, -} +}; -export default () => +export default () => ; diff --git a/site/examples/example12.page.tsx b/site/examples/example12.page.tsx index 1cbcc6cc..ef4e047a 100644 --- a/site/examples/example12.page.tsx +++ b/site/examples/example12.page.tsx @@ -1,5 +1,5 @@ -import { PipelineDebugger } from "site/components/PipelineDebugger" -import type { InputProblem } from "lib/types/InputProblem" +import { PipelineDebugger } from "site/components/PipelineDebugger"; +import type { InputProblem } from "lib/types/InputProblem"; export const inputProblem: InputProblem = { chips: [ @@ -92,6 +92,6 @@ export const inputProblem: InputProblem = { GND: ["y-"], }, maxMspPairDistance: 5, -} +}; -export default () => +export default () => ; diff --git a/site/examples/example13.page.tsx b/site/examples/example13.page.tsx index 581198f7..cc626306 100644 --- a/site/examples/example13.page.tsx +++ b/site/examples/example13.page.tsx @@ -1,5 +1,5 @@ -import { PipelineDebugger } from "site/components/PipelineDebugger" -import type { InputProblem } from "lib/types/InputProblem" +import { PipelineDebugger } from "site/components/PipelineDebugger"; +import type { InputProblem } from "lib/types/InputProblem"; export const inputProblem: InputProblem = { chips: [ @@ -214,6 +214,6 @@ export const inputProblem: InputProblem = { v3_3: ["y+"], }, maxMspPairDistance: 5, -} +}; -export default () => +export default () => ; diff --git a/site/examples/example14.page.tsx b/site/examples/example14.page.tsx index f09de3ce..284157b0 100644 --- a/site/examples/example14.page.tsx +++ b/site/examples/example14.page.tsx @@ -1,5 +1,5 @@ -import { PipelineDebugger } from "site/components/PipelineDebugger" -import type { InputProblem } from "lib/types/InputProblem" +import { PipelineDebugger } from "site/components/PipelineDebugger"; +import type { InputProblem } from "lib/types/InputProblem"; export const inputProblem: InputProblem = { chips: [ @@ -205,6 +205,6 @@ export const inputProblem: InputProblem = { GND: ["y-"], }, maxMspPairDistance: 2.4, -} +}; -export default () => +export default () => ; diff --git a/site/examples/example15-rp2040-caps.page.tsx b/site/examples/example15-rp2040-caps.page.tsx index add3b766..8121a1aa 100644 --- a/site/examples/example15-rp2040-caps.page.tsx +++ b/site/examples/example15-rp2040-caps.page.tsx @@ -1,5 +1,5 @@ -import { PipelineDebugger } from "site/components/PipelineDebugger" -import type { InputProblem } from "lib/types/InputProblem" +import { PipelineDebugger } from "site/components/PipelineDebugger"; +import type { InputProblem } from "lib/types/InputProblem"; const inputProblem: InputProblem = { chips: [ @@ -618,6 +618,6 @@ const inputProblem: InputProblem = { GND: ["y-"], }, maxMspPairDistance: 2.4, -} +}; -export default () => +export default () => ; diff --git a/site/examples/example16-core-repro51.page.tsx b/site/examples/example16-core-repro51.page.tsx index 1cc10e2e..8748947d 100644 --- a/site/examples/example16-core-repro51.page.tsx +++ b/site/examples/example16-core-repro51.page.tsx @@ -1,5 +1,5 @@ -import { PipelineDebugger } from "site/components/PipelineDebugger" -import type { InputProblem } from "lib/types/InputProblem" +import { PipelineDebugger } from "site/components/PipelineDebugger"; +import type { InputProblem } from "lib/types/InputProblem"; const inputProblem: InputProblem = { chips: [ @@ -102,6 +102,6 @@ const inputProblem: InputProblem = { GND: ["y-"], }, maxMspPairDistance: 2.4, -} +}; -export default () => +export default () => ; diff --git a/site/examples/example17-straight-line-trace.page.tsx b/site/examples/example17-straight-line-trace.page.tsx index cf6adaf6..da5f61d2 100644 --- a/site/examples/example17-straight-line-trace.page.tsx +++ b/site/examples/example17-straight-line-trace.page.tsx @@ -1,5 +1,5 @@ -import { PipelineDebugger } from "site/components/PipelineDebugger" -import type { InputProblem } from "lib/types/InputProblem" +import { PipelineDebugger } from "site/components/PipelineDebugger"; +import type { InputProblem } from "lib/types/InputProblem"; const inputProblem: InputProblem = { chips: [ @@ -160,6 +160,6 @@ const inputProblem: InputProblem = { netConnections: [], availableNetLabelOrientations: {}, maxMspPairDistance: 2.4, -} +}; -export default () => +export default () => ; diff --git a/site/examples/example18.page.tsx b/site/examples/example18.page.tsx index 95adb9ee..07557d4f 100644 --- a/site/examples/example18.page.tsx +++ b/site/examples/example18.page.tsx @@ -1,5 +1,5 @@ -import { PipelineDebugger } from "site/components/PipelineDebugger" -import type { InputProblem } from "lib/types/InputProblem" +import { PipelineDebugger } from "site/components/PipelineDebugger"; +import type { InputProblem } from "lib/types/InputProblem"; const inputProblem: InputProblem = { chips: [ @@ -176,6 +176,6 @@ const inputProblem: InputProblem = { GND: ["y-"], }, maxMspPairDistance: 5, -} +}; -export default () => +export default () => ; diff --git a/site/examples/example19.page.tsx b/site/examples/example19.page.tsx index 2ef6d7f5..f6b83217 100644 --- a/site/examples/example19.page.tsx +++ b/site/examples/example19.page.tsx @@ -1,5 +1,5 @@ -import { PipelineDebugger } from "site/components/PipelineDebugger" -import type { InputProblem } from "lib/types/InputProblem" +import { PipelineDebugger } from "site/components/PipelineDebugger"; +import type { InputProblem } from "lib/types/InputProblem"; const inputProblem: InputProblem = { chips: [ @@ -164,6 +164,6 @@ const inputProblem: InputProblem = { ], availableNetLabelOrientations: {}, maxMspPairDistance: 5, -} +}; -export default () => +export default () => ; diff --git a/site/examples/example20.page.tsx b/site/examples/example20.page.tsx index ab9eaff3..1158c990 100644 --- a/site/examples/example20.page.tsx +++ b/site/examples/example20.page.tsx @@ -1,5 +1,5 @@ -import { PipelineDebugger } from "site/components/PipelineDebugger" -import type { InputProblem } from "lib/types/InputProblem" +import { PipelineDebugger } from "site/components/PipelineDebugger"; +import type { InputProblem } from "lib/types/InputProblem"; const inputProblem: InputProblem = { chips: [ @@ -98,6 +98,6 @@ const inputProblem: InputProblem = { GND: ["y-"], }, maxMspPairDistance: 2.4, -} +}; -export default () => +export default () => ; diff --git a/site/examples/example21.page.tsx b/site/examples/example21.page.tsx index d6cc2f35..7b619826 100644 --- a/site/examples/example21.page.tsx +++ b/site/examples/example21.page.tsx @@ -1,5 +1,5 @@ -import { PipelineDebugger } from "site/components/PipelineDebugger" -import type { InputProblem } from "lib/types/InputProblem" +import { PipelineDebugger } from "site/components/PipelineDebugger"; +import type { InputProblem } from "lib/types/InputProblem"; const inputProblem: InputProblem = { chips: [ @@ -172,6 +172,6 @@ const inputProblem: InputProblem = { LDO_EN: ["x-", "x+"], }, maxMspPairDistance: 2.4, -} as InputProblem +} as InputProblem; -export default () => +export default () => ; diff --git a/site/examples/example22.page.tsx b/site/examples/example22.page.tsx index 6368f4cc..6af364ef 100644 --- a/site/examples/example22.page.tsx +++ b/site/examples/example22.page.tsx @@ -1,5 +1,5 @@ -import { PipelineDebugger } from "site/components/PipelineDebugger" -import type { InputProblem } from "lib/types/InputProblem" +import { PipelineDebugger } from "site/components/PipelineDebugger"; +import type { InputProblem } from "lib/types/InputProblem"; const inputProblem: InputProblem = { chips: [ @@ -103,6 +103,6 @@ const inputProblem: InputProblem = { MMM: ["x+", "x-"], }, maxMspPairDistance: 2.4, -} +}; -export default () => +export default () => ; diff --git a/site/examples/example23.page.tsx b/site/examples/example23.page.tsx index 3519fd44..e8abd6db 100644 --- a/site/examples/example23.page.tsx +++ b/site/examples/example23.page.tsx @@ -1,5 +1,5 @@ -import { PipelineDebugger } from "site/components/PipelineDebugger" -import type { InputProblem } from "lib/types/InputProblem" +import { PipelineDebugger } from "site/components/PipelineDebugger"; +import type { InputProblem } from "lib/types/InputProblem"; const inputProblem: InputProblem = { chips: [ @@ -132,6 +132,6 @@ const inputProblem: InputProblem = { ], availableNetLabelOrientations: {}, maxMspPairDistance: 6.5, -} +}; -export default () => +export default () => ; diff --git a/site/examples/example24.page.tsx b/site/examples/example24.page.tsx index bf6bc507..62b87930 100644 --- a/site/examples/example24.page.tsx +++ b/site/examples/example24.page.tsx @@ -1,5 +1,5 @@ -import { PipelineDebugger } from "site/components/PipelineDebugger" -import type { InputProblem } from "lib/types/InputProblem" +import { PipelineDebugger } from "site/components/PipelineDebugger"; +import type { InputProblem } from "lib/types/InputProblem"; const inputProblem: InputProblem = { chips: [ @@ -123,6 +123,6 @@ const inputProblem: InputProblem = { ], availableNetLabelOrientations: {}, maxMspPairDistance: 6.5, -} +}; -export default () => +export default () => ; diff --git a/site/examples/example25.page.tsx b/site/examples/example25.page.tsx index 17e2b581..b4e9a38c 100644 --- a/site/examples/example25.page.tsx +++ b/site/examples/example25.page.tsx @@ -1,4 +1,4 @@ -import { PipelineDebugger } from "site/components/PipelineDebugger" -import inputProblem from "../../tests/assets/example25.json" +import { PipelineDebugger } from "site/components/PipelineDebugger"; +import inputProblem from "../../tests/assets/example25.json"; -export default () => +export default () => ; diff --git a/site/examples/example26.page.tsx b/site/examples/example26.page.tsx index 705a6743..3f09ed7b 100644 --- a/site/examples/example26.page.tsx +++ b/site/examples/example26.page.tsx @@ -1,4 +1,4 @@ -import { PipelineDebugger } from "site/components/PipelineDebugger" -import inputProblem from "../../tests/assets/example26.json" +import { PipelineDebugger } from "site/components/PipelineDebugger"; +import inputProblem from "../../tests/assets/example26.json"; -export default () => +export default () => ; diff --git a/site/examples/example27.page.tsx b/site/examples/example27.page.tsx index 8aa09273..31e3be09 100644 --- a/site/examples/example27.page.tsx +++ b/site/examples/example27.page.tsx @@ -1,4 +1,4 @@ -import { PipelineDebugger } from "site/components/PipelineDebugger" -import inputProblem from "../../tests/assets/example27.json" +import { PipelineDebugger } from "site/components/PipelineDebugger"; +import inputProblem from "../../tests/assets/example27.json"; -export default () => +export default () => ; diff --git a/tests/examples/example01.test.ts b/tests/examples/example01.test.ts index af3e1c53..13163b25 100644 --- a/tests/examples/example01.test.ts +++ b/tests/examples/example01.test.ts @@ -1,12 +1,12 @@ -import { test, expect } from "bun:test" -import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver" -import { inputProblem } from "site/examples/example01-basic.page" -import "tests/fixtures/matcher" +import { test, expect } from "bun:test"; +import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"; +import { inputProblem } from "site/examples/example01-basic.page"; +import "tests/fixtures/matcher"; test("example01", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) + const solver = new SchematicTracePipelineSolver(inputProblem); - solver.solve() + solver.solve(); - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example02.test.ts b/tests/examples/example02.test.ts index 1dfc634e..a1f56e1e 100644 --- a/tests/examples/example02.test.ts +++ b/tests/examples/example02.test.ts @@ -1,12 +1,12 @@ -import { test, expect } from "bun:test" -import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver" -import { inputProblem } from "site/examples/example02.page" -import "tests/fixtures/matcher" +import { test, expect } from "bun:test"; +import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"; +import { inputProblem } from "site/examples/example02.page"; +import "tests/fixtures/matcher"; test("example02", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) + const solver = new SchematicTracePipelineSolver(inputProblem); - solver.solve() + solver.solve(); - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example03.test.ts b/tests/examples/example03.test.ts index 8ba62771..34dfcb77 100644 --- a/tests/examples/example03.test.ts +++ b/tests/examples/example03.test.ts @@ -1,12 +1,12 @@ -import { test, expect } from "bun:test" -import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver" -import { inputProblem } from "site/examples/example03.page" -import "tests/fixtures/matcher" +import { test, expect } from "bun:test"; +import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"; +import { inputProblem } from "site/examples/example03.page"; +import "tests/fixtures/matcher"; test("example03", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) + const solver = new SchematicTracePipelineSolver(inputProblem); - solver.solve() + solver.solve(); - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example04.test.ts b/tests/examples/example04.test.ts index 497bd0be..90ca5d04 100644 --- a/tests/examples/example04.test.ts +++ b/tests/examples/example04.test.ts @@ -1,12 +1,12 @@ -import { test, expect } from "bun:test" -import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver" -import { inputProblem } from "site/examples/example04-single-symbol.page" -import "tests/fixtures/matcher" +import { test, expect } from "bun:test"; +import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"; +import { inputProblem } from "site/examples/example04-single-symbol.page"; +import "tests/fixtures/matcher"; test("example04", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) + const solver = new SchematicTracePipelineSolver(inputProblem); - solver.solve() + solver.solve(); - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example05.test.ts b/tests/examples/example05.test.ts index 97d87e58..7e75fda0 100644 --- a/tests/examples/example05.test.ts +++ b/tests/examples/example05.test.ts @@ -1,12 +1,12 @@ -import { test, expect } from "bun:test" -import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver" -import { inputProblem } from "site/examples/example05.page" -import "tests/fixtures/matcher" +import { test, expect } from "bun:test"; +import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"; +import { inputProblem } from "site/examples/example05.page"; +import "tests/fixtures/matcher"; test("example05", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) + const solver = new SchematicTracePipelineSolver(inputProblem); - solver.solve() + solver.solve(); - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example06.test.ts b/tests/examples/example06.test.ts index af152dff..6905bb8a 100644 --- a/tests/examples/example06.test.ts +++ b/tests/examples/example06.test.ts @@ -1,12 +1,12 @@ -import { test, expect } from "bun:test" -import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver" -import { inputProblem } from "site/examples/example06.page" -import "tests/fixtures/matcher" +import { test, expect } from "bun:test"; +import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"; +import { inputProblem } from "site/examples/example06.page"; +import "tests/fixtures/matcher"; test("example06", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) + const solver = new SchematicTracePipelineSolver(inputProblem); - solver.solve() + solver.solve(); - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example07.test.ts b/tests/examples/example07.test.ts index 75867038..7c276365 100644 --- a/tests/examples/example07.test.ts +++ b/tests/examples/example07.test.ts @@ -1,12 +1,12 @@ -import { test, expect } from "bun:test" -import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver" -import { inputProblem } from "site/examples/example07.page" -import "tests/fixtures/matcher" +import { test, expect } from "bun:test"; +import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"; +import { inputProblem } from "site/examples/example07.page"; +import "tests/fixtures/matcher"; test("example07", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) + const solver = new SchematicTracePipelineSolver(inputProblem); - solver.solve() + solver.solve(); - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example08.test.ts b/tests/examples/example08.test.ts index 968e1b0c..d9e62b3e 100644 --- a/tests/examples/example08.test.ts +++ b/tests/examples/example08.test.ts @@ -1,12 +1,12 @@ -import { test, expect } from "bun:test" -import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver" -import { inputProblem } from "site/examples/example08.page" -import "tests/fixtures/matcher" +import { test, expect } from "bun:test"; +import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"; +import { inputProblem } from "site/examples/example08.page"; +import "tests/fixtures/matcher"; test("example08", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) + const solver = new SchematicTracePipelineSolver(inputProblem); - solver.solve() + solver.solve(); - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example09.test.ts b/tests/examples/example09.test.ts index cf0a956c..e8ee8637 100644 --- a/tests/examples/example09.test.ts +++ b/tests/examples/example09.test.ts @@ -1,12 +1,12 @@ -import { test, expect } from "bun:test" -import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver" -import { inputProblem } from "site/examples/example09.page" -import "tests/fixtures/matcher" +import { test, expect } from "bun:test"; +import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"; +import { inputProblem } from "site/examples/example09.page"; +import "tests/fixtures/matcher"; test("example09", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) + const solver = new SchematicTracePipelineSolver(inputProblem); - solver.solve() + solver.solve(); - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example10.test.ts b/tests/examples/example10.test.ts index 8099ca55..de816c0d 100644 --- a/tests/examples/example10.test.ts +++ b/tests/examples/example10.test.ts @@ -1,12 +1,12 @@ -import { test, expect } from "bun:test" -import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver" -import { inputProblem } from "site/examples/example10.page" -import "tests/fixtures/matcher" +import { test, expect } from "bun:test"; +import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"; +import { inputProblem } from "site/examples/example10.page"; +import "tests/fixtures/matcher"; test("example10", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) + const solver = new SchematicTracePipelineSolver(inputProblem); - solver.solve() + solver.solve(); - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example11.test.tsx b/tests/examples/example11.test.tsx index 5496549a..d2325e7d 100644 --- a/tests/examples/example11.test.tsx +++ b/tests/examples/example11.test.tsx @@ -1,12 +1,12 @@ -import { test, expect } from "bun:test" -import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver" -import { inputProblem } from "site/examples/example11.page" -import "tests/fixtures/matcher" +import { test, expect } from "bun:test"; +import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"; +import { inputProblem } from "site/examples/example11.page"; +import "tests/fixtures/matcher"; test("example11", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) + const solver = new SchematicTracePipelineSolver(inputProblem); - solver.solve() + solver.solve(); - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example12.test.tsx b/tests/examples/example12.test.tsx index 3eac5127..22cb1e4f 100644 --- a/tests/examples/example12.test.tsx +++ b/tests/examples/example12.test.tsx @@ -1,12 +1,12 @@ -import { test, expect } from "bun:test" -import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver" -import { inputProblem } from "site/examples/example12.page" -import "tests/fixtures/matcher" +import { test, expect } from "bun:test"; +import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"; +import { inputProblem } from "site/examples/example12.page"; +import "tests/fixtures/matcher"; test("example12", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) + const solver = new SchematicTracePipelineSolver(inputProblem); - solver.solve() + solver.solve(); - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example13.test.tsx b/tests/examples/example13.test.tsx index dd31aa56..905101da 100644 --- a/tests/examples/example13.test.tsx +++ b/tests/examples/example13.test.tsx @@ -1,12 +1,12 @@ -import { test, expect } from "bun:test" -import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver" -import { inputProblem } from "site/examples/example13.page" -import "tests/fixtures/matcher" +import { test, expect } from "bun:test"; +import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"; +import { inputProblem } from "site/examples/example13.page"; +import "tests/fixtures/matcher"; test("example13", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) + const solver = new SchematicTracePipelineSolver(inputProblem); - solver.solve() + solver.solve(); - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example14.test.tsx b/tests/examples/example14.test.tsx index ba225769..4777fac8 100644 --- a/tests/examples/example14.test.tsx +++ b/tests/examples/example14.test.tsx @@ -1,11 +1,11 @@ -import { expect, test } from "bun:test" -import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver" -import { inputProblem } from "site/examples/example14.page" +import { expect, test } from "bun:test"; +import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"; +import { inputProblem } from "site/examples/example14.page"; test.skip("example14 - should fail with net label placement collision error", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) + const solver = new SchematicTracePipelineSolver(inputProblem); - solver.solve() + solver.solve(); - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example15.test.tsx b/tests/examples/example15.test.tsx index d69cefd8..92618f11 100644 --- a/tests/examples/example15.test.tsx +++ b/tests/examples/example15.test.tsx @@ -1,7 +1,7 @@ -import { expect } from "bun:test" -import { test } from "bun:test" -import { SchematicTracePipelineSolver, type InputProblem } from "lib/index" -import "tests/fixtures/matcher" +import { expect } from "bun:test"; +import { test } from "bun:test"; +import { SchematicTracePipelineSolver, type InputProblem } from "lib/index"; +import "tests/fixtures/matcher"; const inputProblem: InputProblem = { chips: [ @@ -620,10 +620,10 @@ const inputProblem: InputProblem = { GND: ["y-"], }, maxMspPairDistance: 2.4, -} +}; test("example15", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) - solver.solve() - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + const solver = new SchematicTracePipelineSolver(inputProblem); + solver.solve(); + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example16.test.tsx b/tests/examples/example16.test.tsx index 388c36a7..8bf0c261 100644 --- a/tests/examples/example16.test.tsx +++ b/tests/examples/example16.test.tsx @@ -1,7 +1,7 @@ -import { expect } from "bun:test" -import { test } from "bun:test" -import { SchematicTracePipelineSolver, type InputProblem } from "lib/index" -import "tests/fixtures/matcher" +import { expect } from "bun:test"; +import { test } from "bun:test"; +import { SchematicTracePipelineSolver, type InputProblem } from "lib/index"; +import "tests/fixtures/matcher"; const inputProblem: InputProblem = { chips: [ @@ -104,10 +104,10 @@ const inputProblem: InputProblem = { GND: ["y-"], }, maxMspPairDistance: 2.4, -} +}; test("example16", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) - solver.solve() - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + const solver = new SchematicTracePipelineSolver(inputProblem); + solver.solve(); + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example17.test.tsx b/tests/examples/example17.test.tsx index 2be0dd80..bb635128 100644 --- a/tests/examples/example17.test.tsx +++ b/tests/examples/example17.test.tsx @@ -1,7 +1,7 @@ -import { expect } from "bun:test" -import { test } from "bun:test" -import { SchematicTracePipelineSolver, type InputProblem } from "lib/index" -import "tests/fixtures/matcher" +import { expect } from "bun:test"; +import { test } from "bun:test"; +import { SchematicTracePipelineSolver, type InputProblem } from "lib/index"; +import "tests/fixtures/matcher"; const inputProblem: InputProblem = { chips: [ @@ -162,10 +162,10 @@ const inputProblem: InputProblem = { netConnections: [], availableNetLabelOrientations: {}, maxMspPairDistance: 2.4, -} +}; test("example17", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) - solver.solve() - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + const solver = new SchematicTracePipelineSolver(inputProblem); + solver.solve(); + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example18.test.tsx b/tests/examples/example18.test.tsx index 7c14d5b6..bc8993ab 100644 --- a/tests/examples/example18.test.tsx +++ b/tests/examples/example18.test.tsx @@ -1,7 +1,7 @@ -import { expect } from "bun:test" -import { test } from "bun:test" -import { SchematicTracePipelineSolver, type InputProblem } from "lib/index" -import "tests/fixtures/matcher" +import { expect } from "bun:test"; +import { test } from "bun:test"; +import { SchematicTracePipelineSolver, type InputProblem } from "lib/index"; +import "tests/fixtures/matcher"; const inputProblem = { chips: [ @@ -178,10 +178,10 @@ const inputProblem = { GND: ["y-"], }, maxMspPairDistance: 5, -} as InputProblem +} as InputProblem; test("example18", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) - solver.solve() - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + const solver = new SchematicTracePipelineSolver(inputProblem); + solver.solve(); + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example19.test.tsx b/tests/examples/example19.test.tsx index acdd04c0..ebb622b7 100644 --- a/tests/examples/example19.test.tsx +++ b/tests/examples/example19.test.tsx @@ -1,7 +1,7 @@ -import { expect } from "bun:test" -import { test } from "bun:test" -import { SchematicTracePipelineSolver, type InputProblem } from "lib/index" -import "tests/fixtures/matcher" +import { expect } from "bun:test"; +import { test } from "bun:test"; +import { SchematicTracePipelineSolver, type InputProblem } from "lib/index"; +import "tests/fixtures/matcher"; const inputProblem = { chips: [ @@ -166,10 +166,10 @@ const inputProblem = { ], availableNetLabelOrientations: {}, maxMspPairDistance: 5, -} as InputProblem +} as InputProblem; test("example19", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) - solver.solve() - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + const solver = new SchematicTracePipelineSolver(inputProblem); + solver.solve(); + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example20.test.tsx b/tests/examples/example20.test.tsx index dcdc3c78..9c75c6de 100644 --- a/tests/examples/example20.test.tsx +++ b/tests/examples/example20.test.tsx @@ -1,7 +1,7 @@ -import { expect } from "bun:test" -import { test } from "bun:test" -import { SchematicTracePipelineSolver, type InputProblem } from "lib/index" -import "tests/fixtures/matcher" +import { expect } from "bun:test"; +import { test } from "bun:test"; +import { SchematicTracePipelineSolver, type InputProblem } from "lib/index"; +import "tests/fixtures/matcher"; const inputProblem = { chips: [ @@ -181,10 +181,10 @@ const inputProblem = { GND: ["y-"], }, maxMspPairDistance: 5, -} as InputProblem +} as InputProblem; test("example20", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) - solver.solve() - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + const solver = new SchematicTracePipelineSolver(inputProblem); + solver.solve(); + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example21.test.tsx b/tests/examples/example21.test.tsx index ea0c63c9..cede8c81 100644 --- a/tests/examples/example21.test.tsx +++ b/tests/examples/example21.test.tsx @@ -1,7 +1,7 @@ -import { expect } from "bun:test" -import { test } from "bun:test" -import { SchematicTracePipelineSolver, type InputProblem } from "lib/index" -import "tests/fixtures/matcher" +import { expect } from "bun:test"; +import { test } from "bun:test"; +import { SchematicTracePipelineSolver, type InputProblem } from "lib/index"; +import "tests/fixtures/matcher"; const inputProblem = { chips: [ @@ -174,10 +174,10 @@ const inputProblem = { LDO_EN: ["x-", "x+"], }, maxMspPairDistance: 2.4, -} as InputProblem +} as InputProblem; test("example21", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) - solver.solve() - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + const solver = new SchematicTracePipelineSolver(inputProblem); + solver.solve(); + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example22.test.tsx b/tests/examples/example22.test.tsx index 23ef434c..6ca31ce6 100644 --- a/tests/examples/example22.test.tsx +++ b/tests/examples/example22.test.tsx @@ -1,7 +1,7 @@ -import { expect } from "bun:test" -import { test } from "bun:test" -import { SchematicTracePipelineSolver, type InputProblem } from "lib/index" -import "tests/fixtures/matcher" +import { expect } from "bun:test"; +import { test } from "bun:test"; +import { SchematicTracePipelineSolver, type InputProblem } from "lib/index"; +import "tests/fixtures/matcher"; const inputProblem = { chips: [ @@ -100,10 +100,10 @@ const inputProblem = { GND: ["y-"], }, maxMspPairDistance: 2.4, -} as InputProblem +} as InputProblem; test("example22", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) - solver.solve() - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + const solver = new SchematicTracePipelineSolver(inputProblem); + solver.solve(); + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example23.test.tsx b/tests/examples/example23.test.tsx index 0b24d046..b208375d 100644 --- a/tests/examples/example23.test.tsx +++ b/tests/examples/example23.test.tsx @@ -1,7 +1,7 @@ -import { expect } from "bun:test" -import { test } from "bun:test" -import { SchematicTracePipelineSolver, type InputProblem } from "lib/index" -import "tests/fixtures/matcher" +import { expect } from "bun:test"; +import { test } from "bun:test"; +import { SchematicTracePipelineSolver, type InputProblem } from "lib/index"; +import "tests/fixtures/matcher"; const inputProblem = { chips: [ @@ -100,10 +100,10 @@ const inputProblem = { GND: ["y-"], // Changed to y- since trace now goes downward }, maxMspPairDistance: 2.4, -} as InputProblem +} as InputProblem; test("example23", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) - solver.solve() - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + const solver = new SchematicTracePipelineSolver(inputProblem); + solver.solve(); + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example24.test.tsx b/tests/examples/example24.test.tsx index 08bd4fa5..1e28ac30 100644 --- a/tests/examples/example24.test.tsx +++ b/tests/examples/example24.test.tsx @@ -1,7 +1,7 @@ -import { expect } from "bun:test" -import { test } from "bun:test" -import { SchematicTracePipelineSolver, type InputProblem } from "lib/index" -import "tests/fixtures/matcher" +import { expect } from "bun:test"; +import { test } from "bun:test"; +import { SchematicTracePipelineSolver, type InputProblem } from "lib/index"; +import "tests/fixtures/matcher"; const inputProblem = { chips: [ @@ -105,10 +105,10 @@ const inputProblem = { MMM: ["x+", "x-"], }, maxMspPairDistance: 2.4, -} as InputProblem +} as InputProblem; test("example24", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) - solver.solve() - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + const solver = new SchematicTracePipelineSolver(inputProblem); + solver.solve(); + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example25.test.tsx b/tests/examples/example25.test.tsx index 60fa71e9..a986da80 100644 --- a/tests/examples/example25.test.tsx +++ b/tests/examples/example25.test.tsx @@ -1,7 +1,7 @@ -import { expect } from "bun:test" -import { test } from "bun:test" -import { SchematicTracePipelineSolver, type InputProblem } from "lib/index" -import "tests/fixtures/matcher" +import { expect } from "bun:test"; +import { test } from "bun:test"; +import { SchematicTracePipelineSolver, type InputProblem } from "lib/index"; +import "tests/fixtures/matcher"; const inputProblem = { chips: [ @@ -134,10 +134,10 @@ const inputProblem = { ], availableNetLabelOrientations: {}, maxMspPairDistance: 6.5, -} as InputProblem +} as InputProblem; test("example25", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) - solver.solve() - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + const solver = new SchematicTracePipelineSolver(inputProblem); + solver.solve(); + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example26.test.tsx b/tests/examples/example26.test.tsx index d66dc394..fa26bb58 100644 --- a/tests/examples/example26.test.tsx +++ b/tests/examples/example26.test.tsx @@ -1,7 +1,7 @@ -import { expect } from "bun:test" -import { test } from "bun:test" -import { SchematicTracePipelineSolver, type InputProblem } from "lib/index" -import "tests/fixtures/matcher" +import { expect } from "bun:test"; +import { test } from "bun:test"; +import { SchematicTracePipelineSolver, type InputProblem } from "lib/index"; +import "tests/fixtures/matcher"; const inputProblem = { chips: [ @@ -125,10 +125,10 @@ const inputProblem = { ], availableNetLabelOrientations: {}, maxMspPairDistance: 6.5, -} as InputProblem +} as InputProblem; test("example26", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) - solver.solve() - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + const solver = new SchematicTracePipelineSolver(inputProblem); + solver.solve(); + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example27.test.ts b/tests/examples/example27.test.ts index d9189ff9..ede06362 100644 --- a/tests/examples/example27.test.ts +++ b/tests/examples/example27.test.ts @@ -1,7 +1,7 @@ -import { test, expect } from "bun:test" -import type { InputProblem } from "lib/index" -import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver" -import "tests/fixtures/matcher" +import { test, expect } from "bun:test"; +import type { InputProblem } from "lib/index"; +import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"; +import "tests/fixtures/matcher"; const inputProblem: InputProblem = { chips: [ @@ -230,12 +230,12 @@ const inputProblem: InputProblem = { GND: ["y-"], }, maxMspPairDistance: 5, -} +}; test("example01", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) + const solver = new SchematicTracePipelineSolver(inputProblem); - solver.solve() + solver.solve(); - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example28.test.ts b/tests/examples/example28.test.ts index 6fdb0c06..b3cfd4f0 100644 --- a/tests/examples/example28.test.ts +++ b/tests/examples/example28.test.ts @@ -1,13 +1,13 @@ -import { test, expect } from "bun:test" -import type { InputProblem } from "lib/index" -import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver" -import "tests/fixtures/matcher" -import inputProblem from "../../tests/assets/example25.json" +import { test, expect } from "bun:test"; +import type { InputProblem } from "lib/index"; +import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"; +import "tests/fixtures/matcher"; +import inputProblem from "../../tests/assets/example25.json"; test("example28", () => { - const solver = new SchematicTracePipelineSolver(inputProblem as any) + const solver = new SchematicTracePipelineSolver(inputProblem as any); - solver.solve() + solver.solve(); - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example29.test.ts b/tests/examples/example29.test.ts index e32dfc98..a1711b96 100644 --- a/tests/examples/example29.test.ts +++ b/tests/examples/example29.test.ts @@ -1,13 +1,13 @@ -import { test, expect } from "bun:test" -import type { InputProblem } from "lib/index" -import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver" -import "tests/fixtures/matcher" -import inputProblem from "../assets/example26.json" +import { test, expect } from "bun:test"; +import type { InputProblem } from "lib/index"; +import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"; +import "tests/fixtures/matcher"; +import inputProblem from "../assets/example26.json"; test("example29", () => { - const solver = new SchematicTracePipelineSolver(inputProblem as any) + const solver = new SchematicTracePipelineSolver(inputProblem as any); - solver.solve() + solver.solve(); - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/examples/example30.test.ts b/tests/examples/example30.test.ts index bbc63299..8d7c7530 100644 --- a/tests/examples/example30.test.ts +++ b/tests/examples/example30.test.ts @@ -1,12 +1,12 @@ -import { test, expect } from "bun:test" -import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver" -import "tests/fixtures/matcher" -import inputProblem from "../assets/example27.json" +import { test, expect } from "bun:test"; +import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"; +import "tests/fixtures/matcher"; +import inputProblem from "../assets/example27.json"; test("example29", () => { - const solver = new SchematicTracePipelineSolver(inputProblem as any) + const solver = new SchematicTracePipelineSolver(inputProblem as any); - solver.solve() + solver.solve(); - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/fixtures/matcher.ts b/tests/fixtures/matcher.ts index 3c05e4ef..9a636136 100644 --- a/tests/fixtures/matcher.ts +++ b/tests/fixtures/matcher.ts @@ -1,6 +1,6 @@ -import { getSvgFromGraphicsObject, type GraphicsObject } from "graphics-debug" -import { expect, type MatcherResult } from "bun:test" -import type { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver" +import { getSvgFromGraphicsObject, type GraphicsObject } from "graphics-debug"; +import { expect, type MatcherResult } from "bun:test"; +import type { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"; const getAllElms = (graphicsObject: GraphicsObject) => { return [ @@ -9,8 +9,8 @@ const getAllElms = (graphicsObject: GraphicsObject) => { ...(graphicsObject.rects ?? []), ...(graphicsObject.circles ?? []), ...(graphicsObject.texts ?? []), - ] -} + ]; +}; async function toMatchSolverSnapshot( this: any, @@ -18,47 +18,47 @@ async function toMatchSolverSnapshot( testPathOriginal: string, svgName?: string, ): Promise { - const graphicsObject = received.visualize() + const graphicsObject = received.visualize(); - const allElms = getAllElms(graphicsObject) + const allElms = getAllElms(graphicsObject); const lastStep = allElms.reduce((acc, elm) => { - return Math.max(acc, elm.step ?? 0) - }, 0) + return Math.max(acc, elm.step ?? 0); + }, 0); if (lastStep !== 0) { graphicsObject.points = graphicsObject.points?.filter( (p) => p.step === lastStep, - ) + ); graphicsObject.lines = graphicsObject.lines?.filter( (l) => l.step === lastStep, - ) + ); graphicsObject.rects = graphicsObject.rects?.filter( (r) => r.step === lastStep, - ) + ); graphicsObject.circles = graphicsObject.circles?.filter( (c) => c.step === lastStep, - ) + ); graphicsObject.texts = graphicsObject.texts?.filter( (t) => t.step === lastStep, - ) + ); } const svg = getSvgFromGraphicsObject(graphicsObject, { backgroundColor: "white", - }) + }); - return expect(svg).toMatchSvgSnapshot(testPathOriginal, svgName) + return expect(svg).toMatchSvgSnapshot(testPathOriginal, svgName); } expect.extend({ toMatchSolverSnapshot: toMatchSolverSnapshot as any, -}) +}); declare module "bun:test" { interface Matchers { toMatchSolverSnapshot( testPath: string, svgName?: string, - ): Promise + ): Promise; } } diff --git a/tests/fixtures/preload.ts b/tests/fixtures/preload.ts index b48457d6..908cdf8e 100644 --- a/tests/fixtures/preload.ts +++ b/tests/fixtures/preload.ts @@ -1,2 +1,2 @@ -import "bun-match-svg" -import "tests/fixtures/matcher" +import "bun-match-svg"; +import "tests/fixtures/matcher"; diff --git a/tests/functions/generateElbowVariants.test.ts b/tests/functions/generateElbowVariants.test.ts index 3afc3de9..75f56fad 100644 --- a/tests/functions/generateElbowVariants.test.ts +++ b/tests/functions/generateElbowVariants.test.ts @@ -1,7 +1,7 @@ -import { generateElbowVariants } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/generateElbowVariants" -import { test, expect } from "bun:test" -import type { Guideline } from "lib/solvers/GuidelinesSolver/GuidelinesSolver" -import type { Point } from "@tscircuit/math-utils" +import { generateElbowVariants } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/generateElbowVariants"; +import { test, expect } from "bun:test"; +import type { Guideline } from "lib/solvers/GuidelinesSolver/GuidelinesSolver"; +import type { Point } from "@tscircuit/math-utils"; test.skip("generateElbowVariants - simple horizontal segment", () => { const baseElbow: Point[] = [ @@ -9,40 +9,40 @@ test.skip("generateElbowVariants - simple horizontal segment", () => { { x: 1, y: 0 }, { x: 1, y: 2 }, { x: 3, y: 2 }, - ] + ]; const guidelines: Guideline[] = [ { orientation: "vertical", x: 0.5, y: undefined }, { orientation: "vertical", x: 1.5, y: undefined }, { orientation: "horizontal", x: undefined, y: 1 }, - ] + ]; - const result = generateElbowVariants({ baseElbow, guidelines }) + const result = generateElbowVariants({ baseElbow, guidelines }); - expect(result.movableSegments).toHaveLength(2) - expect(result.movableSegments[0].freedom).toBe("y+") - expect(result.movableSegments[1].freedom).toBe("x+") + expect(result.movableSegments).toHaveLength(2); + expect(result.movableSegments[0].freedom).toBe("y+"); + expect(result.movableSegments[1].freedom).toBe("x+"); // Should generate variants for each combination of guideline positions - expect(result.elbowVariants.length).toBeGreaterThan(1) -}) + expect(result.elbowVariants.length).toBeGreaterThan(1); +}); test("generateElbowVariants - no movable segments", () => { const baseElbow: Point[] = [ { x: 0, y: 0 }, { x: 1, y: 0 }, - ] + ]; const guidelines: Guideline[] = [ { orientation: "vertical", x: 0.5, y: undefined }, - ] + ]; - const result = generateElbowVariants({ baseElbow, guidelines }) + const result = generateElbowVariants({ baseElbow, guidelines }); - expect(result.movableSegments).toHaveLength(0) - expect(result.elbowVariants).toHaveLength(1) - expect(result.elbowVariants[0]).toEqual(baseElbow) -}) + expect(result.movableSegments).toHaveLength(0); + expect(result.elbowVariants).toHaveLength(1); + expect(result.elbowVariants[0]).toEqual(baseElbow); +}); test.skip("generateElbowVariants - vertical movable segment", () => { const baseElbow: Point[] = [ @@ -50,21 +50,21 @@ test.skip("generateElbowVariants - vertical movable segment", () => { { x: 0, y: 1 }, { x: 0, y: 2 }, { x: 2, y: 2 }, - ] + ]; const guidelines: Guideline[] = [ { orientation: "horizontal", x: undefined, y: 0.5 }, { orientation: "horizontal", x: undefined, y: 1.5 }, - ] + ]; - const result = generateElbowVariants({ baseElbow, guidelines }) + const result = generateElbowVariants({ baseElbow, guidelines }); - expect(result.movableSegments).toHaveLength(1) - expect(result.movableSegments[0].freedom).toBe("x+") + expect(result.movableSegments).toHaveLength(1); + expect(result.movableSegments[0].freedom).toBe("x+"); // Should include original position plus guideline positions - expect(result.elbowVariants.length).toBe(3) // Original + 2 guidelines -}) + expect(result.elbowVariants.length).toBe(3); // Original + 2 guidelines +}); test.skip("generateElbowVariants - multiple segments with guidelines", () => { const baseElbow: Point[] = [ @@ -73,26 +73,26 @@ test.skip("generateElbowVariants - multiple segments with guidelines", () => { { x: 1, y: 1 }, { x: 2, y: 1 }, { x: 2, y: 2 }, - ] + ]; const guidelines: Guideline[] = [ { orientation: "vertical", x: 0.5, y: undefined }, { orientation: "vertical", x: 1.5, y: undefined }, { orientation: "horizontal", x: undefined, y: 0.5 }, { orientation: "horizontal", x: undefined, y: 1.5 }, - ] + ]; - const result = generateElbowVariants({ baseElbow, guidelines }) + const result = generateElbowVariants({ baseElbow, guidelines }); - expect(result.movableSegments).toHaveLength(3) + expect(result.movableSegments).toHaveLength(3); // First segment moves vertically (y+/y-) - expect(result.movableSegments[0].freedom).toMatch(/^y[+-]$/) + expect(result.movableSegments[0].freedom).toMatch(/^y[+-]$/); // Second segment moves horizontally (x+/x-) - expect(result.movableSegments[1].freedom).toMatch(/^x[+-]$/) + expect(result.movableSegments[1].freedom).toMatch(/^x[+-]$/); // Third segment moves vertically (y+/y-) - expect(result.movableSegments[2].freedom).toMatch(/^y[+-]$/) + expect(result.movableSegments[2].freedom).toMatch(/^y[+-]$/); // Should generate multiple variants - expect(result.elbowVariants.length).toBeGreaterThan(1) -}) + expect(result.elbowVariants.length).toBeGreaterThan(1); +}); diff --git a/tests/functions/getOrthogonalMinimumSpanningTree.test.ts b/tests/functions/getOrthogonalMinimumSpanningTree.test.ts index 2bfef23e..235a833c 100644 --- a/tests/functions/getOrthogonalMinimumSpanningTree.test.ts +++ b/tests/functions/getOrthogonalMinimumSpanningTree.test.ts @@ -1,6 +1,6 @@ -import { getOrthogonalMinimumSpanningTree } from "lib/solvers/MspConnectionPairSolver/getMspConnectionPairsFromPins" -import { test, expect } from "bun:test" -import type { InputPin } from "lib/types/InputProblem" +import { getOrthogonalMinimumSpanningTree } from "lib/solvers/MspConnectionPairSolver/getMspConnectionPairsFromPins"; +import { test, expect } from "bun:test"; +import type { InputPin } from "lib/types/InputProblem"; test("getOrthogonalMinimumSpanningTree", () => { const pins: InputPin[] = [ @@ -8,8 +8,8 @@ test("getOrthogonalMinimumSpanningTree", () => { { x: 1, y: 1, pinId: "B" }, { x: 10, y: 10, pinId: "C" }, { x: 11, y: 11, pinId: "D" }, - ] - const msp = getOrthogonalMinimumSpanningTree(pins) + ]; + const msp = getOrthogonalMinimumSpanningTree(pins); expect( msp .map(([a, b]) => `${a}->${b}`) @@ -19,5 +19,5 @@ test("getOrthogonalMinimumSpanningTree", () => { "B->A C->B D->C" - `) -}) + `); +}); diff --git a/tests/solvers/MspConnectionPairSolver/MspConnectionPairSolver_repro1.test.ts b/tests/solvers/MspConnectionPairSolver/MspConnectionPairSolver_repro1.test.ts index ad0356bb..c71415ee 100644 --- a/tests/solvers/MspConnectionPairSolver/MspConnectionPairSolver_repro1.test.ts +++ b/tests/solvers/MspConnectionPairSolver/MspConnectionPairSolver_repro1.test.ts @@ -1,14 +1,14 @@ -import inputParams from "site/MspConnectionPairSolver/MspConnectionPairSolver01_params.json" -import { test, expect } from "bun:test" -import { MspConnectionPairSolver } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver" -import type { InputProblem } from "lib/types/InputProblem" +import inputParams from "site/MspConnectionPairSolver/MspConnectionPairSolver01_params.json"; +import { test, expect } from "bun:test"; +import { MspConnectionPairSolver } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver"; +import type { InputProblem } from "lib/types/InputProblem"; test("MspConnectionPairSolver_repro1", () => { const solver = new MspConnectionPairSolver({ inputProblem: inputParams.inputProblem as unknown as InputProblem, - }) + }); - solver.solve() + solver.solve(); - expect(solver.mspConnectionPairs.length).toBe(4) -}) + expect(solver.mspConnectionPairs.length).toBe(4); +}); diff --git a/tests/solvers/MspConnectionPairSolver/MspConnectionPairSolver_repro2.test.ts b/tests/solvers/MspConnectionPairSolver/MspConnectionPairSolver_repro2.test.ts index 92b9d977..c279dee6 100644 --- a/tests/solvers/MspConnectionPairSolver/MspConnectionPairSolver_repro2.test.ts +++ b/tests/solvers/MspConnectionPairSolver/MspConnectionPairSolver_repro2.test.ts @@ -1,5 +1,5 @@ -import { MspConnectionPairSolver } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver" -import { test, expect } from "bun:test" +import { MspConnectionPairSolver } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver"; +import { test, expect } from "bun:test"; test("MspConnectionPairSolver should solve problem correctly", () => { const input = { @@ -76,17 +76,17 @@ test("MspConnectionPairSolver should solve problem correctly", () => { availableNetLabelOrientations: {}, maxMspPairDistance: 2, }, - } + }; - const solver = new MspConnectionPairSolver(input as any) - solver.solve() + const solver = new MspConnectionPairSolver(input as any); + solver.solve(); for (const { pins } of solver.mspConnectionPairs) { - const [pin1, pin2] = pins - const dist = Math.sqrt((pin1.x - pin2.x) ** 2 + (pin1.y - pin2.y) ** 2) - expect(dist).toBeLessThan(input.inputProblem.maxMspPairDistance) + const [pin1, pin2] = pins; + const dist = Math.sqrt((pin1.x - pin2.x) ** 2 + (pin1.y - pin2.y) ** 2); + expect(dist).toBeLessThan(input.inputProblem.maxMspPairDistance); } // Add more specific assertions based on expected output // expect(solver.netLabelPlacementSolver!.netLabelPlacements).toMatchInlineSnapshot() -}) +}); diff --git a/tests/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver01.test.ts b/tests/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver01.test.ts index b74c087e..35ac81b7 100644 --- a/tests/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver01.test.ts +++ b/tests/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver01.test.ts @@ -1,12 +1,12 @@ -import { expect, test } from "bun:test" -import { SingleNetLabelPlacementSolver } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver" -import { input } from "site/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver01.page" +import { expect, test } from "bun:test"; +import { SingleNetLabelPlacementSolver } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver"; +import { input } from "site/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver01.page"; test("SingleNetLabelPlacementSolver01 issue reproduction (should pass)", () => { - const solver = new SingleNetLabelPlacementSolver(input as any) + const solver = new SingleNetLabelPlacementSolver(input as any); - solver.solve() + solver.solve(); // TODO: Fix the test - expect(solver.solved).toBe(true) -}) + expect(solver.solved).toBe(true); +}); diff --git a/tests/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver_repro01.test.ts b/tests/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver_repro01.test.ts index c34a93dd..da5190f9 100644 --- a/tests/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver_repro01.test.ts +++ b/tests/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver_repro01.test.ts @@ -1,6 +1,6 @@ -import type { InputProblem } from "lib/types/InputProblem" -import { test, expect } from "bun:test" -import { SchematicTracePipelineSolver } from "lib/index" +import type { InputProblem } from "lib/types/InputProblem"; +import { test, expect } from "bun:test"; +import { SchematicTracePipelineSolver } from "lib/index"; const inputProblem: InputProblem = { chips: [ @@ -101,11 +101,11 @@ const inputProblem: InputProblem = { GND: ["y-"], }, maxMspPairDistance: 2, -} +}; test("SchematicTracePipelineSolver_repro01", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) - solver.solve() + const solver = new SchematicTracePipelineSolver(inputProblem); + solver.solve(); // console.log(solver.schematicTraceLinesSolver!.solvedTracePaths) -}) +}); diff --git a/tests/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver_repro02.test.ts b/tests/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver_repro02.test.ts index 4eb733b3..d84449a4 100644 --- a/tests/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver_repro02.test.ts +++ b/tests/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver_repro02.test.ts @@ -1,5 +1,5 @@ -import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver" -import { test, expect } from "bun:test" +import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"; +import { test, expect } from "bun:test"; test("SchematicTracePipelineSolver should solve problem correctly", () => { const input = { @@ -131,10 +131,10 @@ test("SchematicTracePipelineSolver should solve problem correctly", () => { GND: ["y-"], }, maxMspPairDistance: 2, - } + }; - const solver = new SchematicTracePipelineSolver(input as any) - solver.solve() + const solver = new SchematicTracePipelineSolver(input as any); + solver.solve(); // console.log(solver.netLabelPlacementSolver!.netLabelPlacements) -}) +}); diff --git a/tests/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver_repro03.test.ts b/tests/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver_repro03.test.ts index 2ae6d992..ca7bcd50 100644 --- a/tests/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver_repro03.test.ts +++ b/tests/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver_repro03.test.ts @@ -1,6 +1,6 @@ -import type { InputProblem } from "lib/types/InputProblem" -import { test, expect } from "bun:test" -import { SchematicTracePipelineSolver } from "lib/index" +import type { InputProblem } from "lib/types/InputProblem"; +import { test, expect } from "bun:test"; +import { SchematicTracePipelineSolver } from "lib/index"; const inputProblem: InputProblem = { chips: [ @@ -482,10 +482,10 @@ const inputProblem: InputProblem = { ROW2: ["x-", "x+"], }, maxMspPairDistance: 5, -} +}; test("SchematicTracePipelineSolver_repro03 - infinite loop fixed", () => { - const solver = new SchematicTracePipelineSolver(inputProblem) - solver.solve() - expect(solver.solved).toBe(true) -}) + const solver = new SchematicTracePipelineSolver(inputProblem); + solver.solve(); + expect(solver.solved).toBe(true); +}); diff --git a/tests/solvers/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver_repro01.test.ts b/tests/solvers/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver_repro01.test.ts index 95d4b820..dd9a8711 100644 --- a/tests/solvers/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver_repro01.test.ts +++ b/tests/solvers/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver_repro01.test.ts @@ -1,16 +1,16 @@ -import { test, expect } from "bun:test" -import input from "./SchematicTraceSingleLineSolver_repro01.json" -import { SchematicTraceSingleLineSolver } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver" +import { test, expect } from "bun:test"; +import input from "./SchematicTraceSingleLineSolver_repro01.json"; +import { SchematicTraceSingleLineSolver } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver"; test.skip("SchematicTraceSingleLineSolver_repro01", () => { - const solver = new SchematicTraceSingleLineSolver(input as any) - solver.solve() + const solver = new SchematicTraceSingleLineSolver(input as any); + solver.solve(); // The solver should fail because it cannot find a path around the chip // without guidelines to help navigate around it - expect(solver.failed).toBe(true) + expect(solver.failed).toBe(true); expect(solver.error).toBe( "No more candidate elbows, everything had collisions", - ) - expect(solver.solvedTracePath).toBe(null) -}) + ); + expect(solver.solvedTracePath).toBe(null); +}); diff --git a/tests/solvers/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver_repro02.test.ts b/tests/solvers/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver_repro02.test.ts index abd6253b..792bbf3c 100644 --- a/tests/solvers/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver_repro02.test.ts +++ b/tests/solvers/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver_repro02.test.ts @@ -1,5 +1,5 @@ -import { SchematicTraceSingleLineSolver } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver" -import { test, expect } from "bun:test" +import { SchematicTraceSingleLineSolver } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver"; +import { test, expect } from "bun:test"; test("SchematicTraceSingleLineSolver should solve problem correctly", () => { const input = { @@ -884,21 +884,21 @@ test("SchematicTraceSingleLineSolver should solve problem correctly", () => { availableNetLabelOrientations: {}, maxMspPairDistance: 2, }, - } + }; - const solver = new SchematicTraceSingleLineSolver(input as any) - solver.solve() + const solver = new SchematicTraceSingleLineSolver(input as any); + solver.solve(); // There should be no orthogonal paths even considered as candidates for (const path of solver.allCandidatePaths) { for (let i = 0; i < path.length - 1; i++) { - const start = path[i] - const end = path[i + 1] - const dx = end.x - start.x - const dy = end.y - start.y - const isHorizontal = Math.abs(dy) < 1e-6 - const isVertical = Math.abs(dx) < 1e-6 - expect(isHorizontal || isVertical).toBe(true) + const start = path[i]; + const end = path[i + 1]; + const dx = end.x - start.x; + const dy = end.y - start.y; + const isHorizontal = Math.abs(dy) < 1e-6; + const isVertical = Math.abs(dx) < 1e-6; + expect(isHorizontal || isVertical).toBe(true); } } -}) +}); diff --git a/tests/solvers/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver_shortest.test.ts b/tests/solvers/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver_shortest.test.ts index 394c0c35..54c3037b 100644 --- a/tests/solvers/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver_shortest.test.ts +++ b/tests/solvers/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver_shortest.test.ts @@ -1,19 +1,19 @@ -import { test, expect } from "bun:test" -import { SchematicTraceSingleLineSolver } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver" -import { calculateElbow } from "calculate-elbow" -import { generateElbowVariants } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/generateElbowVariants" -import type { InputChip, InputProblem } from "lib/types/InputProblem" -import type { Guideline } from "lib/solvers/GuidelinesSolver/GuidelinesSolver" +import { test, expect } from "bun:test"; +import { SchematicTraceSingleLineSolver } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver"; +import { calculateElbow } from "calculate-elbow"; +import { generateElbowVariants } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/generateElbowVariants"; +import type { InputChip, InputProblem } from "lib/types/InputProblem"; +import type { Guideline } from "lib/solvers/GuidelinesSolver/GuidelinesSolver"; const pathLength = (pts: { x: number; y: number }[]) => { - let len = 0 + 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) + 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 -} + return len; +}; test("SchematicTraceSingleLineSolver chooses shortest candidate path", () => { const chipA: InputChip = { @@ -22,50 +22,50 @@ test("SchematicTraceSingleLineSolver chooses shortest candidate path", () => { width: 0.2, height: 0.2, pins: [{ pinId: "A1", x: 0, y: 0 }], - } + }; const chipB: InputChip = { chipId: "B", center: { x: 4, y: 2 }, width: 0.2, height: 0.2, pins: [{ pinId: "B1", x: 4, y: 2 }], - } + }; const pins = [ { pinId: "A1", x: 0, y: 0, _facingDirection: "x+" as const, chipId: "A" }, { pinId: "B1", x: 4, y: 2, _facingDirection: "x+" as const, chipId: "B" }, - ] + ]; const guidelines: Guideline[] = [ { orientation: "vertical", x: 4, y: undefined }, { orientation: "vertical", x: 6, y: undefined }, - ] + ]; const inputProblem: InputProblem = { chips: [chipA, chipB], directConnections: [], netConnections: [], availableNetLabelOrientations: {}, - } + }; const solver = new SchematicTraceSingleLineSolver({ pins: pins as any, guidelines, inputProblem, chipMap: { A: chipA, B: chipB }, - }) + }); - solver.solve() - expect(solver.solved).toBe(true) + solver.solve(); + expect(solver.solved).toBe(true); const baseElbow = calculateElbow( { x: pins[0].x, y: pins[0].y, facingDirection: pins[0]._facingDirection }, { x: pins[1].x, y: pins[1].y, facingDirection: pins[1]._facingDirection }, { overshoot: 0.2 }, - ) - const { elbowVariants } = generateElbowVariants({ baseElbow, guidelines }) - const candidates = [baseElbow, ...elbowVariants] - const shortestLength = Math.min(...candidates.map(pathLength)) + ); + const { elbowVariants } = generateElbowVariants({ baseElbow, guidelines }); + const candidates = [baseElbow, ...elbowVariants]; + const shortestLength = Math.min(...candidates.map(pathLength)); - expect(pathLength(solver.solvedTracePath!)).toBe(shortestLength) -}) + expect(pathLength(solver.solvedTracePath!)).toBe(shortestLength); +}); diff --git a/tests/solvers/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2_01-example17-d1_1-u1_1.test.ts b/tests/solvers/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2_01-example17-d1_1-u1_1.test.ts index 585a2b71..a7c29b54 100644 --- a/tests/solvers/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2_01-example17-d1_1-u1_1.test.ts +++ b/tests/solvers/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2_01-example17-d1_1-u1_1.test.ts @@ -1,5 +1,5 @@ -import { SchematicTraceSingleLineSolver2 } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2" -import { test, expect } from "bun:test" +import { SchematicTraceSingleLineSolver2 } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2"; +import { test, expect } from "bun:test"; test("SchematicTraceSingleLineSolver2 should solve problem correctly", () => { const input = { @@ -314,10 +314,10 @@ test("SchematicTraceSingleLineSolver2 should solve problem correctly", () => { availableNetLabelOrientations: {}, maxMspPairDistance: 2.4, }, - } + }; - const solver = new SchematicTraceSingleLineSolver2(input as any) - solver.solve() + const solver = new SchematicTraceSingleLineSolver2(input as any); + solver.solve(); - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/solvers/TraceCleanupSolver/TraceCleanupSolver.test.ts b/tests/solvers/TraceCleanupSolver/TraceCleanupSolver.test.ts index 0d3d95b5..92783ecd 100644 --- a/tests/solvers/TraceCleanupSolver/TraceCleanupSolver.test.ts +++ b/tests/solvers/TraceCleanupSolver/TraceCleanupSolver.test.ts @@ -1,7 +1,7 @@ -import { expect } from "bun:test" -import { test } from "bun:test" -import inputData from "../../assets/TraceCleanupSolver.test.input.json" -import { TraceCleanupSolver } from "lib/solvers/TraceCleanupSolver/TraceCleanupSolver" +import { expect } from "bun:test"; +import { test } from "bun:test"; +import inputData from "../../assets/TraceCleanupSolver.test.input.json"; +import { TraceCleanupSolver } from "lib/solvers/TraceCleanupSolver/TraceCleanupSolver"; test("TraceCleanupSolver snapshot", () => { const solver = new TraceCleanupSolver({ @@ -13,7 +13,7 @@ test("TraceCleanupSolver snapshot", () => { new Set(v as any), ]), ), - } as any) - solver.solve() - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + } as any); + solver.solve(); + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.test.ts b/tests/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.test.ts index f057eb44..cabf29b6 100644 --- a/tests/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.test.ts +++ b/tests/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.test.ts @@ -1,18 +1,18 @@ -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" -import testInput from "tests/assets/TraceLabelOverlapAvoidanceSolver.test.input.json" +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"; +import testInput from "tests/assets/TraceLabelOverlapAvoidanceSolver.test.input.json"; test("TraceLabelOverlapAvoidanceSolver snapshot", () => { const solver = new TraceLabelOverlapAvoidanceSolver({ inputProblem: inputProblem as any, netLabelPlacements: testInput.netLabelPlacements as any, traces: testInput.traces as any, - }) + }); - solver.solve() + solver.solve(); - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/renderComparisonView01.test.ts b/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/renderComparisonView01.test.ts index c24af93a..e0c38461 100644 --- a/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/renderComparisonView01.test.ts +++ b/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/renderComparisonView01.test.ts @@ -1,22 +1,22 @@ -import { MergedNetLabelObstacleSolver } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver" -import { expect } from "bun:test" -import { test } from "bun:test" +import { MergedNetLabelObstacleSolver } 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" -import inputData from "../../../assets/1.input.json" +} from "graphics-debug"; +import { NetLabelPlacementSolver } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"; +import inputData from "../../../assets/1.input.json"; test("NetLabelPlacementSolver-to-MergedNetLabelObstacles snapshot", () => { const solver1 = new MergedNetLabelObstacleSolver( inputData.mergedNetLabelObstacleSolver as any, - ) + ); const solver2 = new NetLabelPlacementSolver( inputData.netLabelPlacementSolver as any, - ) - solver1.solve() - solver2.solve() + ); + solver1.solve(); + solver2.solve(); const sideBySide = getSvgFromGraphicsObject( stackGraphicsHorizontally([solver2.visualize(), solver1.visualize()], { titles: ["NetLabelPlacementSolver", "MergedNetLabelObstacles"], @@ -24,6 +24,6 @@ test("NetLabelPlacementSolver-to-MergedNetLabelObstacles snapshot", () => { { backgroundColor: "white", }, - ) - expect(sideBySide).toMatchSvgSnapshot(import.meta.path) -}) + ); + expect(sideBySide).toMatchSvgSnapshot(import.meta.path); +}); diff --git a/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/renderComparisonView02.test.ts b/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/renderComparisonView02.test.ts index c885eb2a..999a8b17 100644 --- a/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/renderComparisonView02.test.ts +++ b/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/renderComparisonView02.test.ts @@ -1,20 +1,20 @@ -import { MergedNetLabelObstacleSolver } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver" -import { expect } from "bun:test" -import { test } from "bun:test" +import { MergedNetLabelObstacleSolver } 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" -import inputData from "../../../assets/2.input.json" +} from "graphics-debug"; +import { SingleOverlapSolver } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver"; +import inputData from "../../../assets/2.input.json"; test("MergedNetLabelObstaclesSolver-to-SingleOverlapSolver snapshot", () => { const solver1 = new MergedNetLabelObstacleSolver( inputData.mergedNetLabelObstacleSolver as any, - ) - const solver2 = new SingleOverlapSolver(inputData.singleOverlapSolver as any) - solver1.solve() - solver2.solve() + ); + const solver2 = new SingleOverlapSolver(inputData.singleOverlapSolver as any); + solver1.solve(); + solver2.solve(); const sideBySide = getSvgFromGraphicsObject( stackGraphicsHorizontally([solver1.visualize(), solver2.visualize()], { titles: ["MergedNetLabelObstaclesSolver", "SingleOverlapSolver"], @@ -22,6 +22,6 @@ test("MergedNetLabelObstaclesSolver-to-SingleOverlapSolver snapshot", () => { { backgroundColor: "white", }, - ) - expect(sideBySide).toMatchSvgSnapshot(import.meta.path) -}) + ); + expect(sideBySide).toMatchSvgSnapshot(import.meta.path); +}); diff --git a/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/renderComparisonView03.test.ts b/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/renderComparisonView03.test.ts index 7fff54e5..081d0064 100644 --- a/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/renderComparisonView03.test.ts +++ b/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/renderComparisonView03.test.ts @@ -1,19 +1,19 @@ -import { expect } from "bun:test" -import { test } from "bun:test" +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 inputData from "../../../assets/3.input.json" -import { TraceCleanupSolver } from "lib/solvers/TraceCleanupSolver/TraceCleanupSolver" +} from "graphics-debug"; +import { SingleOverlapSolver } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver"; +import inputData from "../../../assets/3.input.json"; +import { TraceCleanupSolver } from "lib/solvers/TraceCleanupSolver/TraceCleanupSolver"; test("SingleOverlapSolver-to-TraceCleanupSolver snapshot", () => { // Convert targetTraceIds to Set // @ts-expect-error inputData.traceCleanupSolver.targetTraceIds = new Set( inputData.traceCleanupSolver.targetTraceIds, - ) + ); // Convert mergedLabelNetIdMap arrays to Sets // @ts-expect-error @@ -21,12 +21,12 @@ test("SingleOverlapSolver-to-TraceCleanupSolver snapshot", () => { Object.entries(inputData.traceCleanupSolver.mergedLabelNetIdMap).map( ([key, value]) => [key, new Set(value)], ), - ) + ); - const solver1 = new TraceCleanupSolver(inputData.traceCleanupSolver as any) - const solver2 = new SingleOverlapSolver(inputData.singleOverlapSolver as any) - solver1.solve() - solver2.solve() + const solver1 = new TraceCleanupSolver(inputData.traceCleanupSolver as any); + const solver2 = new SingleOverlapSolver(inputData.singleOverlapSolver as any); + solver1.solve(); + solver2.solve(); const sideBySide = getSvgFromGraphicsObject( stackGraphicsHorizontally([solver2.visualize(), solver1.visualize()], { titles: ["SingleOverlapSolver", "TraceCleanupSolver"], @@ -34,6 +34,6 @@ test("SingleOverlapSolver-to-TraceCleanupSolver snapshot", () => { { backgroundColor: "white", }, - ) - expect(sideBySide).toMatchSvgSnapshot(import.meta.path) -}) + ); + expect(sideBySide).toMatchSvgSnapshot(import.meta.path); +}); diff --git a/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/MergedNetLabelObstacles.test.ts b/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/MergedNetLabelObstacles.test.ts index 3a6b94d3..64a16819 100644 --- a/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/MergedNetLabelObstacles.test.ts +++ b/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/MergedNetLabelObstacles.test.ts @@ -1,14 +1,14 @@ -import { MergedNetLabelObstacleSolver } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver" -import { expect } from "bun:test" -import { test } from "bun:test" -import inputData from "../../../assets/MergedNetLabelObstacles.test.input.json" +import { MergedNetLabelObstacleSolver } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver"; +import { expect } from "bun:test"; +import { test } from "bun:test"; +import inputData from "../../../assets/MergedNetLabelObstacles.test.input.json"; test("LabelMergingSolver snapshot", () => { const solver = new MergedNetLabelObstacleSolver({ netLabelPlacements: inputData.netLabelPlacements as any, inputProblem: inputData.inputProblem as any, traces: inputData.traces as any, - }) - solver.solve() - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + }); + 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 index 153abaea..a9f52ec7 100644 --- a/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/OverlapAvoidanceStepSolver.test.ts +++ b/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/OverlapAvoidanceStepSolver.test.ts @@ -1,7 +1,7 @@ -import { OverlapAvoidanceStepSolver } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver" -import { expect } from "bun:test" -import { test } from "bun:test" -import inputData from "../../../assets/OverlapAvoidanceStepSolver.test.input.json" +import { OverlapAvoidanceStepSolver } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver"; +import { expect } from "bun:test"; +import { test } from "bun:test"; +import inputData from "../../../assets/OverlapAvoidanceStepSolver.test.input.json"; test("OverlapAvoidanceStepSolver snapshot", () => { const solver = new OverlapAvoidanceStepSolver({ @@ -14,7 +14,7 @@ test("OverlapAvoidanceStepSolver snapshot", () => { new Set(v as any), ]), ), - } as any) - solver.solve() - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + } 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 index 51e2434c..2fbd0102 100644 --- a/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/SingleOverlapSolver.test.ts +++ b/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/SingleOverlapSolver.test.ts @@ -1,10 +1,10 @@ -import { expect } from "bun:test" -import { test } from "bun:test" -import { SingleOverlapSolver } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver" -import inputData from "../../../assets/SingleOverlapSolver.test.input.json" +import { expect } from "bun:test"; +import { test } from "bun:test"; +import { SingleOverlapSolver } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver"; +import inputData from "../../../assets/SingleOverlapSolver.test.input.json"; test("SingleOverlapSolver snapshot", () => { - const solver = new SingleOverlapSolver(inputData as any) - solver.solve() - expect(solver).toMatchSolverSnapshot(import.meta.path) -}) + const solver = new SingleOverlapSolver(inputData as any); + solver.solve(); + expect(solver).toMatchSolverSnapshot(import.meta.path); +}); diff --git a/tests/svg.test.ts b/tests/svg.test.ts index 9251566a..71bbeff3 100644 --- a/tests/svg.test.ts +++ b/tests/svg.test.ts @@ -1,11 +1,11 @@ -import { expect, test } from "bun:test" +import { expect, test } from "bun:test"; const testSvg = ` - ` + `; test("svg snapshot example", async () => { // First run will create the snapshot // Subsequent runs will compare against the saved snapshot - await expect(testSvg).toMatchSvgSnapshot(import.meta.path) -}) + await expect(testSvg).toMatchSvgSnapshot(import.meta.path); +}); diff --git a/vite.config.ts b/vite.config.ts index 51c90bf8..c842d927 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,5 +1,5 @@ -import { defineConfig } from "vite" -import path from "path" +import { defineConfig } from "vite"; +import path from "path"; export default defineConfig({ resolve: { @@ -12,4 +12,4 @@ export default defineConfig({ server: { port: 5020, }, -}) +});