Skip to content

Commit 86b8743

Browse files
authored
Merge branch 'main' into snapshot-test
2 parents aa84bb7 + e11fca0 commit 86b8743

File tree

15 files changed

+370
-171
lines changed

15 files changed

+370
-171
lines changed

lib/solvers/RectDiffExpansionSolver/RectDiffExpansionSolver.ts

Lines changed: 59 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
import { BaseSolver } from "@tscircuit/solver-utils"
22
import type { GraphicsObject } from "graphics-debug"
3-
import type { CapacityMeshNode } from "../../types/capacity-mesh-types"
3+
import type { CapacityMeshNode, RTreeRect } from "lib/types/capacity-mesh-types"
44
import { expandRectFromSeed } from "../../utils/expandRectFromSeed"
55
import { finalizeRects } from "../../utils/finalizeRects"
66
import { allLayerNode } from "../../utils/buildHardPlacedByLayer"
77
import { resizeSoftOverlaps } from "../../utils/resizeSoftOverlaps"
88
import { rectsToMeshNodes } from "./rectsToMeshNodes"
99
import type { XYRect, Candidate3D, Placed3D } from "../../rectdiff-types"
10-
import type { SimpleRouteJson } from "../../types/srj-types"
10+
import type { SimpleRouteJson } from "lib/types/srj-types"
11+
import {
12+
buildZIndexMap,
13+
obstacleToXYRect,
14+
obstacleZs,
15+
} from "../RectDiffSeedingSolver/layers"
16+
import RBush from "rbush"
17+
import { rectToTree } from "../../utils/rectToTree"
18+
import { sameTreeRect } from "../../utils/sameTreeRect"
1119

