@tscircuit/rectdiff 0.0.14 → 0.0.16

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 CHANGED
@@ -159,6 +159,14 @@ declare class FindSegmentsWithAdjacentEmptySpaceSolver extends BaseSolver {
159
159
  visualize(): Required<GraphicsObject>;
160
160
  }
161
161
 
162
+ type ExpandEdgesToEmptySpaceSolverInput = {
163
+ inputMeshNodes: CapacityMeshNode[];
164
+ segmentsWithAdjacentEmptySpace: Array<SegmentWithAdjacentEmptySpace>;
165
+ boardVoid?: {
166
+ boardVoidRects: XYRect[];
167
+ layerCount: number;
168
+ };
169
+ };
162
170
  interface ExpandedSegment {
163
171
  segment: SegmentWithAdjacentEmptySpace;
164
172
  newNode: CapacityMeshNode;
@@ -180,10 +188,7 @@ declare class ExpandEdgesToEmptySpaceSolver extends BaseSolver {
180
188
  } | null;
181
189
  lastExpandedSegment: ExpandedSegment | null;
182
190
  rectSpatialIndex: RBush<CapacityMeshNode>;
183
- constructor(input: {
184
- inputMeshNodes: CapacityMeshNode[];
185
- segmentsWithAdjacentEmptySpace: Array<SegmentWithAdjacentEmptySpace>;
186
- });
191
+ constructor(input: ExpandEdgesToEmptySpaceSolverInput);
187
192
  _step(): void;
188
193
  getOutput(): {
189
194
  expandedSegments: ExpandedSegment[];
@@ -191,9 +196,14 @@ declare class ExpandEdgesToEmptySpaceSolver extends BaseSolver {
191
196
  visualize(): Required<GraphicsObject>;
192
197
  }
193
198
 
194
- declare class GapFillSolverPipeline extends BasePipelineSolver<{
199
+ type GapFillSolverInput = {
195
200
  meshNodes: CapacityMeshNode[];
196
- }> {
201
+ boardVoid?: {
202
+ boardVoidRects: XYRect[];
203
+ layerCount: number;
204
+ };
205
+ };
206
+ declare class GapFillSolverPipeline extends BasePipelineSolver<GapFillSolverInput> {
197
207
  findSegmentsWithAdjacentEmptySpaceSolver?: FindSegmentsWithAdjacentEmptySpaceSolver;
198
208
  expandEdgesToEmptySpaceSolver?: ExpandEdgesToEmptySpaceSolver;
199
209
  pipelineDef: PipelineStep<any>[];
@@ -264,8 +274,6 @@ declare class RectDiffSeedingSolver extends BaseSolver {
264
274
  totalSeedsThisGrid: number;
265
275
  consumedSeedsThisGrid: number;
266
276
  };
267
- /** Get color based on z layer for visualization. */
268
- private getColorForZLayer;
269
277
  /** Visualization focused on the grid seeding phase. */
270
278
  visualize(): GraphicsObject;
271
279
  }
@@ -331,11 +339,11 @@ declare class RectDiffExpansionSolver extends BaseSolver {
331
339
  type RectDiffGridSolverPipelineInput = {
332
340
  simpleRouteJson: SimpleRouteJson;
333
341
  gridOptions?: Partial<GridFill3DOptions>;
342
+ boardVoidRects?: XYRect[];
334
343
  };
335
344
  declare class RectDiffGridSolverPipeline extends BasePipelineSolver<RectDiffGridSolverPipelineInput> {
336
345
  rectDiffSeedingSolver?: RectDiffSeedingSolver;
337
346
  rectDiffExpansionSolver?: RectDiffExpansionSolver;
338
- private boardVoidRects?;
339
347
  private obstacleIndexByLayer;
340
348
  constructor(inputProblem: RectDiffGridSolverPipelineInput);
341
349
  pipelineDef: PipelineStep<any>[];
@@ -353,7 +361,9 @@ interface RectDiffPipelineInput {
353
361
  declare class RectDiffPipeline extends BasePipelineSolver<RectDiffPipelineInput> {
354
362
  rectDiffGridSolverPipeline?: RectDiffGridSolverPipeline;
355
363
  gapFillSolver?: GapFillSolverPipeline;
364
+ boardVoidRects: XYRect[] | undefined;
356
365
  pipelineDef: PipelineStep<any>[];
366
+ _setup(): void;
357
367
  getConstructorParams(): RectDiffPipelineInput[];
358
368
  getOutput(): {
359
369
  meshNodes: CapacityMeshNode[];
package/dist/index.js CHANGED
@@ -331,6 +331,26 @@ var ExpandEdgesToEmptySpaceSolver = class extends BaseSolver2 {
331
331
  this.input = input;
332
332
  this.unprocessedSegments = [...this.input.segmentsWithAdjacentEmptySpace];
333
333
  this.rectSpatialIndex = new RBush();
334
+ this.rectSpatialIndex.load(
335
+ this.input.boardVoid?.boardVoidRects.map((rect, index) => ({
336
+ capacityMeshNodeId: `void-rect-${index}`,
337
+ center: {
338
+ x: rect.x + rect.width / 2,
339
+ y: rect.y + rect.height / 2
340
+ },
341
+ width: rect.width,
342
+ height: rect.height,
343
+ availableZ: Array.from(
344
+ { length: this.input.boardVoid?.layerCount || 0 },
345
+ (_, i) => i
346
+ ),
347
+ layer: "void",
348
+ minX: rect.x,
349
+ minY: rect.y,
350
+ maxX: rect.x + rect.width,
351
+ maxY: rect.y + rect.height
352
+ })) || []
353
+ );
334
354
  this.rectSpatialIndex.load(
335
355
  this.input.inputMeshNodes.map((n) => ({
336
356
  ...n,
@@ -566,7 +586,8 @@ var GapFillSolverPipeline = class extends BasePipelineSolver {
566
586
  (gapFillPipeline) => [
567
587
  {
568
588
  inputMeshNodes: gapFillPipeline.inputProblem.meshNodes,
569
- segmentsWithAdjacentEmptySpace: gapFillPipeline.findSegmentsWithAdjacentEmptySpaceSolver.getOutput().segmentsWithAdjacentEmptySpace
589
+ segmentsWithAdjacentEmptySpace: gapFillPipeline.findSegmentsWithAdjacentEmptySpaceSolver.getOutput().segmentsWithAdjacentEmptySpace,
590
+ boardVoid: gapFillPipeline.inputProblem.boardVoid
570
591
  }
571
592
  ],
572
593
  {
@@ -1520,6 +1541,20 @@ function resizeSoftOverlaps(params, newIndex) {
1520
1541
  }
1521
1542
  }
1522
1543
 
1544
+ // lib/utils/getColorForZLayer.ts
1545
+ var getColorForZLayer = (zLayers) => {
1546
+ const minZ = Math.min(...zLayers);
1547
+ const colors = [
1548
+ { fill: "#dbeafe", stroke: "#3b82f6" },
1549
+ { fill: "#fef3c7", stroke: "#f59e0b" },
1550
+ { fill: "#d1fae5", stroke: "#10b981" },
1551
+ { fill: "#e9d5ff", stroke: "#a855f7" },
1552
+ { fill: "#fed7aa", stroke: "#f97316" },
1553
+ { fill: "#fecaca", stroke: "#ef4444" }
1554
+ ];
1555
+ return colors[minZ % colors.length];
1556
+ };
1557
+
1523
1558
  // lib/solvers/RectDiffSeedingSolver/RectDiffSeedingSolver.ts
1524
1559
  import RBush3 from "rbush";
1525
1560
  var RectDiffSeedingSolver = class extends BaseSolver3 {
@@ -1768,19 +1803,6 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
1768
1803
  consumedSeedsThisGrid: this.consumedSeedsThisGrid
1769
1804
  };
1770
1805
  }
1771
- /** Get color based on z layer for visualization. */
1772
- getColorForZLayer(zLayers) {
1773
- const minZ = Math.min(...zLayers);
1774
- const colors = [
1775
- { fill: "#dbeafe", stroke: "#3b82f6" },
1776
- { fill: "#fef3c7", stroke: "#f59e0b" },
1777
- { fill: "#d1fae5", stroke: "#10b981" },
1778
- { fill: "#e9d5ff", stroke: "#a855f7" },
1779
- { fill: "#fed7aa", stroke: "#f97316" },
1780
- { fill: "#fecaca", stroke: "#ef4444" }
1781
- ];
1782
- return colors[minZ % colors.length];
1783
- }
1784
1806
  /** Visualization focused on the grid seeding phase. */
1785
1807
  visualize() {
1786
1808
  const rects = [];
@@ -1867,7 +1889,7 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
1867
1889
  }
1868
1890
  if (this.placed?.length) {
1869
1891
  for (const placement of this.placed) {
1870
- const colors = this.getColorForZLayer(placement.zLayers);
1892
+ const colors = getColorForZLayer(placement.zLayers);
1871
1893
  rects.push({
1872
1894
  center: {
1873
1895
  x: placement.rect.x + placement.rect.width / 2,
@@ -2145,7 +2167,8 @@ import "rbush";
2145
2167
 
2146
2168
  // lib/solvers/RectDiffGridSolverPipeline/buildObstacleIndexes.ts
2147
2169
  import RBush5 from "rbush";
2148
- var buildObstacleIndexes = (srj) => {
2170
+ var buildObstacleIndexesByLayer = (params) => {
2171
+ const { srj, boardVoidRects } = params;
2149
2172
  const { layerNames, zIndexByName } = buildZIndexMap(srj);
2150
2173
  const layerCount = Math.max(1, layerNames.length, srj.layerCount || 1);
2151
2174
  const bounds = {
@@ -2168,10 +2191,8 @@ var buildObstacleIndexes = (srj) => {
2168
2191
  };
2169
2192
  obstacleIndexByLayer[z]?.insert(treeRect);
2170
2193
  };
2171
- let boardVoidRects = [];
2172
2194
  if (srj.outline && srj.outline.length > 2) {
2173
- boardVoidRects = computeInverseRects(bounds, srj.outline);
2174
- for (const voidRect of boardVoidRects) {
2195
+ for (const voidRect of boardVoidRects ?? []) {
2175
2196
  for (let z = 0; z < layerCount; z++) insertObstacle(voidRect, z);
2176
2197
  }
2177
2198
  }
@@ -2190,22 +2211,21 @@ var buildObstacleIndexes = (srj) => {
2190
2211
  }
2191
2212
  for (const z of zLayers) insertObstacle(rect, z);
2192
2213
  }
2193
- return { obstacleIndexByLayer, boardVoidRects };
2214
+ return { obstacleIndexByLayer };
2194
2215
  };
2195
2216
 
2196
2217
  // lib/solvers/RectDiffGridSolverPipeline/RectDiffGridSolverPipeline.ts
2197
2218
  var RectDiffGridSolverPipeline = class extends BasePipelineSolver2 {
2198
2219
  rectDiffSeedingSolver;
2199
2220
  rectDiffExpansionSolver;
2200
- boardVoidRects;
2201
2221
  obstacleIndexByLayer;
2202
2222
  constructor(inputProblem) {
2203
2223
  super(inputProblem);
2204
- const { obstacleIndexByLayer, boardVoidRects } = buildObstacleIndexes(
2205
- inputProblem.simpleRouteJson
2206
- );
2224
+ const { obstacleIndexByLayer } = buildObstacleIndexesByLayer({
2225
+ srj: inputProblem.simpleRouteJson,
2226
+ boardVoidRects: inputProblem.boardVoidRects
2227
+ });
2207
2228
  this.obstacleIndexByLayer = obstacleIndexByLayer;
2208
- this.boardVoidRects = boardVoidRects;
2209
2229
  }
2210
2230
  pipelineDef = [
2211
2231
  definePipelineStep2(
@@ -2216,7 +2236,7 @@ var RectDiffGridSolverPipeline = class extends BasePipelineSolver2 {
2216
2236
  simpleRouteJson: pipeline.inputProblem.simpleRouteJson,
2217
2237
  gridOptions: pipeline.inputProblem.gridOptions,
2218
2238
  obstacleIndexByLayer: pipeline.obstacleIndexByLayer,
2219
- boardVoidRects: pipeline.boardVoidRects
2239
+ boardVoidRects: pipeline.inputProblem.boardVoidRects
2220
2240
  }
2221
2241
  ]
2222
2242
  ),
@@ -2227,7 +2247,7 @@ var RectDiffGridSolverPipeline = class extends BasePipelineSolver2 {
2227
2247
  {
2228
2248
  initialSnapshot: {
2229
2249
  ...pipeline.rectDiffSeedingSolver.getOutput(),
2230
- boardVoidRects: pipeline.boardVoidRects ?? []
2250
+ boardVoidRects: pipeline.inputProblem.boardVoidRects ?? []
2231
2251
  },
2232
2252
  obstacleIndexByLayer: pipeline.obstacleIndexByLayer
2233
2253
  }
@@ -2333,6 +2353,7 @@ function createBaseVisualization(srj, title = "RectDiff") {
2333
2353
  var RectDiffPipeline = class extends BasePipelineSolver3 {
2334
2354
  rectDiffGridSolverPipeline;
2335
2355
  gapFillSolver;
2356
+ boardVoidRects;
2336
2357
  pipelineDef = [
2337
2358
  definePipelineStep3(
2338
2359
  "rectDiffGridSolverPipeline",
@@ -2340,7 +2361,8 @@ var RectDiffPipeline = class extends BasePipelineSolver3 {
2340
2361
  (rectDiffPipeline) => [
2341
2362
  {
2342
2363
  simpleRouteJson: rectDiffPipeline.inputProblem.simpleRouteJson,
2343
- gridOptions: rectDiffPipeline.inputProblem.gridOptions
2364
+ gridOptions: rectDiffPipeline.inputProblem.gridOptions,
2365
+ boardVoidRects: rectDiffPipeline.boardVoidRects
2344
2366
  }
2345
2367
  ]
2346
2368
  ),
@@ -2349,11 +2371,28 @@ var RectDiffPipeline = class extends BasePipelineSolver3 {
2349
2371
  GapFillSolverPipeline,
2350
2372
  (rectDiffPipeline) => [
2351
2373
  {
2352
- meshNodes: rectDiffPipeline.rectDiffGridSolverPipeline?.getOutput().meshNodes ?? []
2374
+ meshNodes: rectDiffPipeline.rectDiffGridSolverPipeline?.getOutput().meshNodes ?? [],
2375
+ boardVoid: {
2376
+ boardVoidRects: rectDiffPipeline.boardVoidRects || [],
2377
+ layerCount: rectDiffPipeline.inputProblem.simpleRouteJson.layerCount || 0
2378
+ }
2353
2379
  }
2354
2380
  ]
2355
2381
  )
2356
2382
  ];
2383
+ _setup() {
2384
+ if (this.inputProblem.simpleRouteJson.outline) {
2385
+ this.boardVoidRects = computeInverseRects(
2386
+ {
2387
+ x: this.inputProblem.simpleRouteJson.bounds.minX,
2388
+ y: this.inputProblem.simpleRouteJson.bounds.minY,
2389
+ width: this.inputProblem.simpleRouteJson.bounds.maxX - this.inputProblem.simpleRouteJson.bounds.minX,
2390
+ height: this.inputProblem.simpleRouteJson.bounds.maxY - this.inputProblem.simpleRouteJson.bounds.minY
2391
+ },
2392
+ this.inputProblem.simpleRouteJson.outline ?? []
2393
+ );
2394
+ }
2395
+ }
2357
2396
  getConstructorParams() {
2358
2397
  return [this.inputProblem];
2359
2398
  }
@@ -2368,7 +2407,6 @@ var RectDiffPipeline = class extends BasePipelineSolver3 {
2368
2407
  return { meshNodes: [] };
2369
2408
  }
2370
2409
  initialVisualize() {
2371
- console.log("RectDiffPipeline - initialVisualize");
2372
2410
  const graphics = createBaseVisualization(
2373
2411
  this.inputProblem.simpleRouteJson,
2374
2412
  "RectDiffPipeline - Initial"
@@ -4,12 +4,13 @@ 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 "./rectdiff-types"
7
+ import type { GridFill3DOptions, XYRect } from "./rectdiff-types"
8
8
  import type { CapacityMeshNode } from "./types/capacity-mesh-types"
9
9
  import type { GraphicsObject } from "graphics-debug"
10
10
  import { GapFillSolverPipeline } from "./solvers/GapFillSolver/GapFillSolverPipeline"
11
11
  import { RectDiffGridSolverPipeline } from "./solvers/RectDiffGridSolverPipeline/RectDiffGridSolverPipeline"
12
12
  import { createBaseVisualization } from "./rectdiff-visualization"
13
+ import { computeInverseRects } from "./solvers/RectDiffSeedingSolver/computeInverseRects"
13
14
 
14
15
  export interface RectDiffPipelineInput {
15
16
  simpleRouteJson: SimpleRouteJson
@@ -19,6 +20,7 @@ export interface RectDiffPipelineInput {
19
20
  export class RectDiffPipeline extends BasePipelineSolver<RectDiffPipelineInput> {
20
21
  rectDiffGridSolverPipeline?: RectDiffGridSolverPipeline
21
22
  gapFillSolver?: GapFillSolverPipeline
23
+ boardVoidRects: XYRect[] | undefined
22
24
 
23
25
  override pipelineDef: PipelineStep<any>[] = [
24
26
  definePipelineStep(
@@ -28,6 +30,7 @@ export class RectDiffPipeline extends BasePipelineSolver<RectDiffPipelineInput>
28
30
  {
29
31
  simpleRouteJson: rectDiffPipeline.inputProblem.simpleRouteJson,
30
32
  gridOptions: rectDiffPipeline.inputProblem.gridOptions,
33
+ boardVoidRects: rectDiffPipeline.boardVoidRects,
31
34
  },
32
35
  ],
33
36
  ),
@@ -39,11 +42,34 @@ export class RectDiffPipeline extends BasePipelineSolver<RectDiffPipelineInput>
39
42
  meshNodes:
40
43
  rectDiffPipeline.rectDiffGridSolverPipeline?.getOutput()
41
44
  .meshNodes ?? [],
45
+ boardVoid: {
46
+ boardVoidRects: rectDiffPipeline.boardVoidRects || [],
47
+ layerCount:
48
+ rectDiffPipeline.inputProblem.simpleRouteJson.layerCount || 0,
49
+ },
42
50
  },
43
51
  ],
44
52
  ),
45
53
  ]
46
54
 
55
+ override _setup(): void {
56
+ if (this.inputProblem.simpleRouteJson.outline) {
57
+ this.boardVoidRects = computeInverseRects(
58
+ {
59
+ x: this.inputProblem.simpleRouteJson.bounds.minX,
60
+ y: this.inputProblem.simpleRouteJson.bounds.minY,
61
+ width:
62
+ this.inputProblem.simpleRouteJson.bounds.maxX -
63
+ this.inputProblem.simpleRouteJson.bounds.minX,
64
+ height:
65
+ this.inputProblem.simpleRouteJson.bounds.maxY -
66
+ this.inputProblem.simpleRouteJson.bounds.minY,
67
+ },
68
+ this.inputProblem.simpleRouteJson.outline ?? [],
69
+ )
70
+ }
71
+ }
72
+
47
73
  override getConstructorParams() {
48
74
  return [this.inputProblem]
49
75
  }
@@ -60,7 +86,6 @@ export class RectDiffPipeline extends BasePipelineSolver<RectDiffPipelineInput>
60
86
  }
61
87
 
62
88
  override initialVisualize(): GraphicsObject {
63
- console.log("RectDiffPipeline - initialVisualize")
64
89
  const graphics = createBaseVisualization(
65
90
  this.inputProblem.simpleRouteJson,
66
91
  "RectDiffPipeline - Initial",
@@ -7,9 +7,19 @@ import { EDGE_MAP, EDGES } from "./edge-constants"
7
7
  import { getBoundsFromCorners } from "./getBoundsFromCorners"
8
8
  import type { Bounds } from "@tscircuit/math-utils"
9
9
  import { midpoint, segmentToBoxMinDistance } from "@tscircuit/math-utils"
10
+ import type { XYRect } from "lib/rectdiff-types"
10
11
 
11
12
  const EPS = 1e-4
12
13
 
14
+ export type ExpandEdgesToEmptySpaceSolverInput = {
15
+ inputMeshNodes: CapacityMeshNode[]
16
+ segmentsWithAdjacentEmptySpace: Array<SegmentWithAdjacentEmptySpace>
17
+ boardVoid?: {
18
+ boardVoidRects: XYRect[]
19
+ layerCount: number
20
+ }
21
+ }
22
+
13
23
  export interface ExpandedSegment {
14
24
  segment: SegmentWithAdjacentEmptySpace
15
25
  newNode: CapacityMeshNode
@@ -28,15 +38,31 @@ export class ExpandEdgesToEmptySpaceSolver extends BaseSolver {
28
38
 
29
39
  rectSpatialIndex: RBush<CapacityMeshNode>
30
40
 
31
- constructor(
32
- private input: {
33
- inputMeshNodes: CapacityMeshNode[]
34
- segmentsWithAdjacentEmptySpace: Array<SegmentWithAdjacentEmptySpace>
35
- },
36
- ) {
41
+ constructor(private input: ExpandEdgesToEmptySpaceSolverInput) {
37
42
  super()
38
43
  this.unprocessedSegments = [...this.input.segmentsWithAdjacentEmptySpace]
39
44
  this.rectSpatialIndex = new RBush<CapacityMeshNode>()
45
+ // create fake bound for the boardVoidRects
46
+ this.rectSpatialIndex.load(
47
+ this.input.boardVoid?.boardVoidRects.map((rect, index) => ({
48
+ capacityMeshNodeId: `void-rect-${index}`,
49
+ center: {
50
+ x: rect.x + rect.width / 2,
51
+ y: rect.y + rect.height / 2,
52
+ },
53
+ width: rect.width,
54
+ height: rect.height,
55
+ availableZ: Array.from(
56
+ { length: this.input.boardVoid?.layerCount || 0 },
57
+ (_, i) => i,
58
+ ),
59
+ layer: "void",
60
+ minX: rect.x,
61
+ minY: rect.y,
62
+ maxX: rect.x + rect.width,
63
+ maxY: rect.y + rect.height,
64
+ })) || [],
65
+ )
40
66
  this.rectSpatialIndex.load(
41
67
  this.input.inputMeshNodes.map((n) => ({
42
68
  ...n,
@@ -3,15 +3,21 @@ import {
3
3
  definePipelineStep,
4
4
  type PipelineStep,
5
5
  } from "@tscircuit/solver-utils"
6
- import type { SimpleRouteJson } from "lib/types/srj-types"
7
6
  import type { CapacityMeshNode } from "lib/types/capacity-mesh-types"
8
7
  import type { GraphicsObject } from "graphics-debug"
9
8
  import { FindSegmentsWithAdjacentEmptySpaceSolver } from "./FindSegmentsWithAdjacentEmptySpaceSolver"
10
9
  import { ExpandEdgesToEmptySpaceSolver } from "./ExpandEdgesToEmptySpaceSolver"
10
+ import type { XYRect } from "lib/rectdiff-types"
11
11
 
12
- export class GapFillSolverPipeline extends BasePipelineSolver<{
12
+ type GapFillSolverInput = {
13
13
  meshNodes: CapacityMeshNode[]
14
- }> {
14
+ boardVoid?: {
15
+ boardVoidRects: XYRect[]
16
+ layerCount: number
17
+ }
18
+ }
19
+
20
+ export class GapFillSolverPipeline extends BasePipelineSolver<GapFillSolverInput> {
15
21
  findSegmentsWithAdjacentEmptySpaceSolver?: FindSegmentsWithAdjacentEmptySpaceSolver
16
22
  expandEdgesToEmptySpaceSolver?: ExpandEdgesToEmptySpaceSolver
17
23
 
@@ -39,6 +45,7 @@ export class GapFillSolverPipeline extends BasePipelineSolver<{
39
45
  segmentsWithAdjacentEmptySpace:
40
46
  gapFillPipeline.findSegmentsWithAdjacentEmptySpaceSolver!.getOutput()
41
47
  .segmentsWithAdjacentEmptySpace,
48
+ boardVoid: gapFillPipeline.inputProblem.boardVoid,
42
49
  },
43
50
  ],
44
51
  {
@@ -10,26 +10,26 @@ import { RectDiffSeedingSolver } from "lib/solvers/RectDiffSeedingSolver/RectDif
10
10
  import { RectDiffExpansionSolver } from "lib/solvers/RectDiffExpansionSolver/RectDiffExpansionSolver"
11
11
  import type { GraphicsObject } from "graphics-debug"
12
12
  import RBush from "rbush"
13
- import { buildObstacleIndexes } from "./buildObstacleIndexes"
13
+ import { buildObstacleIndexesByLayer } from "./buildObstacleIndexes"
14
14
 
15
15
  export type RectDiffGridSolverPipelineInput = {
16
16
  simpleRouteJson: SimpleRouteJson
17
17
  gridOptions?: Partial<GridFill3DOptions>
18
+ boardVoidRects?: XYRect[]
18
19
  }
19
20
 
20
21
  export class RectDiffGridSolverPipeline extends BasePipelineSolver<RectDiffGridSolverPipelineInput> {
21
22
  rectDiffSeedingSolver?: RectDiffSeedingSolver
22
23
  rectDiffExpansionSolver?: RectDiffExpansionSolver
23
- private boardVoidRects?: XYRect[]
24
24
  private obstacleIndexByLayer: Array<RBush<RTreeRect>>
25
25
 
26
26
  constructor(inputProblem: RectDiffGridSolverPipelineInput) {
27
27
  super(inputProblem)
28
- const { obstacleIndexByLayer, boardVoidRects } = buildObstacleIndexes(
29
- inputProblem.simpleRouteJson,
30
- )
28
+ const { obstacleIndexByLayer } = buildObstacleIndexesByLayer({
29
+ srj: inputProblem.simpleRouteJson,
30
+ boardVoidRects: inputProblem.boardVoidRects,
31
+ })
31
32
  this.obstacleIndexByLayer = obstacleIndexByLayer
32
- this.boardVoidRects = boardVoidRects
33
33
  }
34
34
 
35
35
  override pipelineDef: PipelineStep<any>[] = [
@@ -41,7 +41,7 @@ export class RectDiffGridSolverPipeline extends BasePipelineSolver<RectDiffGridS
41
41
  simpleRouteJson: pipeline.inputProblem.simpleRouteJson,
42
42
  gridOptions: pipeline.inputProblem.gridOptions,
43
43
  obstacleIndexByLayer: pipeline.obstacleIndexByLayer,
44
- boardVoidRects: pipeline.boardVoidRects,
44
+ boardVoidRects: pipeline.inputProblem.boardVoidRects,
45
45
  },
46
46
  ],
47
47
  ),
@@ -52,7 +52,7 @@ export class RectDiffGridSolverPipeline extends BasePipelineSolver<RectDiffGridS
52
52
  {
53
53
  initialSnapshot: {
54
54
  ...pipeline.rectDiffSeedingSolver!.getOutput(),
55
- boardVoidRects: pipeline.boardVoidRects ?? [],
55
+ boardVoidRects: pipeline.inputProblem.boardVoidRects ?? [],
56
56
  },
57
57
  obstacleIndexByLayer: pipeline.obstacleIndexByLayer,
58
58
  },
@@ -9,12 +9,13 @@ import {
9
9
  import type { XYRect } from "lib/rectdiff-types"
10
10
  import type { RTreeRect } from "lib/types/capacity-mesh-types"
11
11
 
12
- export const buildObstacleIndexes = (
13
- srj: SimpleRouteJson,
14
- ): {
12
+ export const buildObstacleIndexesByLayer = (params: {
13
+ srj: SimpleRouteJson
14
+ boardVoidRects?: XYRect[]
15
+ }): {
15
16
  obstacleIndexByLayer: Array<RBush<RTreeRect>>
16
- boardVoidRects: XYRect[]
17
17
  } => {
18
+ const { srj, boardVoidRects } = params
18
19
  const { layerNames, zIndexByName } = buildZIndexMap(srj)
19
20
  const layerCount = Math.max(1, layerNames.length, srj.layerCount || 1)
20
21
  const bounds: XYRect = {
@@ -39,10 +40,8 @@ export const buildObstacleIndexes = (
39
40
  obstacleIndexByLayer[z]?.insert(treeRect)
40
41
  }
41
42
 
42
- let boardVoidRects: XYRect[] = []
43
43
  if (srj.outline && srj.outline.length > 2) {
44
- boardVoidRects = computeInverseRects(bounds, srj.outline as any)
45
- for (const voidRect of boardVoidRects) {
44
+ for (const voidRect of boardVoidRects ?? []) {
46
45
  for (let z = 0; z < layerCount; z++) insertObstacle(voidRect, z)
47
46
  }
48
47
  }
@@ -66,5 +65,5 @@ export const buildObstacleIndexes = (
66
65
  for (const z of zLayers) insertObstacle(rect, z)
67
66
  }
68
67
 
69
- return { obstacleIndexByLayer, boardVoidRects }
68
+ return { obstacleIndexByLayer }
70
69
  }
@@ -18,6 +18,7 @@ import { longestFreeSpanAroundZ } from "./longestFreeSpanAroundZ"
18
18
  import { allLayerNode } from "../../utils/buildHardPlacedByLayer"
19
19
  import { isFullyOccupiedAtPoint } from "lib/utils/isFullyOccupiedAtPoint"
20
20
  import { resizeSoftOverlaps } from "../../utils/resizeSoftOverlaps"
21
+ import { getColorForZLayer } from "lib/utils/getColorForZLayer"
21
22
  import RBush from "rbush"
22
23
  import type { RTreeRect } from "lib/types/capacity-mesh-types"
23
24
 
@@ -336,23 +337,6 @@ export class RectDiffSeedingSolver extends BaseSolver {
336
337
  }
337
338
  }
338
339
 
339
- /** Get color based on z layer for visualization. */
340
- private getColorForZLayer(zLayers: number[]): {
341
- fill: string
342
- stroke: string
343
- } {
344
- const minZ = Math.min(...zLayers)
345
- const colors = [
346
- { fill: "#dbeafe", stroke: "#3b82f6" },
347
- { fill: "#fef3c7", stroke: "#f59e0b" },
348
- { fill: "#d1fae5", stroke: "#10b981" },
349
- { fill: "#e9d5ff", stroke: "#a855f7" },
350
- { fill: "#fed7aa", stroke: "#f97316" },
351
- { fill: "#fecaca", stroke: "#ef4444" },
352
- ] as const
353
- return colors[minZ % colors.length]!
354
- }
355
-
356
340
  /** Visualization focused on the grid seeding phase. */
357
341
  override visualize(): GraphicsObject {
358
342
  const rects: NonNullable<GraphicsObject["rects"]> = []
@@ -460,7 +444,7 @@ export class RectDiffSeedingSolver extends BaseSolver {
460
444
  // current placements (streaming) during grid fill
461
445
  if (this.placed?.length) {
462
446
  for (const placement of this.placed) {
463
- const colors = this.getColorForZLayer(placement.zLayers)
447
+ const colors = getColorForZLayer(placement.zLayers)
464
448
  rects.push({
465
449
  center: {
466
450
  x: placement.rect.x + placement.rect.width / 2,
@@ -0,0 +1,17 @@
1
+ export const getColorForZLayer = (
2
+ zLayers: number[],
3
+ ): {
4
+ fill: string
5
+ stroke: string
6
+ } => {
7
+ const minZ = Math.min(...zLayers)
8
+ const colors = [
9
+ { fill: "#dbeafe", stroke: "#3b82f6" },
10
+ { fill: "#fef3c7", stroke: "#f59e0b" },
11
+ { fill: "#d1fae5", stroke: "#10b981" },
12
+ { fill: "#e9d5ff", stroke: "#a855f7" },
13
+ { fill: "#fed7aa", stroke: "#f97316" },
14
+ { fill: "#fecaca", stroke: "#ef4444" },
15
+ ] as const
16
+ return colors[minZ % colors.length]!
17
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/rectdiff",
3
- "version": "0.0.14",
3
+ "version": "0.0.16",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -1,19 +1,4 @@
1
- <svg width="640" height="640" viewBox="0 0 640 640" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="white"/><g><polyline data-points="-8,-6 -2,-6 -2,2 2,2 2,-6 8,-6 8,6 -8,6 -8,-6" data-type="line" data-label="outline" points="40,530 250,530 250,250.00000000000006 390,250.00000000000006 390,530 600,530 600,110.00000000000006 40,110.00000000000006 40,530" fill="none" stroke="#111827" stroke-width="0.35000000000000003"/></g><g><rect data-type="rect" data-label="obstacle" data-x="-5.51" data-y="-4" x="117.70000000000002" y="448.80000000000007" width="18.899999999999977" height="22.399999999999977" fill="#fee2e2" stroke="#ef4444" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="-4.49" data-y="-4" x="153.4" y="448.80000000000007" width="18.899999999999977" height="22.399999999999977" fill="#fee2e2" stroke="#ef4444" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="4.49" data-y="-4" x="467.70000000000005" y="448.80000000000007" width="18.899999999999977" height="22.399999999999977" fill="#fee2e2" stroke="#ef4444" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="5.51" data-y="-4" x="503.4" y="448.80000000000007" width="18.899999999999977" height="22.399999999999977" fill="#fee2e2" stroke="#ef4444" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="void" data-x="0" data-y="-2" x="250" y="250.00000000000006" width="140" height="279.99999999999994" fill="rgba(0, 0, 0, 0.5)" stroke="none" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="free
2
- z:0,1" data-x="-5" data-y="1.1600000000000006" x="40" y="110" width="210" height="338.80000000000007" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="free
3
- z:0,1" data-x="5" data-y="1.1600000000000006" x="390" y="110" width="210" height="338.80000000000007" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="free
4
- z:0,1" data-x="0" data-y="4" x="250" y="110.00000000000006" width="140" height="140" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="free
5
- z:0,1" data-x="-5.48" data-y="-5.16" x="40" y="471.20000000000005" width="176.39999999999998" height="58.799999999999955" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="free
6
- z:0,1" data-x="5.48" data-y="-5.16" x="423.6" y="471.20000000000005" width="176.39999999999998" height="58.799999999999955" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="free
7
- z:0,1" data-x="-2.4800000000000004" data-y="-4.84" x="216.39999999999998" y="448.80000000000007" width="33.60000000000002" height="81.19999999999993" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="free
8
- z:0,1" data-x="2.4800000000000004" data-y="-4.84" x="390" y="448.80000000000007" width="33.60000000000002" height="81.19999999999993" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="free
9
- z:1" data-x="-5.48" data-y="-4" x="40" y="448.80000000000007" width="176.39999999999998" height="22.399999999999977" fill="#fef3c7" stroke="#f59e0b" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="free
10
- z:1" data-x="5.48" data-y="-4" x="423.6" y="448.80000000000007" width="176.39999999999998" height="22.399999999999977" fill="#fef3c7" stroke="#f59e0b" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="free
11
- z:0" data-x="6.890000000000001" data-y="-4" x="522.3" y="448.80000000000007" width="77.70000000000005" height="22.399999999999977" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="free
12
- z:0" data-x="-6.89" data-y="-4" x="40" y="448.80000000000007" width="77.70000000000002" height="22.399999999999977" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="free
13
- z:0" data-x="-5" data-y="-4" x="136.6" y="448.80000000000007" width="16.80000000000001" height="22.399999999999977" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="free
14
- z:0" data-x="-3.5900000000000003" data-y="-4" x="172.3" y="448.80000000000007" width="44.099999999999966" height="22.399999999999977" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="free
15
- z:0" data-x="3.5900000000000007" data-y="-4" x="423.6" y="448.80000000000007" width="44.10000000000002" height="22.399999999999977" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="free
16
- z:0" data-x="5" data-y="-4" x="486.6" y="448.80000000000007" width="16.799999999999955" height="22.399999999999977" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.02857142857142857"/></g><g id="crosshair" style="display: none"><line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5"/><line id="crosshair-v" x1="0" x2="640" stroke="#666" stroke-width="0.5"/><text id="coordinates" font-family="monospace" font-size="12" fill="#666"></text></g><script><![CDATA[
1
+ <svg width="640" height="640" viewBox="0 0 640 640" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="white"/><g><rect data-type="rect" data-label="node" data-x="-5" data-y="1.1600000000000008" x="40" y="110" width="210" height="338.80000000000007" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="5" data-y="1.1600000000000008" x="390" y="110" width="210" height="338.80000000000007" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="0" data-y="4" x="250" y="110.00000000000006" width="140" height="140" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="-5.48" data-y="-5.16" x="40" y="471.20000000000005" width="176.39999999999998" height="58.799999999999955" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="5.48" data-y="-5.16" x="423.6" y="471.20000000000005" width="176.39999999999998" height="58.799999999999955" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="-2.4800000000000004" data-y="-4.84" x="216.39999999999998" y="448.80000000000007" width="33.60000000000002" height="81.19999999999993" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="2.4800000000000004" data-y="-4.84" x="390" y="448.80000000000007" width="33.60000000000002" height="81.19999999999993" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="-5.48" data-y="-4" x="40" y="448.80000000000007" width="176.39999999999998" height="22.399999999999977" fill="#fef3c7" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="5.48" data-y="-4" x="423.6" y="448.80000000000007" width="176.39999999999998" height="22.399999999999977" fill="#fef3c7" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="6.890000000000001" data-y="-4" x="522.3000000000001" y="448.80000000000007" width="77.69999999999993" height="22.399999999999977" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="-6.89" data-y="-4" x="40" y="448.80000000000007" width="77.70000000000002" height="22.399999999999977" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="-5" data-y="-4" x="136.6" y="448.80000000000007" width="16.80000000000001" height="22.399999999999977" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="-3.5900000000000003" data-y="-4" x="172.3" y="448.80000000000007" width="44.099999999999966" height="22.399999999999977" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="3.5900000000000007" data-y="-4" x="423.6" y="448.80000000000007" width="44.10000000000002" height="22.399999999999977" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="5" data-y="-4" x="486.6" y="448.80000000000007" width="16.799999999999955" height="22.399999999999977" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="-5.51" data-y="-4" x="117.70000000000002" y="448.80000000000007" width="18.899999999999977" height="22.399999999999977" fill="red" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="-4.49" data-y="-4" x="153.4" y="448.80000000000007" width="18.899999999999977" height="22.399999999999977" fill="red" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="4.49" data-y="-4" x="467.70000000000005" y="448.80000000000007" width="18.899999999999977" height="22.399999999999977" fill="red" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="5.51" data-y="-4" x="503.4" y="448.80000000000007" width="18.899999999999977" height="22.399999999999977" fill="red" stroke="black" stroke-width="0.02857142857142857"/></g><g id="crosshair" style="display: none"><line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5"/><line id="crosshair-v" x1="0" x2="640" stroke="#666" stroke-width="0.5"/><text id="coordinates" font-family="monospace" font-size="12" fill="#666"></text></g><script><![CDATA[
17
2
  document.currentScript.parentElement.addEventListener('mousemove', (e) => {
18
3
  const svg = e.currentTarget;
19
4
  const rect = svg.getBoundingClientRect();