@tscircuit/rectdiff 0.0.13 → 0.0.14
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 -10
- package/dist/index.js +257 -126
- 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 +39 -68
- 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/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/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,44 @@ 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
|
|
|
1492
1523
|
// lib/solvers/RectDiffSeedingSolver/RectDiffSeedingSolver.ts
|
|
1524
|
+
import RBush3 from "rbush";
|
|
1493
1525
|
var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
1494
1526
|
constructor(input) {
|
|
1495
1527
|
super();
|
|
@@ -1501,12 +1533,11 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1501
1533
|
layerCount;
|
|
1502
1534
|
bounds;
|
|
1503
1535
|
options;
|
|
1504
|
-
obstaclesByLayer;
|
|
1505
1536
|
boardVoidRects;
|
|
1506
1537
|
gridIndex;
|
|
1507
1538
|
candidates;
|
|
1508
1539
|
placed;
|
|
1509
|
-
|
|
1540
|
+
placedIndexByLayer;
|
|
1510
1541
|
expansionIndex;
|
|
1511
1542
|
edgeAnalysisDone;
|
|
1512
1543
|
totalSeedsThisGrid;
|
|
@@ -1522,34 +1553,6 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1522
1553
|
width: srj.bounds.maxX - srj.bounds.minX,
|
|
1523
1554
|
height: srj.bounds.maxY - srj.bounds.minY
|
|
1524
1555
|
};
|
|
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
1556
|
const trace = Math.max(0.01, srj.minTraceWidth || 0.15);
|
|
1554
1557
|
const defaults = {
|
|
1555
1558
|
gridSizes: [],
|
|
@@ -1570,21 +1573,19 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1570
1573
|
gridSizes: opts.gridSizes ?? // re-use the helper that was previously in engine
|
|
1571
1574
|
computeDefaultGridSizes(bounds)
|
|
1572
1575
|
};
|
|
1573
|
-
const placedByLayer = Array.from(
|
|
1574
|
-
{ length: layerCount },
|
|
1575
|
-
() => []
|
|
1576
|
-
);
|
|
1577
1576
|
this.srj = srj;
|
|
1578
1577
|
this.layerNames = layerNames;
|
|
1579
1578
|
this.layerCount = layerCount;
|
|
1580
1579
|
this.bounds = bounds;
|
|
1581
1580
|
this.options = options;
|
|
1582
|
-
this.
|
|
1583
|
-
this.boardVoidRects = boardVoidRects;
|
|
1581
|
+
this.boardVoidRects = this.input.boardVoidRects;
|
|
1584
1582
|
this.gridIndex = 0;
|
|
1585
1583
|
this.candidates = [];
|
|
1586
1584
|
this.placed = [];
|
|
1587
|
-
this.
|
|
1585
|
+
this.placedIndexByLayer = Array.from(
|
|
1586
|
+
{ length: layerCount },
|
|
1587
|
+
() => new RBush3()
|
|
1588
|
+
);
|
|
1588
1589
|
this.expansionIndex = 0;
|
|
1589
1590
|
this.edgeAnalysisDone = false;
|
|
1590
1591
|
this.totalSeedsThisGrid = 0;
|
|
@@ -1622,9 +1623,9 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1622
1623
|
bounds: this.bounds,
|
|
1623
1624
|
gridSize: grid,
|
|
1624
1625
|
layerCount: this.layerCount,
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1626
|
+
hardPlacedByLayer,
|
|
1627
|
+
obstacleIndexByLayer: this.input.obstacleIndexByLayer,
|
|
1628
|
+
placedIndexByLayer: this.placedIndexByLayer
|
|
1628
1629
|
});
|
|
1629
1630
|
this.totalSeedsThisGrid = this.candidates.length;
|
|
1630
1631
|
this.consumedSeedsThisGrid = 0;
|
|
@@ -1642,8 +1643,8 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1642
1643
|
bounds: this.bounds,
|
|
1643
1644
|
minSize,
|
|
1644
1645
|
layerCount: this.layerCount,
|
|
1645
|
-
|
|
1646
|
-
|
|
1646
|
+
obstacleIndexByLayer: this.input.obstacleIndexByLayer,
|
|
1647
|
+
placedIndexByLayer: this.placedIndexByLayer,
|
|
1647
1648
|
hardPlacedByLayer
|
|
1648
1649
|
});
|
|
1649
1650
|
this.edgeAnalysisDone = true;
|
|
@@ -1665,8 +1666,8 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1665
1666
|
layerCount: this.layerCount,
|
|
1666
1667
|
minSpan: minMulti.minLayers,
|
|
1667
1668
|
maxSpan: maxMultiLayerSpan,
|
|
1668
|
-
|
|
1669
|
-
|
|
1669
|
+
obstacleIndexByLayer: this.input.obstacleIndexByLayer,
|
|
1670
|
+
additionalBlockersByLayer: hardPlacedByLayer
|
|
1670
1671
|
});
|
|
1671
1672
|
const attempts = [];
|
|
1672
1673
|
if (span.length >= minMulti.minLayers) {
|
|
@@ -1685,8 +1686,8 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1685
1686
|
for (const attempt of ordered) {
|
|
1686
1687
|
const hardBlockers = [];
|
|
1687
1688
|
for (const z of attempt.layers) {
|
|
1688
|
-
|
|
1689
|
-
|
|
1689
|
+
const obstacleLayer = this.input.obstacleIndexByLayer[z];
|
|
1690
|
+
if (obstacleLayer) hardBlockers.push(...obstacleLayer.all());
|
|
1690
1691
|
if (hardPlacedByLayer[z]) hardBlockers.push(...hardPlacedByLayer[z]);
|
|
1691
1692
|
}
|
|
1692
1693
|
const rect = expandRectFromSeed({
|
|
@@ -1702,25 +1703,34 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1702
1703
|
if (!rect) continue;
|
|
1703
1704
|
const placed = { rect, zLayers: [...attempt.layers] };
|
|
1704
1705
|
const newIndex = this.placed.push(placed) - 1;
|
|
1705
|
-
for (const z of attempt.layers)
|
|
1706
|
+
for (const z of attempt.layers) {
|
|
1707
|
+
const idx = this.placedIndexByLayer[z];
|
|
1708
|
+
if (idx) {
|
|
1709
|
+
idx.insert({
|
|
1710
|
+
...rect,
|
|
1711
|
+
minX: rect.x,
|
|
1712
|
+
minY: rect.y,
|
|
1713
|
+
maxX: rect.x + rect.width,
|
|
1714
|
+
maxY: rect.y + rect.height
|
|
1715
|
+
});
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1706
1718
|
resizeSoftOverlaps(
|
|
1707
1719
|
{
|
|
1708
1720
|
layerCount: this.layerCount,
|
|
1709
1721
|
placed: this.placed,
|
|
1710
|
-
|
|
1711
|
-
|
|
1722
|
+
options: this.options,
|
|
1723
|
+
placedIndexByLayer: this.placedIndexByLayer
|
|
1712
1724
|
},
|
|
1713
1725
|
newIndex
|
|
1714
1726
|
);
|
|
1715
1727
|
this.candidates = this.candidates.filter(
|
|
1716
|
-
(c) => !isFullyOccupiedAtPoint(
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
{ x: c.x, y: c.y }
|
|
1723
|
-
)
|
|
1728
|
+
(c) => !isFullyOccupiedAtPoint({
|
|
1729
|
+
layerCount: this.layerCount,
|
|
1730
|
+
obstacleIndexByLayer: this.input.obstacleIndexByLayer,
|
|
1731
|
+
placedIndexByLayer: this.placedIndexByLayer,
|
|
1732
|
+
point: { x: c.x, y: c.y }
|
|
1733
|
+
})
|
|
1724
1734
|
);
|
|
1725
1735
|
return;
|
|
1726
1736
|
}
|
|
@@ -1748,12 +1758,10 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1748
1758
|
layerCount: this.layerCount,
|
|
1749
1759
|
bounds: this.bounds,
|
|
1750
1760
|
options: this.options,
|
|
1751
|
-
obstaclesByLayer: this.obstaclesByLayer,
|
|
1752
1761
|
boardVoidRects: this.boardVoidRects,
|
|
1753
1762
|
gridIndex: this.gridIndex,
|
|
1754
1763
|
candidates: this.candidates,
|
|
1755
1764
|
placed: this.placed,
|
|
1756
|
-
placedByLayer: this.placedByLayer,
|
|
1757
1765
|
expansionIndex: this.expansionIndex,
|
|
1758
1766
|
edgeAnalysisDone: this.edgeAnalysisDone,
|
|
1759
1767
|
totalSeedsThisGrid: this.totalSeedsThisGrid,
|
|
@@ -1897,23 +1905,27 @@ function finalizeRects(params) {
|
|
|
1897
1905
|
maxY: p.rect.y + p.rect.height,
|
|
1898
1906
|
zLayers: [...p.zLayers].sort((a, b) => a - b)
|
|
1899
1907
|
}));
|
|
1900
|
-
const
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
}
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1908
|
+
const { zIndexByName } = buildZIndexMap(params.srj);
|
|
1909
|
+
const layersByKey = /* @__PURE__ */ new Map();
|
|
1910
|
+
for (const obstacle of params.srj.obstacles ?? []) {
|
|
1911
|
+
const rect = obstacleToXYRect(obstacle);
|
|
1912
|
+
if (!rect) continue;
|
|
1913
|
+
const zLayers = obstacle.zLayers?.length && obstacle.zLayers.length > 0 ? obstacle.zLayers : obstacleZs(obstacle, zIndexByName);
|
|
1914
|
+
const key = `${rect.x}:${rect.y}:${rect.width}:${rect.height}`;
|
|
1915
|
+
let entry = layersByKey.get(key);
|
|
1916
|
+
if (!entry) {
|
|
1917
|
+
entry = { rect, layers: /* @__PURE__ */ new Set() };
|
|
1918
|
+
layersByKey.set(key, entry);
|
|
1919
|
+
}
|
|
1920
|
+
zLayers.forEach((layer) => entry.layers.add(layer));
|
|
1921
|
+
}
|
|
1922
|
+
for (const { rect, layers } of layersByKey.values()) {
|
|
1911
1923
|
out.push({
|
|
1912
1924
|
minX: rect.x,
|
|
1913
1925
|
minY: rect.y,
|
|
1914
1926
|
maxX: rect.x + rect.width,
|
|
1915
1927
|
maxY: rect.y + rect.height,
|
|
1916
|
-
zLayers:
|
|
1928
|
+
zLayers: Array.from(layers).sort((a, b) => a - b),
|
|
1917
1929
|
isObstacle: true
|
|
1918
1930
|
});
|
|
1919
1931
|
}
|
|
@@ -1942,6 +1954,21 @@ function rectsToMeshNodes(rects) {
|
|
|
1942
1954
|
return out;
|
|
1943
1955
|
}
|
|
1944
1956
|
|
|
1957
|
+
// lib/solvers/RectDiffExpansionSolver/RectDiffExpansionSolver.ts
|
|
1958
|
+
import RBush4 from "rbush";
|
|
1959
|
+
|
|
1960
|
+
// lib/utils/rectToTree.ts
|
|
1961
|
+
var rectToTree = (rect) => ({
|
|
1962
|
+
...rect,
|
|
1963
|
+
minX: rect.x,
|
|
1964
|
+
minY: rect.y,
|
|
1965
|
+
maxX: rect.x + rect.width,
|
|
1966
|
+
maxY: rect.y + rect.height
|
|
1967
|
+
});
|
|
1968
|
+
|
|
1969
|
+
// lib/utils/sameTreeRect.ts
|
|
1970
|
+
var sameTreeRect = (a, b) => a.minX === b.minX && a.minY === b.minY && a.maxX === b.maxX && a.maxY === b.maxY;
|
|
1971
|
+
|
|
1945
1972
|
// lib/solvers/RectDiffExpansionSolver/RectDiffExpansionSolver.ts
|
|
1946
1973
|
var RectDiffExpansionSolver = class extends BaseSolver4 {
|
|
1947
1974
|
constructor(input) {
|
|
@@ -1955,12 +1982,11 @@ var RectDiffExpansionSolver = class extends BaseSolver4 {
|
|
|
1955
1982
|
layerCount;
|
|
1956
1983
|
bounds;
|
|
1957
1984
|
options;
|
|
1958
|
-
obstaclesByLayer;
|
|
1959
1985
|
boardVoidRects;
|
|
1960
1986
|
gridIndex;
|
|
1961
1987
|
candidates;
|
|
1962
1988
|
placed;
|
|
1963
|
-
|
|
1989
|
+
placedIndexByLayer;
|
|
1964
1990
|
expansionIndex;
|
|
1965
1991
|
edgeAnalysisDone;
|
|
1966
1992
|
totalSeedsThisGrid;
|
|
@@ -1970,6 +1996,39 @@ var RectDiffExpansionSolver = class extends BaseSolver4 {
|
|
|
1970
1996
|
this.stats = {
|
|
1971
1997
|
gridIndex: this.gridIndex
|
|
1972
1998
|
};
|
|
1999
|
+
if (this.input.obstacleIndexByLayer) {
|
|
2000
|
+
} else {
|
|
2001
|
+
const { zIndexByName } = buildZIndexMap(this.srj);
|
|
2002
|
+
this.input.obstacleIndexByLayer = Array.from(
|
|
2003
|
+
{ length: this.layerCount },
|
|
2004
|
+
() => new RBush4()
|
|
2005
|
+
);
|
|
2006
|
+
const insertObstacle = (rect, z) => {
|
|
2007
|
+
const tree = this.input.obstacleIndexByLayer[z];
|
|
2008
|
+
if (tree) tree.insert(rectToTree(rect));
|
|
2009
|
+
};
|
|
2010
|
+
for (const voidRect of this.boardVoidRects ?? []) {
|
|
2011
|
+
for (let z = 0; z < this.layerCount; z++) insertObstacle(voidRect, z);
|
|
2012
|
+
}
|
|
2013
|
+
for (const obstacle of this.srj.obstacles ?? []) {
|
|
2014
|
+
const rect = obstacleToXYRect(obstacle);
|
|
2015
|
+
if (!rect) continue;
|
|
2016
|
+
const zLayers = obstacle.zLayers?.length && obstacle.zLayers.length > 0 ? obstacle.zLayers : obstacleZs(obstacle, zIndexByName);
|
|
2017
|
+
zLayers.forEach((z) => {
|
|
2018
|
+
if (z >= 0 && z < this.layerCount) insertObstacle(rect, z);
|
|
2019
|
+
});
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
this.placedIndexByLayer = Array.from(
|
|
2023
|
+
{ length: this.layerCount },
|
|
2024
|
+
() => new RBush4()
|
|
2025
|
+
);
|
|
2026
|
+
for (const placement of this.placed ?? []) {
|
|
2027
|
+
for (const z of placement.zLayers) {
|
|
2028
|
+
const tree = this.placedIndexByLayer[z];
|
|
2029
|
+
if (tree) tree.insert(rectToTree(placement.rect));
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
1973
2032
|
}
|
|
1974
2033
|
_step() {
|
|
1975
2034
|
if (this.solved) return;
|
|
@@ -1993,7 +2052,8 @@ var RectDiffExpansionSolver = class extends BaseSolver4 {
|
|
|
1993
2052
|
});
|
|
1994
2053
|
const hardBlockers = [];
|
|
1995
2054
|
for (const z of p.zLayers) {
|
|
1996
|
-
|
|
2055
|
+
const obstacleTree = this.input.obstacleIndexByLayer[z];
|
|
2056
|
+
if (obstacleTree) hardBlockers.push(...obstacleTree.all());
|
|
1997
2057
|
hardBlockers.push(...hardPlacedByLayer[z] ?? []);
|
|
1998
2058
|
}
|
|
1999
2059
|
const oldRect = p.rect;
|
|
@@ -2010,16 +2070,18 @@ var RectDiffExpansionSolver = class extends BaseSolver4 {
|
|
|
2010
2070
|
if (expanded) {
|
|
2011
2071
|
this.placed[idx] = { rect: expanded, zLayers: p.zLayers };
|
|
2012
2072
|
for (const z of p.zLayers) {
|
|
2013
|
-
const
|
|
2014
|
-
|
|
2015
|
-
|
|
2073
|
+
const tree = this.placedIndexByLayer[z];
|
|
2074
|
+
if (tree) {
|
|
2075
|
+
tree.remove(rectToTree(oldRect), sameTreeRect);
|
|
2076
|
+
tree.insert(rectToTree(expanded));
|
|
2077
|
+
}
|
|
2016
2078
|
}
|
|
2017
2079
|
resizeSoftOverlaps(
|
|
2018
2080
|
{
|
|
2019
2081
|
layerCount: this.layerCount,
|
|
2020
2082
|
placed: this.placed,
|
|
2021
|
-
|
|
2022
|
-
|
|
2083
|
+
options: this.options,
|
|
2084
|
+
placedIndexByLayer: this.placedIndexByLayer
|
|
2023
2085
|
},
|
|
2024
2086
|
idx
|
|
2025
2087
|
);
|
|
@@ -2030,7 +2092,7 @@ var RectDiffExpansionSolver = class extends BaseSolver4 {
|
|
|
2030
2092
|
if (this.solved) return;
|
|
2031
2093
|
const rects = finalizeRects({
|
|
2032
2094
|
placed: this.placed,
|
|
2033
|
-
|
|
2095
|
+
srj: this.srj,
|
|
2034
2096
|
boardVoidRects: this.boardVoidRects
|
|
2035
2097
|
});
|
|
2036
2098
|
this._meshNodes = rectsToMeshNodes(rects);
|
|
@@ -2078,10 +2140,73 @@ z:${placement.zLayers.join(",")}`
|
|
|
2078
2140
|
}
|
|
2079
2141
|
};
|
|
2080
2142
|
|
|
2143
|
+
// lib/solvers/RectDiffGridSolverPipeline/RectDiffGridSolverPipeline.ts
|
|
2144
|
+
import "rbush";
|
|
2145
|
+
|
|
2146
|
+
// lib/solvers/RectDiffGridSolverPipeline/buildObstacleIndexes.ts
|
|
2147
|
+
import RBush5 from "rbush";
|
|
2148
|
+
var buildObstacleIndexes = (srj) => {
|
|
2149
|
+
const { layerNames, zIndexByName } = buildZIndexMap(srj);
|
|
2150
|
+
const layerCount = Math.max(1, layerNames.length, srj.layerCount || 1);
|
|
2151
|
+
const bounds = {
|
|
2152
|
+
x: srj.bounds.minX,
|
|
2153
|
+
y: srj.bounds.minY,
|
|
2154
|
+
width: srj.bounds.maxX - srj.bounds.minX,
|
|
2155
|
+
height: srj.bounds.maxY - srj.bounds.minY
|
|
2156
|
+
};
|
|
2157
|
+
const obstacleIndexByLayer = Array.from(
|
|
2158
|
+
{ length: layerCount },
|
|
2159
|
+
() => new RBush5()
|
|
2160
|
+
);
|
|
2161
|
+
const insertObstacle = (rect, z) => {
|
|
2162
|
+
const treeRect = {
|
|
2163
|
+
...rect,
|
|
2164
|
+
minX: rect.x,
|
|
2165
|
+
minY: rect.y,
|
|
2166
|
+
maxX: rect.x + rect.width,
|
|
2167
|
+
maxY: rect.y + rect.height
|
|
2168
|
+
};
|
|
2169
|
+
obstacleIndexByLayer[z]?.insert(treeRect);
|
|
2170
|
+
};
|
|
2171
|
+
let boardVoidRects = [];
|
|
2172
|
+
if (srj.outline && srj.outline.length > 2) {
|
|
2173
|
+
boardVoidRects = computeInverseRects(bounds, srj.outline);
|
|
2174
|
+
for (const voidRect of boardVoidRects) {
|
|
2175
|
+
for (let z = 0; z < layerCount; z++) insertObstacle(voidRect, z);
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
for (const obstacle of srj.obstacles ?? []) {
|
|
2179
|
+
const rect = obstacleToXYRect(obstacle);
|
|
2180
|
+
if (!rect) continue;
|
|
2181
|
+
const zLayers = obstacleZs(obstacle, zIndexByName);
|
|
2182
|
+
const invalidZs = zLayers.filter((z) => z < 0 || z >= layerCount);
|
|
2183
|
+
if (invalidZs.length) {
|
|
2184
|
+
throw new Error(
|
|
2185
|
+
`RectDiff: obstacle uses z-layer indices ${invalidZs.join(",")} outside 0-${layerCount - 1}`
|
|
2186
|
+
);
|
|
2187
|
+
}
|
|
2188
|
+
if ((!obstacle.zLayers || obstacle.zLayers.length === 0) && zLayers.length) {
|
|
2189
|
+
obstacle.zLayers = zLayers;
|
|
2190
|
+
}
|
|
2191
|
+
for (const z of zLayers) insertObstacle(rect, z);
|
|
2192
|
+
}
|
|
2193
|
+
return { obstacleIndexByLayer, boardVoidRects };
|
|
2194
|
+
};
|
|
2195
|
+
|
|
2081
2196
|
// lib/solvers/RectDiffGridSolverPipeline/RectDiffGridSolverPipeline.ts
|
|
2082
2197
|
var RectDiffGridSolverPipeline = class extends BasePipelineSolver2 {
|
|
2083
2198
|
rectDiffSeedingSolver;
|
|
2084
2199
|
rectDiffExpansionSolver;
|
|
2200
|
+
boardVoidRects;
|
|
2201
|
+
obstacleIndexByLayer;
|
|
2202
|
+
constructor(inputProblem) {
|
|
2203
|
+
super(inputProblem);
|
|
2204
|
+
const { obstacleIndexByLayer, boardVoidRects } = buildObstacleIndexes(
|
|
2205
|
+
inputProblem.simpleRouteJson
|
|
2206
|
+
);
|
|
2207
|
+
this.obstacleIndexByLayer = obstacleIndexByLayer;
|
|
2208
|
+
this.boardVoidRects = boardVoidRects;
|
|
2209
|
+
}
|
|
2085
2210
|
pipelineDef = [
|
|
2086
2211
|
definePipelineStep2(
|
|
2087
2212
|
"rectDiffSeedingSolver",
|
|
@@ -2089,7 +2214,9 @@ var RectDiffGridSolverPipeline = class extends BasePipelineSolver2 {
|
|
|
2089
2214
|
(pipeline) => [
|
|
2090
2215
|
{
|
|
2091
2216
|
simpleRouteJson: pipeline.inputProblem.simpleRouteJson,
|
|
2092
|
-
gridOptions: pipeline.inputProblem.gridOptions
|
|
2217
|
+
gridOptions: pipeline.inputProblem.gridOptions,
|
|
2218
|
+
obstacleIndexByLayer: pipeline.obstacleIndexByLayer,
|
|
2219
|
+
boardVoidRects: pipeline.boardVoidRects
|
|
2093
2220
|
}
|
|
2094
2221
|
]
|
|
2095
2222
|
),
|
|
@@ -2098,7 +2225,11 @@ var RectDiffGridSolverPipeline = class extends BasePipelineSolver2 {
|
|
|
2098
2225
|
RectDiffExpansionSolver,
|
|
2099
2226
|
(pipeline) => [
|
|
2100
2227
|
{
|
|
2101
|
-
initialSnapshot:
|
|
2228
|
+
initialSnapshot: {
|
|
2229
|
+
...pipeline.rectDiffSeedingSolver.getOutput(),
|
|
2230
|
+
boardVoidRects: pipeline.boardVoidRects ?? []
|
|
2231
|
+
},
|
|
2232
|
+
obstacleIndexByLayer: pipeline.obstacleIndexByLayer
|
|
2102
2233
|
}
|
|
2103
2234
|
]
|
|
2104
2235
|
)
|