@tscircuit/rectdiff 0.0.22 → 0.0.23
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/components/SolverDebugger3d.tsx +2 -2
- package/dist/index.d.ts +23 -3
- package/dist/index.js +206 -54
- package/lib/RectDiffPipeline.ts +62 -22
- package/lib/fixtures/twoNodeExpansionFixture.ts +10 -2
- package/lib/rectdiff-visualization.ts +2 -1
- package/lib/solvers/RectDiffExpansionSolver/RectDiffExpansionSolver.ts +8 -3
- package/lib/solvers/RectDiffGridSolverPipeline/RectDiffGridSolverPipeline.ts +48 -9
- package/lib/solvers/RectDiffGridSolverPipeline/buildObstacleIndexes.ts +14 -6
- package/lib/solvers/RectDiffSeedingSolver/RectDiffSeedingSolver.ts +41 -5
- package/lib/solvers/RectDiffSeedingSolver/layers.ts +9 -5
- package/lib/utils/finalizeRects.ts +17 -9
- package/lib/utils/padRect.ts +11 -0
- package/lib/utils/renderObstacleClearance.ts +50 -0
- package/package.json +1 -1
- package/pages/bugreport11.page.tsx +1 -0
- package/tests/board-outline.test.ts +1 -1
- package/tests/fixtures/makeSimpleRouteOutlineGraphics.ts +5 -1
- package/tests/should-expand-node.test.ts +9 -1
- package/tests/solver/bugreport11-b2de3c/__snapshots__/bugreport11-b2de3c-clearance.snap.svg +44 -0
- package/tests/solver/bugreport11-b2de3c/bugreport11-b2de3c-clearance.test.ts +97 -0
|
@@ -602,7 +602,7 @@ export const SolverDebugger3d: React.FC<SolverDebugger3dProps> = ({
|
|
|
602
602
|
useEffect(() => {
|
|
603
603
|
const interval = setInterval(() => {
|
|
604
604
|
// Only update if solver has output available
|
|
605
|
-
if (solver.solved ||
|
|
605
|
+
if (solver.solved || solver.stats?.placed > 0) {
|
|
606
606
|
updateMeshNodes()
|
|
607
607
|
}
|
|
608
608
|
}, 100) // Poll every 100ms during active solving
|
|
@@ -731,7 +731,7 @@ export const SolverDebugger3d: React.FC<SolverDebugger3dProps> = ({
|
|
|
731
731
|
{/* Render 2D view */}
|
|
732
732
|
{renderMode === "2d" && (
|
|
733
733
|
<GenericSolverDebugger
|
|
734
|
-
solver={solver
|
|
734
|
+
solver={solver}
|
|
735
735
|
onSolverCompleted={handleSolverCompleted}
|
|
736
736
|
/>
|
|
737
737
|
)}
|
package/dist/index.d.ts
CHANGED
|
@@ -220,6 +220,9 @@ type RectDiffSeedingSolverInput = {
|
|
|
220
220
|
obstacleIndexByLayer: Array<RBush<RTreeRect>>;
|
|
221
221
|
gridOptions?: Partial<GridFill3DOptions>;
|
|
222
222
|
boardVoidRects?: XYRect[];
|
|
223
|
+
layerNames: string[];
|
|
224
|
+
zIndexByName: Map<string, number>;
|
|
225
|
+
obstacleClearance?: number;
|
|
223
226
|
};
|
|
224
227
|
/**
|
|
225
228
|
* First phase of RectDiff: grid-based seeding and placement.
|
|
@@ -258,7 +261,6 @@ declare class RectDiffSeedingSolver extends BaseSolver {
|
|
|
258
261
|
* expansion phase solver.
|
|
259
262
|
*/
|
|
260
263
|
getOutput(): {
|
|
261
|
-
srj: SimpleRouteJson;
|
|
262
264
|
layerNames: string[];
|
|
263
265
|
layerCount: number;
|
|
264
266
|
bounds: XYRect;
|
|
@@ -274,13 +276,14 @@ declare class RectDiffSeedingSolver extends BaseSolver {
|
|
|
274
276
|
edgeAnalysisDone: boolean;
|
|
275
277
|
totalSeedsThisGrid: number;
|
|
276
278
|
consumedSeedsThisGrid: number;
|
|
279
|
+
obstacles: Obstacle[];
|
|
280
|
+
obstacleClearance: number | undefined;
|
|
277
281
|
};
|
|
278
282
|
/** Visualization focused on the grid seeding phase. */
|
|
279
283
|
visualize(): GraphicsObject;
|
|
280
284
|
}
|
|
281
285
|
|
|
282
286
|
type RectDiffExpansionSolverInput = {
|
|
283
|
-
srj: SimpleRouteJson;
|
|
284
287
|
layerNames: string[];
|
|
285
288
|
layerCount: number;
|
|
286
289
|
bounds: XYRect;
|
|
@@ -297,6 +300,10 @@ type RectDiffExpansionSolverInput = {
|
|
|
297
300
|
totalSeedsThisGrid: number;
|
|
298
301
|
consumedSeedsThisGrid: number;
|
|
299
302
|
obstacleIndexByLayer: Array<RBush<RTreeRect>>;
|
|
303
|
+
zIndexByName: Map<string, number>;
|
|
304
|
+
layerNamesCanonical: string[];
|
|
305
|
+
obstacles: Obstacle[];
|
|
306
|
+
obstacleClearance?: number;
|
|
300
307
|
};
|
|
301
308
|
/**
|
|
302
309
|
* Second phase of RectDiff: expand placed rects to their maximal extents.
|
|
@@ -322,14 +329,24 @@ declare class RectDiffExpansionSolver extends BaseSolver {
|
|
|
322
329
|
}
|
|
323
330
|
|
|
324
331
|
type RectDiffGridSolverPipelineInput = {
|
|
325
|
-
|
|
332
|
+
bounds: Bounds;
|
|
333
|
+
obstacles: Obstacle[];
|
|
334
|
+
connections: SimpleRouteConnection[];
|
|
335
|
+
outline?: Pick<SimpleRouteJson, "outline">;
|
|
336
|
+
layerCount: number;
|
|
337
|
+
minTraceWidth: number;
|
|
338
|
+
obstacleClearance?: number;
|
|
326
339
|
gridOptions?: Partial<GridFill3DOptions>;
|
|
327
340
|
boardVoidRects?: XYRect[];
|
|
341
|
+
layerNames?: string[];
|
|
342
|
+
zIndexByName?: Map<string, number>;
|
|
328
343
|
};
|
|
329
344
|
declare class RectDiffGridSolverPipeline extends BasePipelineSolver<RectDiffGridSolverPipelineInput> {
|
|
330
345
|
rectDiffSeedingSolver?: RectDiffSeedingSolver;
|
|
331
346
|
rectDiffExpansionSolver?: RectDiffExpansionSolver;
|
|
332
347
|
private obstacleIndexByLayer;
|
|
348
|
+
private layerNames;
|
|
349
|
+
private zIndexByName;
|
|
333
350
|
constructor(inputProblem: RectDiffGridSolverPipelineInput);
|
|
334
351
|
pipelineDef: PipelineStep<any>[];
|
|
335
352
|
getConstructorParams(): RectDiffGridSolverPipelineInput[];
|
|
@@ -342,11 +359,14 @@ declare class RectDiffGridSolverPipeline extends BasePipelineSolver<RectDiffGrid
|
|
|
342
359
|
interface RectDiffPipelineInput {
|
|
343
360
|
simpleRouteJson: SimpleRouteJson;
|
|
344
361
|
gridOptions?: Partial<GridFill3DOptions>;
|
|
362
|
+
obstacleClearance?: number;
|
|
345
363
|
}
|
|
346
364
|
declare class RectDiffPipeline extends BasePipelineSolver<RectDiffPipelineInput> {
|
|
347
365
|
rectDiffGridSolverPipeline?: RectDiffGridSolverPipeline;
|
|
348
366
|
gapFillSolver?: GapFillSolverPipeline;
|
|
349
367
|
boardVoidRects: XYRect[] | undefined;
|
|
368
|
+
zIndexByName?: Map<string, number>;
|
|
369
|
+
layerNames?: string[];
|
|
350
370
|
pipelineDef: PipelineStep<any>[];
|
|
351
371
|
_setup(): void;
|
|
352
372
|
getConstructorParams(): RectDiffPipelineInput[];
|
package/dist/index.js
CHANGED
|
@@ -859,11 +859,11 @@ function canonicalizeLayerOrder(names) {
|
|
|
859
859
|
return a.localeCompare(b);
|
|
860
860
|
});
|
|
861
861
|
}
|
|
862
|
-
function buildZIndexMap(
|
|
862
|
+
function buildZIndexMap(params) {
|
|
863
863
|
const names = canonicalizeLayerOrder(
|
|
864
|
-
(
|
|
864
|
+
(params.obstacles ?? []).flatMap((o) => o.layers ?? [])
|
|
865
865
|
);
|
|
866
|
-
const declaredLayerCount = Math.max(1,
|
|
866
|
+
const declaredLayerCount = Math.max(1, params.layerCount || names.length || 1);
|
|
867
867
|
const fallback = Array.from(
|
|
868
868
|
{ length: declaredLayerCount },
|
|
869
869
|
(_, i) => i === 0 ? "top" : i === declaredLayerCount - 1 ? "bottom" : `inner${i}`
|
|
@@ -1710,7 +1710,14 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1710
1710
|
_setup() {
|
|
1711
1711
|
const srj = this.input.simpleRouteJson;
|
|
1712
1712
|
const opts = this.input.gridOptions ?? {};
|
|
1713
|
-
const
|
|
1713
|
+
const precomputed = this.input.layerNames && this.input.zIndexByName;
|
|
1714
|
+
const { layerNames, zIndexByName } = precomputed ? {
|
|
1715
|
+
layerNames: this.input.layerNames,
|
|
1716
|
+
zIndexByName: this.input.zIndexByName
|
|
1717
|
+
} : buildZIndexMap({
|
|
1718
|
+
obstacles: srj.obstacles,
|
|
1719
|
+
layerCount: srj.layerCount
|
|
1720
|
+
});
|
|
1714
1721
|
const layerCount = Math.max(1, layerNames.length, srj.layerCount || 1);
|
|
1715
1722
|
const bounds = {
|
|
1716
1723
|
x: srj.bounds.minX,
|
|
@@ -1908,7 +1915,6 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1908
1915
|
*/
|
|
1909
1916
|
getOutput() {
|
|
1910
1917
|
return {
|
|
1911
|
-
srj: this.srj,
|
|
1912
1918
|
layerNames: this.layerNames,
|
|
1913
1919
|
layerCount: this.layerCount,
|
|
1914
1920
|
bounds: this.bounds,
|
|
@@ -1920,7 +1926,9 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1920
1926
|
expansionIndex: this.expansionIndex,
|
|
1921
1927
|
edgeAnalysisDone: this.edgeAnalysisDone,
|
|
1922
1928
|
totalSeedsThisGrid: this.totalSeedsThisGrid,
|
|
1923
|
-
consumedSeedsThisGrid: this.consumedSeedsThisGrid
|
|
1929
|
+
consumedSeedsThisGrid: this.consumedSeedsThisGrid,
|
|
1930
|
+
obstacles: this.srj.obstacles,
|
|
1931
|
+
obstacleClearance: this.input.obstacleClearance
|
|
1924
1932
|
};
|
|
1925
1933
|
}
|
|
1926
1934
|
/** Visualization focused on the grid seeding phase. */
|
|
@@ -1968,6 +1976,29 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1968
1976
|
});
|
|
1969
1977
|
}
|
|
1970
1978
|
}
|
|
1979
|
+
if (this.input.obstacleClearance && this.input.obstacleClearance > 0) {
|
|
1980
|
+
for (const obstacle of srj.obstacles ?? []) {
|
|
1981
|
+
const pad = this.input.obstacleClearance;
|
|
1982
|
+
const expanded = {
|
|
1983
|
+
x: obstacle.center.x - obstacle.width / 2 - pad,
|
|
1984
|
+
y: obstacle.center.y - obstacle.height / 2 - pad,
|
|
1985
|
+
width: obstacle.width + 2 * pad,
|
|
1986
|
+
height: obstacle.height + 2 * pad
|
|
1987
|
+
};
|
|
1988
|
+
rects.push({
|
|
1989
|
+
center: {
|
|
1990
|
+
x: expanded.x + expanded.width / 2,
|
|
1991
|
+
y: expanded.y + expanded.height / 2
|
|
1992
|
+
},
|
|
1993
|
+
width: expanded.width,
|
|
1994
|
+
height: expanded.height,
|
|
1995
|
+
fill: "rgba(234, 179, 8, 0.15)",
|
|
1996
|
+
stroke: "rgba(202, 138, 4, 0.9)",
|
|
1997
|
+
layer: "obstacle-clearance",
|
|
1998
|
+
label: "clearance"
|
|
1999
|
+
});
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
1971
2002
|
if (this.boardVoidRects) {
|
|
1972
2003
|
let outlineBBox = null;
|
|
1973
2004
|
if (srj.outline && srj.outline.length > 0) {
|
|
@@ -2001,8 +2032,6 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
2001
2032
|
points.push({
|
|
2002
2033
|
x: cand.x,
|
|
2003
2034
|
y: cand.y,
|
|
2004
|
-
fill: "#9333ea",
|
|
2005
|
-
stroke: "#6b21a8",
|
|
2006
2035
|
label: `z:${cand.z}`
|
|
2007
2036
|
});
|
|
2008
2037
|
}
|
|
@@ -2047,12 +2076,17 @@ function finalizeRects(params) {
|
|
|
2047
2076
|
maxY: p.rect.y + p.rect.height,
|
|
2048
2077
|
zLayers: [...p.zLayers].sort((a, b) => a - b)
|
|
2049
2078
|
}));
|
|
2050
|
-
const { zIndexByName } = buildZIndexMap(params.srj);
|
|
2051
2079
|
const layersByKey = /* @__PURE__ */ new Map();
|
|
2052
|
-
for (const obstacle of params.
|
|
2053
|
-
const
|
|
2054
|
-
if (!
|
|
2055
|
-
const
|
|
2080
|
+
for (const obstacle of params.obstacles ?? []) {
|
|
2081
|
+
const baseRect = obstacleToXYRect(obstacle);
|
|
2082
|
+
if (!baseRect) continue;
|
|
2083
|
+
const rect = params.obstacleClearance ? {
|
|
2084
|
+
x: baseRect.x - params.obstacleClearance,
|
|
2085
|
+
y: baseRect.y - params.obstacleClearance,
|
|
2086
|
+
width: baseRect.width + 2 * params.obstacleClearance,
|
|
2087
|
+
height: baseRect.height + 2 * params.obstacleClearance
|
|
2088
|
+
} : baseRect;
|
|
2089
|
+
const zLayers = obstacle.zLayers?.length && obstacle.zLayers.length > 0 ? obstacle.zLayers : obstacleZs(obstacle, params.zIndexByName);
|
|
2056
2090
|
const key = `${rect.x}:${rect.y}:${rect.width}:${rect.height}`;
|
|
2057
2091
|
let entry = layersByKey.get(key);
|
|
2058
2092
|
if (!entry) {
|
|
@@ -2182,8 +2216,10 @@ var RectDiffExpansionSolver = class extends BaseSolver4 {
|
|
|
2182
2216
|
if (this.solved) return;
|
|
2183
2217
|
const rects = finalizeRects({
|
|
2184
2218
|
placed: this.input.placed,
|
|
2185
|
-
|
|
2186
|
-
|
|
2219
|
+
obstacles: this.input.obstacles,
|
|
2220
|
+
zIndexByName: this.input.zIndexByName,
|
|
2221
|
+
boardVoidRects: this.input.boardVoidRects,
|
|
2222
|
+
obstacleClearance: this.input.obstacleClearance
|
|
2187
2223
|
});
|
|
2188
2224
|
this._meshNodes = rectsToMeshNodes(rects);
|
|
2189
2225
|
this.solved = true;
|
|
@@ -2246,9 +2282,25 @@ import "rbush";
|
|
|
2246
2282
|
|
|
2247
2283
|
// lib/solvers/RectDiffGridSolverPipeline/buildObstacleIndexes.ts
|
|
2248
2284
|
import RBush5 from "rbush";
|
|
2285
|
+
|
|
2286
|
+
// lib/utils/padRect.ts
|
|
2287
|
+
var padRect = (rect, clearance) => {
|
|
2288
|
+
if (!clearance || clearance <= 0) return rect;
|
|
2289
|
+
return {
|
|
2290
|
+
x: rect.x - clearance,
|
|
2291
|
+
y: rect.y - clearance,
|
|
2292
|
+
width: rect.width + 2 * clearance,
|
|
2293
|
+
height: rect.height + 2 * clearance
|
|
2294
|
+
};
|
|
2295
|
+
};
|
|
2296
|
+
|
|
2297
|
+
// lib/solvers/RectDiffGridSolverPipeline/buildObstacleIndexes.ts
|
|
2249
2298
|
var buildObstacleIndexesByLayer = (params) => {
|
|
2250
|
-
const { srj, boardVoidRects } = params;
|
|
2251
|
-
const { layerNames, zIndexByName } = buildZIndexMap(
|
|
2299
|
+
const { srj, boardVoidRects, obstacleClearance } = params;
|
|
2300
|
+
const { layerNames, zIndexByName } = buildZIndexMap({
|
|
2301
|
+
obstacles: srj.obstacles,
|
|
2302
|
+
layerCount: srj.layerCount
|
|
2303
|
+
});
|
|
2252
2304
|
const layerCount = Math.max(1, layerNames.length, srj.layerCount || 1);
|
|
2253
2305
|
const bounds = {
|
|
2254
2306
|
x: srj.bounds.minX,
|
|
@@ -2277,8 +2329,9 @@ var buildObstacleIndexesByLayer = (params) => {
|
|
|
2277
2329
|
}
|
|
2278
2330
|
}
|
|
2279
2331
|
for (const obstacle of srj.obstacles ?? []) {
|
|
2280
|
-
const
|
|
2281
|
-
if (!
|
|
2332
|
+
const rectBase = obstacleToXYRect(obstacle);
|
|
2333
|
+
if (!rectBase) continue;
|
|
2334
|
+
const rect = padRect(rectBase, obstacleClearance ?? 0);
|
|
2282
2335
|
const zLayers = obstacleZs(obstacle, zIndexByName);
|
|
2283
2336
|
const invalidZs = zLayers.filter((z) => z < 0 || z >= layerCount);
|
|
2284
2337
|
if (invalidZs.length) {
|
|
@@ -2291,7 +2344,7 @@ var buildObstacleIndexesByLayer = (params) => {
|
|
|
2291
2344
|
}
|
|
2292
2345
|
for (const z of zLayers) insertObstacle(rect, z);
|
|
2293
2346
|
}
|
|
2294
|
-
return { obstacleIndexByLayer };
|
|
2347
|
+
return { obstacleIndexByLayer, layerNames, zIndexByName };
|
|
2295
2348
|
};
|
|
2296
2349
|
|
|
2297
2350
|
// lib/solvers/RectDiffGridSolverPipeline/RectDiffGridSolverPipeline.ts
|
|
@@ -2299,13 +2352,25 @@ var RectDiffGridSolverPipeline = class extends BasePipelineSolver2 {
|
|
|
2299
2352
|
rectDiffSeedingSolver;
|
|
2300
2353
|
rectDiffExpansionSolver;
|
|
2301
2354
|
obstacleIndexByLayer;
|
|
2355
|
+
layerNames;
|
|
2356
|
+
zIndexByName;
|
|
2302
2357
|
constructor(inputProblem) {
|
|
2303
2358
|
super(inputProblem);
|
|
2304
|
-
const { obstacleIndexByLayer } = buildObstacleIndexesByLayer({
|
|
2305
|
-
srj:
|
|
2306
|
-
|
|
2359
|
+
const { obstacleIndexByLayer, layerNames, zIndexByName } = buildObstacleIndexesByLayer({
|
|
2360
|
+
srj: {
|
|
2361
|
+
bounds: inputProblem.bounds,
|
|
2362
|
+
obstacles: inputProblem.obstacles,
|
|
2363
|
+
connections: inputProblem.connections,
|
|
2364
|
+
outline: inputProblem.outline?.outline,
|
|
2365
|
+
layerCount: inputProblem.layerCount,
|
|
2366
|
+
minTraceWidth: inputProblem.minTraceWidth
|
|
2367
|
+
},
|
|
2368
|
+
boardVoidRects: inputProblem.boardVoidRects,
|
|
2369
|
+
obstacleClearance: inputProblem.obstacleClearance
|
|
2307
2370
|
});
|
|
2308
2371
|
this.obstacleIndexByLayer = obstacleIndexByLayer;
|
|
2372
|
+
this.layerNames = inputProblem.layerNames ?? layerNames;
|
|
2373
|
+
this.zIndexByName = inputProblem.zIndexByName ?? zIndexByName;
|
|
2309
2374
|
}
|
|
2310
2375
|
pipelineDef = [
|
|
2311
2376
|
definePipelineStep2(
|
|
@@ -2313,10 +2378,20 @@ var RectDiffGridSolverPipeline = class extends BasePipelineSolver2 {
|
|
|
2313
2378
|
RectDiffSeedingSolver,
|
|
2314
2379
|
(pipeline) => [
|
|
2315
2380
|
{
|
|
2316
|
-
simpleRouteJson:
|
|
2381
|
+
simpleRouteJson: {
|
|
2382
|
+
bounds: pipeline.inputProblem.bounds,
|
|
2383
|
+
obstacles: pipeline.inputProblem.obstacles,
|
|
2384
|
+
connections: pipeline.inputProblem.connections,
|
|
2385
|
+
outline: pipeline.inputProblem.outline?.outline,
|
|
2386
|
+
layerCount: pipeline.inputProblem.layerCount,
|
|
2387
|
+
minTraceWidth: pipeline.inputProblem.minTraceWidth
|
|
2388
|
+
},
|
|
2317
2389
|
gridOptions: pipeline.inputProblem.gridOptions,
|
|
2318
2390
|
obstacleIndexByLayer: pipeline.obstacleIndexByLayer,
|
|
2319
|
-
boardVoidRects: pipeline.inputProblem.boardVoidRects
|
|
2391
|
+
boardVoidRects: pipeline.inputProblem.boardVoidRects,
|
|
2392
|
+
layerNames: pipeline.layerNames,
|
|
2393
|
+
zIndexByName: pipeline.zIndexByName,
|
|
2394
|
+
obstacleClearance: pipeline.inputProblem.obstacleClearance
|
|
2320
2395
|
}
|
|
2321
2396
|
]
|
|
2322
2397
|
),
|
|
@@ -2330,10 +2405,9 @@ var RectDiffGridSolverPipeline = class extends BasePipelineSolver2 {
|
|
|
2330
2405
|
}
|
|
2331
2406
|
return [
|
|
2332
2407
|
{
|
|
2333
|
-
srj: pipeline.inputProblem.simpleRouteJson,
|
|
2334
2408
|
layerNames: output.layerNames ?? [],
|
|
2335
2409
|
boardVoidRects: pipeline.inputProblem.boardVoidRects ?? [],
|
|
2336
|
-
layerCount: pipeline.inputProblem.
|
|
2410
|
+
layerCount: pipeline.inputProblem.layerCount,
|
|
2337
2411
|
bounds: output.bounds,
|
|
2338
2412
|
candidates: output.candidates,
|
|
2339
2413
|
consumedSeedsThisGrid: output.placed.length,
|
|
@@ -2343,7 +2417,11 @@ var RectDiffGridSolverPipeline = class extends BasePipelineSolver2 {
|
|
|
2343
2417
|
gridIndex: output.gridIndex,
|
|
2344
2418
|
expansionIndex: output.expansionIndex,
|
|
2345
2419
|
obstacleIndexByLayer: pipeline.obstacleIndexByLayer,
|
|
2346
|
-
options: output.options
|
|
2420
|
+
options: output.options,
|
|
2421
|
+
zIndexByName: pipeline.zIndexByName,
|
|
2422
|
+
layerNamesCanonical: pipeline.layerNames,
|
|
2423
|
+
obstacles: pipeline.inputProblem.obstacles,
|
|
2424
|
+
obstacleClearance: pipeline.inputProblem.obstacleClearance
|
|
2347
2425
|
}
|
|
2348
2426
|
];
|
|
2349
2427
|
}
|
|
@@ -2424,6 +2502,7 @@ function createBaseVisualization(srj, title = "RectDiff") {
|
|
|
2424
2502
|
}
|
|
2425
2503
|
for (const obstacle of srj.obstacles ?? []) {
|
|
2426
2504
|
if (obstacle.type === "rect" || obstacle.type === "oval") {
|
|
2505
|
+
const layerLabel = (obstacle.zLayers ?? []).join(",") || "all";
|
|
2427
2506
|
rects.push({
|
|
2428
2507
|
center: { x: obstacle.center.x, y: obstacle.center.y },
|
|
2429
2508
|
width: obstacle.width,
|
|
@@ -2431,7 +2510,8 @@ function createBaseVisualization(srj, title = "RectDiff") {
|
|
|
2431
2510
|
fill: "#fee2e2",
|
|
2432
2511
|
stroke: "#ef4444",
|
|
2433
2512
|
layer: "obstacle",
|
|
2434
|
-
label:
|
|
2513
|
+
label: `obstacle
|
|
2514
|
+
z:${layerLabel}`
|
|
2435
2515
|
});
|
|
2436
2516
|
}
|
|
2437
2517
|
}
|
|
@@ -2444,20 +2524,72 @@ function createBaseVisualization(srj, title = "RectDiff") {
|
|
|
2444
2524
|
};
|
|
2445
2525
|
}
|
|
2446
2526
|
|
|
2527
|
+
// lib/utils/renderObstacleClearance.ts
|
|
2528
|
+
var buildObstacleClearanceGraphics = (params) => {
|
|
2529
|
+
const { srj, clearance } = params;
|
|
2530
|
+
const c = clearance ?? 0;
|
|
2531
|
+
if (c <= 0) {
|
|
2532
|
+
return {
|
|
2533
|
+
title: "Obstacle Clearance",
|
|
2534
|
+
coordinateSystem: "cartesian",
|
|
2535
|
+
rects: []
|
|
2536
|
+
};
|
|
2537
|
+
}
|
|
2538
|
+
const rects = [];
|
|
2539
|
+
for (const obstacle of srj.obstacles ?? []) {
|
|
2540
|
+
if (obstacle.type !== "rect" && obstacle.type !== "oval") continue;
|
|
2541
|
+
const expanded = {
|
|
2542
|
+
x: obstacle.center.x - obstacle.width / 2 - c,
|
|
2543
|
+
y: obstacle.center.y - obstacle.height / 2 - c,
|
|
2544
|
+
width: obstacle.width + 2 * c,
|
|
2545
|
+
height: obstacle.height + 2 * c
|
|
2546
|
+
};
|
|
2547
|
+
rects.push({
|
|
2548
|
+
center: {
|
|
2549
|
+
x: expanded.x + expanded.width / 2,
|
|
2550
|
+
y: expanded.y + expanded.height / 2
|
|
2551
|
+
},
|
|
2552
|
+
width: expanded.width,
|
|
2553
|
+
height: expanded.height,
|
|
2554
|
+
stroke: "rgba(202, 138, 4, 0.9)",
|
|
2555
|
+
fill: "rgba(234, 179, 8, 0.15)",
|
|
2556
|
+
layer: "obstacle-clearance",
|
|
2557
|
+
label: `clearance
|
|
2558
|
+
z:${(obstacle.zLayers ?? []).join(",") || "all"}`
|
|
2559
|
+
});
|
|
2560
|
+
}
|
|
2561
|
+
return {
|
|
2562
|
+
title: "Obstacle Clearance",
|
|
2563
|
+
coordinateSystem: "cartesian",
|
|
2564
|
+
rects
|
|
2565
|
+
};
|
|
2566
|
+
};
|
|
2567
|
+
|
|
2447
2568
|
// lib/RectDiffPipeline.ts
|
|
2569
|
+
import { mergeGraphics } from "graphics-debug";
|
|
2448
2570
|
var RectDiffPipeline = class extends BasePipelineSolver3 {
|
|
2449
2571
|
rectDiffGridSolverPipeline;
|
|
2450
2572
|
gapFillSolver;
|
|
2451
2573
|
boardVoidRects;
|
|
2574
|
+
zIndexByName;
|
|
2575
|
+
layerNames;
|
|
2452
2576
|
pipelineDef = [
|
|
2453
2577
|
definePipelineStep3(
|
|
2454
2578
|
"rectDiffGridSolverPipeline",
|
|
2455
2579
|
RectDiffGridSolverPipeline,
|
|
2456
2580
|
(rectDiffPipeline) => [
|
|
2457
2581
|
{
|
|
2458
|
-
|
|
2582
|
+
bounds: rectDiffPipeline.inputProblem.simpleRouteJson.bounds,
|
|
2583
|
+
obstacles: rectDiffPipeline.inputProblem.simpleRouteJson.obstacles,
|
|
2584
|
+
connections: rectDiffPipeline.inputProblem.simpleRouteJson.connections,
|
|
2585
|
+
outline: rectDiffPipeline.inputProblem.simpleRouteJson.outline ? { outline: rectDiffPipeline.inputProblem.simpleRouteJson.outline } : void 0,
|
|
2586
|
+
layerCount: rectDiffPipeline.inputProblem.simpleRouteJson.layerCount,
|
|
2459
2587
|
gridOptions: rectDiffPipeline.inputProblem.gridOptions,
|
|
2460
|
-
boardVoidRects: rectDiffPipeline.boardVoidRects
|
|
2588
|
+
boardVoidRects: rectDiffPipeline.boardVoidRects,
|
|
2589
|
+
layerNames: rectDiffPipeline.layerNames,
|
|
2590
|
+
zIndexByName: rectDiffPipeline.zIndexByName,
|
|
2591
|
+
minTraceWidth: rectDiffPipeline.inputProblem.simpleRouteJson.minTraceWidth,
|
|
2592
|
+
obstacleClearance: rectDiffPipeline.inputProblem.obstacleClearance
|
|
2461
2593
|
}
|
|
2462
2594
|
]
|
|
2463
2595
|
),
|
|
@@ -2476,6 +2608,12 @@ var RectDiffPipeline = class extends BasePipelineSolver3 {
|
|
|
2476
2608
|
)
|
|
2477
2609
|
];
|
|
2478
2610
|
_setup() {
|
|
2611
|
+
const { zIndexByName, layerNames } = buildZIndexMap({
|
|
2612
|
+
obstacles: this.inputProblem.simpleRouteJson.obstacles,
|
|
2613
|
+
layerCount: this.inputProblem.simpleRouteJson.layerCount
|
|
2614
|
+
});
|
|
2615
|
+
this.zIndexByName = zIndexByName;
|
|
2616
|
+
this.layerNames = layerNames;
|
|
2479
2617
|
if (this.inputProblem.simpleRouteJson.outline) {
|
|
2480
2618
|
this.boardVoidRects = computeInverseRects(
|
|
2481
2619
|
{
|
|
@@ -2502,13 +2640,19 @@ var RectDiffPipeline = class extends BasePipelineSolver3 {
|
|
|
2502
2640
|
return { meshNodes: [] };
|
|
2503
2641
|
}
|
|
2504
2642
|
initialVisualize() {
|
|
2505
|
-
const
|
|
2643
|
+
const base = createBaseVisualization(
|
|
2506
2644
|
this.inputProblem.simpleRouteJson,
|
|
2507
2645
|
"RectDiffPipeline - Initial"
|
|
2508
2646
|
);
|
|
2647
|
+
const clearance = buildObstacleClearanceGraphics({
|
|
2648
|
+
srj: this.inputProblem.simpleRouteJson,
|
|
2649
|
+
clearance: this.inputProblem.obstacleClearance
|
|
2650
|
+
});
|
|
2509
2651
|
const initialNodes = this.rectDiffGridSolverPipeline?.getOutput().meshNodes ?? [];
|
|
2510
|
-
|
|
2511
|
-
|
|
2652
|
+
const nodeRects = {
|
|
2653
|
+
title: "Initial Nodes",
|
|
2654
|
+
coordinateSystem: "cartesian",
|
|
2655
|
+
rects: initialNodes.map((node) => ({
|
|
2512
2656
|
center: node.center,
|
|
2513
2657
|
width: node.width,
|
|
2514
2658
|
height: node.height,
|
|
@@ -2519,37 +2663,45 @@ var RectDiffPipeline = class extends BasePipelineSolver3 {
|
|
|
2519
2663
|
`node ${node.capacityMeshNodeId}`,
|
|
2520
2664
|
`z:${node.availableZ.join(",")}`
|
|
2521
2665
|
].join("\n")
|
|
2522
|
-
})
|
|
2523
|
-
}
|
|
2524
|
-
return
|
|
2666
|
+
}))
|
|
2667
|
+
};
|
|
2668
|
+
return mergeGraphics(mergeGraphics(base, clearance), nodeRects);
|
|
2525
2669
|
}
|
|
2526
2670
|
finalVisualize() {
|
|
2527
|
-
const
|
|
2671
|
+
const base = createBaseVisualization(
|
|
2528
2672
|
this.inputProblem.simpleRouteJson,
|
|
2529
2673
|
"RectDiffPipeline - Final"
|
|
2530
2674
|
);
|
|
2675
|
+
const clearance = buildObstacleClearanceGraphics({
|
|
2676
|
+
srj: this.inputProblem.simpleRouteJson,
|
|
2677
|
+
clearance: this.inputProblem.obstacleClearance
|
|
2678
|
+
});
|
|
2531
2679
|
const { meshNodes: outputNodes } = this.getOutput();
|
|
2532
2680
|
const initialNodeIds = new Set(
|
|
2533
2681
|
(this.rectDiffGridSolverPipeline?.getOutput().meshNodes ?? []).map(
|
|
2534
2682
|
(n) => n.capacityMeshNodeId
|
|
2535
2683
|
)
|
|
2536
2684
|
);
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
`z
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2685
|
+
const nodeRects = {
|
|
2686
|
+
title: "Final Nodes",
|
|
2687
|
+
coordinateSystem: "cartesian",
|
|
2688
|
+
rects: outputNodes.map((node) => {
|
|
2689
|
+
const isExpanded = !initialNodeIds.has(node.capacityMeshNodeId);
|
|
2690
|
+
return {
|
|
2691
|
+
center: node.center,
|
|
2692
|
+
width: node.width,
|
|
2693
|
+
height: node.height,
|
|
2694
|
+
stroke: isExpanded ? "rgba(0, 128, 0, 0.8)" : "rgba(0, 0, 0, 0.3)",
|
|
2695
|
+
fill: isExpanded ? "rgba(0, 200, 0, 0.3)" : "rgba(100, 100, 100, 0.1)",
|
|
2696
|
+
layer: `z${node.availableZ.join(",")}`,
|
|
2697
|
+
label: [
|
|
2698
|
+
`${isExpanded ? "[expanded] " : ""}node ${node.capacityMeshNodeId}`,
|
|
2699
|
+
`z:${node.availableZ.join(",")}`
|
|
2700
|
+
].join("\n")
|
|
2701
|
+
};
|
|
2702
|
+
})
|
|
2703
|
+
};
|
|
2704
|
+
return mergeGraphics(mergeGraphics(base, clearance), nodeRects);
|
|
2553
2705
|
}
|
|
2554
2706
|
};
|
|
2555
2707
|
export {
|