@tscircuit/rectdiff 0.0.11 → 0.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +163 -27
- package/dist/index.js +1885 -1676
- package/lib/RectDiffPipeline.ts +18 -17
- package/lib/{solvers/rectdiff/types.ts → rectdiff-types.ts} +0 -34
- package/lib/{solvers/rectdiff/visualization.ts → rectdiff-visualization.ts} +2 -1
- package/lib/solvers/GapFillSolver/FindSegmentsWithAdjacentEmptySpaceSolver.ts +9 -12
- package/lib/solvers/GapFillSolver/GapFillSolverPipeline.ts +1 -1
- package/lib/solvers/GapFillSolver/visuallyOffsetLine.ts +5 -2
- package/lib/solvers/RectDiffExpansionSolver/RectDiffExpansionSolver.ts +205 -0
- package/lib/solvers/{rectdiff → RectDiffExpansionSolver}/rectsToMeshNodes.ts +1 -2
- package/lib/solvers/RectDiffGridSolverPipeline/RectDiffGridSolverPipeline.ts +87 -0
- package/lib/solvers/RectDiffSeedingSolver/RectDiffSeedingSolver.ts +516 -0
- package/lib/solvers/RectDiffSeedingSolver/computeCandidates3D.ts +109 -0
- package/lib/solvers/RectDiffSeedingSolver/computeDefaultGridSizes.ts +9 -0
- package/lib/solvers/{rectdiff/candidates.ts → RectDiffSeedingSolver/computeEdgeCandidates3D.ts} +39 -220
- package/lib/solvers/{rectdiff/geometry → RectDiffSeedingSolver}/computeInverseRects.ts +6 -2
- package/lib/solvers/{rectdiff → RectDiffSeedingSolver}/layers.ts +4 -3
- package/lib/solvers/RectDiffSeedingSolver/longestFreeSpanAroundZ.ts +52 -0
- package/lib/utils/buildHardPlacedByLayer.ts +14 -0
- package/lib/{solvers/rectdiff/geometry.ts → utils/expandRectFromSeed.ts} +21 -120
- package/lib/utils/finalizeRects.ts +49 -0
- package/lib/utils/isFullyOccupiedAtPoint.ts +21 -0
- package/lib/utils/rectdiff-geometry.ts +94 -0
- package/lib/utils/resizeSoftOverlaps.ts +74 -0
- package/package.json +1 -1
- package/tests/board-outline.test.ts +2 -1
- package/tests/obstacle-extra-layers.test.ts +1 -1
- package/tests/obstacle-zlayers.test.ts +1 -1
- package/utils/rectsEqual.ts +2 -2
- package/utils/rectsOverlap.ts +2 -2
- package/lib/solvers/RectDiffSolver.ts +0 -231
- package/lib/solvers/rectdiff/engine.ts +0 -481
- /package/lib/solvers/{rectdiff/geometry → RectDiffSeedingSolver}/isPointInPolygon.ts +0 -0
package/lib/RectDiffPipeline.ts
CHANGED
|
@@ -4,12 +4,12 @@ import {
|
|
|
4
4
|
type PipelineStep,
|
|
5
5
|
} from "@tscircuit/solver-utils"
|
|
6
6
|
import type { SimpleRouteJson } from "./types/srj-types"
|
|
7
|
-
import type { GridFill3DOptions } from "./
|
|
8
|
-
import { RectDiffSolver } from "./solvers/RectDiffSolver"
|
|
7
|
+
import type { GridFill3DOptions } from "./rectdiff-types"
|
|
9
8
|
import type { CapacityMeshNode } from "./types/capacity-mesh-types"
|
|
10
9
|
import type { GraphicsObject } from "graphics-debug"
|
|
11
|
-
import { createBaseVisualization } from "./solvers/rectdiff/visualization"
|
|
12
10
|
import { GapFillSolverPipeline } from "./solvers/GapFillSolver/GapFillSolverPipeline"
|
|
11
|
+
import { RectDiffGridSolverPipeline } from "./solvers/RectDiffGridSolverPipeline/RectDiffGridSolverPipeline"
|
|
12
|
+
import { createBaseVisualization } from "./rectdiff-visualization"
|
|
13
13
|
|
|
14
14
|
export interface RectDiffPipelineInput {
|
|
15
15
|
simpleRouteJson: SimpleRouteJson
|
|
@@ -17,24 +17,19 @@ export interface RectDiffPipelineInput {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export class RectDiffPipeline extends BasePipelineSolver<RectDiffPipelineInput> {
|
|
20
|
-
|
|
20
|
+
rectDiffGridSolverPipeline?: RectDiffGridSolverPipeline
|
|
21
21
|
gapFillSolver?: GapFillSolverPipeline
|
|
22
22
|
|
|
23
23
|
override pipelineDef: PipelineStep<any>[] = [
|
|
24
24
|
definePipelineStep(
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
(rectDiffPipeline) => [
|
|
25
|
+
"rectDiffGridSolverPipeline",
|
|
26
|
+
RectDiffGridSolverPipeline,
|
|
27
|
+
(rectDiffPipeline: RectDiffPipeline) => [
|
|
28
28
|
{
|
|
29
29
|
simpleRouteJson: rectDiffPipeline.inputProblem.simpleRouteJson,
|
|
30
30
|
gridOptions: rectDiffPipeline.inputProblem.gridOptions,
|
|
31
31
|
},
|
|
32
32
|
],
|
|
33
|
-
{
|
|
34
|
-
onSolved: () => {
|
|
35
|
-
// RectDiff mesh generation completed
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
33
|
),
|
|
39
34
|
definePipelineStep(
|
|
40
35
|
"gapFillSolver",
|
|
@@ -42,7 +37,8 @@ export class RectDiffPipeline extends BasePipelineSolver<RectDiffPipelineInput>
|
|
|
42
37
|
(rectDiffPipeline: RectDiffPipeline) => [
|
|
43
38
|
{
|
|
44
39
|
meshNodes:
|
|
45
|
-
rectDiffPipeline.
|
|
40
|
+
rectDiffPipeline.rectDiffGridSolverPipeline?.getOutput()
|
|
41
|
+
.meshNodes ?? [],
|
|
46
42
|
},
|
|
47
43
|
],
|
|
48
44
|
),
|
|
@@ -57,7 +53,10 @@ export class RectDiffPipeline extends BasePipelineSolver<RectDiffPipelineInput>
|
|
|
57
53
|
if (gapFillOutput) {
|
|
58
54
|
return { meshNodes: gapFillOutput.outputNodes }
|
|
59
55
|
}
|
|
60
|
-
|
|
56
|
+
if (this.rectDiffGridSolverPipeline) {
|
|
57
|
+
return this.rectDiffGridSolverPipeline.getOutput()
|
|
58
|
+
}
|
|
59
|
+
return { meshNodes: [] }
|
|
61
60
|
}
|
|
62
61
|
|
|
63
62
|
override initialVisualize(): GraphicsObject {
|
|
@@ -67,8 +66,10 @@ export class RectDiffPipeline extends BasePipelineSolver<RectDiffPipelineInput>
|
|
|
67
66
|
"RectDiffPipeline - Initial",
|
|
68
67
|
)
|
|
69
68
|
|
|
70
|
-
// Show initial mesh nodes from
|
|
71
|
-
const initialNodes =
|
|
69
|
+
// Show initial mesh nodes from grid pipeline if available
|
|
70
|
+
const initialNodes =
|
|
71
|
+
this.rectDiffGridSolverPipeline?.getOutput().meshNodes ?? []
|
|
72
|
+
|
|
72
73
|
for (const node of initialNodes) {
|
|
73
74
|
graphics.rects!.push({
|
|
74
75
|
center: node.center,
|
|
@@ -95,7 +96,7 @@ export class RectDiffPipeline extends BasePipelineSolver<RectDiffPipelineInput>
|
|
|
95
96
|
|
|
96
97
|
const { meshNodes: outputNodes } = this.getOutput()
|
|
97
98
|
const initialNodeIds = new Set(
|
|
98
|
-
(this.
|
|
99
|
+
(this.rectDiffGridSolverPipeline?.getOutput().meshNodes ?? []).map(
|
|
99
100
|
(n) => n.capacityMeshNodeId,
|
|
100
101
|
),
|
|
101
102
|
)
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
// lib/solvers/rectdiff/types.ts
|
|
2
|
-
import type { SimpleRouteJson } from "../../types/srj-types"
|
|
3
|
-
|
|
4
2
|
export type XYRect = { x: number; y: number; width: number; height: number }
|
|
5
3
|
|
|
6
4
|
export type Rect3d = {
|
|
@@ -33,35 +31,3 @@ export type Candidate3D = {
|
|
|
33
31
|
isEdgeSeed?: boolean
|
|
34
32
|
}
|
|
35
33
|
export type Placed3D = { rect: XYRect; zLayers: number[] }
|
|
36
|
-
|
|
37
|
-
export type Phase = "GRID" | "EXPANSION" | "GAP_FILL" | "DONE"
|
|
38
|
-
|
|
39
|
-
export type RectDiffState = {
|
|
40
|
-
// static
|
|
41
|
-
srj: SimpleRouteJson
|
|
42
|
-
layerNames: string[]
|
|
43
|
-
layerCount: number
|
|
44
|
-
bounds: XYRect
|
|
45
|
-
options: Required<
|
|
46
|
-
Omit<GridFill3DOptions, "gridSizes" | "maxMultiLayerSpan">
|
|
47
|
-
> & {
|
|
48
|
-
gridSizes: number[]
|
|
49
|
-
maxMultiLayerSpan: number | undefined
|
|
50
|
-
}
|
|
51
|
-
obstaclesByLayer: XYRect[][]
|
|
52
|
-
boardVoidRects: XYRect[] // newly added for viz
|
|
53
|
-
|
|
54
|
-
// evolving
|
|
55
|
-
phase: Phase
|
|
56
|
-
gridIndex: number // index in gridSizes
|
|
57
|
-
candidates: Candidate3D[]
|
|
58
|
-
placed: Placed3D[]
|
|
59
|
-
placedByLayer: XYRect[][]
|
|
60
|
-
expansionIndex: number
|
|
61
|
-
/** Whether we've already run the edge-analysis seeding pass. */
|
|
62
|
-
edgeAnalysisDone: boolean
|
|
63
|
-
|
|
64
|
-
// progress bookkeeping
|
|
65
|
-
totalSeedsThisGrid: number
|
|
66
|
-
consumedSeedsThisGrid: number
|
|
67
|
-
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { GraphicsObject } from "graphics-debug"
|
|
2
|
-
import type { SimpleRouteJson } from "
|
|
2
|
+
import type { SimpleRouteJson } from "./types/srj-types"
|
|
3
|
+
import type { XYRect } from "./rectdiff-types"
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Create basic visualization showing board bounds/outline and obstacles.
|
|
@@ -158,8 +158,7 @@ export class FindSegmentsWithAdjacentEmptySpaceSolver extends BaseSolver {
|
|
|
158
158
|
graphics.lines.push({
|
|
159
159
|
points: visuallyOffsetLine(
|
|
160
160
|
[unprocessedEdge.start, unprocessedEdge.end],
|
|
161
|
-
unprocessedEdge.facingDirection,
|
|
162
|
-
-0.1,
|
|
161
|
+
{ dir: unprocessedEdge.facingDirection, amt: -0.1 },
|
|
163
162
|
),
|
|
164
163
|
strokeColor: "rgba(0, 0, 255, 0.5)",
|
|
165
164
|
strokeDash: "5 5",
|
|
@@ -183,11 +182,10 @@ export class FindSegmentsWithAdjacentEmptySpaceSolver extends BaseSolver {
|
|
|
183
182
|
if (this.lastOverlappingEdges) {
|
|
184
183
|
for (const edge of this.lastOverlappingEdges) {
|
|
185
184
|
graphics.lines.push({
|
|
186
|
-
points: visuallyOffsetLine(
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
),
|
|
185
|
+
points: visuallyOffsetLine([edge.start, edge.end], {
|
|
186
|
+
dir: edge.facingDirection,
|
|
187
|
+
amt: 0.05,
|
|
188
|
+
}),
|
|
191
189
|
strokeColor: "red",
|
|
192
190
|
strokeDash: "2 2",
|
|
193
191
|
})
|
|
@@ -197,11 +195,10 @@ export class FindSegmentsWithAdjacentEmptySpaceSolver extends BaseSolver {
|
|
|
197
195
|
if (this.lastUncoveredSegments) {
|
|
198
196
|
for (const edge of this.lastUncoveredSegments) {
|
|
199
197
|
graphics.lines.push({
|
|
200
|
-
points: visuallyOffsetLine(
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
),
|
|
198
|
+
points: visuallyOffsetLine([edge.start, edge.end], {
|
|
199
|
+
dir: edge.facingDirection,
|
|
200
|
+
amt: -0.05,
|
|
201
|
+
}),
|
|
205
202
|
strokeColor: "green",
|
|
206
203
|
strokeDash: "2 2",
|
|
207
204
|
})
|
|
@@ -21,9 +21,12 @@ const OFFSET_DIR_MAP = {
|
|
|
21
21
|
*/
|
|
22
22
|
export const visuallyOffsetLine = (
|
|
23
23
|
line: Array<{ x: number; y: number }>,
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
options: {
|
|
25
|
+
dir: "x-" | "x+" | "y-" | "y+"
|
|
26
|
+
amt: number
|
|
27
|
+
},
|
|
26
28
|
) => {
|
|
29
|
+
const { dir, amt } = options
|
|
27
30
|
const offset = OFFSET_DIR_MAP[dir]
|
|
28
31
|
return line.map((p) => ({
|
|
29
32
|
x: p.x + offset.x * amt,
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { BaseSolver } from "@tscircuit/solver-utils"
|
|
2
|
+
import type { GraphicsObject } from "graphics-debug"
|
|
3
|
+
import type { CapacityMeshNode } from "../../types/capacity-mesh-types"
|
|
4
|
+
import { expandRectFromSeed } from "../../utils/expandRectFromSeed"
|
|
5
|
+
import { finalizeRects } from "../../utils/finalizeRects"
|
|
6
|
+
import { allLayerNode } from "../../utils/buildHardPlacedByLayer"
|
|
7
|
+
import { resizeSoftOverlaps } from "../../utils/resizeSoftOverlaps"
|
|
8
|
+
import { rectsToMeshNodes } from "./rectsToMeshNodes"
|
|
9
|
+
import type { XYRect, Candidate3D, Placed3D } from "../../rectdiff-types"
|
|
10
|
+
import type { SimpleRouteJson } from "../../types/srj-types"
|
|
11
|
+
|
|
12
|
+
export type RectDiffExpansionSolverSnapshot = {
|
|
13
|
+
srj: SimpleRouteJson
|
|
14
|
+
layerNames: string[]
|
|
15
|
+
layerCount: number
|
|
16
|
+
bounds: XYRect
|
|
17
|
+
options: {
|
|
18
|
+
gridSizes: number[]
|
|
19
|
+
// the engine only uses gridSizes here, other options are ignored
|
|
20
|
+
[key: string]: any
|
|
21
|
+
}
|
|
22
|
+
obstaclesByLayer: XYRect[][]
|
|
23
|
+
boardVoidRects: XYRect[]
|
|
24
|
+
gridIndex: number
|
|
25
|
+
candidates: Candidate3D[]
|
|
26
|
+
placed: Placed3D[]
|
|
27
|
+
placedByLayer: XYRect[][]
|
|
28
|
+
expansionIndex: number
|
|
29
|
+
edgeAnalysisDone: boolean
|
|
30
|
+
totalSeedsThisGrid: number
|
|
31
|
+
consumedSeedsThisGrid: number
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type RectDiffExpansionSolverInput = {
|
|
35
|
+
initialSnapshot: RectDiffExpansionSolverSnapshot
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Second phase of RectDiff: expand placed rects to their maximal extents.
|
|
40
|
+
*
|
|
41
|
+
* This solver takes the intermediate data produced by RectDiffSeedingSolver
|
|
42
|
+
* and runs the EXPANSION phase, then finalizes to capacity mesh nodes.
|
|
43
|
+
*/
|
|
44
|
+
export class RectDiffExpansionSolver extends BaseSolver {
|
|
45
|
+
// Engine fields (same shape used by rectdiff/engine.ts)
|
|
46
|
+
private srj!: SimpleRouteJson
|
|
47
|
+
private layerNames!: string[]
|
|
48
|
+
private layerCount!: number
|
|
49
|
+
private bounds!: XYRect
|
|
50
|
+
private options!: {
|
|
51
|
+
gridSizes: number[]
|
|
52
|
+
// the engine only uses gridSizes here, other options are ignored
|
|
53
|
+
[key: string]: any
|
|
54
|
+
}
|
|
55
|
+
private obstaclesByLayer!: XYRect[][]
|
|
56
|
+
private boardVoidRects!: XYRect[]
|
|
57
|
+
private gridIndex!: number
|
|
58
|
+
private candidates!: Candidate3D[]
|
|
59
|
+
private placed!: Placed3D[]
|
|
60
|
+
private placedByLayer!: XYRect[][]
|
|
61
|
+
private expansionIndex!: number
|
|
62
|
+
private edgeAnalysisDone!: boolean
|
|
63
|
+
private totalSeedsThisGrid!: number
|
|
64
|
+
private consumedSeedsThisGrid!: number
|
|
65
|
+
|
|
66
|
+
private _meshNodes: CapacityMeshNode[] = []
|
|
67
|
+
|
|
68
|
+
constructor(private input: RectDiffExpansionSolverInput) {
|
|
69
|
+
super()
|
|
70
|
+
// Copy engine snapshot fields directly onto this solver instance
|
|
71
|
+
Object.assign(this, this.input.initialSnapshot)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
override _setup() {
|
|
75
|
+
this.stats = {
|
|
76
|
+
gridIndex: this.gridIndex,
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
override _step() {
|
|
81
|
+
if (this.solved) return
|
|
82
|
+
|
|
83
|
+
this._stepExpansion()
|
|
84
|
+
|
|
85
|
+
this.stats.gridIndex = this.gridIndex
|
|
86
|
+
this.stats.placed = this.placed.length
|
|
87
|
+
|
|
88
|
+
if (this.expansionIndex >= this.placed.length) {
|
|
89
|
+
this.finalizeIfNeeded()
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private _stepExpansion(): void {
|
|
94
|
+
if (this.expansionIndex >= this.placed.length) {
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const idx = this.expansionIndex
|
|
99
|
+
const p = this.placed[idx]!
|
|
100
|
+
const lastGrid = this.options.gridSizes[this.options.gridSizes.length - 1]!
|
|
101
|
+
|
|
102
|
+
const hardPlacedByLayer = allLayerNode({
|
|
103
|
+
layerCount: this.layerCount,
|
|
104
|
+
placed: this.placed,
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
// HARD blockers only: obstacles on p.zLayers + full-stack nodes
|
|
108
|
+
const hardBlockers: XYRect[] = []
|
|
109
|
+
for (const z of p.zLayers) {
|
|
110
|
+
hardBlockers.push(...(this.obstaclesByLayer[z] ?? []))
|
|
111
|
+
hardBlockers.push(...(hardPlacedByLayer[z] ?? []))
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const oldRect = p.rect
|
|
115
|
+
const expanded = expandRectFromSeed({
|
|
116
|
+
startX: p.rect.x + p.rect.width / 2,
|
|
117
|
+
startY: p.rect.y + p.rect.height / 2,
|
|
118
|
+
gridSize: lastGrid,
|
|
119
|
+
bounds: this.bounds,
|
|
120
|
+
blockers: hardBlockers,
|
|
121
|
+
initialCellRatio: 0,
|
|
122
|
+
maxAspectRatio: null,
|
|
123
|
+
minReq: { width: p.rect.width, height: p.rect.height },
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
if (expanded) {
|
|
127
|
+
// Update placement + per-layer index (replace old rect object)
|
|
128
|
+
this.placed[idx] = { rect: expanded, zLayers: p.zLayers }
|
|
129
|
+
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
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Carve overlapped soft neighbors (respect full-stack nodes)
|
|
136
|
+
resizeSoftOverlaps(
|
|
137
|
+
{
|
|
138
|
+
layerCount: this.layerCount,
|
|
139
|
+
placed: this.placed,
|
|
140
|
+
placedByLayer: this.placedByLayer,
|
|
141
|
+
options: this.options,
|
|
142
|
+
},
|
|
143
|
+
idx,
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
this.expansionIndex += 1
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private finalizeIfNeeded() {
|
|
151
|
+
if (this.solved) return
|
|
152
|
+
|
|
153
|
+
const rects = finalizeRects({
|
|
154
|
+
placed: this.placed,
|
|
155
|
+
obstaclesByLayer: this.obstaclesByLayer,
|
|
156
|
+
boardVoidRects: this.boardVoidRects,
|
|
157
|
+
})
|
|
158
|
+
this._meshNodes = rectsToMeshNodes(rects)
|
|
159
|
+
this.solved = true
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
computeProgress(): number {
|
|
163
|
+
if (this.solved) return 1
|
|
164
|
+
const grids = this.options.gridSizes.length
|
|
165
|
+
const base = grids / (grids + 1)
|
|
166
|
+
const denom = Math.max(1, this.placed.length)
|
|
167
|
+
const frac = denom ? this.expansionIndex / denom : 1
|
|
168
|
+
return Math.min(0.999, base + frac * (1 / (grids + 1)))
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
override getOutput(): { meshNodes: CapacityMeshNode[] } {
|
|
172
|
+
if (!this.solved && this._meshNodes.length === 0) {
|
|
173
|
+
this.finalizeIfNeeded()
|
|
174
|
+
}
|
|
175
|
+
return { meshNodes: this._meshNodes }
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Simple visualization of expanded placements. */
|
|
179
|
+
override visualize(): GraphicsObject {
|
|
180
|
+
const rects: NonNullable<GraphicsObject["rects"]> = []
|
|
181
|
+
|
|
182
|
+
for (const placement of this.placed ?? []) {
|
|
183
|
+
rects.push({
|
|
184
|
+
center: {
|
|
185
|
+
x: placement.rect.x + placement.rect.width / 2,
|
|
186
|
+
y: placement.rect.y + placement.rect.height / 2,
|
|
187
|
+
},
|
|
188
|
+
width: placement.rect.width,
|
|
189
|
+
height: placement.rect.height,
|
|
190
|
+
stroke: "rgba(37, 99, 235, 0.9)",
|
|
191
|
+
fill: "rgba(191, 219, 254, 0.5)",
|
|
192
|
+
layer: `z${placement.zLayers.join(",")}`,
|
|
193
|
+
label: `expanded\nz:${placement.zLayers.join(",")}`,
|
|
194
|
+
})
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
title: "RectDiff Expansion",
|
|
199
|
+
coordinateSystem: "cartesian",
|
|
200
|
+
rects,
|
|
201
|
+
points: [],
|
|
202
|
+
lines: [],
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
// lib/solvers/rectdiff/rectsToMeshNodes.ts
|
|
2
|
-
import type { Rect3d } from "./types"
|
|
3
1
|
import type { CapacityMeshNode } from "../../types/capacity-mesh-types"
|
|
2
|
+
import type { Rect3d } from "../../rectdiff-types"
|
|
4
3
|
|
|
5
4
|
export function rectsToMeshNodes(rects: Rect3d[]): CapacityMeshNode[] {
|
|
6
5
|
let id = 0
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BasePipelineSolver,
|
|
3
|
+
definePipelineStep,
|
|
4
|
+
type PipelineStep,
|
|
5
|
+
} 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"
|
|
11
|
+
import type { GraphicsObject } from "graphics-debug"
|
|
12
|
+
|
|
13
|
+
export type RectDiffGridSolverPipelineInput = {
|
|
14
|
+
simpleRouteJson: SimpleRouteJson
|
|
15
|
+
gridOptions?: Partial<GridFill3DOptions>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class RectDiffGridSolverPipeline extends BasePipelineSolver<RectDiffGridSolverPipelineInput> {
|
|
19
|
+
rectDiffSeedingSolver?: RectDiffSeedingSolver
|
|
20
|
+
rectDiffExpansionSolver?: RectDiffExpansionSolver
|
|
21
|
+
|
|
22
|
+
override pipelineDef: PipelineStep<any>[] = [
|
|
23
|
+
definePipelineStep(
|
|
24
|
+
"rectDiffSeedingSolver",
|
|
25
|
+
RectDiffSeedingSolver,
|
|
26
|
+
(pipeline: RectDiffGridSolverPipeline) => [
|
|
27
|
+
{
|
|
28
|
+
simpleRouteJson: pipeline.inputProblem.simpleRouteJson,
|
|
29
|
+
gridOptions: pipeline.inputProblem.gridOptions,
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
),
|
|
33
|
+
definePipelineStep(
|
|
34
|
+
"rectDiffExpansionSolver",
|
|
35
|
+
RectDiffExpansionSolver,
|
|
36
|
+
(pipeline: RectDiffGridSolverPipeline) => [
|
|
37
|
+
{
|
|
38
|
+
initialSnapshot: pipeline.rectDiffSeedingSolver!.getOutput(),
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
),
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
override getConstructorParams() {
|
|
45
|
+
return [this.inputProblem]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
override getOutput(): { meshNodes: CapacityMeshNode[] } {
|
|
49
|
+
if (this.rectDiffExpansionSolver) {
|
|
50
|
+
return this.rectDiffExpansionSolver.getOutput()
|
|
51
|
+
}
|
|
52
|
+
if (this.rectDiffSeedingSolver) {
|
|
53
|
+
const snapshot = this.rectDiffSeedingSolver.getOutput()
|
|
54
|
+
const meshNodes: CapacityMeshNode[] = snapshot.placed.map(
|
|
55
|
+
(placement: any, idx: number) => ({
|
|
56
|
+
capacityMeshNodeId: `grid-${idx}`,
|
|
57
|
+
center: {
|
|
58
|
+
x: placement.rect.x + placement.rect.width / 2,
|
|
59
|
+
y: placement.rect.y + placement.rect.height / 2,
|
|
60
|
+
},
|
|
61
|
+
width: placement.rect.width,
|
|
62
|
+
height: placement.rect.height,
|
|
63
|
+
availableZ: placement.zLayers,
|
|
64
|
+
layer: `z${placement.zLayers.join(",")}`,
|
|
65
|
+
}),
|
|
66
|
+
)
|
|
67
|
+
return { meshNodes }
|
|
68
|
+
}
|
|
69
|
+
return { meshNodes: [] }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
override visualize(): GraphicsObject {
|
|
73
|
+
if (this.rectDiffExpansionSolver) {
|
|
74
|
+
return this.rectDiffExpansionSolver.visualize()
|
|
75
|
+
}
|
|
76
|
+
if (this.rectDiffSeedingSolver) {
|
|
77
|
+
return this.rectDiffSeedingSolver.visualize()
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
title: "RectDiff Grid Pipeline",
|
|
81
|
+
coordinateSystem: "cartesian",
|
|
82
|
+
rects: [],
|
|
83
|
+
points: [],
|
|
84
|
+
lines: [],
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|