@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.
Files changed (33) hide show
  1. package/dist/index.d.ts +163 -27
  2. package/dist/index.js +1885 -1676
  3. package/lib/RectDiffPipeline.ts +18 -17
  4. package/lib/{solvers/rectdiff/types.ts → rectdiff-types.ts} +0 -34
  5. package/lib/{solvers/rectdiff/visualization.ts → rectdiff-visualization.ts} +2 -1
  6. package/lib/solvers/GapFillSolver/FindSegmentsWithAdjacentEmptySpaceSolver.ts +9 -12
  7. package/lib/solvers/GapFillSolver/GapFillSolverPipeline.ts +1 -1
  8. package/lib/solvers/GapFillSolver/visuallyOffsetLine.ts +5 -2
  9. package/lib/solvers/RectDiffExpansionSolver/RectDiffExpansionSolver.ts +205 -0
  10. package/lib/solvers/{rectdiff → RectDiffExpansionSolver}/rectsToMeshNodes.ts +1 -2
  11. package/lib/solvers/RectDiffGridSolverPipeline/RectDiffGridSolverPipeline.ts +87 -0
  12. package/lib/solvers/RectDiffSeedingSolver/RectDiffSeedingSolver.ts +516 -0
  13. package/lib/solvers/RectDiffSeedingSolver/computeCandidates3D.ts +109 -0
  14. package/lib/solvers/RectDiffSeedingSolver/computeDefaultGridSizes.ts +9 -0
  15. package/lib/solvers/{rectdiff/candidates.ts → RectDiffSeedingSolver/computeEdgeCandidates3D.ts} +39 -220
  16. package/lib/solvers/{rectdiff/geometry → RectDiffSeedingSolver}/computeInverseRects.ts +6 -2
  17. package/lib/solvers/{rectdiff → RectDiffSeedingSolver}/layers.ts +4 -3
  18. package/lib/solvers/RectDiffSeedingSolver/longestFreeSpanAroundZ.ts +52 -0
  19. package/lib/utils/buildHardPlacedByLayer.ts +14 -0
  20. package/lib/{solvers/rectdiff/geometry.ts → utils/expandRectFromSeed.ts} +21 -120
  21. package/lib/utils/finalizeRects.ts +49 -0
  22. package/lib/utils/isFullyOccupiedAtPoint.ts +21 -0
  23. package/lib/utils/rectdiff-geometry.ts +94 -0
  24. package/lib/utils/resizeSoftOverlaps.ts +74 -0
  25. package/package.json +1 -1
  26. package/tests/board-outline.test.ts +2 -1
  27. package/tests/obstacle-extra-layers.test.ts +1 -1
  28. package/tests/obstacle-zlayers.test.ts +1 -1
  29. package/utils/rectsEqual.ts +2 -2
  30. package/utils/rectsOverlap.ts +2 -2
  31. package/lib/solvers/RectDiffSolver.ts +0 -231
  32. package/lib/solvers/rectdiff/engine.ts +0 -481
  33. /package/lib/solvers/{rectdiff/geometry → RectDiffSeedingSolver}/isPointInPolygon.ts +0 -0
@@ -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 "./solvers/rectdiff/types"
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
- rectDiffSolver?: RectDiffSolver
20
+ rectDiffGridSolverPipeline?: RectDiffGridSolverPipeline
21
21
  gapFillSolver?: GapFillSolverPipeline
22
22
 
23
23
  override pipelineDef: PipelineStep<any>[] = [
24
24
  definePipelineStep(
25
- "rectDiffSolver",
26
- RectDiffSolver,
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.rectDiffSolver?.getOutput().meshNodes ?? [],
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
- return this.rectDiffSolver!.getOutput()
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 rectDiffSolver if available
71
- const initialNodes = this.rectDiffSolver?.getOutput().meshNodes ?? []
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.rectDiffSolver?.getOutput().meshNodes ?? []).map(
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 "../../types/srj-types"
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
- [edge.start, edge.end],
188
- edge.facingDirection,
189
- 0.05,
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
- [edge.start, edge.end],
202
- edge.facingDirection,
203
- -0.05,
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
  })
@@ -31,7 +31,7 @@ export class GapFillSolverPipeline extends BasePipelineSolver<{
31
31
  },
32
32
  ),
33
33
  definePipelineStep(
34
- "expandEdgesToEmptySpace",
34
+ "expandEdgesToEmptySpaceSolver",
35
35
  ExpandEdgesToEmptySpaceSolver,
36
36
  (gapFillPipeline: GapFillSolverPipeline) => [
37
37
  {
@@ -21,9 +21,12 @@ const OFFSET_DIR_MAP = {
21
21
  */
22
22
  export const visuallyOffsetLine = (
23
23
  line: Array<{ x: number; y: number }>,
24
- dir: "x-" | "x+" | "y-" | "y+",
25
- amt: number,
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
+ }