Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion lib/RectDiffPipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import {
type PipelineStep,
} from "@tscircuit/solver-utils"
import type { SimpleRouteJson } from "./types/srj-types"
import type { GridFill3DOptions } from "./rectdiff-types"
import type { GridFill3DOptions, XYRect } from "./rectdiff-types"
import type { CapacityMeshNode } from "./types/capacity-mesh-types"
import type { GraphicsObject } from "graphics-debug"
import { GapFillSolverPipeline } from "./solvers/GapFillSolver/GapFillSolverPipeline"
import { RectDiffGridSolverPipeline } from "./solvers/RectDiffGridSolverPipeline/RectDiffGridSolverPipeline"
import { createBaseVisualization } from "./rectdiff-visualization"
import { computeInverseRects } from "./solvers/RectDiffSeedingSolver/computeInverseRects"

export interface RectDiffPipelineInput {
simpleRouteJson: SimpleRouteJson
Expand All @@ -19,6 +20,8 @@ export interface RectDiffPipelineInput {
export class RectDiffPipeline extends BasePipelineSolver<RectDiffPipelineInput> {
rectDiffGridSolverPipeline?: RectDiffGridSolverPipeline
gapFillSolver?: GapFillSolverPipeline
// Represents cutout area
boardCutoutArea: XYRect[] = []

override pipelineDef: PipelineStep<any>[] = [
definePipelineStep(
Expand All @@ -28,6 +31,7 @@ export class RectDiffPipeline extends BasePipelineSolver<RectDiffPipelineInput>
{
simpleRouteJson: rectDiffPipeline.inputProblem.simpleRouteJson,
gridOptions: rectDiffPipeline.inputProblem.gridOptions,
boardCutoutArea: rectDiffPipeline.boardCutoutArea,
},
],
),
Expand All @@ -39,11 +43,29 @@ export class RectDiffPipeline extends BasePipelineSolver<RectDiffPipelineInput>
meshNodes:
rectDiffPipeline.rectDiffGridSolverPipeline?.getOutput()
.meshNodes ?? [],
simpleRouteJson: rectDiffPipeline.inputProblem.simpleRouteJson,
boardCutoutArea: rectDiffPipeline.boardCutoutArea,
},
],
),
]

override _setup(): void {
const srj = this.inputProblem.simpleRouteJson
const bounds: XYRect = {
x: srj.bounds.minX,
y: srj.bounds.minY,
width: srj.bounds.maxX - srj.bounds.minX,
height: srj.bounds.maxY - srj.bounds.minY,
}

if (srj.outline && srj.outline.length > 2) {
this.boardCutoutArea = computeInverseRects(bounds, srj.outline)
} else {
this.boardCutoutArea = []
}
}

override getConstructorParams() {
return [this.inputProblem]
}
Expand Down
64 changes: 52 additions & 12 deletions lib/solvers/GapFillSolver/ExpandEdgesToEmptySpaceSolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,29 @@ import { EDGE_MAP, EDGES } from "./edge-constants"
import { getBoundsFromCorners } from "./getBoundsFromCorners"
import type { Bounds } from "@tscircuit/math-utils"
import { midpoint, segmentToBoxMinDistance } from "@tscircuit/math-utils"
import type { XYRect } from "lib/rectdiff-types"

const EPS = 1e-4

type ExpandEdgesToEmptySpaceSolverInput = {
inputMeshNodes: CapacityMeshNode[]
segmentsWithAdjacentEmptySpace: Array<SegmentWithAdjacentEmptySpace>
boardCutoutArea?: XYRect[]
}

export interface ExpandedSegment {
segment: SegmentWithAdjacentEmptySpace
newNode: CapacityMeshNode
}

type IndexedCapacityMeshNode = CapacityMeshNode & {
minX: number
minY: number
maxX: number
maxY: number
_boardCutout?: boolean
}

