@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 +19 -9
- package/dist/index.js +68 -30
- package/lib/RectDiffPipeline.ts +27 -2
- package/lib/solvers/GapFillSolver/ExpandEdgesToEmptySpaceSolver.ts +32 -6
- package/lib/solvers/GapFillSolver/GapFillSolverPipeline.ts +10 -3
- package/lib/solvers/RectDiffGridSolverPipeline/RectDiffGridSolverPipeline.ts +8 -8
- package/lib/solvers/RectDiffGridSolverPipeline/buildObstacleIndexes.ts +7 -8
- package/lib/solvers/RectDiffSeedingSolver/RectDiffSeedingSolver.ts +2 -18
- package/lib/utils/getColorForZLayer.ts +17 -0
- package/package.json +1 -1
- package/tests/__snapshots__/board-outline.snap.svg +1 -16
- package/tests/board-outline.test.ts +10 -3
- package/tests/fixtures/makeCapacityMeshNodeWithLayerInfo.ts +33 -0
- package/tests/solver/__snapshots__/rectDiffGridSolverPipeline.snap.svg +44 -0
- package/tests/solver/rectDiffGridSolverPipeline.test.ts +88 -0
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
|
-
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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"
|
package/lib/RectDiffPipeline.ts
CHANGED
|
@@ -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
|
-
|
|
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 {
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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,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><
|
|
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();
|