1220
export type RectDiffExpansionSolverSnapshot = {
1321
srj: SimpleRouteJson
@@ -19,12 +27,10 @@ export type RectDiffExpansionSolverSnapshot = {
1927
// the engine only uses gridSizes here, other options are ignored
2028
[key: string]: any
2129
}
22-
obstaclesByLayer: XYRect[][]
2330
boardVoidRects: XYRect[]
2431
gridIndex: number
2532
candidates: Candidate3D[]
2633
placed: Placed3D[]
27-
placedByLayer: XYRect[][]
2834
expansionIndex: number
2935
edgeAnalysisDone: boolean
3036
totalSeedsThisGrid: number
@@ -33,6 +39,7 @@ export type RectDiffExpansionSolverSnapshot = {
3339

3440
export type RectDiffExpansionSolverInput = {
3541
initialSnapshot: RectDiffExpansionSolverSnapshot
42+
obstacleIndexByLayer: Array<RBush<RTreeRect>>
3643
}
3744

3845
/**
@@ -52,12 +59,11 @@ export class RectDiffExpansionSolver extends BaseSolver {
5259
// the engine only uses gridSizes here, other options are ignored
5360
[key: string]: any
5461
}
55-
private obstaclesByLayer!: XYRect[][]
5662
private boardVoidRects!: XYRect[]
5763
private gridIndex!: number
5864
private candidates!: Candidate3D[]
5965
private placed!: Placed3D[]
60-
private placedByLayer!: XYRect[][]
66+
private placedIndexByLayer!: Array<RBush<RTreeRect>>
6167
private expansionIndex!: number
6268
private edgeAnalysisDone!: boolean
6369
private totalSeedsThisGrid!: number
@@ -75,6 +81,44 @@ export class RectDiffExpansionSolver extends BaseSolver {
7581
this.stats = {
7682
gridIndex: this.gridIndex,
7783
}
84+
85+
if (this.input.obstacleIndexByLayer) {
86+
} else {
87+
const { zIndexByName } = buildZIndexMap(this.srj)
88+
this.input.obstacleIndexByLayer = Array.from(
89+
{ length: this.layerCount },
90+
() => new RBush<RTreeRect>(),
91+
)
92+
const insertObstacle = (rect: XYRect, z: number) => {
93+
const tree = this.input.obstacleIndexByLayer[z]
94+
if (tree) tree.insert(rectToTree(rect))
95+
}
96+
for (const voidRect of this.boardVoidRects ?? []) {
97+
for (let z = 0; z < this.layerCount; z++) insertObstacle(voidRect, z)
98+
}
99+
for (const obstacle of this.srj.obstacles ?? []) {
100+
const rect = obstacleToXYRect(obstacle as any)
101+
if (!rect) continue
102+
const zLayers =
103+
obstacle.zLayers?.length && obstacle.zLayers.length > 0
104+
? obstacle.zLayers
105+
: obstacleZs(obstacle as any, zIndexByName)
106+
zLayers.forEach((z) => {
107+
if (z >= 0 && z < this.layerCount) insertObstacle(rect, z)
108+
})
109+
}
110+
}
111+
112+
this.placedIndexByLayer = Array.from(
113+
{ length: this.layerCount },
114+
() => new RBush<RTreeRect>(),
115+
)
116+
for (const placement of this.placed ?? []) {
117+
for (const z of placement.zLayers) {
118+
const tree = this.placedIndexByLayer[z]
119+
if (tree) tree.insert(rectToTree(placement.rect))
120+
}
121+
}
78122
}
79123

80124
override _step() {
@@ -107,7 +151,8 @@ export class RectDiffExpansionSolver extends BaseSolver {
107151
// HARD blockers only: obstacles on p.zLayers + full-stack nodes
108152
const hardBlockers: XYRect[] = []
109153
for (const z of p.zLayers) {
110-
hardBlockers.push(...(this.obstaclesByLayer[z] ?? []))
154+
const obstacleTree = this.input.obstacleIndexByLayer[z]
155+
if (obstacleTree) hardBlockers.push(...obstacleTree.all())
111156
hardBlockers.push(...(hardPlacedByLayer[z] ?? []))
112157
}
113158

@@ -127,18 +172,20 @@ export class RectDiffExpansionSolver extends BaseSolver {
127172
// Update placement + per-layer index (replace old rect object)
128173
this.placed[idx] = { rect: expanded, zLayers: p.zLayers }
129174
for (const z of p.zLayers) {
130-
const arr = this.placedByLayer[z]!
131-
const j = arr.findIndex((r) => r === oldRect)
132-
if (j >= 0) arr[j] = expanded
175+
const tree = this.placedIndexByLayer[z]
176+
if (tree) {
177+
tree.remove(rectToTree(oldRect), sameTreeRect)
178+
tree.insert(rectToTree(expanded))
179+
}
133180
}
134181

135182
// Carve overlapped soft neighbors (respect full-stack nodes)
136183
resizeSoftOverlaps(
137184
{
138185
layerCount: this.layerCount,
139186
placed: this.placed,
140-
placedByLayer: this.placedByLayer,
141187
options: this.options,
188+
placedIndexByLayer: this.placedIndexByLayer,
142189
},
143190
idx,
144191
)
@@ -152,7 +199,7 @@ export class RectDiffExpansionSolver extends BaseSolver {
152199

153200
const rects = finalizeRects({
154201
placed: this.placed,
155-
obstaclesByLayer: this.obstaclesByLayer,
202+
srj: this.srj,
156203
boardVoidRects: this.boardVoidRects,
157204
})
158205
this._meshNodes = rectsToMeshNodes(rects)

lib/solvers/RectDiffGridSolverPipeline/RectDiffGridSolverPipeline.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ import {
33
definePipelineStep,
44
type PipelineStep,
55
} from "@tscircuit/solver-utils"
6-
import type { SimpleRouteJson } from "../../types/srj-types"
7-
import type { GridFill3DOptions } from "../../rectdiff-types"
8-
import type { CapacityMeshNode } from "../../types/capacity-mesh-types"
9-
import { RectDiffSeedingSolver } from "../RectDiffSeedingSolver/RectDiffSeedingSolver"
10-
import { RectDiffExpansionSolver } from "../RectDiffExpansionSolver/RectDiffExpansionSolver"
6+
import type { SimpleRouteJson } from "lib/types/srj-types"
7+
import type { GridFill3DOptions, XYRect } from "lib/rectdiff-types"
8+
import type { CapacityMeshNode, RTreeRect } from "lib/types/capacity-mesh-types"
9+
import { RectDiffSeedingSolver } from "lib/solvers/RectDiffSeedingSolver/RectDiffSeedingSolver"
10+
import { RectDiffExpansionSolver } from "lib/solvers/RectDiffExpansionSolver/RectDiffExpansionSolver"
1111
import type { GraphicsObject } from "graphics-debug"
12+
import RBush from "rbush"
13+
import { buildObstacleIndexes } from "./buildObstacleIndexes"
1214

1315
export type RectDiffGridSolverPipelineInput = {
1416
simpleRouteJson: SimpleRouteJson
@@ -18,6 +20,17 @@ export type RectDiffGridSolverPipelineInput = {
1820
export class RectDiffGridSolverPipeline extends BasePipelineSolver<RectDiffGridSolverPipelineInput> {
1921
rectDiffSeedingSolver?: RectDiffSeedingSolver
2022
rectDiffExpansionSolver?: RectDiffExpansionSolver
23+
private boardVoidRects?: XYRect[]
24+
private obstacleIndexByLayer: Array<RBush<RTreeRect>>
25+
26+
constructor(inputProblem: RectDiffGridSolverPipelineInput) {
27+
super(inputProblem)
28+
const { obstacleIndexByLayer, boardVoidRects } = buildObstacleIndexes(
29+
inputProblem.simpleRouteJson,
30+
)
31+
this.obstacleIndexByLayer = obstacleIndexByLayer
32+
this.boardVoidRects = boardVoidRects
33+
}
2134

2235
override pipelineDef: PipelineStep<any>[] = [
2336
definePipelineStep(
@@ -27,6 +40,8 @@ export class RectDiffGridSolverPipeline extends BasePipelineSolver<RectDiffGridS
2740
{
2841
simpleRouteJson: pipeline.inputProblem.simpleRouteJson,
2942
gridOptions: pipeline.inputProblem.gridOptions,
43+
obstacleIndexByLayer: pipeline.obstacleIndexByLayer,
44+
boardVoidRects: pipeline.boardVoidRects,
3045
},
3146
],
3247
),
@@ -35,7 +50,11 @@ export class RectDiffGridSolverPipeline extends BasePipelineSolver<RectDiffGridS
3550
RectDiffExpansionSolver,
3651
(pipeline: RectDiffGridSolverPipeline) => [
3752
{
38-
initialSnapshot: pipeline.rectDiffSeedingSolver!.getOutput(),
53+
initialSnapshot: {
54+
...pipeline.rectDiffSeedingSolver!.getOutput(),
55+
boardVoidRects: pipeline.boardVoidRects ?? [],
56+
},
57+
obstacleIndexByLayer: pipeline.obstacleIndexByLayer,
3958
},
4059
],
4160
),
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import type { SimpleRouteJson } from "lib/types/srj-types"
2+
import RBush from "rbush"
3+
import { computeInverseRects } from "lib/solvers/RectDiffSeedingSolver/computeInverseRects"
4+
import {
5+
buildZIndexMap,
6+
obstacleToXYRect,
7+
obstacleZs,
8+
} from "lib/solvers/RectDiffSeedingSolver/layers"
9+
import type { XYRect } from "lib/rectdiff-types"
10+
import type { RTreeRect } from "lib/types/capacity-mesh-types"
11+
12+
export const buildObstacleIndexes = (
13+
srj: SimpleRouteJson,
14+
): {
15+
obstacleIndexByLayer: Array<RBush<RTreeRect>>
16+
boardVoidRects: XYRect[]
17+
} => {
18+
const { layerNames, zIndexByName } = buildZIndexMap(srj)
19+
const layerCount = Math.max(1, layerNames.length, srj.layerCount || 1)
20+
const bounds: XYRect = {
21+
x: srj.bounds.minX,
22+
y: srj.bounds.minY,
23+
width: srj.bounds.maxX - srj.bounds.minX,
24+
height: srj.bounds.maxY - srj.bounds.minY,
25+
}
26+
const obstacleIndexByLayer = Array.from(
27+
{ length: layerCount },
28+
() => new RBush<RTreeRect>(),
29+
)
30+
31+
const insertObstacle = (rect: XYRect, z: number) => {
32+
const treeRect = {
33+
...rect,
34+
minX: rect.x,
35+
minY: rect.y,
36+
maxX: rect.x + rect.width,
37+
maxY: rect.y + rect.height,
38+
}
39+
obstacleIndexByLayer[z]?.insert(treeRect)
40+
}
41+
42+
let boardVoidRects: XYRect[] = []
43+
if (srj.outline && srj.outline.length > 2) {
44+
boardVoidRects = computeInverseRects(bounds, srj.outline as any)
45+
for (const voidRect of boardVoidRects) {
46+
for (let z = 0; z < layerCount; z++) insertObstacle(voidRect, z)
47+
}
48+
}
49+
50+
for (const obstacle of srj.obstacles ?? []) {
51+
const rect = obstacleToXYRect(obstacle as any)
52+
if (!rect) continue
53+
const zLayers = obstacleZs(obstacle as any, zIndexByName)
54+
const invalidZs = zLayers.filter((z) => z < 0 || z >= layerCount)
55+
if (invalidZs.length) {
56+
throw new Error(
57+
`RectDiff: obstacle uses z-layer indices ${invalidZs.join(",")} outside 0-${layerCount - 1}`,
58+
)
59+
}
60+
if (
61+
(!obstacle.zLayers || obstacle.zLayers.length === 0) &&
62+
zLayers.length
63+
) {
64+
obstacle.zLayers = zLayers
65+
}
66+
for (const z of zLayers) insertObstacle(rect, z)
67+
}
68+
69+
return { obstacleIndexByLayer, boardVoidRects }
70+
}

0 commit comments

Comments
 (0)