From 11037d89c3f533251eff260438d734bd0ed568e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?0hm=E2=98=98=EF=B8=8F?= Date: Fri, 19 Dec 2025 17:53:05 +0530 Subject: [PATCH 1/5] WIP --- lib/RectDiffPipeline.ts | 70 ++++-- lib/solvers/RectDiffSolver.ts | 230 ------------------ lib/solvers/rectdiff/ExpansionPhaseSolver.ts | 74 ++++++ lib/solvers/rectdiff/GridPhaseSolver.ts | 90 +++++++ lib/solvers/rectdiff/engine.ts | 27 +- lib/solvers/rectdiff/types.ts | 5 +- lib/solvers/rectdiff/visualizationColors.ts | 16 ++ .../rectdiff/visualizeRectDiffState.ts | 135 ++++++++++ 8 files changed, 380 insertions(+), 267 deletions(-) delete mode 100644 lib/solvers/RectDiffSolver.ts create mode 100644 lib/solvers/rectdiff/ExpansionPhaseSolver.ts create mode 100644 lib/solvers/rectdiff/GridPhaseSolver.ts create mode 100644 lib/solvers/rectdiff/visualizationColors.ts create mode 100644 lib/solvers/rectdiff/visualizeRectDiffState.ts diff --git a/lib/RectDiffPipeline.ts b/lib/RectDiffPipeline.ts index 425f004..15fd29b 100644 --- a/lib/RectDiffPipeline.ts +++ b/lib/RectDiffPipeline.ts @@ -1,10 +1,16 @@ import { BasePipelineSolver, definePipelineStep } from "@tscircuit/solver-utils" import type { SimpleRouteJson } from "./types/srj-types" import type { GridFill3DOptions } from "./solvers/rectdiff/types" -import { RectDiffSolver } from "./solvers/RectDiffSolver" import type { CapacityMeshNode } from "./types/capacity-mesh-types" import type { GraphicsObject } from "graphics-debug" import { createBaseVisualization } from "./solvers/rectdiff/visualization" +import type { GridSolverOutput } from "./solvers/rectdiff/GridPhaseSolver" +import { GridSolver } from "./solvers/rectdiff/GridPhaseSolver" +import { + ExpansionSolver, + type ExpansionSolverOutput, +} from "./solvers/rectdiff/ExpansionPhaseSolver" +import { visualizeRectDiffState } from "./solvers/rectdiff/visualizeRectDiffState" export interface RectDiffPipelineInput { simpleRouteJson: SimpleRouteJson @@ -12,21 +18,38 @@ export interface RectDiffPipelineInput { } export class RectDiffPipeline extends BasePipelineSolver { - rectDiffSolver?: RectDiffSolver + rectDiffSolver?: ExpansionSolver override pipelineDef = [ + definePipelineStep("gridSolver", GridSolver, (instance) => [ + { + simpleRouteJson: instance.inputProblem.simpleRouteJson, + gridOptions: instance.inputProblem.gridOptions, + }, + ]), + definePipelineStep( - "rectDiffSolver", - RectDiffSolver, - (instance) => [ - { - simpleRouteJson: instance.inputProblem.simpleRouteJson, - gridOptions: instance.inputProblem.gridOptions, - }, - ], + "expansionSolver", + ExpansionSolver, + (instance) => { + const gridOutput = + instance.getStepOutput("gridSolver") + if (!gridOutput) { + throw new Error( + "RectDiffPipeline: gridSolver output is required before expansion", + ) + } + return [ + { + initialState: gridOutput.rectDiffState, + }, + ] + }, { - onSolved: () => { - // RectDiff mesh generation completed + onSolved: (instance) => { + const expansionSolver = + instance.getSolver("expansionSolver") + ;(instance as RectDiffPipeline).rectDiffSolver = expansionSolver }, }, ), @@ -37,16 +60,29 @@ export class RectDiffPipeline extends BasePipelineSolver } override getOutput(): { meshNodes: CapacityMeshNode[] } { - return this.getSolver("rectDiffSolver")!.getOutput() + const expansionOutput = this.getStepOutput( + "rectDiffExpansionSolver", + ) + + if (expansionOutput) { + return { meshNodes: expansionOutput.meshNodes } + } + + return { meshNodes: [] } } override visualize(): GraphicsObject { - const solver = this.getSolver("rectDiffSolver") - if (solver) { - return solver.visualize() + const expansionOutput = this.getStepOutput( + "rectDiffExpansionSolver", + ) + + if (expansionOutput) { + return visualizeRectDiffState( + expansionOutput.rectDiffState, + this.inputProblem.simpleRouteJson, + ) } - // Show board and obstacles even before solver is initialized return createBaseVisualization( this.inputProblem.simpleRouteJson, "RectDiff Pipeline (not started)", diff --git a/lib/solvers/RectDiffSolver.ts b/lib/solvers/RectDiffSolver.ts deleted file mode 100644 index 5e59618..0000000 --- a/lib/solvers/RectDiffSolver.ts +++ /dev/null @@ -1,230 +0,0 @@ -// lib/solvers/RectDiffSolver.ts -import { BaseSolver, BasePipelineSolver } from "@tscircuit/solver-utils" -import type { SimpleRouteJson } from "../types/srj-types" -import type { GraphicsObject } from "graphics-debug" -import type { CapacityMeshNode } from "../types/capacity-mesh-types" - -import type { GridFill3DOptions, RectDiffState } from "./rectdiff/types" -import { - initState, - stepGrid, - stepExpansion, - finalizeRects, - computeProgress, -} from "./rectdiff/engine" -import { rectsToMeshNodes } from "./rectdiff/rectsToMeshNodes" -import { overlaps } from "./rectdiff/geometry" - -/** - * A streaming, one-step-per-iteration solver for capacity mesh generation. - */ -export class RectDiffSolver extends BaseSolver { - private srj: SimpleRouteJson - private gridOptions: Partial - private state!: RectDiffState - private _meshNodes: CapacityMeshNode[] = [] - - constructor(opts: { - simpleRouteJson: SimpleRouteJson - gridOptions?: Partial - }) { - super() - this.srj = opts.simpleRouteJson - this.gridOptions = opts.gridOptions ?? {} - } - - override _setup() { - this.state = initState(this.srj, this.gridOptions) - this.stats = { - phase: this.state.phase, - gridIndex: this.state.gridIndex, - } - } - - /** Exactly ONE small step per call. */ - override _step() { - if (this.state.phase === "GRID") { - stepGrid(this.state) - } else if (this.state.phase === "EXPANSION") { - stepExpansion(this.state) - } else if (this.state.phase === "GAP_FILL") { - this.state.phase = "DONE" - } else if (this.state.phase === "DONE") { - // Finalize once - if (!this.solved) { - const rects = finalizeRects(this.state) - this._meshNodes = rectsToMeshNodes(rects) - this.solved = true - } - return - } - - // Lightweight stats for debugger - this.stats.phase = this.state.phase - this.stats.gridIndex = this.state.gridIndex - this.stats.placed = this.state.placed.length - } - - /** Compute solver progress (0 to 1). */ - computeProgress(): number { - if (this.solved || this.state.phase === "DONE") { - return 1 - } - return computeProgress(this.state) - } - - override getOutput(): { meshNodes: CapacityMeshNode[] } { - return { meshNodes: this._meshNodes } - } - - /** Get color based on z layer for visualization. */ - private getColorForZLayer(zLayers: number[]): { - fill: string - stroke: string - } { - const minZ = Math.min(...zLayers) - const colors = [ - { fill: "#dbeafe", stroke: "#3b82f6" }, - { fill: "#fef3c7", stroke: "#f59e0b" }, - { fill: "#d1fae5", stroke: "#10b981" }, - { fill: "#e9d5ff", stroke: "#a855f7" }, - { fill: "#fed7aa", stroke: "#f97316" }, - { fill: "#fecaca", stroke: "#ef4444" }, - ] - return colors[minZ % colors.length]! - } - - /** Streaming visualization: board + obstacles + current placements. */ - override visualize(): GraphicsObject { - const rects: NonNullable = [] - const points: NonNullable = [] - const lines: NonNullable = [] // Initialize lines array - - // Board bounds - use srj bounds which is always available - const boardBounds = { - minX: this.srj.bounds.minX, - maxX: this.srj.bounds.maxX, - minY: this.srj.bounds.minY, - maxY: this.srj.bounds.maxY, - } - - // board or outline - if (this.srj.outline && this.srj.outline.length > 1) { - lines.push({ - points: [...this.srj.outline, this.srj.outline[0]!], // Close the loop by adding the first point again - strokeColor: "#111827", - strokeWidth: 0.01, - label: "outline", - }) - } else { - rects.push({ - center: { - x: (boardBounds.minX + boardBounds.maxX) / 2, - y: (boardBounds.minY + boardBounds.maxY) / 2, - }, - width: boardBounds.maxX - boardBounds.minX, - height: boardBounds.maxY - boardBounds.minY, - fill: "none", - stroke: "#111827", - label: "board", - }) - } - - // obstacles (rect & oval as bounding boxes) - for (const obstacle of this.srj.obstacles ?? []) { - if (obstacle.type === "rect" || obstacle.type === "oval") { - rects.push({ - center: { x: obstacle.center.x, y: obstacle.center.y }, - width: obstacle.width, - height: obstacle.height, - fill: "#fee2e2", - stroke: "#ef4444", - layer: "obstacle", - label: "obstacle", - }) - } - } - - // board void rects - if (this.state?.boardVoidRects) { - // If outline exists, compute its bbox to hide outer padding voids - let outlineBBox: { - x: number - y: number - width: number - height: number - } | null = null - - if (this.srj.outline && this.srj.outline.length > 0) { - const xs = this.srj.outline.map((p) => p.x) - const ys = this.srj.outline.map((p) => p.y) - const minX = Math.min(...xs) - const minY = Math.min(...ys) - outlineBBox = { - x: minX, - y: minY, - width: Math.max(...xs) - minX, - height: Math.max(...ys) - minY, - } - } - - for (const r of this.state.boardVoidRects) { - // If we have an outline, only show voids that overlap its bbox (hides outer padding) - if (outlineBBox && !overlaps(r, outlineBBox)) { - continue - } - - rects.push({ - center: { x: r.x + r.width / 2, y: r.y + r.height / 2 }, - width: r.width, - height: r.height, - fill: "rgba(0, 0, 0, 0.5)", - stroke: "none", - label: "void", - }) - } - } - - // candidate positions (where expansion started from) - if (this.state?.candidates?.length) { - for (const cand of this.state.candidates) { - points.push({ - x: cand.x, - y: cand.y, - fill: "#9333ea", - stroke: "#6b21a8", - label: `z:${cand.z}`, - } as any) - } - } - - // current placements (streaming) if not yet solved - if (this.state?.placed?.length) { - for (const p of this.state.placed) { - const colors = this.getColorForZLayer(p.zLayers) - rects.push({ - center: { - x: p.rect.x + p.rect.width / 2, - y: p.rect.y + p.rect.height / 2, - }, - width: p.rect.width, - height: p.rect.height, - fill: colors.fill, - stroke: colors.stroke, - label: `free\nz:${p.zLayers.join(",")}`, - }) - } - } - - return { - title: `RectDiff (${this.state?.phase ?? "init"})`, - coordinateSystem: "cartesian", - rects, - points, - lines, // Include lines in the returned GraphicsObject - } - } -} - -// Re-export types for convenience -export type { GridFill3DOptions } from "./rectdiff/types" diff --git a/lib/solvers/rectdiff/ExpansionPhaseSolver.ts b/lib/solvers/rectdiff/ExpansionPhaseSolver.ts new file mode 100644 index 0000000..5fa362a --- /dev/null +++ b/lib/solvers/rectdiff/ExpansionPhaseSolver.ts @@ -0,0 +1,74 @@ +import { BaseSolver } from "@tscircuit/solver-utils" +import type { CapacityMeshNode } from "../../types/capacity-mesh-types" +import { computeProgress, finalizeRects, stepExpansion } from "./engine" +import type { Rect3d, RectDiffState } from "./types" +import { rectsToMeshNodes } from "./rectsToMeshNodes" +import type { GraphicsObject } from "graphics-debug" +import { visualizeRectDiffState } from "./visualizeRectDiffState" + +export type ExpansionSolverInput = { + initialState: RectDiffState +} + +export type ExpansionSolverOutput = { + rectDiffState: RectDiffState + meshNodes: CapacityMeshNode[] +} + +export class ExpansionSolver extends BaseSolver { + private rectDiffState!: RectDiffState + private meshNodeList: CapacityMeshNode[] = [] + + constructor(private rectDiffExpansionInput: ExpansionSolverInput) { + super() + } + + override _setup() { + this.rectDiffState = this.rectDiffExpansionInput.initialState + this.stats = { + expansionIndex: this.rectDiffState.expansionIndex, + } + } + + override _step() { + if (this.rectDiffState.expansionIndex >= this.rectDiffState.placed.length) { + if (this.meshNodeList.length === 0) { + this.finalizeMeshNodes() + } + this.solved = true + return + } + + stepExpansion(this.rectDiffState) + + this.stats.expansionIndex = this.rectDiffState.expansionIndex + this.stats.placed = this.rectDiffState.placed.length + } + + private finalizeMeshNodes() { + const rectList: Rect3d[] = finalizeRects(this.rectDiffState) + this.meshNodeList = rectsToMeshNodes(rectList) + } + + computeProgress(): number { + return computeProgress(this.rectDiffState) + } + + override visualize(): GraphicsObject { + return visualizeRectDiffState(this.rectDiffState, { + bounds: this.rectDiffState.srj.bounds, + obstacles: this.rectDiffState.srj.obstacles, + connections: this.rectDiffState.srj.connections, + layerCount: this.rectDiffState.srj.layerCount, + minTraceWidth: this.rectDiffState.srj.minTraceWidth, + outline: this.rectDiffState.srj.outline, + } as any) + } + + override getOutput(): ExpansionSolverOutput { + return { + rectDiffState: this.rectDiffState, + meshNodes: this.meshNodeList, + } + } +} diff --git a/lib/solvers/rectdiff/GridPhaseSolver.ts b/lib/solvers/rectdiff/GridPhaseSolver.ts new file mode 100644 index 0000000..a2b01e2 --- /dev/null +++ b/lib/solvers/rectdiff/GridPhaseSolver.ts @@ -0,0 +1,90 @@ +import { BaseSolver } from "@tscircuit/solver-utils" +import type { SimpleRouteJson } from "../../types/srj-types" +import type { GridFill3DOptions, RectDiffState } from "./types" +import { computeProgress, initState, stepGrid } from "./engine" +import type { GraphicsObject } from "graphics-debug" +import { visualizeRectDiffState } from "./visualizeRectDiffState" + +export type GridSolverInput = { + simpleRouteJson: SimpleRouteJson + gridOptions?: Partial +} + +export type GridSolverOutput = { + rectDiffState: RectDiffState +} + +export class GridSolver extends BaseSolver { + private rectDiffState!: RectDiffState + + constructor(private gridSolverInput: GridSolverInput) { + super() + } + + override _setup() { + const { simpleRouteJson, gridOptions } = this.gridSolverInput + this.rectDiffState = initState(simpleRouteJson, gridOptions ?? {}) + this.stats = { + gridIndex: this.rectDiffState.gridIndex, + } + } + + override _step() { + stepGrid(this.rectDiffState) + + this.stats.gridIndex = this.rectDiffState.gridIndex + this.stats.placed = this.rectDiffState.placed.length + + // Mark solved when all grids + edge analysis done + if ( + this.rectDiffState.candidates.length === 0 && + this.rectDiffState.edgeAnalysisDone && + this.rectDiffState.gridIndex >= + this.rectDiffState.options.gridSizes.length - 1 + ) { + this.solved = true + } + } + + computeProgress(): number { + return computeProgress(this.rectDiffState) + } + + override getOutput(): GridSolverOutput { + return { rectDiffState: this.rectDiffState } + } + + override visualize(): GraphicsObject { + const baseVisualization = visualizeRectDiffState( + this.rectDiffState, + this.gridSolverInput.simpleRouteJson, + ) + + const gridPointList: NonNullable = [] + + const boundsRect = this.rectDiffState.bounds + const gridSizeList = this.rectDiffState.options.gridSizes + const currentGridSize = + gridSizeList[this.rectDiffState.gridIndex] ?? + gridSizeList[gridSizeList.length - 1]! + + const maxX = boundsRect.x + boundsRect.width + const maxY = boundsRect.y + boundsRect.height + + for (let x = boundsRect.x; x <= maxX; x += currentGridSize) { + for (let y = boundsRect.y; y <= maxY; y += currentGridSize) { + gridPointList.push({ + x, + y, + label: "grid", + }) + } + } + + return { + ...baseVisualization, + title: `RectDiff GRID`, + points: [...(baseVisualization.points ?? []), ...gridPointList], + } + } +} diff --git a/lib/solvers/rectdiff/engine.ts b/lib/solvers/rectdiff/engine.ts index 963625b..3a3836e 100644 --- a/lib/solvers/rectdiff/engine.ts +++ b/lib/solvers/rectdiff/engine.ts @@ -113,7 +113,6 @@ export function initState( options, obstaclesByLayer, boardVoidRects, - phase: "GRID", gridIndex: 0, candidates: [], placed: [], @@ -278,7 +277,6 @@ export function stepGrid(state: RectDiffState): void { state.consumedSeedsThisGrid = 0 return } - state.phase = "EXPANSION" state.expansionIndex = 0 return } @@ -367,7 +365,7 @@ export function stepGrid(state: RectDiffState): void { export function stepExpansion(state: RectDiffState): void { if (state.expansionIndex >= state.placed.length) { // Transition to gap fill phase instead of done - state.phase = "GAP_FILL" + return } @@ -464,18 +462,13 @@ export function finalizeRects(state: RectDiffState): Rect3d[] { */ export function computeProgress(state: RectDiffState): number { const grids = state.options.gridSizes.length - if (state.phase === "GRID") { - const g = state.gridIndex - const base = g / (grids + 1) // reserve final slice for expansion - const denom = Math.max(1, state.totalSeedsThisGrid) - const frac = denom ? state.consumedSeedsThisGrid / denom : 1 - return Math.min(0.999, base + frac * (1 / (grids + 1))) - } - if (state.phase === "EXPANSION") { - const base = grids / (grids + 1) - const denom = Math.max(1, state.placed.length) - const frac = denom ? state.expansionIndex / denom : 1 - return Math.min(0.999, base + frac * (1 / (grids + 1))) - } - return 1 + const totalSeeds = Math.max(1, state.totalSeedsThisGrid) + const consumedSeeds = state.consumedSeedsThisGrid + const placedCount = Math.max(1, state.placed.length) + const expansionIndex = state.expansionIndex + + const gridProgress = Math.min(1, consumedSeeds / totalSeeds) + const expansionProgress = Math.min(1, expansionIndex / placedCount) + + return Math.min(1, (gridProgress * grids + expansionProgress) / (grids + 1)) } diff --git a/lib/solvers/rectdiff/types.ts b/lib/solvers/rectdiff/types.ts index 599fe79..e0c5205 100644 --- a/lib/solvers/rectdiff/types.ts +++ b/lib/solvers/rectdiff/types.ts @@ -34,8 +34,6 @@ export type Candidate3D = { } export type Placed3D = { rect: XYRect; zLayers: number[] } -export type Phase = "GRID" | "EXPANSION" | "GAP_FILL" | "DONE" - export type RectDiffState = { // static srj: SimpleRouteJson @@ -52,9 +50,10 @@ export type RectDiffState = { boardVoidRects: XYRect[] // newly added for viz // evolving - phase: Phase gridIndex: number // index in gridSizes + candidates: Candidate3D[] + placed: Placed3D[] placedByLayer: XYRect[][] expansionIndex: number diff --git a/lib/solvers/rectdiff/visualizationColors.ts b/lib/solvers/rectdiff/visualizationColors.ts new file mode 100644 index 0000000..14259d1 --- /dev/null +++ b/lib/solvers/rectdiff/visualizationColors.ts @@ -0,0 +1,16 @@ +export function getColorForZLayer(zLayerList: number[]): { + fill: string + stroke: string +} { + const minZLayerIndex = Math.min(...zLayerList) + const colorStyleList = [ + { fill: "#dbeafe", stroke: "#3b82f6" }, + { fill: "#fef3c7", stroke: "#f59e0b" }, + { fill: "#d1fae5", stroke: "#10b981" }, + { fill: "#e9d5ff", stroke: "#a855f7" }, + { fill: "#fed7aa", stroke: "#f97316" }, + { fill: "#fecaca", stroke: "#ef4444" }, + ] as const + + return colorStyleList[minZLayerIndex % colorStyleList.length]! +} diff --git a/lib/solvers/rectdiff/visualizeRectDiffState.ts b/lib/solvers/rectdiff/visualizeRectDiffState.ts new file mode 100644 index 0000000..9cdbb9d --- /dev/null +++ b/lib/solvers/rectdiff/visualizeRectDiffState.ts @@ -0,0 +1,135 @@ +import type { GraphicsObject } from "graphics-debug" +import type { SimpleRouteJson } from "../../types/srj-types" +import type { RectDiffState } from "./types" +import { overlaps } from "./geometry" +import { getColorForZLayer } from "./visualizationColors" + +export function visualizeRectDiffState( + rectDiffState: RectDiffState | undefined, + simpleRouteJson: SimpleRouteJson, +): GraphicsObject { + const rectList: NonNullable = [] + const pointList: NonNullable = [] + const lineList: NonNullable = [] + + const boardBounds = { + minX: simpleRouteJson.bounds.minX, + maxX: simpleRouteJson.bounds.maxX, + minY: simpleRouteJson.bounds.minY, + maxY: simpleRouteJson.bounds.maxY, + } + + if (simpleRouteJson.outline && simpleRouteJson.outline.length > 1) { + lineList.push({ + points: [...simpleRouteJson.outline, simpleRouteJson.outline[0]!], + strokeColor: "#111827", + strokeWidth: 0.01, + label: "outline", + }) + } else { + rectList.push({ + center: { + x: (boardBounds.minX + boardBounds.maxX) / 2, + y: (boardBounds.minY + boardBounds.maxY) / 2, + }, + width: boardBounds.maxX - boardBounds.minX, + height: boardBounds.maxY - boardBounds.minY, + fill: "none", + stroke: "#111827", + label: "board", + }) + } + + for (const obstacle of simpleRouteJson.obstacles ?? []) { + if (obstacle.type === "rect" || obstacle.type === "oval") { + rectList.push({ + center: { x: obstacle.center.x, y: obstacle.center.y }, + width: obstacle.width, + height: obstacle.height, + fill: "#fee2e2", + stroke: "#ef4444", + layer: "obstacle", + label: "obstacle", + }) + } + } + + if (rectDiffState?.boardVoidRects) { + let outlineBoundingRect: { + x: number + y: number + width: number + height: number + } | null = null + + if (simpleRouteJson.outline && simpleRouteJson.outline.length > 0) { + const xCoordinateList = simpleRouteJson.outline.map( + (outlinePoint) => outlinePoint.x, + ) + const yCoordinateList = simpleRouteJson.outline.map( + (outlinePoint) => outlinePoint.y, + ) + const minX = Math.min(...xCoordinateList) + const minY = Math.min(...yCoordinateList) + outlineBoundingRect = { + x: minX, + y: minY, + width: Math.max(...xCoordinateList) - minX, + height: Math.max(...yCoordinateList) - minY, + } + } + + for (const voidRect of rectDiffState.boardVoidRects) { + if (outlineBoundingRect && !overlaps(voidRect, outlineBoundingRect)) { + continue + } + + rectList.push({ + center: { + x: voidRect.x + voidRect.width / 2, + y: voidRect.y + voidRect.height / 2, + }, + width: voidRect.width, + height: voidRect.height, + fill: "rgba(0, 0, 0, 0.5)", + stroke: "none", + label: "void", + }) + } + } + + if (rectDiffState?.candidates?.length) { + for (const candidate of rectDiffState.candidates) { + pointList.push({ + x: candidate.x, + y: candidate.y, + label: `z:${candidate.z}`, + }) + } + } + + if (rectDiffState?.placed?.length) { + for (const placedRect of rectDiffState.placed) { + const colorStyle = getColorForZLayer(placedRect.zLayers) + rectList.push({ + center: { + x: placedRect.rect.x + placedRect.rect.width / 2, + y: placedRect.rect.y + placedRect.rect.height / 2, + }, + width: placedRect.rect.width, + height: placedRect.rect.height, + fill: colorStyle.fill, + stroke: colorStyle.stroke, + label: `free\nz:${placedRect.zLayers.join(",")}`, + }) + } + } + + return { + title: "RectDiff", + coordinateSystem: "cartesian", + rects: rectList, + points: pointList, + lines: lineList, + } +} From dd55e66b76ef94fd924dd62c8bb4eef832ab1908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?0hm=E2=98=98=EF=B8=8F?= Date: Fri, 19 Dec 2025 17:54:11 +0530 Subject: [PATCH 2/5] WIP --- lib/RectDiffPipeline.ts | 6 +++--- .../{rectdiff => expansion}/ExpansionPhaseSolver.ts | 8 ++++---- lib/solvers/{rectdiff => gridphase}/GridPhaseSolver.ts | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) rename lib/solvers/{rectdiff => expansion}/ExpansionPhaseSolver.ts (90%) rename lib/solvers/{rectdiff => gridphase}/GridPhaseSolver.ts (91%) diff --git a/lib/RectDiffPipeline.ts b/lib/RectDiffPipeline.ts index 15fd29b..336e4f9 100644 --- a/lib/RectDiffPipeline.ts +++ b/lib/RectDiffPipeline.ts @@ -4,12 +4,12 @@ import type { GridFill3DOptions } from "./solvers/rectdiff/types" import type { CapacityMeshNode } from "./types/capacity-mesh-types" import type { GraphicsObject } from "graphics-debug" import { createBaseVisualization } from "./solvers/rectdiff/visualization" -import type { GridSolverOutput } from "./solvers/rectdiff/GridPhaseSolver" -import { GridSolver } from "./solvers/rectdiff/GridPhaseSolver" +import type { GridSolverOutput } from "./solvers/gridphase/GridPhaseSolver" +import { GridSolver } from "./solvers/gridphase/GridPhaseSolver" import { ExpansionSolver, type ExpansionSolverOutput, -} from "./solvers/rectdiff/ExpansionPhaseSolver" +} from "./solvers/expansion/ExpansionPhaseSolver" import { visualizeRectDiffState } from "./solvers/rectdiff/visualizeRectDiffState" export interface RectDiffPipelineInput { diff --git a/lib/solvers/rectdiff/ExpansionPhaseSolver.ts b/lib/solvers/expansion/ExpansionPhaseSolver.ts similarity index 90% rename from lib/solvers/rectdiff/ExpansionPhaseSolver.ts rename to lib/solvers/expansion/ExpansionPhaseSolver.ts index 5fa362a..c481192 100644 --- a/lib/solvers/rectdiff/ExpansionPhaseSolver.ts +++ b/lib/solvers/expansion/ExpansionPhaseSolver.ts @@ -1,10 +1,10 @@ import { BaseSolver } from "@tscircuit/solver-utils" import type { CapacityMeshNode } from "../../types/capacity-mesh-types" -import { computeProgress, finalizeRects, stepExpansion } from "./engine" -import type { Rect3d, RectDiffState } from "./types" -import { rectsToMeshNodes } from "./rectsToMeshNodes" +import { computeProgress, finalizeRects, stepExpansion } from "../rectdiff/engine" +import type { Rect3d, RectDiffState } from "../rectdiff/types" +import { rectsToMeshNodes } from "../rectdiff/rectsToMeshNodes" import type { GraphicsObject } from "graphics-debug" -import { visualizeRectDiffState } from "./visualizeRectDiffState" +import { visualizeRectDiffState } from "../rectdiff/visualizeRectDiffState" export type ExpansionSolverInput = { initialState: RectDiffState diff --git a/lib/solvers/rectdiff/GridPhaseSolver.ts b/lib/solvers/gridphase/GridPhaseSolver.ts similarity index 91% rename from lib/solvers/rectdiff/GridPhaseSolver.ts rename to lib/solvers/gridphase/GridPhaseSolver.ts index a2b01e2..266a834 100644 --- a/lib/solvers/rectdiff/GridPhaseSolver.ts +++ b/lib/solvers/gridphase/GridPhaseSolver.ts @@ -1,9 +1,9 @@ import { BaseSolver } from "@tscircuit/solver-utils" import type { SimpleRouteJson } from "../../types/srj-types" -import type { GridFill3DOptions, RectDiffState } from "./types" -import { computeProgress, initState, stepGrid } from "./engine" +import type { GridFill3DOptions, RectDiffState } from "../rectdiff/types" +import { computeProgress, initState, stepGrid } from "../rectdiff/engine" import type { GraphicsObject } from "graphics-debug" -import { visualizeRectDiffState } from "./visualizeRectDiffState" +import { visualizeRectDiffState } from "../rectdiff/visualizeRectDiffState" export type GridSolverInput = { simpleRouteJson: SimpleRouteJson From c82e2047d0fabbcc7ed1fc368cef98ee4dfd9500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?0hm=E2=98=98=EF=B8=8F?= Date: Fri, 19 Dec 2025 17:55:52 +0530 Subject: [PATCH 3/5] WIP --- lib/RectDiffPipeline.ts | 6 +++--- .../{ExpansionPhaseSolver.ts => ExpansionSolver.ts} | 0 .../{gridphase/GridPhaseSolver.ts => grid/GridSolver.ts} | 0 3 files changed, 3 insertions(+), 3 deletions(-) rename lib/solvers/expansion/{ExpansionPhaseSolver.ts => ExpansionSolver.ts} (100%) rename lib/solvers/{gridphase/GridPhaseSolver.ts => grid/GridSolver.ts} (100%) diff --git a/lib/RectDiffPipeline.ts b/lib/RectDiffPipeline.ts index 336e4f9..9c15155 100644 --- a/lib/RectDiffPipeline.ts +++ b/lib/RectDiffPipeline.ts @@ -4,12 +4,12 @@ import type { GridFill3DOptions } from "./solvers/rectdiff/types" import type { CapacityMeshNode } from "./types/capacity-mesh-types" import type { GraphicsObject } from "graphics-debug" import { createBaseVisualization } from "./solvers/rectdiff/visualization" -import type { GridSolverOutput } from "./solvers/gridphase/GridPhaseSolver" -import { GridSolver } from "./solvers/gridphase/GridPhaseSolver" +import type { GridSolverOutput } from "./solvers/grid/GridSolver" +import { GridSolver } from "./solvers/grid/GridSolver" import { ExpansionSolver, type ExpansionSolverOutput, -} from "./solvers/expansion/ExpansionPhaseSolver" +} from "./solvers/expansion/ExpansionSolver" import { visualizeRectDiffState } from "./solvers/rectdiff/visualizeRectDiffState" export interface RectDiffPipelineInput { diff --git a/lib/solvers/expansion/ExpansionPhaseSolver.ts b/lib/solvers/expansion/ExpansionSolver.ts similarity index 100% rename from lib/solvers/expansion/ExpansionPhaseSolver.ts rename to lib/solvers/expansion/ExpansionSolver.ts diff --git a/lib/solvers/gridphase/GridPhaseSolver.ts b/lib/solvers/grid/GridSolver.ts similarity index 100% rename from lib/solvers/gridphase/GridPhaseSolver.ts rename to lib/solvers/grid/GridSolver.ts From bebca8cdc40d09fb36060f68bbfa7b4e253b3223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?0hm=E2=98=98=EF=B8=8F?= Date: Fri, 19 Dec 2025 18:16:57 +0530 Subject: [PATCH 4/5] WIP --- lib/RectDiffPipeline.ts | 10 ++++++---- lib/solvers/expansion/ExpansionSolver.ts | 6 +++++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/RectDiffPipeline.ts b/lib/RectDiffPipeline.ts index 9c15155..35ca0ca 100644 --- a/lib/RectDiffPipeline.ts +++ b/lib/RectDiffPipeline.ts @@ -72,11 +72,13 @@ export class RectDiffPipeline extends BasePipelineSolver } override visualize(): GraphicsObject { - const expansionOutput = this.getStepOutput( - "rectDiffExpansionSolver", - ) + if (this.activeSubSolver) { + return this.activeSubSolver.visualize() + } - if (expansionOutput) { + const expansionOutput = + this.getStepOutput("expansionSolver") + if (this.solved && expansionOutput) { return visualizeRectDiffState( expansionOutput.rectDiffState, this.inputProblem.simpleRouteJson, diff --git a/lib/solvers/expansion/ExpansionSolver.ts b/lib/solvers/expansion/ExpansionSolver.ts index c481192..febfba0 100644 --- a/lib/solvers/expansion/ExpansionSolver.ts +++ b/lib/solvers/expansion/ExpansionSolver.ts @@ -1,6 +1,10 @@ import { BaseSolver } from "@tscircuit/solver-utils" import type { CapacityMeshNode } from "../../types/capacity-mesh-types" -import { computeProgress, finalizeRects, stepExpansion } from "../rectdiff/engine" +import { + computeProgress, + finalizeRects, + stepExpansion, +} from "../rectdiff/engine" import type { Rect3d, RectDiffState } from "../rectdiff/types" import { rectsToMeshNodes } from "../rectdiff/rectsToMeshNodes" import type { GraphicsObject } from "graphics-debug" From 43fe46ecf1c4c499266f7d48597cdd053cba2771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?0hm=E2=98=98=EF=B8=8F?= Date: Fri, 19 Dec 2025 18:22:45 +0530 Subject: [PATCH 5/5] WIP --- lib/RectDiffPipeline.ts | 5 ++--- lib/solvers/expansion/ExpansionSolver.ts | 20 ++++++-------------- lib/solvers/grid/GridSolver.ts | 24 ++++++++++++++++++++++-- lib/solvers/rectdiff/engine.ts | 16 ---------------- lib/solvers/rectdiff/layers.ts | 13 +++++++++---- 5 files changed, 39 insertions(+), 39 deletions(-) diff --git a/lib/RectDiffPipeline.ts b/lib/RectDiffPipeline.ts index 35ca0ca..cea203f 100644 --- a/lib/RectDiffPipeline.ts +++ b/lib/RectDiffPipeline.ts @@ -60,9 +60,8 @@ export class RectDiffPipeline extends BasePipelineSolver } override getOutput(): { meshNodes: CapacityMeshNode[] } { - const expansionOutput = this.getStepOutput( - "rectDiffExpansionSolver", - ) + const expansionOutput = + this.getStepOutput("expansionSolver") if (expansionOutput) { return { meshNodes: expansionOutput.meshNodes } diff --git a/lib/solvers/expansion/ExpansionSolver.ts b/lib/solvers/expansion/ExpansionSolver.ts index febfba0..e8da822 100644 --- a/lib/solvers/expansion/ExpansionSolver.ts +++ b/lib/solvers/expansion/ExpansionSolver.ts @@ -1,10 +1,6 @@ import { BaseSolver } from "@tscircuit/solver-utils" import type { CapacityMeshNode } from "../../types/capacity-mesh-types" -import { - computeProgress, - finalizeRects, - stepExpansion, -} from "../rectdiff/engine" +import { finalizeRects, stepExpansion } from "../rectdiff/engine" import type { Rect3d, RectDiffState } from "../rectdiff/types" import { rectsToMeshNodes } from "../rectdiff/rectsToMeshNodes" import type { GraphicsObject } from "graphics-debug" @@ -55,18 +51,14 @@ export class ExpansionSolver extends BaseSolver { } computeProgress(): number { - return computeProgress(this.rectDiffState) + const placedCount = Math.max(1, this.rectDiffState.placed.length) + const expansionIndex = this.rectDiffState.expansionIndex + + return Math.min(1, expansionIndex / placedCount) } override visualize(): GraphicsObject { - return visualizeRectDiffState(this.rectDiffState, { - bounds: this.rectDiffState.srj.bounds, - obstacles: this.rectDiffState.srj.obstacles, - connections: this.rectDiffState.srj.connections, - layerCount: this.rectDiffState.srj.layerCount, - minTraceWidth: this.rectDiffState.srj.minTraceWidth, - outline: this.rectDiffState.srj.outline, - } as any) + return visualizeRectDiffState(this.rectDiffState, this.rectDiffState.srj) } override getOutput(): ExpansionSolverOutput { diff --git a/lib/solvers/grid/GridSolver.ts b/lib/solvers/grid/GridSolver.ts index 266a834..45a65c6 100644 --- a/lib/solvers/grid/GridSolver.ts +++ b/lib/solvers/grid/GridSolver.ts @@ -1,7 +1,7 @@ import { BaseSolver } from "@tscircuit/solver-utils" import type { SimpleRouteJson } from "../../types/srj-types" import type { GridFill3DOptions, RectDiffState } from "../rectdiff/types" -import { computeProgress, initState, stepGrid } from "../rectdiff/engine" +import { initState, stepGrid } from "../rectdiff/engine" import type { GraphicsObject } from "graphics-debug" import { visualizeRectDiffState } from "../rectdiff/visualizeRectDiffState" @@ -47,7 +47,27 @@ export class GridSolver extends BaseSolver { } computeProgress(): number { - return computeProgress(this.rectDiffState) + const gridSizeList = this.rectDiffState.options.gridSizes + const currentGridIndex = this.rectDiffState.gridIndex + const totalGridCount = gridSizeList.length + + // Progress through grid sizes + const gridIndexProgress = currentGridIndex / totalGridCount + + // Progress through current grid's seeds + const totalSeeds = Math.max(1, this.rectDiffState.totalSeedsThisGrid) + const consumedSeeds = this.rectDiffState.consumedSeedsThisGrid + const seedProgress = Math.min(1, consumedSeeds / totalSeeds) + + // Combine: gridIndex weight + current grid seed progress + const baseProgress = gridIndexProgress + seedProgress / totalGridCount + + // Account for edge analysis as final step + if (this.rectDiffState.edgeAnalysisDone) { + return Math.min(1, baseProgress) + } + + return Math.min(0.99, baseProgress) } override getOutput(): GridSolverOutput { diff --git a/lib/solvers/rectdiff/engine.ts b/lib/solvers/rectdiff/engine.ts index 3a3836e..d848a77 100644 --- a/lib/solvers/rectdiff/engine.ts +++ b/lib/solvers/rectdiff/engine.ts @@ -456,19 +456,3 @@ export function finalizeRects(state: RectDiffState): Rect3d[] { return out } - -/** - * Calculate rough progress number for BaseSolver.progress. - */ -export function computeProgress(state: RectDiffState): number { - const grids = state.options.gridSizes.length - const totalSeeds = Math.max(1, state.totalSeedsThisGrid) - const consumedSeeds = state.consumedSeedsThisGrid - const placedCount = Math.max(1, state.placed.length) - const expansionIndex = state.expansionIndex - - const gridProgress = Math.min(1, consumedSeeds / totalSeeds) - const expansionProgress = Math.min(1, expansionIndex / placedCount) - - return Math.min(1, (gridProgress * grids + expansionProgress) / (grids + 1)) -} diff --git a/lib/solvers/rectdiff/layers.ts b/lib/solvers/rectdiff/layers.ts index 7b960a9..0d51354 100644 --- a/lib/solvers/rectdiff/layers.ts +++ b/lib/solvers/rectdiff/layers.ts @@ -77,8 +77,13 @@ export function obstacleZs(ob: Obstacle, zIndexByName: Map) { } export function obstacleToXYRect(ob: Obstacle): XYRect | null { - const w = ob.width as any - const h = ob.height as any - if (typeof w !== "number" || typeof h !== "number") return null - return { x: ob.center.x - w / 2, y: ob.center.y - h / 2, width: w, height: h } + const width = ob.width + const height = ob.height + if (typeof width !== "number" || typeof height !== "number") return null + return { + x: ob.center.x - width / 2, + y: ob.center.y - height / 2, + width, + height, + } }