@tscircuit/rectdiff 0.0.13 → 0.0.15
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 +16 -12
- package/dist/index.js +272 -141
- package/lib/RectDiffPipeline.ts +0 -1
- package/lib/solvers/RectDiffExpansionSolver/RectDiffExpansionSolver.ts +59 -12
- package/lib/solvers/RectDiffGridSolverPipeline/RectDiffGridSolverPipeline.ts +25 -6
- package/lib/solvers/RectDiffGridSolverPipeline/buildObstacleIndexes.ts +70 -0
- package/lib/solvers/RectDiffSeedingSolver/RectDiffSeedingSolver.ts +41 -86
- package/lib/solvers/RectDiffSeedingSolver/computeCandidates3D.ts +15 -15
- package/lib/solvers/RectDiffSeedingSolver/computeEdgeCandidates3D.ts +16 -16
- package/lib/solvers/RectDiffSeedingSolver/longestFreeSpanAroundZ.ts +17 -9
- package/lib/types/capacity-mesh-types.ts +9 -0
- package/lib/utils/finalizeRects.ts +25 -20
- package/lib/utils/getColorForZLayer.ts +17 -0
- package/lib/utils/isFullyOccupiedAtPoint.ts +23 -16
- package/lib/utils/rectToTree.ts +10 -0
- package/lib/utils/resizeSoftOverlaps.ts +36 -7
- package/lib/utils/sameTreeRect.ts +7 -0
- package/package.json +1 -1
- package/tests/examples/example01.test.tsx +18 -1
- package/tests/fixtures/getPerLayerVisualizations.ts +130 -0
- 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.js
CHANGED
|
@@ -1038,12 +1038,20 @@ function computeDefaultGridSizes(bounds) {
|
|
|
1038
1038
|
}
|
|
1039
1039
|
|
|
1040
1040
|
// lib/utils/isFullyOccupiedAtPoint.ts
|
|
1041
|
-
|
|
1041
|
+
import "rbush";
|
|
1042
|
+
function isFullyOccupiedAtPoint(params) {
|
|
1043
|
+
const query = {
|
|
1044
|
+
minX: params.point.x,
|
|
1045
|
+
minY: params.point.y,
|
|
1046
|
+
maxX: params.point.x,
|
|
1047
|
+
maxY: params.point.y
|
|
1048
|
+
};
|
|
1042
1049
|
for (let z = 0; z < params.layerCount; z++) {
|
|
1043
|
-
const
|
|
1044
|
-
const
|
|
1045
|
-
const
|
|
1046
|
-
|
|
1050
|
+
const obstacleIdx = params.obstacleIndexByLayer[z];
|
|
1051
|
+
const hasObstacle = !!obstacleIdx && obstacleIdx.search(query).length > 0;
|
|
1052
|
+
const placedIdx = params.placedIndexByLayer[z];
|
|
1053
|
+
const hasPlaced = !!placedIdx && placedIdx.search(query).length > 0;
|
|
1054
|
+
if (!hasObstacle && !hasPlaced) return false;
|
|
1047
1055
|
}
|
|
1048
1056
|
return true;
|
|
1049
1057
|
}
|
|
@@ -1057,15 +1065,20 @@ function longestFreeSpanAroundZ(params) {
|
|
|
1057
1065
|
layerCount,
|
|
1058
1066
|
minSpan,
|
|
1059
1067
|
maxSpan,
|
|
1060
|
-
|
|
1061
|
-
|
|
1068
|
+
obstacleIndexByLayer,
|
|
1069
|
+
additionalBlockersByLayer
|
|
1062
1070
|
} = params;
|
|
1063
1071
|
const isFreeAt = (layer) => {
|
|
1064
|
-
const
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1072
|
+
const query = {
|
|
1073
|
+
minX: x,
|
|
1074
|
+
minY: y,
|
|
1075
|
+
maxX: x,
|
|
1076
|
+
maxY: y
|
|
1077
|
+
};
|
|
1078
|
+
const obstacleIdx = obstacleIndexByLayer[layer];
|
|
1079
|
+
if (obstacleIdx && obstacleIdx.search(query).length > 0) return false;
|
|
1080
|
+
const extras = additionalBlockersByLayer?.[layer] ?? [];
|
|
1081
|
+
return !extras.some((b) => containsPoint(b, { x, y }));
|
|
1069
1082
|
};
|
|
1070
1083
|
let lo = z;
|
|
1071
1084
|
let hi = z;
|
|
@@ -1089,8 +1102,8 @@ function computeCandidates3D(params) {
|
|
|
1089
1102
|
bounds,
|
|
1090
1103
|
gridSize,
|
|
1091
1104
|
layerCount,
|
|
1092
|
-
|
|
1093
|
-
|
|
1105
|
+
obstacleIndexByLayer,
|
|
1106
|
+
placedIndexByLayer,
|
|
1094
1107
|
hardPlacedByLayer
|
|
1095
1108
|
} = params;
|
|
1096
1109
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -1099,14 +1112,12 @@ function computeCandidates3D(params) {
|
|
|
1099
1112
|
if (Math.abs(x - bounds.x) < EPS4 || Math.abs(y - bounds.y) < EPS4 || x > bounds.x + bounds.width - gridSize - EPS4 || y > bounds.y + bounds.height - gridSize - EPS4) {
|
|
1100
1113
|
continue;
|
|
1101
1114
|
}
|
|
1102
|
-
if (isFullyOccupiedAtPoint(
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
{ x, y }
|
|
1109
|
-
))
|
|
1115
|
+
if (isFullyOccupiedAtPoint({
|
|
1116
|
+
layerCount,
|
|
1117
|
+
obstacleIndexByLayer,
|
|
1118
|
+
placedIndexByLayer,
|
|
1119
|
+
point: { x, y }
|
|
1120
|
+
}))
|
|
1110
1121
|
continue;
|
|
1111
1122
|
let bestSpan = [];
|
|
1112
1123
|
let bestZ = 0;
|
|
@@ -1118,8 +1129,8 @@ function computeCandidates3D(params) {
|
|
|
1118
1129
|
layerCount,
|
|
1119
1130
|
minSpan: 1,
|
|
1120
1131
|
maxSpan: void 0,
|
|
1121
|
-
|
|
1122
|
-
|
|
1132
|
+
obstacleIndexByLayer,
|
|
1133
|
+
additionalBlockersByLayer: hardPlacedByLayer
|
|
1123
1134
|
});
|
|
1124
1135
|
if (s.length > bestSpan.length) {
|
|
1125
1136
|
bestSpan = s;
|
|
@@ -1128,7 +1139,7 @@ function computeCandidates3D(params) {
|
|
|
1128
1139
|
}
|
|
1129
1140
|
const anchorZ = bestSpan.length ? bestSpan[Math.floor(bestSpan.length / 2)] : bestZ;
|
|
1130
1141
|
const hardAtZ = [
|
|
1131
|
-
...
|
|
1142
|
+
...obstacleIndexByLayer[anchorZ]?.all() ?? [],
|
|
1132
1143
|
...hardPlacedByLayer[anchorZ] ?? []
|
|
1133
1144
|
];
|
|
1134
1145
|
const d = Math.min(
|
|
@@ -1203,8 +1214,8 @@ function computeEdgeCandidates3D(params) {
|
|
|
1203
1214
|
bounds,
|
|
1204
1215
|
minSize,
|
|
1205
1216
|
layerCount,
|
|
1206
|
-
|
|
1207
|
-
|
|
1217
|
+
obstacleIndexByLayer,
|
|
1218
|
+
placedIndexByLayer,
|
|
1208
1219
|
hardPlacedByLayer
|
|
1209
1220
|
} = params;
|
|
1210
1221
|
const out = [];
|
|
@@ -1212,14 +1223,12 @@ function computeEdgeCandidates3D(params) {
|
|
|
1212
1223
|
const dedup = /* @__PURE__ */ new Set();
|
|
1213
1224
|
const key = (p) => `${p.z}|${p.x.toFixed(6)}|${p.y.toFixed(6)}`;
|
|
1214
1225
|
function fullyOcc(p) {
|
|
1215
|
-
return isFullyOccupiedAtPoint(
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
p
|
|
1222
|
-
);
|
|
1226
|
+
return isFullyOccupiedAtPoint({
|
|
1227
|
+
layerCount,
|
|
1228
|
+
obstacleIndexByLayer,
|
|
1229
|
+
placedIndexByLayer,
|
|
1230
|
+
point: p
|
|
1231
|
+
});
|
|
1223
1232
|
}
|
|
1224
1233
|
function pushIfFree(p) {
|
|
1225
1234
|
const { x, y, z } = p;
|
|
@@ -1227,7 +1236,7 @@ function computeEdgeCandidates3D(params) {
|
|
|
1227
1236
|
return;
|
|
1228
1237
|
if (fullyOcc({ x, y })) return;
|
|
1229
1238
|
const hard = [
|
|
1230
|
-
...
|
|
1239
|
+
...obstacleIndexByLayer[z]?.all() ?? [],
|
|
1231
1240
|
...hardPlacedByLayer[z] ?? []
|
|
1232
1241
|
];
|
|
1233
1242
|
const d = Math.min(
|
|
@@ -1244,14 +1253,14 @@ function computeEdgeCandidates3D(params) {
|
|
|
1244
1253
|
layerCount,
|
|
1245
1254
|
minSpan: 1,
|
|
1246
1255
|
maxSpan: void 0,
|
|
1247
|
-
|
|
1248
|
-
|
|
1256
|
+
obstacleIndexByLayer,
|
|
1257
|
+
additionalBlockersByLayer: hardPlacedByLayer
|
|
1249
1258
|
});
|
|
1250
1259
|
out.push({ x, y, z, distance: d, zSpanLen: span.length, isEdgeSeed: true });
|
|
1251
1260
|
}
|
|
1252
1261
|
for (let z = 0; z < layerCount; z++) {
|
|
1253
1262
|
const blockers = [
|
|
1254
|
-
...
|
|
1263
|
+
...obstacleIndexByLayer[z]?.all() ?? [],
|
|
1255
1264
|
...hardPlacedByLayer[z] ?? []
|
|
1256
1265
|
];
|
|
1257
1266
|
const corners = [
|
|
@@ -1475,21 +1484,58 @@ function resizeSoftOverlaps(params, newIndex) {
|
|
|
1475
1484
|
}
|
|
1476
1485
|
}
|
|
1477
1486
|
}
|
|
1487
|
+
const rectToTree2 = (rect) => ({
|
|
1488
|
+
...rect,
|
|
1489
|
+
minX: rect.x,
|
|
1490
|
+
minY: rect.y,
|
|
1491
|
+
maxX: rect.x + rect.width,
|
|
1492
|
+
maxY: rect.y + rect.height
|
|
1493
|
+
});
|
|
1494
|
+
const sameRect = (a, b) => a.minX === b.minX && a.minY === b.minY && a.maxX === b.maxX && a.maxY === b.maxY;
|
|
1478
1495
|
removeIdx.sort((a, b) => b - a).forEach((idx) => {
|
|
1479
1496
|
const rem = params.placed.splice(idx, 1)[0];
|
|
1480
|
-
|
|
1481
|
-
const
|
|
1482
|
-
|
|
1483
|
-
|
|
1497
|
+
if (params.placedIndexByLayer) {
|
|
1498
|
+
for (const z of rem.zLayers) {
|
|
1499
|
+
const tree = params.placedIndexByLayer[z];
|
|
1500
|
+
if (tree) tree.remove(rectToTree2(rem.rect), sameRect);
|
|
1501
|
+
}
|
|
1484
1502
|
}
|
|
1485
1503
|
});
|
|
1486
1504
|
for (const p of toAdd) {
|
|
1487
1505
|
params.placed.push(p);
|
|
1488
|
-
for (const z of p.zLayers)
|
|
1506
|
+
for (const z of p.zLayers) {
|
|
1507
|
+
if (params.placedIndexByLayer) {
|
|
1508
|
+
const idx = params.placedIndexByLayer[z];
|
|
1509
|
+
if (idx) {
|
|
1510
|
+
idx.insert({
|
|
1511
|
+
...p.rect,
|
|
1512
|
+
minX: p.rect.x,
|
|
1513
|
+
minY: p.rect.y,
|
|
1514
|
+
maxX: p.rect.x + p.rect.width,
|
|
1515
|
+
maxY: p.rect.y + p.rect.height
|
|
1516
|
+
});
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1489
1520
|
}
|
|
1490
1521
|
}
|
|
1491
1522
|
|
|
1523
|
+
// lib/utils/getColorForZLayer.ts
|
|
1524
|
+
var getColorForZLayer = (zLayers) => {
|
|
1525
|
+
const minZ = Math.min(...zLayers);
|
|
1526
|
+
const colors = [
|
|
1527
|
+
{ fill: "#dbeafe", stroke: "#3b82f6" },
|
|
1528
|
+
{ fill: "#fef3c7", stroke: "#f59e0b" },
|
|
1529
|
+
{ fill: "#d1fae5", stroke: "#10b981" },
|
|
1530
|
+
{ fill: "#e9d5ff", stroke: "#a855f7" },
|
|
1531
|
+
{ fill: "#fed7aa", stroke: "#f97316" },
|
|
1532
|
+
{ fill: "#fecaca", stroke: "#ef4444" }
|
|
1533
|
+
];
|
|
1534
|
+
return colors[minZ % colors.length];
|
|
1535
|
+
};
|
|
1536
|
+
|
|
1492
1537
|
// lib/solvers/RectDiffSeedingSolver/RectDiffSeedingSolver.ts
|
|
1538
|
+
import RBush3 from "rbush";
|
|
1493
1539
|
var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
1494
1540
|
constructor(input) {
|
|
1495
1541
|
super();
|
|
@@ -1501,12 +1547,11 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1501
1547
|
layerCount;
|
|
1502
1548
|
bounds;
|
|
1503
1549
|
options;
|
|
1504
|
-
obstaclesByLayer;
|
|
1505
1550
|
boardVoidRects;
|
|
1506
1551
|
gridIndex;
|
|
1507
1552
|
candidates;
|
|
1508
1553
|
placed;
|
|
1509
|
-
|
|
1554
|
+
placedIndexByLayer;
|
|
1510
1555
|
expansionIndex;
|
|
1511
1556
|
edgeAnalysisDone;
|
|
1512
1557
|
totalSeedsThisGrid;
|
|
@@ -1522,34 +1567,6 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1522
1567
|
width: srj.bounds.maxX - srj.bounds.minX,
|
|
1523
1568
|
height: srj.bounds.maxY - srj.bounds.minY
|
|
1524
1569
|
};
|
|
1525
|
-
const obstaclesByLayer = Array.from(
|
|
1526
|
-
{ length: layerCount },
|
|
1527
|
-
() => []
|
|
1528
|
-
);
|
|
1529
|
-
let boardVoidRects = [];
|
|
1530
|
-
if (srj.outline && srj.outline.length > 2) {
|
|
1531
|
-
boardVoidRects = computeInverseRects(bounds, srj.outline);
|
|
1532
|
-
for (const voidR of boardVoidRects) {
|
|
1533
|
-
for (let z = 0; z < layerCount; z++) {
|
|
1534
|
-
obstaclesByLayer[z].push(voidR);
|
|
1535
|
-
}
|
|
1536
|
-
}
|
|
1537
|
-
}
|
|
1538
|
-
for (const obstacle of srj.obstacles ?? []) {
|
|
1539
|
-
const rect = obstacleToXYRect(obstacle);
|
|
1540
|
-
if (!rect) continue;
|
|
1541
|
-
const zLayers = obstacleZs(obstacle, zIndexByName);
|
|
1542
|
-
const invalidZs = zLayers.filter((z) => z < 0 || z >= layerCount);
|
|
1543
|
-
if (invalidZs.length) {
|
|
1544
|
-
throw new Error(
|
|
1545
|
-
`RectDiff: obstacle uses z-layer indices ${invalidZs.join(",")} outside 0-${layerCount - 1}`
|
|
1546
|
-
);
|
|
1547
|
-
}
|
|
1548
|
-
if ((!obstacle.zLayers || obstacle.zLayers.length === 0) && zLayers.length) {
|
|
1549
|
-
obstacle.zLayers = zLayers;
|
|
1550
|
-
}
|
|
1551
|
-
for (const z of zLayers) obstaclesByLayer[z].push(rect);
|
|
1552
|
-
}
|
|
1553
1570
|
const trace = Math.max(0.01, srj.minTraceWidth || 0.15);
|
|
1554
1571
|
const defaults = {
|
|
1555
1572
|
gridSizes: [],
|
|
@@ -1570,21 +1587,19 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1570
1587
|
gridSizes: opts.gridSizes ?? // re-use the helper that was previously in engine
|
|
1571
1588
|
computeDefaultGridSizes(bounds)
|
|
1572
1589
|
};
|
|
1573
|
-
const placedByLayer = Array.from(
|
|
1574
|
-
{ length: layerCount },
|
|
1575
|
-
() => []
|
|
1576
|
-
);
|
|
1577
1590
|
this.srj = srj;
|
|
1578
1591
|
this.layerNames = layerNames;
|
|
1579
1592
|
this.layerCount = layerCount;
|
|
1580
1593
|
this.bounds = bounds;
|
|
1581
1594
|
this.options = options;
|
|
1582
|
-
this.
|
|
1583
|
-
this.boardVoidRects = boardVoidRects;
|
|
1595
|
+
this.boardVoidRects = this.input.boardVoidRects;
|
|
1584
1596
|
this.gridIndex = 0;
|
|
1585
1597
|
this.candidates = [];
|
|
1586
1598
|
this.placed = [];
|
|
1587
|
-
this.
|
|
1599
|
+
this.placedIndexByLayer = Array.from(
|
|
1600
|
+
{ length: layerCount },
|
|
1601
|
+
() => new RBush3()
|
|
1602
|
+
);
|
|
1588
1603
|
this.expansionIndex = 0;
|
|
1589
1604
|
this.edgeAnalysisDone = false;
|
|
1590
1605
|
this.totalSeedsThisGrid = 0;
|
|
@@ -1622,9 +1637,9 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1622
1637
|
bounds: this.bounds,
|
|
1623
1638
|
gridSize: grid,
|
|
1624
1639
|
layerCount: this.layerCount,
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1640
|
+
hardPlacedByLayer,
|
|
1641
|
+
obstacleIndexByLayer: this.input.obstacleIndexByLayer,
|
|
1642
|
+
placedIndexByLayer: this.placedIndexByLayer
|
|
1628
1643
|
});
|
|
1629
1644
|
this.totalSeedsThisGrid = this.candidates.length;
|
|
1630
1645
|
this.consumedSeedsThisGrid = 0;
|
|
@@ -1642,8 +1657,8 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1642
1657
|
bounds: this.bounds,
|
|
1643
1658
|
minSize,
|
|
1644
1659
|
layerCount: this.layerCount,
|
|
1645
|
-
|
|
1646
|
-
|
|
1660
|
+
obstacleIndexByLayer: this.input.obstacleIndexByLayer,
|
|
1661
|
+
placedIndexByLayer: this.placedIndexByLayer,
|
|
1647
1662
|
hardPlacedByLayer
|
|
1648
1663
|
});
|
|
1649
1664
|
this.edgeAnalysisDone = true;
|
|
@@ -1665,8 +1680,8 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1665
1680
|
layerCount: this.layerCount,
|
|
1666
1681
|
minSpan: minMulti.minLayers,
|
|
1667
1682
|
maxSpan: maxMultiLayerSpan,
|
|
1668
|
-
|
|
1669
|
-
|
|
1683
|
+
obstacleIndexByLayer: this.input.obstacleIndexByLayer,
|
|
1684
|
+
additionalBlockersByLayer: hardPlacedByLayer
|
|
1670
1685
|
});
|
|
1671
1686
|
const attempts = [];
|
|
1672
1687
|
if (span.length >= minMulti.minLayers) {
|
|
@@ -1685,8 +1700,8 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1685
1700
|
for (const attempt of ordered) {
|
|
1686
1701
|
const hardBlockers = [];
|
|
1687
1702
|
for (const z of attempt.layers) {
|
|
1688
|
-
|
|
1689
|
-
|
|
1703
|
+
const obstacleLayer = this.input.obstacleIndexByLayer[z];
|
|
1704
|
+
if (obstacleLayer) hardBlockers.push(...obstacleLayer.all());
|
|
1690
1705
|
if (hardPlacedByLayer[z]) hardBlockers.push(...hardPlacedByLayer[z]);
|
|
1691
1706
|
}
|
|
1692
1707
|
const rect = expandRectFromSeed({
|
|
@@ -1702,25 +1717,34 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1702
1717
|
if (!rect) continue;
|
|
1703
1718
|
const placed = { rect, zLayers: [...attempt.layers] };
|
|
1704
1719
|
const newIndex = this.placed.push(placed) - 1;
|
|
1705
|
-
for (const z of attempt.layers)
|
|
1720
|
+
for (const z of attempt.layers) {
|
|
1721
|
+
const idx = this.placedIndexByLayer[z];
|
|
1722
|
+
if (idx) {
|
|
1723
|
+
idx.insert({
|
|
1724
|
+
...rect,
|
|
1725
|
+
minX: rect.x,
|
|
1726
|
+
minY: rect.y,
|
|
1727
|
+
maxX: rect.x + rect.width,
|
|
1728
|
+
maxY: rect.y + rect.height
|
|
1729
|
+
});
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1706
1732
|
resizeSoftOverlaps(
|
|
1707
1733
|
{
|
|
1708
1734
|
layerCount: this.layerCount,
|
|
1709
1735
|
placed: this.placed,
|
|
1710
|
-
|
|
1711
|
-
|
|
1736
|
+
options: this.options,
|
|
1737
|
+
placedIndexByLayer: this.placedIndexByLayer
|
|
1712
1738
|
},
|
|
1713
1739
|
newIndex
|
|
1714
1740
|
);
|
|
1715
1741
|
this.candidates = this.candidates.filter(
|
|
1716
|
-
(c) => !isFullyOccupiedAtPoint(
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
{ x: c.x, y: c.y }
|
|
1723
|
-
)
|
|
1742
|
+
(c) => !isFullyOccupiedAtPoint({
|
|
1743
|
+
layerCount: this.layerCount,
|
|
1744
|
+
obstacleIndexByLayer: this.input.obstacleIndexByLayer,
|
|
1745
|
+
placedIndexByLayer: this.placedIndexByLayer,
|
|
1746
|
+
point: { x: c.x, y: c.y }
|
|
1747
|
+
})
|
|
1724
1748
|
);
|
|
1725
1749
|
return;
|
|
1726
1750
|
}
|
|
@@ -1748,31 +1772,16 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1748
1772
|
layerCount: this.layerCount,
|
|
1749
1773
|
bounds: this.bounds,
|
|
1750
1774
|
options: this.options,
|
|
1751
|
-
obstaclesByLayer: this.obstaclesByLayer,
|
|
1752
1775
|
boardVoidRects: this.boardVoidRects,
|
|
1753
1776
|
gridIndex: this.gridIndex,
|
|
1754
1777
|
candidates: this.candidates,
|
|
1755
1778
|
placed: this.placed,
|
|
1756
|
-
placedByLayer: this.placedByLayer,
|
|
1757
1779
|
expansionIndex: this.expansionIndex,
|
|
1758
1780
|
edgeAnalysisDone: this.edgeAnalysisDone,
|
|
1759
1781
|
totalSeedsThisGrid: this.totalSeedsThisGrid,
|
|
1760
1782
|
consumedSeedsThisGrid: this.consumedSeedsThisGrid
|
|
1761
1783
|
};
|
|
1762
1784
|
}
|
|
1763
|
-
/** Get color based on z layer for visualization. */
|
|
1764
|
-
getColorForZLayer(zLayers) {
|
|
1765
|
-
const minZ = Math.min(...zLayers);
|
|
1766
|
-
const colors = [
|
|
1767
|
-
{ fill: "#dbeafe", stroke: "#3b82f6" },
|
|
1768
|
-
{ fill: "#fef3c7", stroke: "#f59e0b" },
|
|
1769
|
-
{ fill: "#d1fae5", stroke: "#10b981" },
|
|
1770
|
-
{ fill: "#e9d5ff", stroke: "#a855f7" },
|
|
1771
|
-
{ fill: "#fed7aa", stroke: "#f97316" },
|
|
1772
|
-
{ fill: "#fecaca", stroke: "#ef4444" }
|
|
1773
|
-
];
|
|
1774
|
-
return colors[minZ % colors.length];
|
|
1775
|
-
}
|
|
1776
1785
|
/** Visualization focused on the grid seeding phase. */
|
|
1777
1786
|
visualize() {
|
|
1778
1787
|
const rects = [];
|
|
@@ -1859,7 +1868,7 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1859
1868
|
}
|
|
1860
1869
|
if (this.placed?.length) {
|
|
1861
1870
|
for (const placement of this.placed) {
|
|
1862
|
-
const colors =
|
|
1871
|
+
const colors = getColorForZLayer(placement.zLayers);
|
|
1863
1872
|
rects.push({
|
|
1864
1873
|
center: {
|
|
1865
1874
|
x: placement.rect.x + placement.rect.width / 2,
|
|
@@ -1897,23 +1906,27 @@ function finalizeRects(params) {
|
|
|
1897
1906
|
maxY: p.rect.y + p.rect.height,
|
|
1898
1907
|
zLayers: [...p.zLayers].sort((a, b) => a - b)
|
|
1899
1908
|
}));
|
|
1900
|
-
const
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
}
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1909
|
+
const { zIndexByName } = buildZIndexMap(params.srj);
|
|
1910
|
+
const layersByKey = /* @__PURE__ */ new Map();
|
|
1911
|
+
for (const obstacle of params.srj.obstacles ?? []) {
|
|
1912
|
+
const rect = obstacleToXYRect(obstacle);
|
|
1913
|
+
if (!rect) continue;
|
|
1914
|
+
const zLayers = obstacle.zLayers?.length && obstacle.zLayers.length > 0 ? obstacle.zLayers : obstacleZs(obstacle, zIndexByName);
|
|
1915
|
+
const key = `${rect.x}:${rect.y}:${rect.width}:${rect.height}`;
|
|
1916
|
+
let entry = layersByKey.get(key);
|
|
1917
|
+
if (!entry) {
|
|
1918
|
+
entry = { rect, layers: /* @__PURE__ */ new Set() };
|
|
1919
|
+
layersByKey.set(key, entry);
|
|
1920
|
+
}
|
|
1921
|
+
zLayers.forEach((layer) => entry.layers.add(layer));
|
|
1922
|
+
}
|
|
1923
|
+
for (const { rect, layers } of layersByKey.values()) {
|
|
1911
1924
|
out.push({
|
|
1912
1925
|
minX: rect.x,
|
|
1913
1926
|
minY: rect.y,
|
|
1914
1927
|
maxX: rect.x + rect.width,
|
|
1915
1928
|
maxY: rect.y + rect.height,
|
|
1916
|
-
zLayers:
|
|
1929
|
+
zLayers: Array.from(layers).sort((a, b) => a - b),
|
|
1917
1930
|
isObstacle: true
|
|
1918
1931
|
});
|
|
1919
1932
|
}
|
|
@@ -1942,6 +1955,21 @@ function rectsToMeshNodes(rects) {
|
|
|
1942
1955
|
return out;
|
|
1943
1956
|
}
|
|
1944
1957
|
|
|
1958
|
+
// lib/solvers/RectDiffExpansionSolver/RectDiffExpansionSolver.ts
|
|
1959
|
+
import RBush4 from "rbush";
|
|
1960
|
+
|
|
1961
|
+
// lib/utils/rectToTree.ts
|
|
1962
|
+
var rectToTree = (rect) => ({
|
|
1963
|
+
...rect,
|
|
1964
|
+
minX: rect.x,
|
|
1965
|
+
minY: rect.y,
|
|
1966
|
+
maxX: rect.x + rect.width,
|
|
1967
|
+
maxY: rect.y + rect.height
|
|
1968
|
+
});
|
|
1969
|
+
|
|
1970
|
+
// lib/utils/sameTreeRect.ts
|
|
1971
|
+
var sameTreeRect = (a, b) => a.minX === b.minX && a.minY === b.minY && a.maxX === b.maxX && a.maxY === b.maxY;
|
|
1972
|
+
|
|
1945
1973
|
// lib/solvers/RectDiffExpansionSolver/RectDiffExpansionSolver.ts
|
|
1946
1974
|
var RectDiffExpansionSolver = class extends BaseSolver4 {
|
|
1947
1975
|
constructor(input) {
|
|
@@ -1955,12 +1983,11 @@ var RectDiffExpansionSolver = class extends BaseSolver4 {
|
|
|
1955
1983
|
layerCount;
|
|
1956
1984
|
bounds;
|
|
1957
1985
|
options;
|
|
1958
|
-
obstaclesByLayer;
|
|
1959
1986
|
boardVoidRects;
|
|
1960
1987
|
gridIndex;
|
|
1961
1988
|
candidates;
|
|
1962
1989
|
placed;
|
|
1963
|
-
|
|
1990
|
+
placedIndexByLayer;
|
|
1964
1991
|
expansionIndex;
|
|
1965
1992
|
edgeAnalysisDone;
|
|
1966
1993
|
totalSeedsThisGrid;
|
|
@@ -1970,6 +1997,39 @@ var RectDiffExpansionSolver = class extends BaseSolver4 {
|
|
|
1970
1997
|
this.stats = {
|
|
1971
1998
|
gridIndex: this.gridIndex
|
|
1972
1999
|
};
|
|
2000
|
+
if (this.input.obstacleIndexByLayer) {
|
|
2001
|
+
} else {
|
|
2002
|
+
const { zIndexByName } = buildZIndexMap(this.srj);
|
|
2003
|
+
this.input.obstacleIndexByLayer = Array.from(
|
|
2004
|
+
{ length: this.layerCount },
|
|
2005
|
+
() => new RBush4()
|
|
2006
|
+
);
|
|
2007
|
+
const insertObstacle = (rect, z) => {
|
|
2008
|
+
const tree = this.input.obstacleIndexByLayer[z];
|
|
2009
|
+
if (tree) tree.insert(rectToTree(rect));
|
|
2010
|
+
};
|
|
2011
|
+
for (const voidRect of this.boardVoidRects ?? []) {
|
|
2012
|
+
for (let z = 0; z < this.layerCount; z++) insertObstacle(voidRect, z);
|
|
2013
|
+
}
|
|
2014
|
+
for (const obstacle of this.srj.obstacles ?? []) {
|
|
2015
|
+
const rect = obstacleToXYRect(obstacle);
|
|
2016
|
+
if (!rect) continue;
|
|
2017
|
+
const zLayers = obstacle.zLayers?.length && obstacle.zLayers.length > 0 ? obstacle.zLayers : obstacleZs(obstacle, zIndexByName);
|
|
2018
|
+
zLayers.forEach((z) => {
|
|
2019
|
+
if (z >= 0 && z < this.layerCount) insertObstacle(rect, z);
|
|
2020
|
+
});
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
this.placedIndexByLayer = Array.from(
|
|
2024
|
+
{ length: this.layerCount },
|
|
2025
|
+
() => new RBush4()
|
|
2026
|
+
);
|
|
2027
|
+
for (const placement of this.placed ?? []) {
|
|
2028
|
+
for (const z of placement.zLayers) {
|
|
2029
|
+
const tree = this.placedIndexByLayer[z];
|
|
2030
|
+
if (tree) tree.insert(rectToTree(placement.rect));
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
1973
2033
|
}
|
|
1974
2034
|
_step() {
|
|
1975
2035
|
if (this.solved) return;
|
|
@@ -1993,7 +2053,8 @@ var RectDiffExpansionSolver = class extends BaseSolver4 {
|
|
|
1993
2053
|
});
|
|
1994
2054
|
const hardBlockers = [];
|
|
1995
2055
|
for (const z of p.zLayers) {
|
|
1996
|
-
|
|
2056
|
+
const obstacleTree = this.input.obstacleIndexByLayer[z];
|
|
2057
|
+
if (obstacleTree) hardBlockers.push(...obstacleTree.all());
|
|
1997
2058
|
hardBlockers.push(...hardPlacedByLayer[z] ?? []);
|
|
1998
2059
|
}
|
|
1999
2060
|
const oldRect = p.rect;
|
|
@@ -2010,16 +2071,18 @@ var RectDiffExpansionSolver = class extends BaseSolver4 {
|
|
|
2010
2071
|
if (expanded) {
|
|
2011
2072
|
this.placed[idx] = { rect: expanded, zLayers: p.zLayers };
|
|
2012
2073
|
for (const z of p.zLayers) {
|
|
2013
|
-
const
|
|
2014
|
-
|
|
2015
|
-
|
|
2074
|
+
const tree = this.placedIndexByLayer[z];
|
|
2075
|
+
if (tree) {
|
|
2076
|
+
tree.remove(rectToTree(oldRect), sameTreeRect);
|
|
2077
|
+
tree.insert(rectToTree(expanded));
|
|
2078
|
+
}
|
|
2016
2079
|
}
|
|
2017
2080
|
resizeSoftOverlaps(
|
|
2018
2081
|
{
|
|
2019
2082
|
layerCount: this.layerCount,
|
|
2020
2083
|
placed: this.placed,
|
|
2021
|
-
|
|
2022
|
-
|
|
2084
|
+
options: this.options,
|
|
2085
|
+
placedIndexByLayer: this.placedIndexByLayer
|
|
2023
2086
|
},
|
|
2024
2087
|
idx
|
|
2025
2088
|
);
|
|
@@ -2030,7 +2093,7 @@ var RectDiffExpansionSolver = class extends BaseSolver4 {
|
|
|
2030
2093
|
if (this.solved) return;
|
|
2031
2094
|
const rects = finalizeRects({
|
|
2032
2095
|
placed: this.placed,
|
|
2033
|
-
|
|
2096
|
+
srj: this.srj,
|
|
2034
2097
|
boardVoidRects: this.boardVoidRects
|
|
2035
2098
|
});
|
|
2036
2099
|
this._meshNodes = rectsToMeshNodes(rects);
|
|
@@ -2078,10 +2141,73 @@ z:${placement.zLayers.join(",")}`
|
|
|
2078
2141
|
}
|
|
2079
2142
|
};
|
|
2080
2143
|
|
|
2144
|
+
// lib/solvers/RectDiffGridSolverPipeline/RectDiffGridSolverPipeline.ts
|
|
2145
|
+
import "rbush";
|
|
2146
|
+
|
|
2147
|
+
// lib/solvers/RectDiffGridSolverPipeline/buildObstacleIndexes.ts
|
|
2148
|
+
import RBush5 from "rbush";
|
|
2149
|
+
var buildObstacleIndexes = (srj) => {
|
|
2150
|
+
const { layerNames, zIndexByName } = buildZIndexMap(srj);
|
|
2151
|
+
const layerCount = Math.max(1, layerNames.length, srj.layerCount || 1);
|
|
2152
|
+
const bounds = {
|
|
2153
|
+
x: srj.bounds.minX,
|
|
2154
|
+
y: srj.bounds.minY,
|
|
2155
|
+
width: srj.bounds.maxX - srj.bounds.minX,
|
|
2156
|
+
height: srj.bounds.maxY - srj.bounds.minY
|
|
2157
|
+
};
|
|
2158
|
+
const obstacleIndexByLayer = Array.from(
|
|
2159
|
+
{ length: layerCount },
|
|
2160
|
+
() => new RBush5()
|
|
2161
|
+
);
|
|
2162
|
+
const insertObstacle = (rect, z) => {
|
|
2163
|
+
const treeRect = {
|
|
2164
|
+
...rect,
|
|
2165
|
+
minX: rect.x,
|
|
2166
|
+
minY: rect.y,
|
|
2167
|
+
maxX: rect.x + rect.width,
|
|
2168
|
+
maxY: rect.y + rect.height
|
|
2169
|
+
};
|
|
2170
|
+
obstacleIndexByLayer[z]?.insert(treeRect);
|
|
2171
|
+
};
|
|
2172
|
+
let boardVoidRects = [];
|
|
2173
|
+
if (srj.outline && srj.outline.length > 2) {
|
|
2174
|
+
boardVoidRects = computeInverseRects(bounds, srj.outline);
|
|
2175
|
+
for (const voidRect of boardVoidRects) {
|
|
2176
|
+
for (let z = 0; z < layerCount; z++) insertObstacle(voidRect, z);
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
for (const obstacle of srj.obstacles ?? []) {
|
|
2180
|
+
const rect = obstacleToXYRect(obstacle);
|
|
2181
|
+
if (!rect) continue;
|
|
2182
|
+
const zLayers = obstacleZs(obstacle, zIndexByName);
|
|
2183
|
+
const invalidZs = zLayers.filter((z) => z < 0 || z >= layerCount);
|
|
2184
|
+
if (invalidZs.length) {
|
|
2185
|
+
throw new Error(
|
|
2186
|
+
`RectDiff: obstacle uses z-layer indices ${invalidZs.join(",")} outside 0-${layerCount - 1}`
|
|
2187
|
+
);
|
|
2188
|
+
}
|
|
2189
|
+
if ((!obstacle.zLayers || obstacle.zLayers.length === 0) && zLayers.length) {
|
|
2190
|
+
obstacle.zLayers = zLayers;
|
|
2191
|
+
}
|
|
2192
|
+
for (const z of zLayers) insertObstacle(rect, z);
|
|
2193
|
+
}
|
|
2194
|
+
return { obstacleIndexByLayer, boardVoidRects };
|
|
2195
|
+
};
|
|
2196
|
+
|
|
2081
2197
|
// lib/solvers/RectDiffGridSolverPipeline/RectDiffGridSolverPipeline.ts
|
|
2082
2198
|
var RectDiffGridSolverPipeline = class extends BasePipelineSolver2 {
|
|
2083
2199
|
rectDiffSeedingSolver;
|
|
2084
2200
|
rectDiffExpansionSolver;
|
|
2201
|
+
boardVoidRects;
|
|
2202
|
+
obstacleIndexByLayer;
|
|
2203
|
+
constructor(inputProblem) {
|
|
2204
|
+
super(inputProblem);
|
|
2205
|
+
const { obstacleIndexByLayer, boardVoidRects } = buildObstacleIndexes(
|
|
2206
|
+
inputProblem.simpleRouteJson
|
|
2207
|
+
);
|
|
2208
|
+
this.obstacleIndexByLayer = obstacleIndexByLayer;
|
|
2209
|
+
this.boardVoidRects = boardVoidRects;
|
|
2210
|
+
}
|
|
2085
2211
|
pipelineDef = [
|
|
2086
2212
|
definePipelineStep2(
|
|
2087
2213
|
"rectDiffSeedingSolver",
|
|
@@ -2089,7 +2215,9 @@ var RectDiffGridSolverPipeline = class extends BasePipelineSolver2 {
|
|
|
2089
2215
|
(pipeline) => [
|
|
2090
2216
|
{
|
|
2091
2217
|
simpleRouteJson: pipeline.inputProblem.simpleRouteJson,
|
|
2092
|
-
gridOptions: pipeline.inputProblem.gridOptions
|
|
2218
|
+
gridOptions: pipeline.inputProblem.gridOptions,
|
|
2219
|
+
obstacleIndexByLayer: pipeline.obstacleIndexByLayer,
|
|
2220
|
+
boardVoidRects: pipeline.boardVoidRects
|
|
2093
2221
|
}
|
|
2094
2222
|
]
|
|
2095
2223
|
),
|
|
@@ -2098,7 +2226,11 @@ var RectDiffGridSolverPipeline = class extends BasePipelineSolver2 {
|
|
|
2098
2226
|
RectDiffExpansionSolver,
|
|
2099
2227
|
(pipeline) => [
|
|
2100
2228
|
{
|
|
2101
|
-
initialSnapshot:
|
|
2229
|
+
initialSnapshot: {
|
|
2230
|
+
...pipeline.rectDiffSeedingSolver.getOutput(),
|
|
2231
|
+
boardVoidRects: pipeline.boardVoidRects ?? []
|
|
2232
|
+
},
|
|
2233
|
+
obstacleIndexByLayer: pipeline.obstacleIndexByLayer
|
|
2102
2234
|
}
|
|
2103
2235
|
]
|
|
2104
2236
|
)
|
|
@@ -2237,7 +2369,6 @@ var RectDiffPipeline = class extends BasePipelineSolver3 {
|
|
|
2237
2369
|
return { meshNodes: [] };
|
|
2238
2370
|
}
|
|
2239
2371
|
initialVisualize() {
|
|
2240
|
-
console.log("RectDiffPipeline - initialVisualize");
|
|
2241
2372
|
const graphics = createBaseVisualization(
|
|
2242
2373
|
this.inputProblem.simpleRouteJson,
|
|
2243
2374
|
"RectDiffPipeline - Initial"
|