@tscircuit/rectdiff 0.0.22 → 0.0.24
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 +291 -80
- package/lib/RectDiffPipeline.ts +42 -35
- package/lib/buildFinalRectDiffVisualization.ts +46 -0
- 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 +16 -5
- package/lib/solvers/RectDiffSeedingSolver/computeCandidates3D.ts +12 -2
- package/lib/solvers/RectDiffSeedingSolver/computeEdgeCandidates3D.ts +53 -13
- package/lib/solvers/RectDiffSeedingSolver/layers.ts +9 -5
- package/lib/utils/buildOutlineGraphics.ts +39 -0
- package/lib/utils/expandRectFromSeed.ts +11 -1
- 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/__snapshots__/rectDiffGridSolverPipeline.snap.svg +1 -1
- package/tests/solver/bugreport01-be84eb/__snapshots__/bugreport01-be84eb.snap.svg +2 -2
- package/tests/solver/bugreport02-bc4361/__snapshots__/bugreport02-bc4361.snap.svg +2 -2
- package/tests/solver/bugreport03-fe4a17/__snapshots__/bugreport03-fe4a17.snap.svg +1 -1
- package/tests/solver/bugreport07-d3f3be/__snapshots__/bugreport07-d3f3be.snap.svg +1 -1
- package/tests/solver/bugreport08-e3ec95/__snapshots__/bugreport08-e3ec95.snap.svg +1 -1
- package/tests/solver/bugreport09-618e09/__snapshots__/bugreport09-618e09.snap.svg +2 -2
- package/tests/solver/bugreport10-71239a/__snapshots__/bugreport10-71239a.snap.svg +2 -2
- package/tests/solver/bugreport11-b2de3c/__snapshots__/bugreport11-b2de3c.snap.svg +1 -1
- package/tests/solver/bugreport11-b2de3c/bugreport11-b2de3c-clearance-equivalence.test.ts +52 -0
- package/tests/solver/bugreport12-35ce1c/__snapshots__/bugreport12-35ce1c.snap.svg +1 -1
- package/tests/solver/bugreport13-b9a758/__snapshots__/bugreport13-b9a758.snap.svg +2 -2
- package/tests/solver/bugreport16-d95f38/__snapshots__/bugreport16-d95f38.snap.svg +2 -2
- package/tests/solver/bugreport19/__snapshots__/bugreport19.snap.svg +1 -1
- package/tests/solver/bugreport22-2a75ce/__snapshots__/bugreport22-2a75ce.snap.svg +1 -1
- package/tests/solver/bugreport23-LGA15x4/__snapshots__/bugreport23-LGA15x4.snap.svg +1 -1
- package/tests/solver/bugreport24-05597c/__snapshots__/bugreport24-05597c.snap.svg +1 -1
- package/tests/solver/bugreport26-66b0b2/__snapshots__/bugreport26-66b0b2.snap.svg +2 -2
- package/tests/solver/bugreport27-dd3734/__snapshots__/bugreport27-dd3734.snap.svg +2 -2
- package/tests/solver/bugreport28-18a9ef/__snapshots__/bugreport28-18a9ef.snap.svg +2 -2
- package/tests/solver/bugreport29-7deae8/__snapshots__/bugreport29-7deae8.snap.svg +2 -2
- package/tests/solver/bugreport30-2174c8/__snapshots__/bugreport30-2174c8.snap.svg +1 -1
- package/tests/solver/bugreport33-213d45/__snapshots__/bugreport33-213d45.snap.svg +2 -2
- package/tests/solver/bugreport34-e9dea2/__snapshots__/bugreport34-e9dea2.snap.svg +2 -2
- package/tests/solver/bugreport35-191db9/__snapshots__/bugreport35-191db9.snap.svg +2 -2
- package/tests/solver/bugreport36-bf8303/__snapshots__/bugreport36-bf8303.snap.svg +1 -1
- package/tests/solver/interaction/__snapshots__/interaction.snap.svg +1 -1
- package/tests/solver/multi-point/__snapshots__/multi-point.snap.svg +1 -1
- package/tests/solver/pcb_trace_id-should-return-root-connection-name/__snapshots__/pcb_trace_id-should-return-root-connection-name.snap.svg +1 -1
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}`
|
|
@@ -960,6 +960,13 @@ var searchStripUp = ({
|
|
|
960
960
|
});
|
|
961
961
|
|
|
962
962
|
// lib/utils/expandRectFromSeed.ts
|
|
963
|
+
var quantize = (value, precision = 1e-6) => Math.round(value / precision) * precision;
|
|
964
|
+
var quantizeRect = (rect) => ({
|
|
965
|
+
x: quantize(rect.x),
|
|
966
|
+
y: quantize(rect.y),
|
|
967
|
+
width: quantize(rect.width),
|
|
968
|
+
height: quantize(rect.height)
|
|
969
|
+
});
|
|
963
970
|
function maxExpandRight(params) {
|
|
964
971
|
const { r, bounds, blockers, maxAspect } = params;
|
|
965
972
|
let maxWidth = bounds.x + bounds.width - r.x;
|
|
@@ -1173,7 +1180,7 @@ function expandRectFromSeed(params) {
|
|
|
1173
1180
|
if (r.width + EPS4 >= minReq.width && r.height + EPS4 >= minReq.height) {
|
|
1174
1181
|
const area = r.width * r.height;
|
|
1175
1182
|
if (area > bestArea) {
|
|
1176
|
-
best = r;
|
|
1183
|
+
best = quantizeRect(r);
|
|
1177
1184
|
bestArea = area;
|
|
1178
1185
|
}
|
|
1179
1186
|
}
|
|
@@ -1247,6 +1254,7 @@ function longestFreeSpanAroundZ(params) {
|
|
|
1247
1254
|
}
|
|
1248
1255
|
|
|
1249
1256
|
// lib/solvers/RectDiffSeedingSolver/computeCandidates3D.ts
|
|
1257
|
+
var quantize2 = (value, precision = 1e-6) => Math.round(value / precision) * precision;
|
|
1250
1258
|
function computeCandidates3D(params) {
|
|
1251
1259
|
const {
|
|
1252
1260
|
bounds,
|
|
@@ -1296,12 +1304,13 @@ function computeCandidates3D(params) {
|
|
|
1296
1304
|
distancePointToRectEdges({ x, y }, bounds),
|
|
1297
1305
|
...hardAtZ.length ? hardAtZ.map((b) => distancePointToRectEdges({ x, y }, b)) : [Infinity]
|
|
1298
1306
|
);
|
|
1307
|
+
const distance = quantize2(d);
|
|
1299
1308
|
const k = `${x.toFixed(6)}|${y.toFixed(6)}`;
|
|
1300
1309
|
const cand = {
|
|
1301
1310
|
x,
|
|
1302
1311
|
y,
|
|
1303
1312
|
z: anchorZ,
|
|
1304
|
-
distance
|
|
1313
|
+
distance,
|
|
1305
1314
|
zSpanLen: bestSpan.length
|
|
1306
1315
|
};
|
|
1307
1316
|
const prev = out.get(k);
|
|
@@ -1311,18 +1320,28 @@ function computeCandidates3D(params) {
|
|
|
1311
1320
|
}
|
|
1312
1321
|
}
|
|
1313
1322
|
const arr = Array.from(out.values());
|
|
1314
|
-
arr.sort(
|
|
1323
|
+
arr.sort(
|
|
1324
|
+
(a, b) => b.zSpanLen - a.zSpanLen || b.distance - a.distance || a.z - b.z || a.x - b.x || a.y - b.y
|
|
1325
|
+
);
|
|
1315
1326
|
return arr;
|
|
1316
1327
|
}
|
|
1317
1328
|
|
|
1318
1329
|
// lib/solvers/RectDiffSeedingSolver/computeEdgeCandidates3D.ts
|
|
1330
|
+
var quantize3 = (value, precision = 1e-6) => Math.round(value / precision) * precision;
|
|
1319
1331
|
function computeUncoveredSegments(params) {
|
|
1320
1332
|
const { lineStart, lineEnd, coveringIntervals, minSegmentLength } = params;
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1333
|
+
const lineStartQ = quantize3(lineStart);
|
|
1334
|
+
const lineEndQ = quantize3(lineEnd);
|
|
1335
|
+
const normalizedIntervals = coveringIntervals.map((i) => {
|
|
1336
|
+
const s = quantize3(i.start);
|
|
1337
|
+
const e = quantize3(i.end);
|
|
1338
|
+
return { start: Math.min(s, e), end: Math.max(s, e) };
|
|
1339
|
+
}).filter((i) => i.end > i.start + EPS4);
|
|
1340
|
+
if (normalizedIntervals.length === 0) {
|
|
1341
|
+
const center = (lineStartQ + lineEndQ) / 2;
|
|
1342
|
+
return [{ start: lineStartQ, end: lineEndQ, center }];
|
|
1343
|
+
}
|
|
1344
|
+
const sorted = [...normalizedIntervals].sort((a, b) => a.start - b.start);
|
|
1326
1345
|
const merged = [];
|
|
1327
1346
|
let current = { ...sorted[0] };
|
|
1328
1347
|
for (let i = 1; i < sorted.length; i++) {
|
|
@@ -1336,8 +1355,8 @@ function computeUncoveredSegments(params) {
|
|
|
1336
1355
|
}
|
|
1337
1356
|
merged.push(current);
|
|
1338
1357
|
const uncovered = [];
|
|
1339
|
-
if (merged[0].start >
|
|
1340
|
-
const start =
|
|
1358
|
+
if (merged[0].start > lineStartQ + EPS4) {
|
|
1359
|
+
const start = lineStartQ;
|
|
1341
1360
|
const end = merged[0].start;
|
|
1342
1361
|
if (end - start >= minSegmentLength) {
|
|
1343
1362
|
uncovered.push({ start, end, center: (start + end) / 2 });
|
|
@@ -1350,9 +1369,9 @@ function computeUncoveredSegments(params) {
|
|
|
1350
1369
|
uncovered.push({ start, end, center: (start + end) / 2 });
|
|
1351
1370
|
}
|
|
1352
1371
|
}
|
|
1353
|
-
if (merged[merged.length - 1].end <
|
|
1372
|
+
if (merged[merged.length - 1].end < lineEndQ - EPS4) {
|
|
1354
1373
|
const start = merged[merged.length - 1].end;
|
|
1355
|
-
const end =
|
|
1374
|
+
const end = lineEndQ;
|
|
1356
1375
|
if (end - start >= minSegmentLength) {
|
|
1357
1376
|
uncovered.push({ start, end, center: (start + end) / 2 });
|
|
1358
1377
|
}
|
|
@@ -1381,18 +1400,28 @@ function computeEdgeCandidates3D(params) {
|
|
|
1381
1400
|
});
|
|
1382
1401
|
}
|
|
1383
1402
|
function pushIfFree(p) {
|
|
1384
|
-
const
|
|
1403
|
+
const qx = quantize3(p.x);
|
|
1404
|
+
const qy = quantize3(p.y);
|
|
1405
|
+
const { z } = p;
|
|
1406
|
+
const x = qx;
|
|
1407
|
+
const y = qy;
|
|
1385
1408
|
if (x < bounds.x + EPS4 || y < bounds.y + EPS4 || x > bounds.x + bounds.width - EPS4 || y > bounds.y + bounds.height - EPS4)
|
|
1386
1409
|
return;
|
|
1387
1410
|
if (fullyOcc({ x, y })) return;
|
|
1388
1411
|
const hard = [
|
|
1389
1412
|
...obstacleIndexByLayer[z]?.all() ?? [],
|
|
1390
1413
|
...hardPlacedByLayer[z] ?? []
|
|
1391
|
-
]
|
|
1414
|
+
].map((b) => ({
|
|
1415
|
+
x: quantize3(b.x),
|
|
1416
|
+
y: quantize3(b.y),
|
|
1417
|
+
width: quantize3(b.width),
|
|
1418
|
+
height: quantize3(b.height)
|
|
1419
|
+
}));
|
|
1392
1420
|
const d = Math.min(
|
|
1393
1421
|
distancePointToRectEdges({ x, y }, bounds),
|
|
1394
1422
|
...hard.length ? hard.map((b) => distancePointToRectEdges({ x, y }, b)) : [Infinity]
|
|
1395
1423
|
);
|
|
1424
|
+
const distance = quantize3(d);
|
|
1396
1425
|
const k = key({ x, y, z });
|
|
1397
1426
|
if (dedup.has(k)) return;
|
|
1398
1427
|
dedup.add(k);
|
|
@@ -1406,13 +1435,25 @@ function computeEdgeCandidates3D(params) {
|
|
|
1406
1435
|
obstacleIndexByLayer,
|
|
1407
1436
|
additionalBlockersByLayer: hardPlacedByLayer
|
|
1408
1437
|
});
|
|
1409
|
-
out.push({
|
|
1438
|
+
out.push({
|
|
1439
|
+
x,
|
|
1440
|
+
y,
|
|
1441
|
+
z,
|
|
1442
|
+
distance,
|
|
1443
|
+
zSpanLen: span.length,
|
|
1444
|
+
isEdgeSeed: true
|
|
1445
|
+
});
|
|
1410
1446
|
}
|
|
1411
1447
|
for (let z = 0; z < layerCount; z++) {
|
|
1412
1448
|
const blockers = [
|
|
1413
1449
|
...obstacleIndexByLayer[z]?.all() ?? [],
|
|
1414
1450
|
...hardPlacedByLayer[z] ?? []
|
|
1415
|
-
]
|
|
1451
|
+
].map((b) => ({
|
|
1452
|
+
x: quantize3(b.x),
|
|
1453
|
+
y: quantize3(b.y),
|
|
1454
|
+
width: quantize3(b.width),
|
|
1455
|
+
height: quantize3(b.height)
|
|
1456
|
+
}));
|
|
1416
1457
|
const corners = [
|
|
1417
1458
|
{ x: bounds.x + \u03B4, y: bounds.y + \u03B4 },
|
|
1418
1459
|
// top-left
|
|
@@ -1585,7 +1626,9 @@ function computeEdgeCandidates3D(params) {
|
|
|
1585
1626
|
}
|
|
1586
1627
|
}
|
|
1587
1628
|
}
|
|
1588
|
-
out.sort(
|
|
1629
|
+
out.sort(
|
|
1630
|
+
(a, b) => b.zSpanLen - a.zSpanLen || b.distance - a.distance || a.z - b.z || a.x - b.x || a.y - b.y
|
|
1631
|
+
);
|
|
1589
1632
|
return out;
|
|
1590
1633
|
}
|
|
1591
1634
|
|
|
@@ -1710,7 +1753,14 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1710
1753
|
_setup() {
|
|
1711
1754
|
const srj = this.input.simpleRouteJson;
|
|
1712
1755
|
const opts = this.input.gridOptions ?? {};
|
|
1713
|
-
const
|
|
1756
|
+
const precomputed = this.input.layerNames && this.input.zIndexByName;
|
|
1757
|
+
const { layerNames, zIndexByName } = precomputed ? {
|
|
1758
|
+
layerNames: this.input.layerNames,
|
|
1759
|
+
zIndexByName: this.input.zIndexByName
|
|
1760
|
+
} : buildZIndexMap({
|
|
1761
|
+
obstacles: srj.obstacles,
|
|
1762
|
+
layerCount: srj.layerCount
|
|
1763
|
+
});
|
|
1714
1764
|
const layerCount = Math.max(1, layerNames.length, srj.layerCount || 1);
|
|
1715
1765
|
const bounds = {
|
|
1716
1766
|
x: srj.bounds.minX,
|
|
@@ -1908,7 +1958,6 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1908
1958
|
*/
|
|
1909
1959
|
getOutput() {
|
|
1910
1960
|
return {
|
|
1911
|
-
srj: this.srj,
|
|
1912
1961
|
layerNames: this.layerNames,
|
|
1913
1962
|
layerCount: this.layerCount,
|
|
1914
1963
|
bounds: this.bounds,
|
|
@@ -1920,7 +1969,9 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1920
1969
|
expansionIndex: this.expansionIndex,
|
|
1921
1970
|
edgeAnalysisDone: this.edgeAnalysisDone,
|
|
1922
1971
|
totalSeedsThisGrid: this.totalSeedsThisGrid,
|
|
1923
|
-
consumedSeedsThisGrid: this.consumedSeedsThisGrid
|
|
1972
|
+
consumedSeedsThisGrid: this.consumedSeedsThisGrid,
|
|
1973
|
+
obstacles: this.srj.obstacles,
|
|
1974
|
+
obstacleClearance: this.input.obstacleClearance
|
|
1924
1975
|
};
|
|
1925
1976
|
}
|
|
1926
1977
|
/** Visualization focused on the grid seeding phase. */
|
|
@@ -2001,8 +2052,6 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
2001
2052
|
points.push({
|
|
2002
2053
|
x: cand.x,
|
|
2003
2054
|
y: cand.y,
|
|
2004
|
-
fill: "#9333ea",
|
|
2005
|
-
stroke: "#6b21a8",
|
|
2006
2055
|
label: `z:${cand.z}`
|
|
2007
2056
|
});
|
|
2008
2057
|
}
|
|
@@ -2047,12 +2096,17 @@ function finalizeRects(params) {
|
|
|
2047
2096
|
maxY: p.rect.y + p.rect.height,
|
|
2048
2097
|
zLayers: [...p.zLayers].sort((a, b) => a - b)
|
|
2049
2098
|
}));
|
|
2050
|
-
const { zIndexByName } = buildZIndexMap(params.srj);
|
|
2051
2099
|
const layersByKey = /* @__PURE__ */ new Map();
|
|
2052
|
-
for (const obstacle of params.
|
|
2053
|
-
const
|
|
2054
|
-
if (!
|
|
2055
|
-
const
|
|
2100
|
+
for (const obstacle of params.obstacles ?? []) {
|
|
2101
|
+
const baseRect = obstacleToXYRect(obstacle);
|
|
2102
|
+
if (!baseRect) continue;
|
|
2103
|
+
const rect = params.obstacleClearance ? {
|
|
2104
|
+
x: baseRect.x - params.obstacleClearance,
|
|
2105
|
+
y: baseRect.y - params.obstacleClearance,
|
|
2106
|
+
width: baseRect.width + 2 * params.obstacleClearance,
|
|
2107
|
+
height: baseRect.height + 2 * params.obstacleClearance
|
|
2108
|
+
} : baseRect;
|
|
2109
|
+
const zLayers = obstacle.zLayers?.length && obstacle.zLayers.length > 0 ? obstacle.zLayers : obstacleZs(obstacle, params.zIndexByName);
|
|
2056
2110
|
const key = `${rect.x}:${rect.y}:${rect.width}:${rect.height}`;
|
|
2057
2111
|
let entry = layersByKey.get(key);
|
|
2058
2112
|
if (!entry) {
|
|
@@ -2182,8 +2236,10 @@ var RectDiffExpansionSolver = class extends BaseSolver4 {
|
|
|
2182
2236
|
if (this.solved) return;
|
|
2183
2237
|
const rects = finalizeRects({
|
|
2184
2238
|
placed: this.input.placed,
|
|
2185
|
-
|
|
2186
|
-
|
|
2239
|
+
obstacles: this.input.obstacles,
|
|
2240
|
+
zIndexByName: this.input.zIndexByName,
|
|
2241
|
+
boardVoidRects: this.input.boardVoidRects,
|
|
2242
|
+
obstacleClearance: this.input.obstacleClearance
|
|
2187
2243
|
});
|
|
2188
2244
|
this._meshNodes = rectsToMeshNodes(rects);
|
|
2189
2245
|
this.solved = true;
|
|
@@ -2246,9 +2302,25 @@ import "rbush";
|
|
|
2246
2302
|
|
|
2247
2303
|
// lib/solvers/RectDiffGridSolverPipeline/buildObstacleIndexes.ts
|
|
2248
2304
|
import RBush5 from "rbush";
|
|
2305
|
+
|
|
2306
|
+
// lib/utils/padRect.ts
|
|
2307
|
+
var padRect = (rect, clearance) => {
|
|
2308
|
+
if (!clearance || clearance <= 0) return rect;
|
|
2309
|
+
return {
|
|
2310
|
+
x: rect.x - clearance,
|
|
2311
|
+
y: rect.y - clearance,
|
|
2312
|
+
width: rect.width + 2 * clearance,
|
|
2313
|
+
height: rect.height + 2 * clearance
|
|
2314
|
+
};
|
|
2315
|
+
};
|
|
2316
|
+
|
|
2317
|
+
// lib/solvers/RectDiffGridSolverPipeline/buildObstacleIndexes.ts
|
|
2249
2318
|
var buildObstacleIndexesByLayer = (params) => {
|
|
2250
|
-
const { srj, boardVoidRects } = params;
|
|
2251
|
-
const { layerNames, zIndexByName } = buildZIndexMap(
|
|
2319
|
+
const { srj, boardVoidRects, obstacleClearance } = params;
|
|
2320
|
+
const { layerNames, zIndexByName } = buildZIndexMap({
|
|
2321
|
+
obstacles: srj.obstacles,
|
|
2322
|
+
layerCount: srj.layerCount
|
|
2323
|
+
});
|
|
2252
2324
|
const layerCount = Math.max(1, layerNames.length, srj.layerCount || 1);
|
|
2253
2325
|
const bounds = {
|
|
2254
2326
|
x: srj.bounds.minX,
|
|
@@ -2277,8 +2349,9 @@ var buildObstacleIndexesByLayer = (params) => {
|
|
|
2277
2349
|
}
|
|
2278
2350
|
}
|
|
2279
2351
|
for (const obstacle of srj.obstacles ?? []) {
|
|
2280
|
-
const
|
|
2281
|
-
if (!
|
|
2352
|
+
const rectBase = obstacleToXYRect(obstacle);
|
|
2353
|
+
if (!rectBase) continue;
|
|
2354
|
+
const rect = padRect(rectBase, obstacleClearance ?? 0);
|
|
2282
2355
|
const zLayers = obstacleZs(obstacle, zIndexByName);
|
|
2283
2356
|
const invalidZs = zLayers.filter((z) => z < 0 || z >= layerCount);
|
|
2284
2357
|
if (invalidZs.length) {
|
|
@@ -2291,7 +2364,7 @@ var buildObstacleIndexesByLayer = (params) => {
|
|
|
2291
2364
|
}
|
|
2292
2365
|
for (const z of zLayers) insertObstacle(rect, z);
|
|
2293
2366
|
}
|
|
2294
|
-
return { obstacleIndexByLayer };
|
|
2367
|
+
return { obstacleIndexByLayer, layerNames, zIndexByName };
|
|
2295
2368
|
};
|
|
2296
2369
|
|
|
2297
2370
|
// lib/solvers/RectDiffGridSolverPipeline/RectDiffGridSolverPipeline.ts
|
|
@@ -2299,13 +2372,25 @@ var RectDiffGridSolverPipeline = class extends BasePipelineSolver2 {
|
|
|
2299
2372
|
rectDiffSeedingSolver;
|
|
2300
2373
|
rectDiffExpansionSolver;
|
|
2301
2374
|
obstacleIndexByLayer;
|
|
2375
|
+
layerNames;
|
|
2376
|
+
zIndexByName;
|
|
2302
2377
|
constructor(inputProblem) {
|
|
2303
2378
|
super(inputProblem);
|
|
2304
|
-
const { obstacleIndexByLayer } = buildObstacleIndexesByLayer({
|
|
2305
|
-
srj:
|
|
2306
|
-
|
|
2379
|
+
const { obstacleIndexByLayer, layerNames, zIndexByName } = buildObstacleIndexesByLayer({
|
|
2380
|
+
srj: {
|
|
2381
|
+
bounds: inputProblem.bounds,
|
|
2382
|
+
obstacles: inputProblem.obstacles,
|
|
2383
|
+
connections: inputProblem.connections,
|
|
2384
|
+
outline: inputProblem.outline?.outline,
|
|
2385
|
+
layerCount: inputProblem.layerCount,
|
|
2386
|
+
minTraceWidth: inputProblem.minTraceWidth
|
|
2387
|
+
},
|
|
2388
|
+
boardVoidRects: inputProblem.boardVoidRects,
|
|
2389
|
+
obstacleClearance: inputProblem.obstacleClearance
|
|
2307
2390
|
});
|
|
2308
2391
|
this.obstacleIndexByLayer = obstacleIndexByLayer;
|
|
2392
|
+
this.layerNames = inputProblem.layerNames ?? layerNames;
|
|
2393
|
+
this.zIndexByName = inputProblem.zIndexByName ?? zIndexByName;
|
|
2309
2394
|
}
|
|
2310
2395
|
pipelineDef = [
|
|
2311
2396
|
definePipelineStep2(
|
|
@@ -2313,10 +2398,20 @@ var RectDiffGridSolverPipeline = class extends BasePipelineSolver2 {
|
|
|
2313
2398
|
RectDiffSeedingSolver,
|
|
2314
2399
|
(pipeline) => [
|
|
2315
2400
|
{
|
|
2316
|
-
simpleRouteJson:
|
|
2401
|
+
simpleRouteJson: {
|
|
2402
|
+
bounds: pipeline.inputProblem.bounds,
|
|
2403
|
+
obstacles: pipeline.inputProblem.obstacles,
|
|
2404
|
+
connections: pipeline.inputProblem.connections,
|
|
2405
|
+
outline: pipeline.inputProblem.outline?.outline,
|
|
2406
|
+
layerCount: pipeline.inputProblem.layerCount,
|
|
2407
|
+
minTraceWidth: pipeline.inputProblem.minTraceWidth
|
|
2408
|
+
},
|
|
2317
2409
|
gridOptions: pipeline.inputProblem.gridOptions,
|
|
2318
2410
|
obstacleIndexByLayer: pipeline.obstacleIndexByLayer,
|
|
2319
|
-
boardVoidRects: pipeline.inputProblem.boardVoidRects
|
|
2411
|
+
boardVoidRects: pipeline.inputProblem.boardVoidRects,
|
|
2412
|
+
layerNames: pipeline.layerNames,
|
|
2413
|
+
zIndexByName: pipeline.zIndexByName,
|
|
2414
|
+
obstacleClearance: pipeline.inputProblem.obstacleClearance
|
|
2320
2415
|
}
|
|
2321
2416
|
]
|
|
2322
2417
|
),
|
|
@@ -2330,10 +2425,9 @@ var RectDiffGridSolverPipeline = class extends BasePipelineSolver2 {
|
|
|
2330
2425
|
}
|
|
2331
2426
|
return [
|
|
2332
2427
|
{
|
|
2333
|
-
srj: pipeline.inputProblem.simpleRouteJson,
|
|
2334
2428
|
layerNames: output.layerNames ?? [],
|
|
2335
2429
|
boardVoidRects: pipeline.inputProblem.boardVoidRects ?? [],
|
|
2336
|
-
layerCount: pipeline.inputProblem.
|
|
2430
|
+
layerCount: pipeline.inputProblem.layerCount,
|
|
2337
2431
|
bounds: output.bounds,
|
|
2338
2432
|
candidates: output.candidates,
|
|
2339
2433
|
consumedSeedsThisGrid: output.placed.length,
|
|
@@ -2343,7 +2437,11 @@ var RectDiffGridSolverPipeline = class extends BasePipelineSolver2 {
|
|
|
2343
2437
|
gridIndex: output.gridIndex,
|
|
2344
2438
|
expansionIndex: output.expansionIndex,
|
|
2345
2439
|
obstacleIndexByLayer: pipeline.obstacleIndexByLayer,
|
|
2346
|
-
options: output.options
|
|
2440
|
+
options: output.options,
|
|
2441
|
+
zIndexByName: pipeline.zIndexByName,
|
|
2442
|
+
layerNamesCanonical: pipeline.layerNames,
|
|
2443
|
+
obstacles: pipeline.inputProblem.obstacles,
|
|
2444
|
+
obstacleClearance: pipeline.inputProblem.obstacleClearance
|
|
2347
2445
|
}
|
|
2348
2446
|
];
|
|
2349
2447
|
}
|
|
@@ -2424,6 +2522,7 @@ function createBaseVisualization(srj, title = "RectDiff") {
|
|
|
2424
2522
|
}
|
|
2425
2523
|
for (const obstacle of srj.obstacles ?? []) {
|
|
2426
2524
|
if (obstacle.type === "rect" || obstacle.type === "oval") {
|
|
2525
|
+
const layerLabel = (obstacle.zLayers ?? []).join(",") || "all";
|
|
2427
2526
|
rects.push({
|
|
2428
2527
|
center: { x: obstacle.center.x, y: obstacle.center.y },
|
|
2429
2528
|
width: obstacle.width,
|
|
@@ -2431,7 +2530,8 @@ function createBaseVisualization(srj, title = "RectDiff") {
|
|
|
2431
2530
|
fill: "#fee2e2",
|
|
2432
2531
|
stroke: "#ef4444",
|
|
2433
2532
|
layer: "obstacle",
|
|
2434
|
-
label:
|
|
2533
|
+
label: `obstacle
|
|
2534
|
+
z:${layerLabel}`
|
|
2435
2535
|
});
|
|
2436
2536
|
}
|
|
2437
2537
|
}
|
|
@@ -2444,20 +2544,140 @@ function createBaseVisualization(srj, title = "RectDiff") {
|
|
|
2444
2544
|
};
|
|
2445
2545
|
}
|
|
2446
2546
|
|
|
2547
|
+
// lib/buildFinalRectDiffVisualization.ts
|
|
2548
|
+
import { mergeGraphics } from "graphics-debug";
|
|
2549
|
+
|
|
2550
|
+
// lib/utils/buildOutlineGraphics.ts
|
|
2551
|
+
var buildOutlineGraphics = ({
|
|
2552
|
+
srj
|
|
2553
|
+
}) => {
|
|
2554
|
+
const hasOutline = srj.outline && srj.outline.length > 1;
|
|
2555
|
+
const lines = hasOutline ? [
|
|
2556
|
+
{
|
|
2557
|
+
points: [...srj.outline, srj.outline[0]],
|
|
2558
|
+
strokeColor: "#111827",
|
|
2559
|
+
strokeWidth: 0.1,
|
|
2560
|
+
label: "outline"
|
|
2561
|
+
}
|
|
2562
|
+
] : [
|
|
2563
|
+
{
|
|
2564
|
+
points: [
|
|
2565
|
+
{ x: srj.bounds.minX, y: srj.bounds.minY },
|
|
2566
|
+
{ x: srj.bounds.maxX, y: srj.bounds.minY },
|
|
2567
|
+
{ x: srj.bounds.maxX, y: srj.bounds.maxY },
|
|
2568
|
+
{ x: srj.bounds.minX, y: srj.bounds.maxY },
|
|
2569
|
+
{ x: srj.bounds.minX, y: srj.bounds.minY }
|
|
2570
|
+
],
|
|
2571
|
+
strokeColor: "#111827",
|
|
2572
|
+
strokeWidth: 0.1,
|
|
2573
|
+
label: "bounds"
|
|
2574
|
+
}
|
|
2575
|
+
];
|
|
2576
|
+
return {
|
|
2577
|
+
title: "SimpleRoute Outline",
|
|
2578
|
+
coordinateSystem: "cartesian",
|
|
2579
|
+
lines
|
|
2580
|
+
};
|
|
2581
|
+
};
|
|
2582
|
+
|
|
2583
|
+
// lib/utils/renderObstacleClearance.ts
|
|
2584
|
+
var buildObstacleClearanceGraphics = (params) => {
|
|
2585
|
+
const { srj, clearance } = params;
|
|
2586
|
+
const c = clearance ?? 0;
|
|
2587
|
+
if (c <= 0) {
|
|
2588
|
+
return {
|
|
2589
|
+
title: "Obstacle Clearance",
|
|
2590
|
+
coordinateSystem: "cartesian",
|
|
2591
|
+
rects: []
|
|
2592
|
+
};
|
|
2593
|
+
}
|
|
2594
|
+
const rects = [];
|
|
2595
|
+
for (const obstacle of srj.obstacles ?? []) {
|
|
2596
|
+
if (obstacle.type !== "rect" && obstacle.type !== "oval") continue;
|
|
2597
|
+
const expanded = {
|
|
2598
|
+
x: obstacle.center.x - obstacle.width / 2 - c,
|
|
2599
|
+
y: obstacle.center.y - obstacle.height / 2 - c,
|
|
2600
|
+
width: obstacle.width + 2 * c,
|
|
2601
|
+
height: obstacle.height + 2 * c
|
|
2602
|
+
};
|
|
2603
|
+
rects.push({
|
|
2604
|
+
center: {
|
|
2605
|
+
x: expanded.x + expanded.width / 2,
|
|
2606
|
+
y: expanded.y + expanded.height / 2
|
|
2607
|
+
},
|
|
2608
|
+
width: expanded.width,
|
|
2609
|
+
height: expanded.height,
|
|
2610
|
+
stroke: "rgba(202, 138, 4, 0.9)",
|
|
2611
|
+
fill: "rgba(234, 179, 8, 0.15)",
|
|
2612
|
+
layer: "obstacle-clearance",
|
|
2613
|
+
label: `clearance
|
|
2614
|
+
z:${(obstacle.zLayers ?? []).join(",") || "all"}`
|
|
2615
|
+
});
|
|
2616
|
+
}
|
|
2617
|
+
return {
|
|
2618
|
+
title: "Obstacle Clearance",
|
|
2619
|
+
coordinateSystem: "cartesian",
|
|
2620
|
+
rects
|
|
2621
|
+
};
|
|
2622
|
+
};
|
|
2623
|
+
|
|
2624
|
+
// lib/buildFinalRectDiffVisualization.ts
|
|
2625
|
+
var buildFinalRectDiffVisualization = ({
|
|
2626
|
+
srj,
|
|
2627
|
+
meshNodes,
|
|
2628
|
+
obstacleClearance
|
|
2629
|
+
}) => {
|
|
2630
|
+
const outline = buildOutlineGraphics({ srj });
|
|
2631
|
+
const clearance = buildObstacleClearanceGraphics({
|
|
2632
|
+
srj,
|
|
2633
|
+
clearance: obstacleClearance
|
|
2634
|
+
});
|
|
2635
|
+
const rects = meshNodes.map((node) => ({
|
|
2636
|
+
center: node.center,
|
|
2637
|
+
width: node.width,
|
|
2638
|
+
height: node.height,
|
|
2639
|
+
stroke: getColorForZLayer(node.availableZ).stroke,
|
|
2640
|
+
fill: node._containsObstacle ? "#fca5a5" : getColorForZLayer(node.availableZ).fill,
|
|
2641
|
+
layer: `z${node.availableZ.join(",")}`,
|
|
2642
|
+
label: `node ${node.capacityMeshNodeId}
|
|
2643
|
+
z:${node.availableZ.join(",")}`
|
|
2644
|
+
}));
|
|
2645
|
+
const nodesGraphic = {
|
|
2646
|
+
title: "RectDiffPipeline - Final",
|
|
2647
|
+
coordinateSystem: "cartesian",
|
|
2648
|
+
rects,
|
|
2649
|
+
lines: [],
|
|
2650
|
+
points: [],
|
|
2651
|
+
texts: []
|
|
2652
|
+
};
|
|
2653
|
+
return mergeGraphics(mergeGraphics(nodesGraphic, outline), clearance);
|
|
2654
|
+
};
|
|
2655
|
+
|
|
2447
2656
|
// lib/RectDiffPipeline.ts
|
|
2657
|
+
import { mergeGraphics as mergeGraphics2 } from "graphics-debug";
|
|
2448
2658
|
var RectDiffPipeline = class extends BasePipelineSolver3 {
|
|
2449
2659
|
rectDiffGridSolverPipeline;
|
|
2450
2660
|
gapFillSolver;
|
|
2451
2661
|
boardVoidRects;
|
|
2662
|
+
zIndexByName;
|
|
2663
|
+
layerNames;
|
|
2452
2664
|
pipelineDef = [
|
|
2453
2665
|
definePipelineStep3(
|
|
2454
2666
|
"rectDiffGridSolverPipeline",
|
|
2455
2667
|
RectDiffGridSolverPipeline,
|
|
2456
2668
|
(rectDiffPipeline) => [
|
|
2457
2669
|
{
|
|
2458
|
-
|
|
2670
|
+
bounds: rectDiffPipeline.inputProblem.simpleRouteJson.bounds,
|
|
2671
|
+
obstacles: rectDiffPipeline.inputProblem.simpleRouteJson.obstacles,
|
|
2672
|
+
connections: rectDiffPipeline.inputProblem.simpleRouteJson.connections,
|
|
2673
|
+
outline: rectDiffPipeline.inputProblem.simpleRouteJson.outline ? { outline: rectDiffPipeline.inputProblem.simpleRouteJson.outline } : void 0,
|
|
2674
|
+
layerCount: rectDiffPipeline.inputProblem.simpleRouteJson.layerCount,
|
|
2459
2675
|
gridOptions: rectDiffPipeline.inputProblem.gridOptions,
|
|
2460
|
-
boardVoidRects: rectDiffPipeline.boardVoidRects
|
|
2676
|
+
boardVoidRects: rectDiffPipeline.boardVoidRects,
|
|
2677
|
+
layerNames: rectDiffPipeline.layerNames,
|
|
2678
|
+
zIndexByName: rectDiffPipeline.zIndexByName,
|
|
2679
|
+
minTraceWidth: rectDiffPipeline.inputProblem.simpleRouteJson.minTraceWidth,
|
|
2680
|
+
obstacleClearance: rectDiffPipeline.inputProblem.obstacleClearance
|
|
2461
2681
|
}
|
|
2462
2682
|
]
|
|
2463
2683
|
),
|
|
@@ -2476,6 +2696,12 @@ var RectDiffPipeline = class extends BasePipelineSolver3 {
|
|
|
2476
2696
|
)
|
|
2477
2697
|
];
|
|
2478
2698
|
_setup() {
|
|
2699
|
+
const { zIndexByName, layerNames } = buildZIndexMap({
|
|
2700
|
+
obstacles: this.inputProblem.simpleRouteJson.obstacles,
|
|
2701
|
+
layerCount: this.inputProblem.simpleRouteJson.layerCount
|
|
2702
|
+
});
|
|
2703
|
+
this.zIndexByName = zIndexByName;
|
|
2704
|
+
this.layerNames = layerNames;
|
|
2479
2705
|
if (this.inputProblem.simpleRouteJson.outline) {
|
|
2480
2706
|
this.boardVoidRects = computeInverseRects(
|
|
2481
2707
|
{
|
|
@@ -2502,13 +2728,19 @@ var RectDiffPipeline = class extends BasePipelineSolver3 {
|
|
|
2502
2728
|
return { meshNodes: [] };
|
|
2503
2729
|
}
|
|
2504
2730
|
initialVisualize() {
|
|
2505
|
-
const
|
|
2731
|
+
const base = createBaseVisualization(
|
|
2506
2732
|
this.inputProblem.simpleRouteJson,
|
|
2507
2733
|
"RectDiffPipeline - Initial"
|
|
2508
2734
|
);
|
|
2735
|
+
const clearance = buildObstacleClearanceGraphics({
|
|
2736
|
+
srj: this.inputProblem.simpleRouteJson,
|
|
2737
|
+
clearance: this.inputProblem.obstacleClearance
|
|
2738
|
+
});
|
|
2509
2739
|
const initialNodes = this.rectDiffGridSolverPipeline?.getOutput().meshNodes ?? [];
|
|
2510
|
-
|
|
2511
|
-
|
|
2740
|
+
const nodeRects = {
|
|
2741
|
+
title: "Initial Nodes",
|
|
2742
|
+
coordinateSystem: "cartesian",
|
|
2743
|
+
rects: initialNodes.map((node) => ({
|
|
2512
2744
|
center: node.center,
|
|
2513
2745
|
width: node.width,
|
|
2514
2746
|
height: node.height,
|
|
@@ -2519,37 +2751,16 @@ var RectDiffPipeline = class extends BasePipelineSolver3 {
|
|
|
2519
2751
|
`node ${node.capacityMeshNodeId}`,
|
|
2520
2752
|
`z:${node.availableZ.join(",")}`
|
|
2521
2753
|
].join("\n")
|
|
2522
|
-
})
|
|
2523
|
-
}
|
|
2524
|
-
return
|
|
2754
|
+
}))
|
|
2755
|
+
};
|
|
2756
|
+
return mergeGraphics2(mergeGraphics2(base, clearance), nodeRects);
|
|
2525
2757
|
}
|
|
2526
2758
|
finalVisualize() {
|
|
2527
|
-
|
|
2528
|
-
this.inputProblem.simpleRouteJson,
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
const initialNodeIds = new Set(
|
|
2533
|
-
(this.rectDiffGridSolverPipeline?.getOutput().meshNodes ?? []).map(
|
|
2534
|
-
(n) => n.capacityMeshNodeId
|
|
2535
|
-
)
|
|
2536
|
-
);
|
|
2537
|
-
for (const node of outputNodes) {
|
|
2538
|
-
const isExpanded = !initialNodeIds.has(node.capacityMeshNodeId);
|
|
2539
|
-
graphics.rects.push({
|
|
2540
|
-
center: node.center,
|
|
2541
|
-
width: node.width,
|
|
2542
|
-
height: node.height,
|
|
2543
|
-
stroke: isExpanded ? "rgba(0, 128, 0, 0.8)" : "rgba(0, 0, 0, 0.3)",
|
|
2544
|
-
fill: isExpanded ? "rgba(0, 200, 0, 0.3)" : "rgba(100, 100, 100, 0.1)",
|
|
2545
|
-
layer: `z${node.availableZ.join(",")}`,
|
|
2546
|
-
label: [
|
|
2547
|
-
`${isExpanded ? "[expanded] " : ""}node ${node.capacityMeshNodeId}`,
|
|
2548
|
-
`z:${node.availableZ.join(",")}`
|
|
2549
|
-
].join("\n")
|
|
2550
|
-
});
|
|
2551
|
-
}
|
|
2552
|
-
return graphics;
|
|
2759
|
+
return buildFinalRectDiffVisualization({
|
|
2760
|
+
srj: this.inputProblem.simpleRouteJson,
|
|
2761
|
+
meshNodes: this.getOutput().meshNodes,
|
|
2762
|
+
obstacleClearance: this.inputProblem.obstacleClearance
|
|
2763
|
+
});
|
|
2553
2764
|
}
|
|
2554
2765
|
};
|
|
2555
2766
|
export {
|