export class ExpandEdgesToEmptySpaceSolver extends BaseSolver {
unprocessedSegments: Array<SegmentWithAdjacentEmptySpace> = []
expandedSegments: Array<ExpandedSegment> = []
Expand All @@ -26,26 +41,49 @@ export class ExpandEdgesToEmptySpaceSolver extends BaseSolver {
lastSearchCorner2: { x: number; y: number } | null = null
lastExpandedSegment: ExpandedSegment | null = null

rectSpatialIndex: RBush<CapacityMeshNode>
rectSpatialIndex: RBush<IndexedCapacityMeshNode>

constructor(
private input: {
inputMeshNodes: CapacityMeshNode[]
segmentsWithAdjacentEmptySpace: Array<SegmentWithAdjacentEmptySpace>
},
) {
constructor(private input: ExpandEdgesToEmptySpaceSolverInput) {
super()
this.unprocessedSegments = [...this.input.segmentsWithAdjacentEmptySpace]
this.rectSpatialIndex = new RBush<CapacityMeshNode>()
this.rectSpatialIndex.load(
const allLayerZs = Array.from(
new Set(
this.input.inputMeshNodes.flatMap((n) => n.availableZ ?? []).sort(),
),
)

// Add board cutout areas as special mesh nodes that block expansion
const boardCutoutNodes: IndexedCapacityMeshNode[] = (
this.input.boardCutoutArea ?? []
).map((rect, idx) => {
const center = {
x: rect.x + rect.width / 2,
y: rect.y + rect.height / 2,
}
return {
capacityMeshNodeId: `board-void-${idx}`,
center,
width: rect.width,
height: rect.height,
availableZ: allLayerZs.length ? allLayerZs : [0],
layer: "board-void",
_boardCutout: true,
minX: center.x - rect.width / 2,
minY: center.y - rect.height / 2,
maxX: center.x + rect.width / 2,
maxY: center.y + rect.height / 2,
}
})
const meshNodesForIndex: IndexedCapacityMeshNode[] =
this.input.inputMeshNodes.map((n) => ({
...n,
minX: n.center.x - n.width / 2,
minY: n.center.y - n.height / 2,
maxX: n.center.x + n.width / 2,
maxY: n.center.y + n.height / 2,
})),
)
}))
this.rectSpatialIndex = new RBush<IndexedCapacityMeshNode>()
this.rectSpatialIndex.load([...meshNodesForIndex, ...boardCutoutNodes])
}

override _step() {
Expand Down Expand Up @@ -105,7 +143,9 @@ export class ExpandEdgesToEmptySpaceSolver extends BaseSolver {
this.lastSearchBounds = searchBounds
collidingNodes = this.rectSpatialIndex
.search(searchBounds)
.filter((n) => n.availableZ.includes(segment.z))
.filter(
(n) => (n._boardCutout ?? false) || n.availableZ.includes(segment.z),
)
.filter(
(n) => n.capacityMeshNodeId !== segment.parent.capacityMeshNodeId,
)
Expand Down
20 changes: 8 additions & 12 deletions lib/solvers/GapFillSolver/GapFillSolverPipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@ import {
import type { SimpleRouteJson } from "lib/types/srj-types"
import type { CapacityMeshNode } from "lib/types/capacity-mesh-types"
import type { GraphicsObject } from "graphics-debug"
import type { XYRect } from "lib/rectdiff-types"
import { FindSegmentsWithAdjacentEmptySpaceSolver } from "./FindSegmentsWithAdjacentEmptySpaceSolver"
import { ExpandEdgesToEmptySpaceSolver } from "./ExpandEdgesToEmptySpaceSolver"

export class GapFillSolverPipeline extends BasePipelineSolver<{
type GapFillSolverPipelineInput = {
meshNodes: CapacityMeshNode[]
}> {
simpleRouteJson?: SimpleRouteJson
boardCutoutArea?: XYRect[]
}

export class GapFillSolverPipeline extends BasePipelineSolver<GapFillSolverPipelineInput> {
findSegmentsWithAdjacentEmptySpaceSolver?: FindSegmentsWithAdjacentEmptySpaceSolver
expandEdgesToEmptySpaceSolver?: ExpandEdgesToEmptySpaceSolver

Expand All @@ -24,11 +29,6 @@ export class GapFillSolverPipeline extends BasePipelineSolver<{
meshNodes: gapFillPipeline.inputProblem.meshNodes,
},
],
{
onSolved: () => {
// Gap fill solver completed
},
},
),
definePipelineStep(
"expandEdgesToEmptySpaceSolver",
Expand All @@ -39,13 +39,9 @@ export class GapFillSolverPipeline extends BasePipelineSolver<{
segmentsWithAdjacentEmptySpace:
gapFillPipeline.findSegmentsWithAdjacentEmptySpaceSolver!.getOutput()
.segmentsWithAdjacentEmptySpace,
boardCutoutArea: gapFillPipeline.inputProblem.boardCutoutArea,
},
],
{
onSolved: () => {
// Gap fill solver completed
},
},
),
] as const

Expand Down
24 changes: 11 additions & 13 deletions lib/solvers/RectDiffExpansionSolver/RectDiffExpansionSolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@ import { rectsToMeshNodes } from "./rectsToMeshNodes"
import type { XYRect, Candidate3D, Placed3D } from "../../rectdiff-types"
import type { SimpleRouteJson } from "../../types/srj-types"

type RectDiffExpansionOptions = {
gridSizes: number[]
// the engine only uses gridSizes here, other options are ignored
} & Record<string, any>

export type RectDiffExpansionSolverSnapshot = {
srj: SimpleRouteJson
layerNames: string[]
layerCount: number
bounds: XYRect
options: {
gridSizes: number[]
// the engine only uses gridSizes here, other options are ignored
[key: string]: any
}
options: RectDiffExpansionOptions
obstaclesByLayer: XYRect[][]
boardVoidRects: XYRect[]
gridIndex: number
candidates: Candidate3D[]
placed: Placed3D[]
Expand All @@ -33,6 +33,7 @@ export type RectDiffExpansionSolverSnapshot = {

export type RectDiffExpansionSolverInput = {
initialSnapshot: RectDiffExpansionSolverSnapshot
boardCutoutArea: XYRect[]
}

/**
Expand All @@ -47,13 +48,9 @@ export class RectDiffExpansionSolver extends BaseSolver {
private layerNames!: string[]
private layerCount!: number
private bounds!: XYRect
private options!: {
gridSizes: number[]
// the engine only uses gridSizes here, other options are ignored
[key: string]: any
}
private options!: RectDiffExpansionOptions
private obstaclesByLayer!: XYRect[][]
private boardVoidRects!: XYRect[]
private boardCutoutArea!: XYRect[]
private gridIndex!: number
private candidates!: Candidate3D[]
private placed!: Placed3D[]
Expand All @@ -69,6 +66,7 @@ export class RectDiffExpansionSolver extends BaseSolver {
super()
// Copy engine snapshot fields directly onto this solver instance
Object.assign(this, this.input.initialSnapshot)
this.boardCutoutArea = this.input.boardCutoutArea
}

override _setup() {
Expand Down Expand Up @@ -153,7 +151,7 @@ export class RectDiffExpansionSolver extends BaseSolver {
const rects = finalizeRects({
placed: this.placed,
obstaclesByLayer: this.obstaclesByLayer,
boardVoidRects: this.boardVoidRects,
boardCutoutArea: this.boardCutoutArea,
})
this._meshNodes = rectsToMeshNodes(rects)
this.solved = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
type PipelineStep,
} from "@tscircuit/solver-utils"
import type { SimpleRouteJson } from "../../types/srj-types"
import type { GridFill3DOptions } from "../../rectdiff-types"
import type { GridFill3DOptions, XYRect, Placed3D } from "../../rectdiff-types"
import type { CapacityMeshNode } from "../../types/capacity-mesh-types"
import { RectDiffSeedingSolver } from "../RectDiffSeedingSolver/RectDiffSeedingSolver"
import { RectDiffExpansionSolver } from "../RectDiffExpansionSolver/RectDiffExpansionSolver"
Expand All @@ -13,6 +13,7 @@ import type { GraphicsObject } from "graphics-debug"
export type RectDiffGridSolverPipelineInput = {
simpleRouteJson: SimpleRouteJson
gridOptions?: Partial<GridFill3DOptions>
boardCutoutArea?: XYRect[]
}

export class RectDiffGridSolverPipeline extends BasePipelineSolver<RectDiffGridSolverPipelineInput> {
Expand All @@ -27,6 +28,7 @@ export class RectDiffGridSolverPipeline extends BasePipelineSolver<RectDiffGridS
{
simpleRouteJson: pipeline.inputProblem.simpleRouteJson,
gridOptions: pipeline.inputProblem.gridOptions,
boardCutoutArea: pipeline.inputProblem.boardCutoutArea ?? [],
},
],
),
Expand All @@ -36,6 +38,7 @@ export class RectDiffGridSolverPipeline extends BasePipelineSolver<RectDiffGridS
(pipeline: RectDiffGridSolverPipeline) => [
{
initialSnapshot: pipeline.rectDiffSeedingSolver!.getOutput(),
boardCutoutArea: pipeline.inputProblem.boardCutoutArea ?? [],
},
],
),
Expand All @@ -52,7 +55,7 @@ export class RectDiffGridSolverPipeline extends BasePipelineSolver<RectDiffGridS
if (this.rectDiffSeedingSolver) {
const snapshot = this.rectDiffSeedingSolver.getOutput()
const meshNodes: CapacityMeshNode[] = snapshot.placed.map(
(placement: any, idx: number) => ({
(placement: Placed3D, idx: number) => ({
capacityMeshNodeId: `grid-${idx}`,
center: {
x: placement.rect.x + placement.rect.width / 2,
Expand Down
Loading