@tscircuit/capacity-autorouter 0.0.21 → 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/dist/index.d.ts +111 -30
- package/dist/index.js +1326 -703
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -125,6 +125,22 @@ function distance(p1, p2) {
|
|
|
125
125
|
return Math.sqrt(dx * dx + dy * dy);
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
// lib/utils/areNodesBordering.ts
|
|
129
|
+
function areNodesBordering(node1, node2) {
|
|
130
|
+
const n1Left = node1.center.x - node1.width / 2;
|
|
131
|
+
const n1Right = node1.center.x + node1.width / 2;
|
|
132
|
+
const n1Top = node1.center.y - node1.height / 2;
|
|
133
|
+
const n1Bottom = node1.center.y + node1.height / 2;
|
|
134
|
+
const n2Left = node2.center.x - node2.width / 2;
|
|
135
|
+
const n2Right = node2.center.x + node2.width / 2;
|
|
136
|
+
const n2Top = node2.center.y - node2.height / 2;
|
|
137
|
+
const n2Bottom = node2.center.y + node2.height / 2;
|
|
138
|
+
const epsilon = 1e-3;
|
|
139
|
+
const shareVerticalBorder = (Math.abs(n1Right - n2Left) < epsilon || Math.abs(n1Left - n2Right) < epsilon) && Math.min(n1Bottom, n2Bottom) - Math.max(n1Top, n2Top) >= epsilon;
|
|
140
|
+
const shareHorizontalBorder = (Math.abs(n1Bottom - n2Top) < epsilon || Math.abs(n1Top - n2Bottom) < epsilon) && Math.min(n1Right, n2Right) - Math.max(n1Left, n2Left) >= epsilon;
|
|
141
|
+
return shareVerticalBorder || shareHorizontalBorder;
|
|
142
|
+
}
|
|
143
|
+
|
|
128
144
|
// lib/solvers/CapacityMeshSolver/CapacityMeshEdgeSolver.ts
|
|
129
145
|
var CapacityMeshEdgeSolver = class extends BaseSolver {
|
|
130
146
|
constructor(nodes) {
|
|
@@ -140,7 +156,7 @@ var CapacityMeshEdgeSolver = class extends BaseSolver {
|
|
|
140
156
|
this.edges = [];
|
|
141
157
|
for (let i = 0; i < this.nodes.length; i++) {
|
|
142
158
|
for (let j = i + 1; j < this.nodes.length; j++) {
|
|
143
|
-
if (this.areNodesBordering(this.nodes[i], this.nodes[j]) && this.doNodesHaveSharedLayer(this.nodes[i], this.nodes[j])) {
|
|
159
|
+
if (!(this.nodes[i]._strawNode && this.nodes[j]._strawNode) && areNodesBordering(this.nodes[i], this.nodes[j]) && this.doNodesHaveSharedLayer(this.nodes[i], this.nodes[j])) {
|
|
144
160
|
this.edges.push({
|
|
145
161
|
capacityMeshEdgeId: this.getNextCapacityMeshEdgeId(),
|
|
146
162
|
nodeIds: [
|
|
@@ -180,20 +196,6 @@ var CapacityMeshEdgeSolver = class extends BaseSolver {
|
|
|
180
196
|
}
|
|
181
197
|
this.solved = true;
|
|
182
198
|
}
|
|
183
|
-
areNodesBordering(node1, node2) {
|
|
184
|
-
const n1Left = node1.center.x - node1.width / 2;
|
|
185
|
-
const n1Right = node1.center.x + node1.width / 2;
|
|
186
|
-
const n1Top = node1.center.y - node1.height / 2;
|
|
187
|
-
const n1Bottom = node1.center.y + node1.height / 2;
|
|
188
|
-
const n2Left = node2.center.x - node2.width / 2;
|
|
189
|
-
const n2Right = node2.center.x + node2.width / 2;
|
|
190
|
-
const n2Top = node2.center.y - node2.height / 2;
|
|
191
|
-
const n2Bottom = node2.center.y + node2.height / 2;
|
|
192
|
-
const epsilon = 1e-3;
|
|
193
|
-
const shareVerticalBorder = (Math.abs(n1Right - n2Left) < epsilon || Math.abs(n1Left - n2Right) < epsilon) && Math.min(n1Bottom, n2Bottom) - Math.max(n1Top, n2Top) >= epsilon;
|
|
194
|
-
const shareHorizontalBorder = (Math.abs(n1Bottom - n2Top) < epsilon || Math.abs(n1Top - n2Bottom) < epsilon) && Math.min(n1Right, n2Right) - Math.max(n1Left, n2Left) >= epsilon;
|
|
195
|
-
return shareVerticalBorder || shareHorizontalBorder;
|
|
196
|
-
}
|
|
197
199
|
doNodesHaveSharedLayer(node1, node2) {
|
|
198
200
|
return node1.availableZ.some((z) => node2.availableZ.includes(z));
|
|
199
201
|
}
|
|
@@ -233,8 +235,19 @@ var CapacityMeshEdgeSolver = class extends BaseSolver {
|
|
|
233
235
|
(node) => node.capacityMeshNodeId === edge.nodeIds[1]
|
|
234
236
|
);
|
|
235
237
|
if (node1?.center && node2?.center) {
|
|
238
|
+
const lowestZ1 = Math.min(...node1.availableZ);
|
|
239
|
+
const lowestZ2 = Math.min(...node2.availableZ);
|
|
240
|
+
const nodeCenter1Adj = {
|
|
241
|
+
x: node1.center.x + lowestZ1 * node1.width * 0.05,
|
|
242
|
+
y: node1.center.y - lowestZ1 * node1.width * 0.05
|
|
243
|
+
};
|
|
244
|
+
const nodeCenter2Adj = {
|
|
245
|
+
x: node2.center.x + lowestZ2 * node2.width * 0.05,
|
|
246
|
+
y: node2.center.y - lowestZ2 * node2.width * 0.05
|
|
247
|
+
};
|
|
236
248
|
graphics.lines.push({
|
|
237
|
-
points: [
|
|
249
|
+
points: [nodeCenter1Adj, nodeCenter2Adj],
|
|
250
|
+
strokeDash: node1.availableZ.join(",") === node2.availableZ.join(",") ? void 0 : "10 5"
|
|
238
251
|
});
|
|
239
252
|
}
|
|
240
253
|
}
|
|
@@ -1168,6 +1181,7 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1168
1181
|
getTargetIfNodeContainsTarget(node) {
|
|
1169
1182
|
const overlappingObstacles = this.getXYOverlappingObstacles(node);
|
|
1170
1183
|
for (const target of this.targets) {
|
|
1184
|
+
if (!target.availableZ.some((z) => node.availableZ.includes(z))) continue;
|
|
1171
1185
|
const targetObstacle = overlappingObstacles.find(
|
|
1172
1186
|
(o) => isPointInRect(target, o)
|
|
1173
1187
|
);
|
|
@@ -1264,9 +1278,6 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1264
1278
|
return true;
|
|
1265
1279
|
}
|
|
1266
1280
|
}
|
|
1267
|
-
if (nodeRight < this.srj.bounds.minX || nodeLeft > this.srj.bounds.maxX || nodeBottom < this.srj.bounds.minY || nodeTop > this.srj.bounds.maxY) {
|
|
1268
|
-
return true;
|
|
1269
|
-
}
|
|
1270
1281
|
return false;
|
|
1271
1282
|
}
|
|
1272
1283
|
getChildNodes(parent) {
|
|
@@ -1370,14 +1381,15 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1370
1381
|
center: obstacle.center,
|
|
1371
1382
|
width: obstacle.width,
|
|
1372
1383
|
height: obstacle.height,
|
|
1373
|
-
fill: "rgba(255,0,0,0.3)",
|
|
1384
|
+
fill: obstacle.zLayers?.length === 1 && obstacle.zLayers?.includes(1) ? "rgba(0,0,255,0.3)" : "rgba(255,0,0,0.3)",
|
|
1374
1385
|
stroke: "red",
|
|
1375
|
-
label: ["obstacle", obstacle.zLayers.join(",")].join("\n")
|
|
1386
|
+
label: ["obstacle", `z: ${obstacle.zLayers.join(",")}`].join("\n")
|
|
1376
1387
|
});
|
|
1377
1388
|
}
|
|
1378
1389
|
const allNodes = [...this.finishedNodes, ...this.unfinishedNodes];
|
|
1379
1390
|
for (const node of allNodes) {
|
|
1380
1391
|
const lowestZ = Math.min(...node.availableZ);
|
|
1392
|
+
const isNextToBeProcessed = this.unfinishedNodes.length > 0 && node === this.unfinishedNodes[this.unfinishedNodes.length - 1];
|
|
1381
1393
|
graphics.rects.push({
|
|
1382
1394
|
center: {
|
|
1383
1395
|
x: node.center.x + lowestZ * node.width * 0.05,
|
|
@@ -1390,6 +1402,7 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1390
1402
|
"0": "rgba(0,200,200, 0.1)",
|
|
1391
1403
|
"1": "rgba(0,0,200, 0.1)"
|
|
1392
1404
|
}[node.availableZ.join(",")] ?? "rgba(0,200,200,0.1)",
|
|
1405
|
+
stroke: isNextToBeProcessed ? "rgba(255,165,0,0.5)" : void 0,
|
|
1393
1406
|
label: [
|
|
1394
1407
|
node.capacityMeshNodeId,
|
|
1395
1408
|
`availableZ: ${node.availableZ.join(",")}`,
|
|
@@ -1414,6 +1427,146 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1414
1427
|
}
|
|
1415
1428
|
};
|
|
1416
1429
|
|
|
1430
|
+
// lib/solvers/CapacityMeshSolver/CapacityMeshNodeSolver2_NodesUnderObstacles.ts
|
|
1431
|
+
var CapacityMeshNodeSolver2_NodeUnderObstacle = class extends CapacityMeshNodeSolver {
|
|
1432
|
+
constructor(srj, opts = {}) {
|
|
1433
|
+
super(srj, opts);
|
|
1434
|
+
this.srj = srj;
|
|
1435
|
+
this.opts = opts;
|
|
1436
|
+
}
|
|
1437
|
+
isNodeCompletelyOutsideBounds(node) {
|
|
1438
|
+
return node.center.x + node.width / 2 < this.srj.bounds.minX || node.center.x - node.width / 2 > this.srj.bounds.maxX || node.center.y + node.height / 2 < this.srj.bounds.minY || node.center.y - node.height / 2 > this.srj.bounds.maxY;
|
|
1439
|
+
}
|
|
1440
|
+
isNodePartiallyOutsideBounds(node) {
|
|
1441
|
+
return node.center.x - node.width / 2 < this.srj.bounds.minX || node.center.x + node.width / 2 > this.srj.bounds.maxX || node.center.y - node.height / 2 < this.srj.bounds.minY || node.center.y + node.height / 2 > this.srj.bounds.maxY;
|
|
1442
|
+
}
|
|
1443
|
+
createChildNodeAtPosition(parent, opts) {
|
|
1444
|
+
const childNode = {
|
|
1445
|
+
capacityMeshNodeId: this.getNextNodeId(),
|
|
1446
|
+
center: opts.center,
|
|
1447
|
+
width: opts.width,
|
|
1448
|
+
height: opts.height,
|
|
1449
|
+
layer: parent.layer,
|
|
1450
|
+
availableZ: opts.availableZ,
|
|
1451
|
+
_depth: opts._depth ?? (parent._depth ?? 0) + 1,
|
|
1452
|
+
_parent: parent
|
|
1453
|
+
};
|
|
1454
|
+
const overlappingObstacles = this.getXYZOverlappingObstacles(childNode);
|
|
1455
|
+
childNode._containsObstacle = overlappingObstacles.length > 0 || this.isNodePartiallyOutsideBounds(childNode);
|
|
1456
|
+
const target = this.getTargetIfNodeContainsTarget(childNode);
|
|
1457
|
+
if (target) {
|
|
1458
|
+
childNode._targetConnectionName = target.connectionName;
|
|
1459
|
+
childNode._containsTarget = true;
|
|
1460
|
+
}
|
|
1461
|
+
if (childNode._containsObstacle) {
|
|
1462
|
+
childNode._completelyInsideObstacle = this.isNodeCompletelyInsideObstacle(childNode);
|
|
1463
|
+
}
|
|
1464
|
+
return childNode;
|
|
1465
|
+
}
|
|
1466
|
+
getZSubdivisionChildNodes(node) {
|
|
1467
|
+
if (node.availableZ.length === 1) return [];
|
|
1468
|
+
const childNodes = [];
|
|
1469
|
+
const otherZBlocks = [[0], [1]];
|
|
1470
|
+
for (const zBlock of otherZBlocks) {
|
|
1471
|
+
const childNode = this.createChildNodeAtPosition(node, {
|
|
1472
|
+
center: { ...node.center },
|
|
1473
|
+
width: node.width,
|
|
1474
|
+
height: node.height,
|
|
1475
|
+
availableZ: zBlock,
|
|
1476
|
+
// z-subdivision doesn't count towards depth, should be same as parent
|
|
1477
|
+
_depth: node._depth
|
|
1478
|
+
});
|
|
1479
|
+
if (this.isNodeCompletelyOutsideBounds(childNode)) {
|
|
1480
|
+
continue;
|
|
1481
|
+
}
|
|
1482
|
+
childNodes.push(childNode);
|
|
1483
|
+
}
|
|
1484
|
+
return childNodes;
|
|
1485
|
+
}
|
|
1486
|
+
getChildNodes(parent) {
|
|
1487
|
+
if (parent._depth >= this.MAX_DEPTH) return [];
|
|
1488
|
+
const childNodes = [];
|
|
1489
|
+
const childNodeSize = { width: parent.width / 2, height: parent.height / 2 };
|
|
1490
|
+
const childNodePositions = [
|
|
1491
|
+
{
|
|
1492
|
+
x: parent.center.x - childNodeSize.width / 2,
|
|
1493
|
+
y: parent.center.y - childNodeSize.height / 2
|
|
1494
|
+
},
|
|
1495
|
+
{
|
|
1496
|
+
x: parent.center.x + childNodeSize.width / 2,
|
|
1497
|
+
y: parent.center.y - childNodeSize.height / 2
|
|
1498
|
+
},
|
|
1499
|
+
{
|
|
1500
|
+
x: parent.center.x - childNodeSize.width / 2,
|
|
1501
|
+
y: parent.center.y + childNodeSize.height / 2
|
|
1502
|
+
},
|
|
1503
|
+
{
|
|
1504
|
+
x: parent.center.x + childNodeSize.width / 2,
|
|
1505
|
+
y: parent.center.y + childNodeSize.height / 2
|
|
1506
|
+
}
|
|
1507
|
+
];
|
|
1508
|
+
for (const position of childNodePositions) {
|
|
1509
|
+
const childNode = this.createChildNodeAtPosition(parent, {
|
|
1510
|
+
center: position,
|
|
1511
|
+
width: childNodeSize.width,
|
|
1512
|
+
height: childNodeSize.height,
|
|
1513
|
+
availableZ: parent.availableZ
|
|
1514
|
+
});
|
|
1515
|
+
if (this.isNodeCompletelyOutsideBounds(childNode)) {
|
|
1516
|
+
continue;
|
|
1517
|
+
}
|
|
1518
|
+
childNodes.push(childNode);
|
|
1519
|
+
}
|
|
1520
|
+
return childNodes;
|
|
1521
|
+
}
|
|
1522
|
+
shouldNodeBeXYSubdivided(node) {
|
|
1523
|
+
if (node._depth >= this.MAX_DEPTH) return false;
|
|
1524
|
+
if (node._containsTarget) return true;
|
|
1525
|
+
if (node.availableZ.length === 1 && node._depth <= this.MAX_DEPTH)
|
|
1526
|
+
return true;
|
|
1527
|
+
if (node._containsObstacle && !node._completelyInsideObstacle) return true;
|
|
1528
|
+
return false;
|
|
1529
|
+
}
|
|
1530
|
+
_step() {
|
|
1531
|
+
const nextNode = this.unfinishedNodes.pop();
|
|
1532
|
+
if (!nextNode) {
|
|
1533
|
+
this.solved = true;
|
|
1534
|
+
return;
|
|
1535
|
+
}
|
|
1536
|
+
const childNodes = this.getChildNodes(nextNode);
|
|
1537
|
+
const finishedNewNodes = [];
|
|
1538
|
+
const unfinishedNewNodes = [];
|
|
1539
|
+
for (const childNode of childNodes) {
|
|
1540
|
+
const shouldBeXYSubdivided = this.shouldNodeBeXYSubdivided(childNode);
|
|
1541
|
+
const shouldBeZSubdivided = childNode.availableZ.length > 1 && !shouldBeXYSubdivided && childNode._containsObstacle;
|
|
1542
|
+
if (shouldBeXYSubdivided) {
|
|
1543
|
+
unfinishedNewNodes.push(childNode);
|
|
1544
|
+
} else if (!shouldBeXYSubdivided && !childNode._containsObstacle) {
|
|
1545
|
+
finishedNewNodes.push(childNode);
|
|
1546
|
+
} else if (!shouldBeXYSubdivided && childNode._containsTarget) {
|
|
1547
|
+
if (shouldBeZSubdivided) {
|
|
1548
|
+
const zSubNodes = this.getZSubdivisionChildNodes(childNode);
|
|
1549
|
+
finishedNewNodes.push(
|
|
1550
|
+
...zSubNodes.filter(
|
|
1551
|
+
(n) => n._containsTarget || !n._containsObstacle
|
|
1552
|
+
)
|
|
1553
|
+
);
|
|
1554
|
+
} else {
|
|
1555
|
+
finishedNewNodes.push(childNode);
|
|
1556
|
+
}
|
|
1557
|
+
} else if (shouldBeZSubdivided) {
|
|
1558
|
+
finishedNewNodes.push(
|
|
1559
|
+
...this.getZSubdivisionChildNodes(childNode).filter(
|
|
1560
|
+
(zSubNode) => !zSubNode._containsObstacle
|
|
1561
|
+
)
|
|
1562
|
+
);
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
this.unfinishedNodes.push(...unfinishedNewNodes);
|
|
1566
|
+
this.finishedNodes.push(...finishedNewNodes);
|
|
1567
|
+
}
|
|
1568
|
+
};
|
|
1569
|
+
|
|
1417
1570
|
// lib/solvers/CapacityMeshSolver/getNodeEdgeMap.ts
|
|
1418
1571
|
function getNodeEdgeMap(edges) {
|
|
1419
1572
|
const nodeEdgeMap = /* @__PURE__ */ new Map();
|
|
@@ -1712,7 +1865,10 @@ var CapacitySegmentToPointSolver = class extends BaseSolver {
|
|
|
1712
1865
|
(seg) => seg.assignedPoints.map((ap) => ({
|
|
1713
1866
|
x: ap.point.x,
|
|
1714
1867
|
y: ap.point.y,
|
|
1715
|
-
label:
|
|
1868
|
+
label: [
|
|
1869
|
+
`${seg.capacityMeshNodeId}-${ap.connectionName}`,
|
|
1870
|
+
`z: ${seg.availableZ.join(",")}`
|
|
1871
|
+
].join("\n"),
|
|
1716
1872
|
color: this.colorMap[ap.connectionName],
|
|
1717
1873
|
step: 4
|
|
1718
1874
|
}))
|
|
@@ -1904,7 +2060,7 @@ var SingleHighDensityRouteSolver = class extends BaseSolver {
|
|
|
1904
2060
|
const margin = isVia ? this.viaDiameter / 2 : this.obstacleMargin;
|
|
1905
2061
|
const tooClose = node.x < this.bounds.minX + margin || node.x > this.bounds.maxX - margin || node.y < this.bounds.minY + margin || node.y > this.bounds.maxY - margin;
|
|
1906
2062
|
if (tooClose && !isVia) {
|
|
1907
|
-
if (distance(node, this.B) < margin * 2) {
|
|
2063
|
+
if (distance(node, this.B) < margin * 2 || distance(node, this.A) < margin * 2) {
|
|
1908
2064
|
return false;
|
|
1909
2065
|
}
|
|
1910
2066
|
}
|
|
@@ -2884,294 +3040,94 @@ var HighDensitySolver = class extends BaseSolver {
|
|
|
2884
3040
|
}
|
|
2885
3041
|
};
|
|
2886
3042
|
|
|
2887
|
-
//
|
|
2888
|
-
var
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
maxDepthOfNodes;
|
|
2900
|
-
activeCandidateStraightLineDistance;
|
|
2901
|
-
hyperParameters;
|
|
2902
|
-
constructor({
|
|
2903
|
-
simpleRouteJson,
|
|
2904
|
-
nodes,
|
|
2905
|
-
edges,
|
|
2906
|
-
colorMap,
|
|
2907
|
-
MAX_ITERATIONS = 1e6,
|
|
2908
|
-
hyperParameters = {}
|
|
2909
|
-
}) {
|
|
2910
|
-
super();
|
|
2911
|
-
this.MAX_ITERATIONS = MAX_ITERATIONS;
|
|
2912
|
-
this.simpleRouteJson = simpleRouteJson;
|
|
2913
|
-
this.nodes = nodes;
|
|
2914
|
-
this.edges = edges;
|
|
2915
|
-
this.colorMap = colorMap ?? {};
|
|
2916
|
-
const { connectionsWithNodes, connectionNameToGoalNodeIds } = this.getConnectionsWithNodes();
|
|
2917
|
-
this.connectionsWithNodes = connectionsWithNodes;
|
|
2918
|
-
this.connectionNameToGoalNodeIds = connectionNameToGoalNodeIds;
|
|
2919
|
-
this.hyperParameters = hyperParameters;
|
|
2920
|
-
this.usedNodeCapacityMap = new Map(
|
|
2921
|
-
this.nodes.map((node) => [node.capacityMeshNodeId, 0])
|
|
2922
|
-
);
|
|
2923
|
-
this.nodeMap = new Map(
|
|
2924
|
-
this.nodes.map((node) => [node.capacityMeshNodeId, node])
|
|
2925
|
-
);
|
|
2926
|
-
this.nodeEdgeMap = getNodeEdgeMap(this.edges);
|
|
2927
|
-
this.maxDepthOfNodes = Math.max(
|
|
2928
|
-
...this.nodes.map((node) => node._depth ?? 0)
|
|
2929
|
-
);
|
|
2930
|
-
}
|
|
2931
|
-
getTotalCapacity(node) {
|
|
2932
|
-
const depth = node._depth ?? 0;
|
|
2933
|
-
return (this.maxDepthOfNodes - depth + 1) ** 2;
|
|
3043
|
+
// node_modules/circuit-json-to-connectivity-map/dist/index.js
|
|
3044
|
+
var ConnectivityMap = class {
|
|
3045
|
+
netMap;
|
|
3046
|
+
idToNetMap;
|
|
3047
|
+
constructor(netMap) {
|
|
3048
|
+
this.netMap = netMap;
|
|
3049
|
+
this.idToNetMap = {};
|
|
3050
|
+
for (const [netId, ids] of Object.entries(netMap)) {
|
|
3051
|
+
for (const id of ids) {
|
|
3052
|
+
this.idToNetMap[id] = netId;
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
2934
3055
|
}
|
|
2935
|
-
|
|
2936
|
-
const
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
3056
|
+
addConnections(connections) {
|
|
3057
|
+
for (const connection of connections) {
|
|
3058
|
+
const existingNets = /* @__PURE__ */ new Set();
|
|
3059
|
+
for (const id of connection) {
|
|
3060
|
+
const existingNetId = this.idToNetMap[id];
|
|
3061
|
+
if (existingNetId) {
|
|
3062
|
+
existingNets.add(existingNetId);
|
|
3063
|
+
}
|
|
3064
|
+
}
|
|
3065
|
+
let targetNetId;
|
|
3066
|
+
if (existingNets.size === 0) {
|
|
3067
|
+
targetNetId = `connectivity_net${Object.keys(this.netMap).length}`;
|
|
3068
|
+
this.netMap[targetNetId] = [];
|
|
3069
|
+
} else if (existingNets.size === 1) {
|
|
3070
|
+
targetNetId = existingNets.values().next().value ?? `connectivity_net${Object.keys(this.netMap).length}`;
|
|
3071
|
+
} else {
|
|
3072
|
+
targetNetId = existingNets.values().next().value ?? `connectivity_net${Object.keys(this.netMap).length}`;
|
|
3073
|
+
for (const netId of existingNets) {
|
|
3074
|
+
if (netId !== targetNetId) {
|
|
3075
|
+
this.netMap[targetNetId].push(...this.netMap[netId]);
|
|
3076
|
+
this.netMap[netId] = this.netMap[targetNetId];
|
|
3077
|
+
for (const id of this.netMap[targetNetId]) {
|
|
3078
|
+
this.idToNetMap[id] = targetNetId;
|
|
3079
|
+
}
|
|
2951
3080
|
}
|
|
2952
3081
|
}
|
|
2953
|
-
nodesForConnection.push(closestNode);
|
|
2954
3082
|
}
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
3083
|
+
for (const id of connection) {
|
|
3084
|
+
if (!this.netMap[targetNetId].includes(id)) {
|
|
3085
|
+
this.netMap[targetNetId].push(id);
|
|
3086
|
+
}
|
|
3087
|
+
this.idToNetMap[id] = targetNetId;
|
|
2959
3088
|
}
|
|
2960
|
-
connectionNameToGoalNodeIds.set(
|
|
2961
|
-
connection.name,
|
|
2962
|
-
nodesForConnection.map((n) => n.capacityMeshNodeId)
|
|
2963
|
-
);
|
|
2964
|
-
connectionsWithNodes.push({
|
|
2965
|
-
connection,
|
|
2966
|
-
nodes: nodesForConnection,
|
|
2967
|
-
pathFound: false
|
|
2968
|
-
});
|
|
2969
3089
|
}
|
|
2970
|
-
return { connectionsWithNodes, connectionNameToGoalNodeIds };
|
|
2971
|
-
}
|
|
2972
|
-
currentConnectionIndex = 0;
|
|
2973
|
-
candidates;
|
|
2974
|
-
visitedNodes;
|
|
2975
|
-
computeG(prevCandidate, node, endGoal) {
|
|
2976
|
-
return prevCandidate.g + this.getDistanceBetweenNodes(prevCandidate.node, node);
|
|
2977
3090
|
}
|
|
2978
|
-
|
|
2979
|
-
return this.
|
|
3091
|
+
getIdsConnectedToNet(netId) {
|
|
3092
|
+
return this.netMap[netId] || [];
|
|
2980
3093
|
}
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
let currentCandidate = candidate;
|
|
2984
|
-
while (currentCandidate) {
|
|
2985
|
-
path.push(currentCandidate.node);
|
|
2986
|
-
currentCandidate = currentCandidate.prevCandidate;
|
|
2987
|
-
}
|
|
2988
|
-
return path;
|
|
3094
|
+
getNetConnectedToId(id) {
|
|
3095
|
+
return this.idToNetMap[id];
|
|
2989
3096
|
}
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
3097
|
+
areIdsConnected(id1, id2) {
|
|
3098
|
+
if (id1 === id2) return true;
|
|
3099
|
+
const netId1 = this.getNetConnectedToId(id1);
|
|
3100
|
+
if (!netId1) return false;
|
|
3101
|
+
const netId2 = this.getNetConnectedToId(id2);
|
|
3102
|
+
if (!netId2) return false;
|
|
3103
|
+
return netId1 === netId2 || netId2 === id1 || netId2 === id1;
|
|
2994
3104
|
}
|
|
2995
|
-
|
|
2996
|
-
const
|
|
2997
|
-
for (const
|
|
2998
|
-
const
|
|
2999
|
-
if (
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
});
|
|
3105
|
+
areAllIdsConnected(ids) {
|
|
3106
|
+
const netId = this.getNetConnectedToId(ids[0]);
|
|
3107
|
+
for (const id of ids) {
|
|
3108
|
+
const nextNetId = this.getNetConnectedToId(id);
|
|
3109
|
+
if (nextNetId === void 0) {
|
|
3110
|
+
return false;
|
|
3111
|
+
}
|
|
3112
|
+
if (nextNetId !== netId) {
|
|
3113
|
+
return false;
|
|
3005
3114
|
}
|
|
3006
3115
|
}
|
|
3007
|
-
return
|
|
3116
|
+
return true;
|
|
3008
3117
|
}
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
}
|
|
3014
|
-
|
|
3015
|
-
const
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
getDistanceBetweenNodes(A, B) {
|
|
3019
|
-
return Math.sqrt(
|
|
3020
|
-
(A.center.x - B.center.x) ** 2 + (A.center.y - B.center.y) ** 2
|
|
3021
|
-
);
|
|
3022
|
-
}
|
|
3023
|
-
reduceCapacityAlongPath(nextConnection) {
|
|
3024
|
-
for (const node of nextConnection.path ?? []) {
|
|
3025
|
-
this.usedNodeCapacityMap.set(
|
|
3026
|
-
node.capacityMeshNodeId,
|
|
3027
|
-
this.usedNodeCapacityMap.get(node.capacityMeshNodeId) + 1
|
|
3028
|
-
);
|
|
3029
|
-
}
|
|
3030
|
-
}
|
|
3031
|
-
isConnectedToEndGoal(node, endGoal) {
|
|
3032
|
-
return this.nodeEdgeMap.get(node.capacityMeshNodeId).some((edge) => edge.nodeIds.includes(endGoal.capacityMeshNodeId));
|
|
3033
|
-
}
|
|
3034
|
-
_step() {
|
|
3035
|
-
const nextConnection = this.connectionsWithNodes[this.currentConnectionIndex];
|
|
3036
|
-
if (!nextConnection) {
|
|
3037
|
-
this.solved = true;
|
|
3038
|
-
return;
|
|
3039
|
-
}
|
|
3040
|
-
const [start, end] = nextConnection.nodes;
|
|
3041
|
-
if (!this.candidates) {
|
|
3042
|
-
this.candidates = [{ prevCandidate: null, node: start, f: 0, g: 0, h: 0 }];
|
|
3043
|
-
this.visitedNodes = /* @__PURE__ */ new Set([start.capacityMeshNodeId]);
|
|
3044
|
-
this.activeCandidateStraightLineDistance = distance(
|
|
3045
|
-
start.center,
|
|
3046
|
-
end.center
|
|
3047
|
-
);
|
|
3048
|
-
}
|
|
3049
|
-
this.candidates.sort((a, b) => a.f - b.f);
|
|
3050
|
-
const currentCandidate = this.candidates.shift();
|
|
3051
|
-
if (!currentCandidate) {
|
|
3052
|
-
console.error(
|
|
3053
|
-
`Ran out of candidates on connection ${nextConnection.connection.name}`
|
|
3054
|
-
);
|
|
3055
|
-
this.currentConnectionIndex++;
|
|
3056
|
-
this.candidates = null;
|
|
3057
|
-
this.visitedNodes = null;
|
|
3058
|
-
return;
|
|
3059
|
-
}
|
|
3060
|
-
if (this.isConnectedToEndGoal(currentCandidate.node, end)) {
|
|
3061
|
-
nextConnection.path = this.getBacktrackedPath({
|
|
3062
|
-
prevCandidate: currentCandidate,
|
|
3063
|
-
node: end,
|
|
3064
|
-
f: 0,
|
|
3065
|
-
g: 0,
|
|
3066
|
-
h: 0
|
|
3067
|
-
});
|
|
3068
|
-
this.reduceCapacityAlongPath(nextConnection);
|
|
3069
|
-
this.currentConnectionIndex++;
|
|
3070
|
-
this.candidates = null;
|
|
3071
|
-
this.visitedNodes = null;
|
|
3072
|
-
return;
|
|
3073
|
-
}
|
|
3074
|
-
const neighborNodes = this.getNeighboringNodes(currentCandidate.node);
|
|
3075
|
-
for (const neighborNode of neighborNodes) {
|
|
3076
|
-
if (this.visitedNodes?.has(neighborNode.capacityMeshNodeId)) {
|
|
3077
|
-
continue;
|
|
3078
|
-
}
|
|
3079
|
-
if (!this.doesNodeHaveCapacityForTrace(neighborNode)) {
|
|
3080
|
-
continue;
|
|
3081
|
-
}
|
|
3082
|
-
const connectionName = this.connectionsWithNodes[this.currentConnectionIndex].connection.name;
|
|
3083
|
-
if (neighborNode._containsObstacle && !this.canTravelThroughObstacle(neighborNode, connectionName)) {
|
|
3084
|
-
continue;
|
|
3085
|
-
}
|
|
3086
|
-
const g = this.computeG(currentCandidate, neighborNode, end);
|
|
3087
|
-
const h = this.computeH(currentCandidate, neighborNode, end);
|
|
3088
|
-
const f = g + h * this.GREEDY_MULTIPLIER;
|
|
3089
|
-
const newCandidate = {
|
|
3090
|
-
prevCandidate: currentCandidate,
|
|
3091
|
-
node: neighborNode,
|
|
3092
|
-
f,
|
|
3093
|
-
g,
|
|
3094
|
-
h
|
|
3095
|
-
};
|
|
3096
|
-
this.candidates.push(newCandidate);
|
|
3097
|
-
}
|
|
3098
|
-
this.visitedNodes.add(currentCandidate.node.capacityMeshNodeId);
|
|
3099
|
-
}
|
|
3100
|
-
visualize() {
|
|
3101
|
-
const graphics = {
|
|
3102
|
-
lines: [],
|
|
3103
|
-
points: [],
|
|
3104
|
-
rects: [],
|
|
3105
|
-
circles: []
|
|
3106
|
-
};
|
|
3107
|
-
if (this.connectionsWithNodes) {
|
|
3108
|
-
for (let i = 0; i < this.connectionsWithNodes.length; i++) {
|
|
3109
|
-
const conn = this.connectionsWithNodes[i];
|
|
3110
|
-
if (conn.path && conn.path.length > 0) {
|
|
3111
|
-
const pathPoints = conn.path.map(({ center: { x, y }, width }) => ({
|
|
3112
|
-
// slight offset to allow viewing overlapping paths
|
|
3113
|
-
x: x + (i % 10 + i % 19) * (0.01 * width),
|
|
3114
|
-
y: y + (i % 10 + i % 19) * (0.01 * width)
|
|
3115
|
-
}));
|
|
3116
|
-
graphics.lines.push({
|
|
3117
|
-
points: pathPoints,
|
|
3118
|
-
strokeColor: this.colorMap[conn.connection.name]
|
|
3119
|
-
});
|
|
3120
|
-
}
|
|
3121
|
-
}
|
|
3122
|
-
}
|
|
3123
|
-
for (const node of this.nodes) {
|
|
3124
|
-
graphics.rects.push({
|
|
3125
|
-
center: node.center,
|
|
3126
|
-
width: Math.max(node.width - 2, node.width * 0.8),
|
|
3127
|
-
height: Math.max(node.height - 2, node.height * 0.8),
|
|
3128
|
-
fill: node._containsObstacle ? "rgba(255,0,0,0.1)" : "rgba(0,0,0,0.1)",
|
|
3129
|
-
label: `${node.capacityMeshNodeId}
|
|
3130
|
-
${this.usedNodeCapacityMap.get(node.capacityMeshNodeId)}/${this.getTotalCapacity(node).toFixed(2)}
|
|
3131
|
-
${node.width.toFixed(2)}x${node.height.toFixed(2)}`
|
|
3132
|
-
});
|
|
3133
|
-
}
|
|
3134
|
-
if (this.connectionsWithNodes) {
|
|
3135
|
-
for (const conn of this.connectionsWithNodes) {
|
|
3136
|
-
if (conn.connection?.pointsToConnect) {
|
|
3137
|
-
for (const point of conn.connection.pointsToConnect) {
|
|
3138
|
-
graphics.points.push({
|
|
3139
|
-
x: point.x,
|
|
3140
|
-
y: point.y
|
|
3141
|
-
});
|
|
3142
|
-
}
|
|
3143
|
-
}
|
|
3118
|
+
};
|
|
3119
|
+
|
|
3120
|
+
// lib/utils/getConnectivityMapFromSimpleRouteJson.ts
|
|
3121
|
+
var getConnectivityMapFromSimpleRouteJson = (srj) => {
|
|
3122
|
+
const connMap = new ConnectivityMap({});
|
|
3123
|
+
for (const connection of srj.connections) {
|
|
3124
|
+
for (const point of connection.pointsToConnect) {
|
|
3125
|
+
if ("pcb_port_id" in point && point.pcb_port_id) {
|
|
3126
|
+
connMap.addConnections([[connection.name, point.pcb_port_id]]);
|
|
3144
3127
|
}
|
|
3145
3128
|
}
|
|
3146
|
-
const nextConnection = this.connectionsWithNodes[this.currentConnectionIndex];
|
|
3147
|
-
if (nextConnection) {
|
|
3148
|
-
const [start, end] = nextConnection.connection.pointsToConnect;
|
|
3149
|
-
graphics.lines.push({
|
|
3150
|
-
points: [
|
|
3151
|
-
{ x: start.x, y: start.y },
|
|
3152
|
-
{ x: end.x, y: end.y }
|
|
3153
|
-
],
|
|
3154
|
-
strokeColor: "red",
|
|
3155
|
-
strokeDash: "10 5"
|
|
3156
|
-
});
|
|
3157
|
-
}
|
|
3158
|
-
if (this.candidates) {
|
|
3159
|
-
const topCandidates = this.candidates.slice(0, 5);
|
|
3160
|
-
const connectionName = this.connectionsWithNodes[this.currentConnectionIndex].connection.name;
|
|
3161
|
-
topCandidates.forEach((candidate, index) => {
|
|
3162
|
-
const opacity = 0.5 * (1 - index / 5);
|
|
3163
|
-
const backtrackedPath = this.getBacktrackedPath(candidate);
|
|
3164
|
-
graphics.lines.push({
|
|
3165
|
-
points: backtrackedPath.map(({ center: { x, y } }) => ({ x, y })),
|
|
3166
|
-
strokeColor: safeTransparentize(
|
|
3167
|
-
this.colorMap[connectionName] ?? "red",
|
|
3168
|
-
1 - opacity
|
|
3169
|
-
)
|
|
3170
|
-
});
|
|
3171
|
-
});
|
|
3172
|
-
}
|
|
3173
|
-
return graphics;
|
|
3174
3129
|
}
|
|
3130
|
+
return connMap;
|
|
3175
3131
|
};
|
|
3176
3132
|
|
|
3177
3133
|
// lib/utils/getTunedTotalCapacity1.ts
|
|
@@ -3197,267 +3153,44 @@ var calculateOptimalCapacityDepth = (initialWidth, targetMinCapacity = 0.5, maxD
|
|
|
3197
3153
|
return Math.max(1, depth);
|
|
3198
3154
|
};
|
|
3199
3155
|
|
|
3200
|
-
// lib/solvers/
|
|
3201
|
-
var
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
/**
|
|
3208
|
-
* In the FlexibleNegativeCapacity mode, we allow negative capacity
|
|
3209
|
-
*/
|
|
3210
|
-
doesNodeHaveCapacityForTrace(node) {
|
|
3211
|
-
return true;
|
|
3212
|
-
}
|
|
3213
|
-
getTotalCapacity(node) {
|
|
3214
|
-
return getTunedTotalCapacity1(node, this.maxCapacityFactor);
|
|
3156
|
+
// lib/solvers/NetToPointPairsSolver/buildMinimumSpanningTree.ts
|
|
3157
|
+
var KDNode = class {
|
|
3158
|
+
point;
|
|
3159
|
+
left = null;
|
|
3160
|
+
right = null;
|
|
3161
|
+
constructor(point) {
|
|
3162
|
+
this.point = point;
|
|
3215
3163
|
}
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
const remainingCapacity = totalCapacity - usedCapacity;
|
|
3223
|
-
const dist = this.activeCandidateStraightLineDistance;
|
|
3224
|
-
if (remainingCapacity <= 0) {
|
|
3225
|
-
const penalty = (-remainingCapacity + 1) / totalCapacity * dist * (this.NEGATIVE_CAPACITY_PENALTY_FACTOR / 4);
|
|
3226
|
-
return penalty ** 2;
|
|
3164
|
+
};
|
|
3165
|
+
var KDTree = class {
|
|
3166
|
+
root = null;
|
|
3167
|
+
constructor(points) {
|
|
3168
|
+
if (points.length > 0) {
|
|
3169
|
+
this.root = this.buildTree(points, 0);
|
|
3227
3170
|
}
|
|
3228
|
-
return 1 / remainingCapacity * dist * this.REDUCED_CAPACITY_PENALTY_FACTOR / 8;
|
|
3229
|
-
}
|
|
3230
|
-
/**
|
|
3231
|
-
* We're rewarding travel into big nodes.
|
|
3232
|
-
*
|
|
3233
|
-
* To minimize shortest path, you'd want to comment this out.
|
|
3234
|
-
*/
|
|
3235
|
-
getDistanceBetweenNodes(A, B) {
|
|
3236
|
-
const dx = A.center.x - B.center.x;
|
|
3237
|
-
const dy = A.center.y - B.center.y;
|
|
3238
|
-
const szx = Math.max(A.width, B.width);
|
|
3239
|
-
const szy = Math.max(A.height, B.height);
|
|
3240
|
-
const dist = Math.sqrt(dx ** 2 + dy ** 2) / (szx * szy);
|
|
3241
|
-
return dist;
|
|
3242
|
-
}
|
|
3243
|
-
computeG(prevCandidate, node, endGoal) {
|
|
3244
|
-
return prevCandidate.g + this.getDistanceBetweenNodes(prevCandidate.node, node) + this.getNodeCapacityPenalty(node);
|
|
3245
3171
|
}
|
|
3246
|
-
|
|
3247
|
-
|
|
3172
|
+
buildTree(points, depth) {
|
|
3173
|
+
const axis = depth % 2 === 0 ? "x" : "y";
|
|
3174
|
+
points.sort((a, b) => a[axis] - b[axis]);
|
|
3175
|
+
const medianIndex = Math.floor(points.length / 2);
|
|
3176
|
+
const node = new KDNode(points[medianIndex]);
|
|
3177
|
+
if (medianIndex > 0) {
|
|
3178
|
+
node.left = this.buildTree(points.slice(0, medianIndex), depth + 1);
|
|
3179
|
+
}
|
|
3180
|
+
if (medianIndex < points.length - 1) {
|
|
3181
|
+
node.right = this.buildTree(points.slice(medianIndex + 1), depth + 1);
|
|
3182
|
+
}
|
|
3183
|
+
return node;
|
|
3248
3184
|
}
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
netMap;
|
|
3254
|
-
idToNetMap;
|
|
3255
|
-
constructor(netMap) {
|
|
3256
|
-
this.netMap = netMap;
|
|
3257
|
-
this.idToNetMap = {};
|
|
3258
|
-
for (const [netId, ids] of Object.entries(netMap)) {
|
|
3259
|
-
for (const id of ids) {
|
|
3260
|
-
this.idToNetMap[id] = netId;
|
|
3261
|
-
}
|
|
3185
|
+
// Find the nearest neighbor to a query point
|
|
3186
|
+
findNearestNeighbor(queryPoint) {
|
|
3187
|
+
if (!this.root) {
|
|
3188
|
+
throw new Error("Tree is empty");
|
|
3262
3189
|
}
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
for (const id of connection) {
|
|
3268
|
-
const existingNetId = this.idToNetMap[id];
|
|
3269
|
-
if (existingNetId) {
|
|
3270
|
-
existingNets.add(existingNetId);
|
|
3271
|
-
}
|
|
3272
|
-
}
|
|
3273
|
-
let targetNetId;
|
|
3274
|
-
if (existingNets.size === 0) {
|
|
3275
|
-
targetNetId = `connectivity_net${Object.keys(this.netMap).length}`;
|
|
3276
|
-
this.netMap[targetNetId] = [];
|
|
3277
|
-
} else if (existingNets.size === 1) {
|
|
3278
|
-
targetNetId = existingNets.values().next().value ?? `connectivity_net${Object.keys(this.netMap).length}`;
|
|
3279
|
-
} else {
|
|
3280
|
-
targetNetId = existingNets.values().next().value ?? `connectivity_net${Object.keys(this.netMap).length}`;
|
|
3281
|
-
for (const netId of existingNets) {
|
|
3282
|
-
if (netId !== targetNetId) {
|
|
3283
|
-
this.netMap[targetNetId].push(...this.netMap[netId]);
|
|
3284
|
-
this.netMap[netId] = this.netMap[targetNetId];
|
|
3285
|
-
for (const id of this.netMap[targetNetId]) {
|
|
3286
|
-
this.idToNetMap[id] = targetNetId;
|
|
3287
|
-
}
|
|
3288
|
-
}
|
|
3289
|
-
}
|
|
3290
|
-
}
|
|
3291
|
-
for (const id of connection) {
|
|
3292
|
-
if (!this.netMap[targetNetId].includes(id)) {
|
|
3293
|
-
this.netMap[targetNetId].push(id);
|
|
3294
|
-
}
|
|
3295
|
-
this.idToNetMap[id] = targetNetId;
|
|
3296
|
-
}
|
|
3297
|
-
}
|
|
3298
|
-
}
|
|
3299
|
-
getIdsConnectedToNet(netId) {
|
|
3300
|
-
return this.netMap[netId] || [];
|
|
3301
|
-
}
|
|
3302
|
-
getNetConnectedToId(id) {
|
|
3303
|
-
return this.idToNetMap[id];
|
|
3304
|
-
}
|
|
3305
|
-
areIdsConnected(id1, id2) {
|
|
3306
|
-
if (id1 === id2) return true;
|
|
3307
|
-
const netId1 = this.getNetConnectedToId(id1);
|
|
3308
|
-
if (!netId1) return false;
|
|
3309
|
-
const netId2 = this.getNetConnectedToId(id2);
|
|
3310
|
-
if (!netId2) return false;
|
|
3311
|
-
return netId1 === netId2 || netId2 === id1 || netId2 === id1;
|
|
3312
|
-
}
|
|
3313
|
-
areAllIdsConnected(ids) {
|
|
3314
|
-
const netId = this.getNetConnectedToId(ids[0]);
|
|
3315
|
-
for (const id of ids) {
|
|
3316
|
-
const nextNetId = this.getNetConnectedToId(id);
|
|
3317
|
-
if (nextNetId === void 0) {
|
|
3318
|
-
return false;
|
|
3319
|
-
}
|
|
3320
|
-
if (nextNetId !== netId) {
|
|
3321
|
-
return false;
|
|
3322
|
-
}
|
|
3323
|
-
}
|
|
3324
|
-
return true;
|
|
3325
|
-
}
|
|
3326
|
-
};
|
|
3327
|
-
|
|
3328
|
-
// lib/utils/getConnectivityMapFromSimpleRouteJson.ts
|
|
3329
|
-
var getConnectivityMapFromSimpleRouteJson = (srj) => {
|
|
3330
|
-
const connMap = new ConnectivityMap({});
|
|
3331
|
-
for (const connection of srj.connections) {
|
|
3332
|
-
for (const point of connection.pointsToConnect) {
|
|
3333
|
-
if ("pcb_port_id" in point && point.pcb_port_id) {
|
|
3334
|
-
connMap.addConnections([[connection.name, point.pcb_port_id]]);
|
|
3335
|
-
}
|
|
3336
|
-
}
|
|
3337
|
-
}
|
|
3338
|
-
return connMap;
|
|
3339
|
-
};
|
|
3340
|
-
|
|
3341
|
-
// lib/solvers/CapacityMeshSolver/CapacityNodeTargetMerger.ts
|
|
3342
|
-
var CapacityNodeTargetMerger = class extends BaseSolver {
|
|
3343
|
-
constructor(nodes, obstacles, connMap) {
|
|
3344
|
-
super();
|
|
3345
|
-
this.nodes = nodes;
|
|
3346
|
-
this.connMap = connMap;
|
|
3347
|
-
this.MAX_ITERATIONS = 1e5;
|
|
3348
|
-
this.unprocessedObstacles = [...obstacles];
|
|
3349
|
-
this.newNodes = [];
|
|
3350
|
-
this.removedNodeIds = /* @__PURE__ */ new Set();
|
|
3351
|
-
}
|
|
3352
|
-
unprocessedObstacles;
|
|
3353
|
-
newNodes;
|
|
3354
|
-
removedNodeIds;
|
|
3355
|
-
_step() {
|
|
3356
|
-
const obstacle = this.unprocessedObstacles.pop();
|
|
3357
|
-
if (!obstacle) {
|
|
3358
|
-
for (const node of this.nodes) {
|
|
3359
|
-
if (this.removedNodeIds.has(node.capacityMeshNodeId)) continue;
|
|
3360
|
-
this.newNodes.push(node);
|
|
3361
|
-
}
|
|
3362
|
-
this.solved = true;
|
|
3363
|
-
return;
|
|
3364
|
-
}
|
|
3365
|
-
const connectedNodes = this.nodes.filter((n) => {
|
|
3366
|
-
if (!n._targetConnectionName) return false;
|
|
3367
|
-
const implicitlyConnected = doRectsOverlap(n, obstacle);
|
|
3368
|
-
return implicitlyConnected;
|
|
3369
|
-
});
|
|
3370
|
-
if (connectedNodes.length === 0) return;
|
|
3371
|
-
const connectionName = connectedNodes[0]._targetConnectionName;
|
|
3372
|
-
const bounds = {
|
|
3373
|
-
minX: Infinity,
|
|
3374
|
-
minY: Infinity,
|
|
3375
|
-
maxX: -Infinity,
|
|
3376
|
-
maxY: -Infinity
|
|
3377
|
-
};
|
|
3378
|
-
for (const node of connectedNodes) {
|
|
3379
|
-
bounds.minX = Math.min(bounds.minX, node.center.x - node.width / 2);
|
|
3380
|
-
bounds.minY = Math.min(bounds.minY, node.center.y - node.height / 2);
|
|
3381
|
-
bounds.maxX = Math.max(bounds.maxX, node.center.x + node.width / 2);
|
|
3382
|
-
bounds.maxY = Math.max(bounds.maxY, node.center.y + node.height / 2);
|
|
3383
|
-
}
|
|
3384
|
-
const newNode = {
|
|
3385
|
-
capacityMeshNodeId: connectedNodes[0].capacityMeshNodeId,
|
|
3386
|
-
center: {
|
|
3387
|
-
x: (bounds.minX + bounds.maxX) / 2,
|
|
3388
|
-
y: (bounds.minY + bounds.maxY) / 2
|
|
3389
|
-
},
|
|
3390
|
-
width: bounds.maxX - bounds.minX,
|
|
3391
|
-
height: bounds.maxY - bounds.minY,
|
|
3392
|
-
layer: connectedNodes[0].layer,
|
|
3393
|
-
availableZ: connectedNodes[0].availableZ,
|
|
3394
|
-
_completelyInsideObstacle: false,
|
|
3395
|
-
_containsObstacle: true,
|
|
3396
|
-
_containsTarget: true,
|
|
3397
|
-
_targetConnectionName: connectionName,
|
|
3398
|
-
_depth: connectedNodes[0]._depth,
|
|
3399
|
-
_parent: connectedNodes[0]._parent
|
|
3400
|
-
};
|
|
3401
|
-
this.newNodes.push(newNode);
|
|
3402
|
-
for (const node of connectedNodes) {
|
|
3403
|
-
this.removedNodeIds.add(node.capacityMeshNodeId);
|
|
3404
|
-
}
|
|
3405
|
-
}
|
|
3406
|
-
visualize() {
|
|
3407
|
-
const graphics = {
|
|
3408
|
-
rects: []
|
|
3409
|
-
};
|
|
3410
|
-
for (const node of this.newNodes) {
|
|
3411
|
-
graphics.rects.push({
|
|
3412
|
-
center: node.center,
|
|
3413
|
-
width: Math.max(node.width - 2, node.width * 0.8),
|
|
3414
|
-
height: Math.max(node.height - 2, node.height * 0.8),
|
|
3415
|
-
fill: node._containsObstacle ? "rgba(255,0,0,0.1)" : "rgba(0,0,0,0.1)",
|
|
3416
|
-
label: node.capacityMeshNodeId
|
|
3417
|
-
});
|
|
3418
|
-
}
|
|
3419
|
-
return graphics;
|
|
3420
|
-
}
|
|
3421
|
-
};
|
|
3422
|
-
|
|
3423
|
-
// lib/solvers/NetToPointPairsSolver/buildMinimumSpanningTree.ts
|
|
3424
|
-
var KDNode = class {
|
|
3425
|
-
point;
|
|
3426
|
-
left = null;
|
|
3427
|
-
right = null;
|
|
3428
|
-
constructor(point) {
|
|
3429
|
-
this.point = point;
|
|
3430
|
-
}
|
|
3431
|
-
};
|
|
3432
|
-
var KDTree = class {
|
|
3433
|
-
root = null;
|
|
3434
|
-
constructor(points) {
|
|
3435
|
-
if (points.length > 0) {
|
|
3436
|
-
this.root = this.buildTree(points, 0);
|
|
3437
|
-
}
|
|
3438
|
-
}
|
|
3439
|
-
buildTree(points, depth) {
|
|
3440
|
-
const axis = depth % 2 === 0 ? "x" : "y";
|
|
3441
|
-
points.sort((a, b) => a[axis] - b[axis]);
|
|
3442
|
-
const medianIndex = Math.floor(points.length / 2);
|
|
3443
|
-
const node = new KDNode(points[medianIndex]);
|
|
3444
|
-
if (medianIndex > 0) {
|
|
3445
|
-
node.left = this.buildTree(points.slice(0, medianIndex), depth + 1);
|
|
3446
|
-
}
|
|
3447
|
-
if (medianIndex < points.length - 1) {
|
|
3448
|
-
node.right = this.buildTree(points.slice(medianIndex + 1), depth + 1);
|
|
3449
|
-
}
|
|
3450
|
-
return node;
|
|
3451
|
-
}
|
|
3452
|
-
// Find the nearest neighbor to a query point
|
|
3453
|
-
findNearestNeighbor(queryPoint) {
|
|
3454
|
-
if (!this.root) {
|
|
3455
|
-
throw new Error("Tree is empty");
|
|
3456
|
-
}
|
|
3457
|
-
const best = this.root.point;
|
|
3458
|
-
const bestDistance = this.distance(queryPoint, best);
|
|
3459
|
-
this.nearestNeighborSearch(this.root, queryPoint, 0, best, bestDistance);
|
|
3460
|
-
return best;
|
|
3190
|
+
const best = this.root.point;
|
|
3191
|
+
const bestDistance = this.distance(queryPoint, best);
|
|
3192
|
+
this.nearestNeighborSearch(this.root, queryPoint, 0, best, bestDistance);
|
|
3193
|
+
return best;
|
|
3461
3194
|
}
|
|
3462
3195
|
nearestNeighborSearch(node, queryPoint, depth, best, bestDistance) {
|
|
3463
3196
|
if (!node) {
|
|
@@ -4347,6 +4080,7 @@ var createSegmentPointMap = (dedupedSegments, segmentIdToNodeIds) => {
|
|
|
4347
4080
|
var UnravelSectionSolver = class extends BaseSolver {
|
|
4348
4081
|
nodeMap;
|
|
4349
4082
|
dedupedSegments;
|
|
4083
|
+
dedupedSegmentMap;
|
|
4350
4084
|
MUTABLE_HOPS = 1;
|
|
4351
4085
|
unravelSection;
|
|
4352
4086
|
candidates = [];
|
|
@@ -4366,6 +4100,14 @@ var UnravelSectionSolver = class extends BaseSolver {
|
|
|
4366
4100
|
this.MUTABLE_HOPS = params.MUTABLE_HOPS ?? this.MUTABLE_HOPS;
|
|
4367
4101
|
this.nodeMap = params.nodeMap;
|
|
4368
4102
|
this.dedupedSegments = params.dedupedSegments;
|
|
4103
|
+
if (params.dedupedSegmentMap) {
|
|
4104
|
+
this.dedupedSegmentMap = params.dedupedSegmentMap;
|
|
4105
|
+
} else {
|
|
4106
|
+
this.dedupedSegmentMap = /* @__PURE__ */ new Map();
|
|
4107
|
+
for (const segment of this.dedupedSegments) {
|
|
4108
|
+
this.dedupedSegmentMap.set(segment.nodePortSegmentId, segment);
|
|
4109
|
+
}
|
|
4110
|
+
}
|
|
4369
4111
|
this.nodeIdToSegmentIds = params.nodeIdToSegmentIds;
|
|
4370
4112
|
this.segmentIdToNodeIds = params.segmentIdToNodeIds;
|
|
4371
4113
|
this.rootNodeId = params.rootNodeId;
|
|
@@ -4524,14 +4266,20 @@ var UnravelSectionSolver = class extends BaseSolver {
|
|
|
4524
4266
|
const [APointId, BPointId] = issue.segmentPoints;
|
|
4525
4267
|
const pointA = this.getPointInCandidate(candidate, APointId);
|
|
4526
4268
|
const pointB = this.getPointInCandidate(candidate, BPointId);
|
|
4527
|
-
|
|
4269
|
+
const aAvailableZ = this.dedupedSegmentMap.get(
|
|
4270
|
+
pointA.segmentId
|
|
4271
|
+
).availableZ;
|
|
4272
|
+
const bAvailableZ = this.dedupedSegmentMap.get(
|
|
4273
|
+
pointB.segmentId
|
|
4274
|
+
).availableZ;
|
|
4275
|
+
if (this.unravelSection.mutableSegmentIds.has(pointA.segmentId) && aAvailableZ.includes(pointB.z)) {
|
|
4528
4276
|
operations.push({
|
|
4529
4277
|
type: "change_layer",
|
|
4530
4278
|
newZ: pointB.z,
|
|
4531
4279
|
segmentPointIds: [APointId]
|
|
4532
4280
|
});
|
|
4533
4281
|
}
|
|
4534
|
-
if (this.unravelSection.mutableSegmentIds.has(pointB.segmentId)) {
|
|
4282
|
+
if (this.unravelSection.mutableSegmentIds.has(pointB.segmentId) && bAvailableZ.includes(pointA.z)) {
|
|
4535
4283
|
operations.push({
|
|
4536
4284
|
type: "change_layer",
|
|
4537
4285
|
newZ: pointA.z,
|
|
@@ -4986,6 +4734,7 @@ var calculateNodeProbabilityOfFailure = (node, numSameLayerCrossings, numEntryEx
|
|
|
4986
4734
|
// lib/solvers/UnravelSolver/UnravelMultiSectionSolver.ts
|
|
4987
4735
|
var UnravelMultiSectionSolver = class extends BaseSolver {
|
|
4988
4736
|
nodeMap;
|
|
4737
|
+
dedupedSegmentMap;
|
|
4989
4738
|
dedupedSegments;
|
|
4990
4739
|
nodeIdToSegmentIds;
|
|
4991
4740
|
segmentIdToNodeIds;
|
|
@@ -5009,6 +4758,10 @@ var UnravelMultiSectionSolver = class extends BaseSolver {
|
|
|
5009
4758
|
super();
|
|
5010
4759
|
this.MAX_ITERATIONS = 1e5;
|
|
5011
4760
|
this.dedupedSegments = getDedupedSegments(assignedSegments);
|
|
4761
|
+
this.dedupedSegmentMap = /* @__PURE__ */ new Map();
|
|
4762
|
+
for (const segment of this.dedupedSegments) {
|
|
4763
|
+
this.dedupedSegmentMap.set(segment.nodePortSegmentId, segment);
|
|
4764
|
+
}
|
|
5012
4765
|
this.nodeMap = /* @__PURE__ */ new Map();
|
|
5013
4766
|
for (const node of nodes) {
|
|
5014
4767
|
this.nodeMap.set(node.capacityMeshNodeId, node);
|
|
@@ -5087,6 +4840,7 @@ var UnravelMultiSectionSolver = class extends BaseSolver {
|
|
|
5087
4840
|
);
|
|
5088
4841
|
this.activeSolver = new UnravelSectionSolver({
|
|
5089
4842
|
dedupedSegments: this.dedupedSegments,
|
|
4843
|
+
dedupedSegmentMap: this.dedupedSegmentMap,
|
|
5090
4844
|
nodeMap: this.nodeMap,
|
|
5091
4845
|
nodeIdToSegmentIds: this.nodeIdToSegmentIds,
|
|
5092
4846
|
segmentIdToNodeIds: this.segmentIdToNodeIds,
|
|
@@ -5113,148 +4867,963 @@ var UnravelMultiSectionSolver = class extends BaseSolver {
|
|
|
5113
4867
|
segmentPoint.z = pointModification.z ?? segmentPoint.z;
|
|
5114
4868
|
}
|
|
5115
4869
|
}
|
|
5116
|
-
for (const nodeId of this.activeSolver.unravelSection.allNodeIds) {
|
|
5117
|
-
this.nodePfMap.set(
|
|
5118
|
-
nodeId,
|
|
5119
|
-
this.computeNodePf(this.nodeMap.get(nodeId))
|
|
5120
|
-
);
|
|
4870
|
+
for (const nodeId of this.activeSolver.unravelSection.allNodeIds) {
|
|
4871
|
+
this.nodePfMap.set(
|
|
4872
|
+
nodeId,
|
|
4873
|
+
this.computeNodePf(this.nodeMap.get(nodeId))
|
|
4874
|
+
);
|
|
4875
|
+
}
|
|
4876
|
+
this.activeSolver = null;
|
|
4877
|
+
}
|
|
4878
|
+
}
|
|
4879
|
+
visualize() {
|
|
4880
|
+
if (this.activeSolver) {
|
|
4881
|
+
return this.activeSolver.visualize();
|
|
4882
|
+
}
|
|
4883
|
+
const graphics = {
|
|
4884
|
+
lines: [],
|
|
4885
|
+
points: [],
|
|
4886
|
+
rects: [],
|
|
4887
|
+
circles: [],
|
|
4888
|
+
coordinateSystem: "cartesian",
|
|
4889
|
+
title: "Unravel Multi Section Solver"
|
|
4890
|
+
};
|
|
4891
|
+
for (const [nodeId, node] of this.nodeMap.entries()) {
|
|
4892
|
+
const probabilityOfFailure = this.nodePfMap.get(nodeId) || 0;
|
|
4893
|
+
const pf = Math.min(probabilityOfFailure, 1);
|
|
4894
|
+
const red = Math.floor(255 * pf);
|
|
4895
|
+
const green = Math.floor(255 * (1 - pf));
|
|
4896
|
+
const color = `rgb(${red}, ${green}, 0)`;
|
|
4897
|
+
if ((this.attemptsToFixNode.get(nodeId) ?? 0) === 0 && pf === 0) {
|
|
4898
|
+
continue;
|
|
4899
|
+
}
|
|
4900
|
+
graphics.rects.push({
|
|
4901
|
+
center: node.center,
|
|
4902
|
+
label: [
|
|
4903
|
+
nodeId,
|
|
4904
|
+
`${node.width.toFixed(2)}x${node.height.toFixed(2)}`,
|
|
4905
|
+
`Pf: ${probabilityOfFailure.toFixed(3)}`
|
|
4906
|
+
].join("\n"),
|
|
4907
|
+
color,
|
|
4908
|
+
width: node.width / 8,
|
|
4909
|
+
height: node.height / 8
|
|
4910
|
+
});
|
|
4911
|
+
}
|
|
4912
|
+
for (const segmentPoint of this.segmentPointMap.values()) {
|
|
4913
|
+
const segment = this.dedupedSegmentMap.get(segmentPoint.segmentId);
|
|
4914
|
+
graphics.points.push({
|
|
4915
|
+
x: segmentPoint.x,
|
|
4916
|
+
y: segmentPoint.y,
|
|
4917
|
+
label: [
|
|
4918
|
+
segmentPoint.segmentPointId,
|
|
4919
|
+
segmentPoint.segmentId,
|
|
4920
|
+
`z: ${segmentPoint.z}`,
|
|
4921
|
+
`segment.availableZ: ${segment?.availableZ.join(",")}`
|
|
4922
|
+
].join("\n"),
|
|
4923
|
+
color: this.colorMap[segmentPoint.connectionName] || "#000"
|
|
4924
|
+
});
|
|
4925
|
+
}
|
|
4926
|
+
const pointsBySegment = /* @__PURE__ */ new Map();
|
|
4927
|
+
for (const point of this.segmentPointMap.values()) {
|
|
4928
|
+
if (!pointsBySegment.has(point.segmentId)) {
|
|
4929
|
+
pointsBySegment.set(point.segmentId, []);
|
|
4930
|
+
}
|
|
4931
|
+
pointsBySegment.get(point.segmentId).push(point);
|
|
4932
|
+
}
|
|
4933
|
+
for (const [segmentId, points] of pointsBySegment.entries()) {
|
|
4934
|
+
if (points.length < 2) continue;
|
|
4935
|
+
const sortedPoints = [...points].sort(
|
|
4936
|
+
(a, b) => a.x !== b.x ? a.x - b.x : a.y - b.y
|
|
4937
|
+
);
|
|
4938
|
+
for (let i = 0; i < sortedPoints.length - 1; i++) {
|
|
4939
|
+
graphics.lines.push({
|
|
4940
|
+
points: [
|
|
4941
|
+
{ x: sortedPoints[i].x, y: sortedPoints[i].y },
|
|
4942
|
+
{ x: sortedPoints[i + 1].x, y: sortedPoints[i + 1].y }
|
|
4943
|
+
],
|
|
4944
|
+
strokeColor: this.colorMap[segmentId] || "#000"
|
|
4945
|
+
});
|
|
4946
|
+
}
|
|
4947
|
+
}
|
|
4948
|
+
const processedConnections = /* @__PURE__ */ new Set();
|
|
4949
|
+
const allPoints = Array.from(this.segmentPointMap.values());
|
|
4950
|
+
for (let i = 0; i < allPoints.length; i++) {
|
|
4951
|
+
const point1 = allPoints[i];
|
|
4952
|
+
for (let j = i + 1; j < allPoints.length; j++) {
|
|
4953
|
+
const point2 = allPoints[j];
|
|
4954
|
+
if (point1.connectionName !== point2.connectionName || point1.segmentId === point2.segmentId) {
|
|
4955
|
+
continue;
|
|
4956
|
+
}
|
|
4957
|
+
const hasSharedNode = point1.capacityMeshNodeIds.some(
|
|
4958
|
+
(nodeId) => point2.capacityMeshNodeIds.includes(nodeId)
|
|
4959
|
+
);
|
|
4960
|
+
if (hasSharedNode) {
|
|
4961
|
+
const connectionKey = `${point1.segmentPointId}-${point2.segmentPointId}`;
|
|
4962
|
+
if (processedConnections.has(connectionKey)) continue;
|
|
4963
|
+
processedConnections.add(connectionKey);
|
|
4964
|
+
const sameLayer = point1.z === point2.z;
|
|
4965
|
+
const layer = point1.z;
|
|
4966
|
+
let strokeDash;
|
|
4967
|
+
if (sameLayer) {
|
|
4968
|
+
strokeDash = layer === 0 ? void 0 : "10 5";
|
|
4969
|
+
} else {
|
|
4970
|
+
strokeDash = "3 3 10";
|
|
4971
|
+
}
|
|
4972
|
+
graphics.lines.push({
|
|
4973
|
+
points: [
|
|
4974
|
+
{ x: point1.x, y: point1.y },
|
|
4975
|
+
{ x: point2.x, y: point2.y }
|
|
4976
|
+
],
|
|
4977
|
+
strokeDash,
|
|
4978
|
+
strokeColor: this.colorMap[point1.connectionName] || "#666"
|
|
4979
|
+
});
|
|
4980
|
+
}
|
|
4981
|
+
}
|
|
4982
|
+
}
|
|
4983
|
+
return graphics;
|
|
4984
|
+
}
|
|
4985
|
+
getNodesWithPortPoints() {
|
|
4986
|
+
if (!this.solved) {
|
|
4987
|
+
throw new Error(
|
|
4988
|
+
"CapacitySegmentToPointSolver not solved, can't give port points yet"
|
|
4989
|
+
);
|
|
4990
|
+
}
|
|
4991
|
+
const nodeWithPortPointsMap = /* @__PURE__ */ new Map();
|
|
4992
|
+
for (const segment of this.dedupedSegments) {
|
|
4993
|
+
const segId = segment.nodePortSegmentId;
|
|
4994
|
+
for (const nodeId of this.segmentIdToNodeIds.get(segId)) {
|
|
4995
|
+
const node = this.nodeMap.get(nodeId);
|
|
4996
|
+
if (!nodeWithPortPointsMap.has(nodeId)) {
|
|
4997
|
+
nodeWithPortPointsMap.set(nodeId, {
|
|
4998
|
+
capacityMeshNodeId: nodeId,
|
|
4999
|
+
portPoints: [],
|
|
5000
|
+
center: node.center,
|
|
5001
|
+
width: node.width,
|
|
5002
|
+
height: node.height
|
|
5003
|
+
});
|
|
5004
|
+
}
|
|
5005
|
+
}
|
|
5006
|
+
}
|
|
5007
|
+
for (const segmentPoint of this.segmentPointMap.values()) {
|
|
5008
|
+
for (const nodeId of segmentPoint.capacityMeshNodeIds) {
|
|
5009
|
+
const nodeWithPortPoints = nodeWithPortPointsMap.get(nodeId);
|
|
5010
|
+
if (nodeWithPortPoints) {
|
|
5011
|
+
nodeWithPortPoints.portPoints.push({
|
|
5012
|
+
x: segmentPoint.x,
|
|
5013
|
+
y: segmentPoint.y,
|
|
5014
|
+
z: segmentPoint.z,
|
|
5015
|
+
connectionName: segmentPoint.connectionName
|
|
5016
|
+
});
|
|
5017
|
+
}
|
|
5018
|
+
}
|
|
5019
|
+
}
|
|
5020
|
+
return Array.from(nodeWithPortPointsMap.values());
|
|
5021
|
+
}
|
|
5022
|
+
};
|
|
5023
|
+
|
|
5024
|
+
// lib/utils/createRectFromCapacityNode.ts
|
|
5025
|
+
var createRectFromCapacityNode = (node, opts = {}) => {
|
|
5026
|
+
const lowestZ = Math.min(...node.availableZ);
|
|
5027
|
+
return {
|
|
5028
|
+
center: !opts.rectMargin ? {
|
|
5029
|
+
x: node.center.x + lowestZ * node.width * 0.05,
|
|
5030
|
+
y: node.center.y - lowestZ * node.width * 0.05
|
|
5031
|
+
} : node.center,
|
|
5032
|
+
width: opts.rectMargin ? node.width - opts.rectMargin * 2 : Math.max(node.width - 0.5, node.width * 0.8),
|
|
5033
|
+
height: opts.rectMargin ? node.height - opts.rectMargin * 2 : Math.max(node.height - 0.5, node.height * 0.8),
|
|
5034
|
+
fill: node._containsObstacle ? "rgba(255,0,0,0.1)" : {
|
|
5035
|
+
"0,1": "rgba(0,0,0,0.1)",
|
|
5036
|
+
"0": "rgba(0,200,200, 0.1)",
|
|
5037
|
+
"1": "rgba(0,0,200, 0.1)"
|
|
5038
|
+
}[node.availableZ.join(",")] ?? "rgba(0,200,200,0.1)",
|
|
5039
|
+
label: [
|
|
5040
|
+
node.capacityMeshNodeId,
|
|
5041
|
+
`availableZ: ${node.availableZ.join(",")}`,
|
|
5042
|
+
`${node._containsTarget ? "containsTarget" : ""}`,
|
|
5043
|
+
`${node._containsObstacle ? "containsObstacle" : ""}`
|
|
5044
|
+
].filter(Boolean).join("\n")
|
|
5045
|
+
};
|
|
5046
|
+
};
|
|
5047
|
+
|
|
5048
|
+
// lib/solvers/CapacityPathingSolver/CapacityPathingSolver.ts
|
|
5049
|
+
var CapacityPathingSolver = class extends BaseSolver {
|
|
5050
|
+
connectionsWithNodes;
|
|
5051
|
+
usedNodeCapacityMap;
|
|
5052
|
+
simpleRouteJson;
|
|
5053
|
+
nodes;
|
|
5054
|
+
edges;
|
|
5055
|
+
GREEDY_MULTIPLIER = 1.1;
|
|
5056
|
+
nodeMap;
|
|
5057
|
+
nodeEdgeMap;
|
|
5058
|
+
connectionNameToGoalNodeIds;
|
|
5059
|
+
colorMap;
|
|
5060
|
+
maxDepthOfNodes;
|
|
5061
|
+
activeCandidateStraightLineDistance;
|
|
5062
|
+
debug_lastNodeCostMap;
|
|
5063
|
+
hyperParameters;
|
|
5064
|
+
constructor({
|
|
5065
|
+
simpleRouteJson,
|
|
5066
|
+
nodes,
|
|
5067
|
+
edges,
|
|
5068
|
+
colorMap,
|
|
5069
|
+
MAX_ITERATIONS = 1e6,
|
|
5070
|
+
hyperParameters = {}
|
|
5071
|
+
}) {
|
|
5072
|
+
super();
|
|
5073
|
+
this.MAX_ITERATIONS = MAX_ITERATIONS;
|
|
5074
|
+
this.simpleRouteJson = simpleRouteJson;
|
|
5075
|
+
this.nodes = nodes;
|
|
5076
|
+
this.edges = edges;
|
|
5077
|
+
this.colorMap = colorMap ?? {};
|
|
5078
|
+
const { connectionsWithNodes, connectionNameToGoalNodeIds } = this.getConnectionsWithNodes();
|
|
5079
|
+
this.connectionsWithNodes = connectionsWithNodes;
|
|
5080
|
+
this.connectionNameToGoalNodeIds = connectionNameToGoalNodeIds;
|
|
5081
|
+
this.hyperParameters = hyperParameters;
|
|
5082
|
+
this.usedNodeCapacityMap = new Map(
|
|
5083
|
+
this.nodes.map((node) => [node.capacityMeshNodeId, 0])
|
|
5084
|
+
);
|
|
5085
|
+
this.nodeMap = new Map(
|
|
5086
|
+
this.nodes.map((node) => [node.capacityMeshNodeId, node])
|
|
5087
|
+
);
|
|
5088
|
+
this.nodeEdgeMap = getNodeEdgeMap(this.edges);
|
|
5089
|
+
this.maxDepthOfNodes = Math.max(
|
|
5090
|
+
...this.nodes.map((node) => node._depth ?? 0)
|
|
5091
|
+
);
|
|
5092
|
+
this.debug_lastNodeCostMap = /* @__PURE__ */ new Map();
|
|
5093
|
+
}
|
|
5094
|
+
getTotalCapacity(node) {
|
|
5095
|
+
const depth = node._depth ?? 0;
|
|
5096
|
+
return (this.maxDepthOfNodes - depth + 1) ** 2;
|
|
5097
|
+
}
|
|
5098
|
+
getConnectionsWithNodes() {
|
|
5099
|
+
const connectionsWithNodes = [];
|
|
5100
|
+
const nodesWithTargets = this.nodes.filter((node) => node._containsTarget);
|
|
5101
|
+
const connectionNameToGoalNodeIds = /* @__PURE__ */ new Map();
|
|
5102
|
+
for (const connection of this.simpleRouteJson.connections) {
|
|
5103
|
+
const nodesForConnection = [];
|
|
5104
|
+
for (const point of connection.pointsToConnect) {
|
|
5105
|
+
let closestNode = this.nodes[0];
|
|
5106
|
+
let minDistance = Number.MAX_VALUE;
|
|
5107
|
+
for (const node of nodesWithTargets) {
|
|
5108
|
+
const distance3 = Math.sqrt(
|
|
5109
|
+
(node.center.x - point.x) ** 2 + (node.center.y - point.y) ** 2
|
|
5110
|
+
);
|
|
5111
|
+
if (distance3 < minDistance) {
|
|
5112
|
+
minDistance = distance3;
|
|
5113
|
+
closestNode = node;
|
|
5114
|
+
}
|
|
5115
|
+
}
|
|
5116
|
+
nodesForConnection.push(closestNode);
|
|
5117
|
+
}
|
|
5118
|
+
if (nodesForConnection.length < 2) {
|
|
5119
|
+
throw new Error(
|
|
5120
|
+
`Not enough nodes for connection "${connection.name}", only ${nodesForConnection.length} found`
|
|
5121
|
+
);
|
|
5122
|
+
}
|
|
5123
|
+
connectionNameToGoalNodeIds.set(
|
|
5124
|
+
connection.name,
|
|
5125
|
+
nodesForConnection.map((n) => n.capacityMeshNodeId)
|
|
5126
|
+
);
|
|
5127
|
+
connectionsWithNodes.push({
|
|
5128
|
+
connection,
|
|
5129
|
+
nodes: nodesForConnection,
|
|
5130
|
+
pathFound: false
|
|
5131
|
+
});
|
|
5132
|
+
}
|
|
5133
|
+
return { connectionsWithNodes, connectionNameToGoalNodeIds };
|
|
5134
|
+
}
|
|
5135
|
+
currentConnectionIndex = 0;
|
|
5136
|
+
candidates;
|
|
5137
|
+
visitedNodes;
|
|
5138
|
+
computeG(prevCandidate, node, endGoal) {
|
|
5139
|
+
return prevCandidate.g + this.getDistanceBetweenNodes(prevCandidate.node, node);
|
|
5140
|
+
}
|
|
5141
|
+
computeH(prevCandidate, node, endGoal) {
|
|
5142
|
+
return this.getDistanceBetweenNodes(node, endGoal);
|
|
5143
|
+
}
|
|
5144
|
+
getBacktrackedPath(candidate) {
|
|
5145
|
+
const path = [];
|
|
5146
|
+
let currentCandidate = candidate;
|
|
5147
|
+
while (currentCandidate) {
|
|
5148
|
+
path.push(currentCandidate.node);
|
|
5149
|
+
currentCandidate = currentCandidate.prevCandidate;
|
|
5150
|
+
}
|
|
5151
|
+
return path;
|
|
5152
|
+
}
|
|
5153
|
+
getNeighboringNodes(node) {
|
|
5154
|
+
return this.nodeEdgeMap.get(node.capacityMeshNodeId).flatMap(
|
|
5155
|
+
(edge) => edge.nodeIds.filter((n) => n !== node.capacityMeshNodeId)
|
|
5156
|
+
).map((n) => this.nodeMap.get(n));
|
|
5157
|
+
}
|
|
5158
|
+
getCapacityPaths() {
|
|
5159
|
+
const capacityPaths = [];
|
|
5160
|
+
for (const connection of this.connectionsWithNodes) {
|
|
5161
|
+
const path = connection.path;
|
|
5162
|
+
if (path) {
|
|
5163
|
+
capacityPaths.push({
|
|
5164
|
+
capacityPathId: connection.connection.name,
|
|
5165
|
+
connectionName: connection.connection.name,
|
|
5166
|
+
nodeIds: path.map((node) => node.capacityMeshNodeId)
|
|
5167
|
+
});
|
|
5168
|
+
}
|
|
5169
|
+
}
|
|
5170
|
+
return capacityPaths;
|
|
5171
|
+
}
|
|
5172
|
+
doesNodeHaveCapacityForTrace(node) {
|
|
5173
|
+
const usedCapacity = this.usedNodeCapacityMap.get(node.capacityMeshNodeId) ?? 0;
|
|
5174
|
+
const totalCapacity = this.getTotalCapacity(node);
|
|
5175
|
+
if (node.availableZ.length === 1 && !node._containsTarget && usedCapacity > 0)
|
|
5176
|
+
return false;
|
|
5177
|
+
return usedCapacity < totalCapacity;
|
|
5178
|
+
}
|
|
5179
|
+
canTravelThroughObstacle(node, connectionName) {
|
|
5180
|
+
const goalNodeIds = this.connectionNameToGoalNodeIds.get(connectionName);
|
|
5181
|
+
return goalNodeIds?.includes(node.capacityMeshNodeId) ?? false;
|
|
5182
|
+
}
|
|
5183
|
+
getDistanceBetweenNodes(A, B) {
|
|
5184
|
+
return Math.sqrt(
|
|
5185
|
+
(A.center.x - B.center.x) ** 2 + (A.center.y - B.center.y) ** 2
|
|
5186
|
+
);
|
|
5187
|
+
}
|
|
5188
|
+
reduceCapacityAlongPath(nextConnection) {
|
|
5189
|
+
for (const node of nextConnection.path ?? []) {
|
|
5190
|
+
this.usedNodeCapacityMap.set(
|
|
5191
|
+
node.capacityMeshNodeId,
|
|
5192
|
+
this.usedNodeCapacityMap.get(node.capacityMeshNodeId) + 1
|
|
5193
|
+
);
|
|
5194
|
+
}
|
|
5195
|
+
}
|
|
5196
|
+
isConnectedToEndGoal(node, endGoal) {
|
|
5197
|
+
return this.nodeEdgeMap.get(node.capacityMeshNodeId).some((edge) => edge.nodeIds.includes(endGoal.capacityMeshNodeId));
|
|
5198
|
+
}
|
|
5199
|
+
_step() {
|
|
5200
|
+
const nextConnection = this.connectionsWithNodes[this.currentConnectionIndex];
|
|
5201
|
+
if (!nextConnection) {
|
|
5202
|
+
this.solved = true;
|
|
5203
|
+
return;
|
|
5204
|
+
}
|
|
5205
|
+
const [start, end] = nextConnection.nodes;
|
|
5206
|
+
if (!this.candidates) {
|
|
5207
|
+
this.candidates = [{ prevCandidate: null, node: start, f: 0, g: 0, h: 0 }];
|
|
5208
|
+
this.debug_lastNodeCostMap = /* @__PURE__ */ new Map();
|
|
5209
|
+
this.visitedNodes = /* @__PURE__ */ new Set([start.capacityMeshNodeId]);
|
|
5210
|
+
this.activeCandidateStraightLineDistance = distance(
|
|
5211
|
+
start.center,
|
|
5212
|
+
end.center
|
|
5213
|
+
);
|
|
5214
|
+
}
|
|
5215
|
+
this.candidates.sort((a, b) => a.f - b.f);
|
|
5216
|
+
const currentCandidate = this.candidates.shift();
|
|
5217
|
+
if (!currentCandidate) {
|
|
5218
|
+
console.error(
|
|
5219
|
+
`Ran out of candidates on connection ${nextConnection.connection.name}`
|
|
5220
|
+
);
|
|
5221
|
+
this.currentConnectionIndex++;
|
|
5222
|
+
this.candidates = null;
|
|
5223
|
+
this.visitedNodes = null;
|
|
5224
|
+
return;
|
|
5225
|
+
}
|
|
5226
|
+
if (this.isConnectedToEndGoal(currentCandidate.node, end)) {
|
|
5227
|
+
nextConnection.path = this.getBacktrackedPath({
|
|
5228
|
+
prevCandidate: currentCandidate,
|
|
5229
|
+
node: end,
|
|
5230
|
+
f: 0,
|
|
5231
|
+
g: 0,
|
|
5232
|
+
h: 0
|
|
5233
|
+
});
|
|
5234
|
+
this.reduceCapacityAlongPath(nextConnection);
|
|
5235
|
+
this.currentConnectionIndex++;
|
|
5236
|
+
this.candidates = null;
|
|
5237
|
+
this.visitedNodes = null;
|
|
5238
|
+
return;
|
|
5239
|
+
}
|
|
5240
|
+
const neighborNodes = this.getNeighboringNodes(currentCandidate.node);
|
|
5241
|
+
for (const neighborNode of neighborNodes) {
|
|
5242
|
+
if (this.visitedNodes?.has(neighborNode.capacityMeshNodeId)) {
|
|
5243
|
+
continue;
|
|
5244
|
+
}
|
|
5245
|
+
if (!this.doesNodeHaveCapacityForTrace(neighborNode)) {
|
|
5246
|
+
continue;
|
|
5247
|
+
}
|
|
5248
|
+
const connectionName = this.connectionsWithNodes[this.currentConnectionIndex].connection.name;
|
|
5249
|
+
if (neighborNode._containsObstacle && !this.canTravelThroughObstacle(neighborNode, connectionName)) {
|
|
5250
|
+
continue;
|
|
5251
|
+
}
|
|
5252
|
+
const g = this.computeG(currentCandidate, neighborNode, end);
|
|
5253
|
+
const h = this.computeH(currentCandidate, neighborNode, end);
|
|
5254
|
+
const f = g + h * this.GREEDY_MULTIPLIER;
|
|
5255
|
+
this.debug_lastNodeCostMap.set(neighborNode.capacityMeshNodeId, {
|
|
5256
|
+
f,
|
|
5257
|
+
g,
|
|
5258
|
+
h
|
|
5259
|
+
});
|
|
5260
|
+
const newCandidate = {
|
|
5261
|
+
prevCandidate: currentCandidate,
|
|
5262
|
+
node: neighborNode,
|
|
5263
|
+
f,
|
|
5264
|
+
g,
|
|
5265
|
+
h
|
|
5266
|
+
};
|
|
5267
|
+
this.candidates.push(newCandidate);
|
|
5268
|
+
}
|
|
5269
|
+
this.visitedNodes.add(currentCandidate.node.capacityMeshNodeId);
|
|
5270
|
+
}
|
|
5271
|
+
visualize() {
|
|
5272
|
+
const graphics = {
|
|
5273
|
+
lines: [],
|
|
5274
|
+
points: [],
|
|
5275
|
+
rects: [],
|
|
5276
|
+
circles: []
|
|
5277
|
+
};
|
|
5278
|
+
if (this.connectionsWithNodes) {
|
|
5279
|
+
for (let i = 0; i < this.connectionsWithNodes.length; i++) {
|
|
5280
|
+
const conn = this.connectionsWithNodes[i];
|
|
5281
|
+
if (conn.path && conn.path.length > 0) {
|
|
5282
|
+
const pathPoints = conn.path.map(({ center: { x, y }, width }) => ({
|
|
5283
|
+
// slight offset to allow viewing overlapping paths
|
|
5284
|
+
x: x + (i % 10 + i % 19) * (0.01 * width),
|
|
5285
|
+
y: y + (i % 10 + i % 19) * (0.01 * width)
|
|
5286
|
+
}));
|
|
5287
|
+
graphics.lines.push({
|
|
5288
|
+
points: pathPoints,
|
|
5289
|
+
strokeColor: this.colorMap[conn.connection.name]
|
|
5290
|
+
});
|
|
5291
|
+
}
|
|
5292
|
+
}
|
|
5293
|
+
}
|
|
5294
|
+
for (const node of this.nodes) {
|
|
5295
|
+
const nodeCosts = this.debug_lastNodeCostMap.get(node.capacityMeshNodeId);
|
|
5296
|
+
graphics.rects.push({
|
|
5297
|
+
...createRectFromCapacityNode(node),
|
|
5298
|
+
label: [
|
|
5299
|
+
`${node.capacityMeshNodeId}`,
|
|
5300
|
+
`${this.usedNodeCapacityMap.get(node.capacityMeshNodeId)}/${this.getTotalCapacity(node).toFixed(2)}`,
|
|
5301
|
+
`${node.width.toFixed(2)}x${node.height.toFixed(2)}`,
|
|
5302
|
+
`g: ${nodeCosts?.g !== void 0 ? nodeCosts.g.toFixed(2) : "?"}`,
|
|
5303
|
+
`h: ${nodeCosts?.h !== void 0 ? nodeCosts.h.toFixed(2) : "?"}`,
|
|
5304
|
+
`f: ${nodeCosts?.f !== void 0 ? nodeCosts.f.toFixed(2) : "?"}`
|
|
5305
|
+
].join("\n")
|
|
5306
|
+
});
|
|
5307
|
+
}
|
|
5308
|
+
if (this.connectionsWithNodes) {
|
|
5309
|
+
for (const conn of this.connectionsWithNodes) {
|
|
5310
|
+
if (conn.connection?.pointsToConnect) {
|
|
5311
|
+
for (const point of conn.connection.pointsToConnect) {
|
|
5312
|
+
graphics.points.push({
|
|
5313
|
+
x: point.x,
|
|
5314
|
+
y: point.y
|
|
5315
|
+
});
|
|
5316
|
+
}
|
|
5317
|
+
}
|
|
5318
|
+
}
|
|
5319
|
+
}
|
|
5320
|
+
const nextConnection = this.connectionsWithNodes[this.currentConnectionIndex];
|
|
5321
|
+
if (nextConnection) {
|
|
5322
|
+
const [start, end] = nextConnection.connection.pointsToConnect;
|
|
5323
|
+
graphics.lines.push({
|
|
5324
|
+
points: [
|
|
5325
|
+
{ x: start.x, y: start.y },
|
|
5326
|
+
{ x: end.x, y: end.y }
|
|
5327
|
+
],
|
|
5328
|
+
strokeColor: "red",
|
|
5329
|
+
strokeDash: "10 5"
|
|
5330
|
+
});
|
|
5331
|
+
}
|
|
5332
|
+
if (this.candidates) {
|
|
5333
|
+
const topCandidates = this.candidates.slice(0, 5);
|
|
5334
|
+
const connectionName = this.connectionsWithNodes[this.currentConnectionIndex].connection.name;
|
|
5335
|
+
topCandidates.forEach((candidate, index) => {
|
|
5336
|
+
const opacity = 0.5 * (1 - index / 5);
|
|
5337
|
+
const backtrackedPath = this.getBacktrackedPath(candidate);
|
|
5338
|
+
graphics.lines.push({
|
|
5339
|
+
points: backtrackedPath.map(({ center: { x, y } }) => ({ x, y })),
|
|
5340
|
+
strokeColor: safeTransparentize(
|
|
5341
|
+
this.colorMap[connectionName] ?? "red",
|
|
5342
|
+
1 - opacity
|
|
5343
|
+
)
|
|
5344
|
+
});
|
|
5345
|
+
});
|
|
5346
|
+
}
|
|
5347
|
+
return graphics;
|
|
5348
|
+
}
|
|
5349
|
+
};
|
|
5350
|
+
|
|
5351
|
+
// lib/solvers/CapacityPathingSolver/CapacityPathingSolver5.ts
|
|
5352
|
+
var CapacityPathingSolver5 = class extends CapacityPathingSolver {
|
|
5353
|
+
NEGATIVE_CAPACITY_PENALTY_FACTOR = 1;
|
|
5354
|
+
REDUCED_CAPACITY_PENALTY_FACTOR = 1;
|
|
5355
|
+
constructor(...args) {
|
|
5356
|
+
super(...args);
|
|
5357
|
+
this.GREEDY_MULTIPLIER = 2.5;
|
|
5358
|
+
}
|
|
5359
|
+
get maxCapacityFactor() {
|
|
5360
|
+
return this.hyperParameters.MAX_CAPACITY_FACTOR ?? 1;
|
|
5361
|
+
}
|
|
5362
|
+
getTotalCapacity(node) {
|
|
5363
|
+
return getTunedTotalCapacity1(node, this.maxCapacityFactor);
|
|
5364
|
+
}
|
|
5365
|
+
/**
|
|
5366
|
+
* Penalty you pay for using this node
|
|
5367
|
+
*/
|
|
5368
|
+
getNodeCapacityPenalty(node) {
|
|
5369
|
+
return 0.05;
|
|
5370
|
+
if (node.availableZ.length === 1) {
|
|
5371
|
+
return 0;
|
|
5372
|
+
}
|
|
5373
|
+
const totalCapacity = this.getTotalCapacity(node);
|
|
5374
|
+
const usedCapacity = this.usedNodeCapacityMap.get(node.capacityMeshNodeId) ?? 0;
|
|
5375
|
+
const remainingCapacity = totalCapacity - usedCapacity;
|
|
5376
|
+
const dist = this.activeCandidateStraightLineDistance;
|
|
5377
|
+
if (remainingCapacity <= 0) {
|
|
5378
|
+
const penalty = (-remainingCapacity + 1) / totalCapacity * dist * (this.NEGATIVE_CAPACITY_PENALTY_FACTOR / 4);
|
|
5379
|
+
return penalty ** 2;
|
|
5380
|
+
}
|
|
5381
|
+
return 1 / remainingCapacity * dist * this.REDUCED_CAPACITY_PENALTY_FACTOR / 8;
|
|
5382
|
+
}
|
|
5383
|
+
/**
|
|
5384
|
+
* We're rewarding travel into big nodes.
|
|
5385
|
+
*
|
|
5386
|
+
* To minimize shortest path, you'd want to comment this out.
|
|
5387
|
+
*/
|
|
5388
|
+
getDistanceBetweenNodes(A, B) {
|
|
5389
|
+
const dx = A.center.x - B.center.x;
|
|
5390
|
+
const dy = A.center.y - B.center.y;
|
|
5391
|
+
return Math.sqrt(dx ** 2 + dy ** 2);
|
|
5392
|
+
}
|
|
5393
|
+
computeG(prevCandidate, node, endGoal) {
|
|
5394
|
+
return prevCandidate.g + this.getDistanceBetweenNodes(prevCandidate.node, node) + this.getNodeCapacityPenalty(node);
|
|
5395
|
+
}
|
|
5396
|
+
computeH(prevCandidate, node, endGoal) {
|
|
5397
|
+
return this.getDistanceBetweenNodes(node, endGoal);
|
|
5398
|
+
}
|
|
5399
|
+
};
|
|
5400
|
+
|
|
5401
|
+
// lib/solvers/StrawSolver/StrawSolver.ts
|
|
5402
|
+
var StrawSolver = class extends BaseSolver {
|
|
5403
|
+
multiLayerNodes;
|
|
5404
|
+
strawNodes;
|
|
5405
|
+
skippedNodes;
|
|
5406
|
+
unprocessedNodes;
|
|
5407
|
+
strawSize;
|
|
5408
|
+
nodeIdCounter;
|
|
5409
|
+
constructor(params) {
|
|
5410
|
+
super();
|
|
5411
|
+
this.strawSize = params.strawSize ?? 0.5;
|
|
5412
|
+
this.multiLayerNodes = [];
|
|
5413
|
+
this.strawNodes = [];
|
|
5414
|
+
this.skippedNodes = [];
|
|
5415
|
+
this.nodeIdCounter = 0;
|
|
5416
|
+
this.unprocessedNodes = [];
|
|
5417
|
+
for (const node of params.nodes) {
|
|
5418
|
+
if (node.availableZ.length === 1) {
|
|
5419
|
+
this.unprocessedNodes.push(node);
|
|
5420
|
+
} else {
|
|
5421
|
+
this.multiLayerNodes.push(node);
|
|
5422
|
+
}
|
|
5423
|
+
}
|
|
5424
|
+
}
|
|
5425
|
+
getCapacityOfMultiLayerNodesWithinBounds(bounds) {
|
|
5426
|
+
let totalCapacity = 0;
|
|
5427
|
+
for (const node of this.multiLayerNodes) {
|
|
5428
|
+
const nodeMinX = node.center.x - node.width / 2;
|
|
5429
|
+
const nodeMaxX = node.center.x + node.width / 2;
|
|
5430
|
+
const nodeMinY = node.center.y - node.height / 2;
|
|
5431
|
+
const nodeMaxY = node.center.y + node.height / 2;
|
|
5432
|
+
const overlapMinX = Math.max(bounds.minX, nodeMinX);
|
|
5433
|
+
const overlapMaxX = Math.min(bounds.maxX, nodeMaxX);
|
|
5434
|
+
const overlapMinY = Math.max(bounds.minY, nodeMinY);
|
|
5435
|
+
const overlapMaxY = Math.min(bounds.maxY, nodeMaxY);
|
|
5436
|
+
if (overlapMinX < overlapMaxX && overlapMinY < overlapMaxY) {
|
|
5437
|
+
const overlapWidth = overlapMaxX - overlapMinX;
|
|
5438
|
+
const overlapHeight = overlapMaxY - overlapMinY;
|
|
5439
|
+
const overlapArea = overlapWidth * overlapHeight;
|
|
5440
|
+
const nodeArea = node.width * node.height;
|
|
5441
|
+
const proportion = overlapArea / nodeArea;
|
|
5442
|
+
totalCapacity += getTunedTotalCapacity1(node) * proportion;
|
|
5443
|
+
}
|
|
5444
|
+
}
|
|
5445
|
+
return totalCapacity;
|
|
5446
|
+
}
|
|
5447
|
+
getSurroundingCapacities(node) {
|
|
5448
|
+
const searchDistance = Math.min(node.width, node.height);
|
|
5449
|
+
const leftSurroundingCapacity = this.getCapacityOfMultiLayerNodesWithinBounds({
|
|
5450
|
+
minX: node.center.x - node.width / 2 - searchDistance,
|
|
5451
|
+
maxX: node.center.x - node.width / 2,
|
|
5452
|
+
minY: node.center.y - node.height / 2,
|
|
5453
|
+
maxY: node.center.y + node.height / 2
|
|
5454
|
+
});
|
|
5455
|
+
const rightSurroundingCapacity = this.getCapacityOfMultiLayerNodesWithinBounds({
|
|
5456
|
+
minX: node.center.x + node.width / 2,
|
|
5457
|
+
maxX: node.center.x + node.width / 2 + searchDistance,
|
|
5458
|
+
minY: node.center.y - node.height / 2,
|
|
5459
|
+
maxY: node.center.y + node.height / 2
|
|
5460
|
+
});
|
|
5461
|
+
const topSurroundingCapacity = this.getCapacityOfMultiLayerNodesWithinBounds({
|
|
5462
|
+
minX: node.center.x - node.width / 2,
|
|
5463
|
+
maxX: node.center.x + node.width / 2,
|
|
5464
|
+
minY: node.center.y - node.height / 2 - searchDistance,
|
|
5465
|
+
maxY: node.center.y - node.height / 2
|
|
5466
|
+
});
|
|
5467
|
+
const bottomSurroundingCapacity = this.getCapacityOfMultiLayerNodesWithinBounds({
|
|
5468
|
+
minX: node.center.x - node.width / 2,
|
|
5469
|
+
maxX: node.center.x + node.width / 2,
|
|
5470
|
+
minY: node.center.y + node.height / 2,
|
|
5471
|
+
maxY: node.center.y + node.height / 2 + searchDistance
|
|
5472
|
+
});
|
|
5473
|
+
return {
|
|
5474
|
+
leftSurroundingCapacity,
|
|
5475
|
+
rightSurroundingCapacity,
|
|
5476
|
+
topSurroundingCapacity,
|
|
5477
|
+
bottomSurroundingCapacity
|
|
5478
|
+
};
|
|
5479
|
+
}
|
|
5480
|
+
/**
|
|
5481
|
+
* Creates straw nodes from a single-layer node based on surrounding capacities
|
|
5482
|
+
*/
|
|
5483
|
+
createStrawsForNode(node) {
|
|
5484
|
+
const result = [];
|
|
5485
|
+
const {
|
|
5486
|
+
leftSurroundingCapacity,
|
|
5487
|
+
rightSurroundingCapacity,
|
|
5488
|
+
topSurroundingCapacity,
|
|
5489
|
+
bottomSurroundingCapacity
|
|
5490
|
+
} = this.getSurroundingCapacities(node);
|
|
5491
|
+
const horizontalCapacity = leftSurroundingCapacity + rightSurroundingCapacity;
|
|
5492
|
+
const verticalCapacity = topSurroundingCapacity + bottomSurroundingCapacity;
|
|
5493
|
+
const layerPrefersFactor = 1;
|
|
5494
|
+
const effectiveHorizontalCapacity = horizontalCapacity * layerPrefersFactor;
|
|
5495
|
+
if (effectiveHorizontalCapacity > verticalCapacity) {
|
|
5496
|
+
const numStraws = Math.floor(node.height / this.strawSize);
|
|
5497
|
+
const strawHeight = node.height / numStraws;
|
|
5498
|
+
for (let i = 0; i < numStraws; i++) {
|
|
5499
|
+
const strawCenterY = node.center.y - node.height / 2 + i * strawHeight + strawHeight / 2;
|
|
5500
|
+
result.push({
|
|
5501
|
+
capacityMeshNodeId: `${node.capacityMeshNodeId}_straw${i}`,
|
|
5502
|
+
center: { x: node.center.x, y: strawCenterY },
|
|
5503
|
+
width: node.width,
|
|
5504
|
+
height: strawHeight,
|
|
5505
|
+
layer: node.layer,
|
|
5506
|
+
availableZ: [...node.availableZ],
|
|
5507
|
+
_depth: node._depth,
|
|
5508
|
+
_strawNode: true
|
|
5509
|
+
});
|
|
5510
|
+
}
|
|
5511
|
+
} else {
|
|
5512
|
+
const numStraws = Math.floor(node.width / this.strawSize);
|
|
5513
|
+
const strawWidth = node.width / numStraws;
|
|
5514
|
+
for (let i = 0; i < numStraws; i++) {
|
|
5515
|
+
const strawCenterX = node.center.x - node.width / 2 + i * strawWidth + strawWidth / 2;
|
|
5516
|
+
result.push({
|
|
5517
|
+
capacityMeshNodeId: `${node.capacityMeshNodeId}_straw${i}`,
|
|
5518
|
+
center: { x: strawCenterX, y: node.center.y },
|
|
5519
|
+
width: strawWidth,
|
|
5520
|
+
height: node.height,
|
|
5521
|
+
layer: node.layer,
|
|
5522
|
+
availableZ: [...node.availableZ],
|
|
5523
|
+
_depth: node._depth,
|
|
5524
|
+
_strawNode: true
|
|
5525
|
+
});
|
|
5121
5526
|
}
|
|
5122
|
-
this.activeSolver = null;
|
|
5123
5527
|
}
|
|
5528
|
+
return result;
|
|
5124
5529
|
}
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5530
|
+
getResultNodes() {
|
|
5531
|
+
return [...this.multiLayerNodes, ...this.strawNodes, ...this.skippedNodes];
|
|
5532
|
+
}
|
|
5533
|
+
_step() {
|
|
5534
|
+
const rootNode = this.unprocessedNodes.pop();
|
|
5535
|
+
if (!rootNode) {
|
|
5536
|
+
this.solved = true;
|
|
5537
|
+
return;
|
|
5538
|
+
}
|
|
5539
|
+
if (rootNode.width < this.strawSize * 5 && rootNode.height < this.strawSize * 5) {
|
|
5540
|
+
this.skippedNodes.push(rootNode);
|
|
5541
|
+
return;
|
|
5542
|
+
}
|
|
5543
|
+
if (rootNode._containsTarget) {
|
|
5544
|
+
this.skippedNodes.push(rootNode);
|
|
5545
|
+
return;
|
|
5128
5546
|
}
|
|
5547
|
+
const strawNodes = this.createStrawsForNode(rootNode);
|
|
5548
|
+
this.strawNodes.push(...strawNodes);
|
|
5549
|
+
}
|
|
5550
|
+
visualize() {
|
|
5129
5551
|
const graphics = {
|
|
5552
|
+
rects: [],
|
|
5130
5553
|
lines: [],
|
|
5131
5554
|
points: [],
|
|
5132
|
-
rects: [],
|
|
5133
5555
|
circles: [],
|
|
5134
|
-
|
|
5135
|
-
title: "Unravel Multi Section Solver"
|
|
5556
|
+
title: "Straw Solver"
|
|
5136
5557
|
};
|
|
5137
|
-
for (const
|
|
5138
|
-
const probabilityOfFailure = this.nodePfMap.get(nodeId) || 0;
|
|
5139
|
-
const pf = Math.min(probabilityOfFailure, 1);
|
|
5140
|
-
const red = Math.floor(255 * pf);
|
|
5141
|
-
const green = Math.floor(255 * (1 - pf));
|
|
5142
|
-
const color = `rgb(${red}, ${green}, 0)`;
|
|
5558
|
+
for (const node of this.unprocessedNodes) {
|
|
5143
5559
|
graphics.rects.push({
|
|
5144
5560
|
center: node.center,
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5561
|
+
width: node.width,
|
|
5562
|
+
height: node.height,
|
|
5563
|
+
fill: "rgba(200, 200, 200, 0.5)",
|
|
5564
|
+
stroke: "rgba(0, 0, 0, 0.5)",
|
|
5565
|
+
label: `${node.capacityMeshNodeId}
|
|
5566
|
+
Unprocessed
|
|
5567
|
+
${node.width}x${node.height}`
|
|
5151
5568
|
});
|
|
5152
5569
|
}
|
|
5153
|
-
for (const
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5570
|
+
for (const node of this.strawNodes) {
|
|
5571
|
+
const color = node.availableZ[0] === 0 ? "rgba(0, 150, 255, 0.5)" : "rgba(255, 100, 0, 0.5)";
|
|
5572
|
+
graphics.rects.push({
|
|
5573
|
+
center: node.center,
|
|
5574
|
+
width: node.width,
|
|
5575
|
+
height: node.height,
|
|
5576
|
+
fill: color,
|
|
5577
|
+
stroke: "rgba(0, 0, 0, 0.5)",
|
|
5578
|
+
label: `${node.capacityMeshNodeId}
|
|
5579
|
+
Layer: ${node.availableZ[0]}
|
|
5580
|
+
${node.width}x${node.height}`
|
|
5161
5581
|
});
|
|
5162
5582
|
}
|
|
5163
|
-
const
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5583
|
+
for (const node of this.multiLayerNodes) {
|
|
5584
|
+
graphics.rects.push({
|
|
5585
|
+
center: node.center,
|
|
5586
|
+
width: node.width * 0.9,
|
|
5587
|
+
height: node.height * 0.9,
|
|
5588
|
+
fill: "rgba(100, 255, 100, 0.5)",
|
|
5589
|
+
stroke: "rgba(0, 0, 0, 0.5)",
|
|
5590
|
+
layer: `z${node.availableZ.join(",")}`,
|
|
5591
|
+
label: `${node.capacityMeshNodeId}
|
|
5592
|
+
Layers: ${node.availableZ.join(",")}
|
|
5593
|
+
${node.width}x${node.height}`
|
|
5594
|
+
});
|
|
5595
|
+
}
|
|
5596
|
+
return graphics;
|
|
5597
|
+
}
|
|
5598
|
+
};
|
|
5599
|
+
|
|
5600
|
+
// lib/solvers/SingleLayerNodeMerger/SingleLayerNodeMergerSolver.ts
|
|
5601
|
+
var EPSILON = 5e-3;
|
|
5602
|
+
var SingleLayerNodeMergerSolver = class extends BaseSolver {
|
|
5603
|
+
nodeMap;
|
|
5604
|
+
currentBatchNodeIds;
|
|
5605
|
+
absorbedNodeIds;
|
|
5606
|
+
nextBatchNodeIds;
|
|
5607
|
+
batchHadModifications;
|
|
5608
|
+
newNodes;
|
|
5609
|
+
constructor(nodes) {
|
|
5610
|
+
super();
|
|
5611
|
+
this.nodeMap = /* @__PURE__ */ new Map();
|
|
5612
|
+
this.MAX_ITERATIONS = 1e5;
|
|
5613
|
+
for (const node of nodes) {
|
|
5614
|
+
this.nodeMap.set(node.capacityMeshNodeId, node);
|
|
5615
|
+
}
|
|
5616
|
+
this.newNodes = [];
|
|
5617
|
+
this.absorbedNodeIds = /* @__PURE__ */ new Set();
|
|
5618
|
+
const nodeWithArea = [];
|
|
5619
|
+
for (const node of nodes) {
|
|
5620
|
+
if (node.availableZ.length > 1) {
|
|
5621
|
+
this.newNodes.push(node);
|
|
5622
|
+
this.absorbedNodeIds.add(node.capacityMeshNodeId);
|
|
5623
|
+
} else {
|
|
5624
|
+
nodeWithArea.push([node.capacityMeshNodeId, node.width * node.height]);
|
|
5167
5625
|
}
|
|
5168
|
-
pointsBySegment.get(point.segmentId).push(point);
|
|
5169
5626
|
}
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5627
|
+
nodeWithArea.sort((a, b) => a[1] - b[1]);
|
|
5628
|
+
this.currentBatchNodeIds = nodeWithArea.map((n) => n[0]);
|
|
5629
|
+
this.nextBatchNodeIds = [];
|
|
5630
|
+
this.batchHadModifications = false;
|
|
5631
|
+
}
|
|
5632
|
+
getAdjacentSameLayerUnprocessedNodes(rootNode) {
|
|
5633
|
+
const adjacentNodes = [];
|
|
5634
|
+
for (const unprocessedNodeId of this.currentBatchNodeIds) {
|
|
5635
|
+
if (this.absorbedNodeIds.has(unprocessedNodeId)) continue;
|
|
5636
|
+
const unprocessedNode = this.nodeMap.get(unprocessedNodeId);
|
|
5637
|
+
if (unprocessedNode.availableZ[0] !== rootNode.availableZ[0]) continue;
|
|
5638
|
+
if (unprocessedNode._containsTarget && unprocessedNode._targetConnectionName !== rootNode._targetConnectionName)
|
|
5639
|
+
continue;
|
|
5640
|
+
if (!areNodesBordering(rootNode, unprocessedNode)) continue;
|
|
5641
|
+
adjacentNodes.push(unprocessedNode);
|
|
5642
|
+
}
|
|
5643
|
+
return adjacentNodes;
|
|
5644
|
+
}
|
|
5645
|
+
_step() {
|
|
5646
|
+
let rootNodeId = this.currentBatchNodeIds.pop();
|
|
5647
|
+
while (rootNodeId && this.absorbedNodeIds.has(rootNodeId)) {
|
|
5648
|
+
rootNodeId = this.currentBatchNodeIds.pop();
|
|
5649
|
+
}
|
|
5650
|
+
if (!rootNodeId) {
|
|
5651
|
+
if (this.batchHadModifications) {
|
|
5652
|
+
this.currentBatchNodeIds = this.nextBatchNodeIds.sort((a, b) => {
|
|
5653
|
+
const A = this.nodeMap.get(a);
|
|
5654
|
+
const B = this.nodeMap.get(b);
|
|
5655
|
+
return A.width * A.height - B.width * B.height;
|
|
5182
5656
|
});
|
|
5657
|
+
this.nextBatchNodeIds = [];
|
|
5658
|
+
this.batchHadModifications = false;
|
|
5659
|
+
return;
|
|
5183
5660
|
}
|
|
5661
|
+
this.solved = true;
|
|
5662
|
+
this.newNodes.push(
|
|
5663
|
+
...this.nextBatchNodeIds.map((id) => this.nodeMap.get(id))
|
|
5664
|
+
);
|
|
5665
|
+
return;
|
|
5184
5666
|
}
|
|
5185
|
-
const
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5667
|
+
const rootNode = this.nodeMap.get(rootNodeId);
|
|
5668
|
+
let rootNodeHasGrown = false;
|
|
5669
|
+
const adjacentNodes = this.getAdjacentSameLayerUnprocessedNodes(rootNode);
|
|
5670
|
+
if (adjacentNodes.length === 0) {
|
|
5671
|
+
this.nextBatchNodeIds.push(rootNodeId);
|
|
5672
|
+
return;
|
|
5673
|
+
}
|
|
5674
|
+
const adjacentNodesToLeft = adjacentNodes.filter(
|
|
5675
|
+
(adjNode) => adjNode.center.x < rootNode.center.x && Math.abs(adjNode.center.y - rootNode.center.y) < rootNode.height / 2
|
|
5676
|
+
);
|
|
5677
|
+
if (adjacentNodesToLeft.length > 0) {
|
|
5678
|
+
const { width: leftAdjNodeWidth, height: leftAdjNodeHeight } = adjacentNodesToLeft[0];
|
|
5679
|
+
const leftAdjNodesAreAllSameSize = adjacentNodesToLeft.every(
|
|
5680
|
+
(adjNode) => adjNode.width === leftAdjNodeWidth && adjNode.height === leftAdjNodeHeight
|
|
5681
|
+
);
|
|
5682
|
+
const leftAdjNodesTakeUpEntireHeight = Math.abs(
|
|
5683
|
+
adjacentNodesToLeft.reduce((acc, adjNode) => {
|
|
5684
|
+
return acc + adjNode.height;
|
|
5685
|
+
}, 0) - rootNode.height
|
|
5686
|
+
) < EPSILON;
|
|
5687
|
+
if (leftAdjNodesTakeUpEntireHeight && leftAdjNodesAreAllSameSize) {
|
|
5688
|
+
rootNode.width += leftAdjNodeWidth;
|
|
5689
|
+
rootNode.center.x = rootNode.center.x - leftAdjNodeWidth / 2;
|
|
5690
|
+
for (const adjNode of adjacentNodesToLeft) {
|
|
5691
|
+
this.absorbedNodeIds.add(adjNode.capacityMeshNodeId);
|
|
5193
5692
|
}
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
strokeColor: this.colorMap[point1.connectionName] || "#666"
|
|
5216
|
-
});
|
|
5693
|
+
rootNodeHasGrown = true;
|
|
5694
|
+
}
|
|
5695
|
+
}
|
|
5696
|
+
const adjacentNodesToRight = adjacentNodes.filter(
|
|
5697
|
+
(adjNode) => adjNode.center.x > rootNode.center.x && Math.abs(adjNode.center.y - rootNode.center.y) < rootNode.height / 2
|
|
5698
|
+
);
|
|
5699
|
+
if (adjacentNodesToRight.length > 0 && !rootNodeHasGrown) {
|
|
5700
|
+
const { width: rightAdjNodeWidth, height: rightAdjNodeHeight } = adjacentNodesToRight[0];
|
|
5701
|
+
const rightAdjNodesAreAllSameSize = adjacentNodesToRight.every(
|
|
5702
|
+
(adjNode) => adjNode.width === rightAdjNodeWidth && adjNode.height === rightAdjNodeHeight
|
|
5703
|
+
);
|
|
5704
|
+
const rightAdjNodesTakeUpEntireHeight = Math.abs(
|
|
5705
|
+
adjacentNodesToRight.reduce((acc, adjNode) => {
|
|
5706
|
+
return acc + adjNode.height;
|
|
5707
|
+
}, 0) - rootNode.height
|
|
5708
|
+
) < EPSILON;
|
|
5709
|
+
if (rightAdjNodesTakeUpEntireHeight && rightAdjNodesAreAllSameSize) {
|
|
5710
|
+
rootNode.width += rightAdjNodeWidth;
|
|
5711
|
+
rootNode.center.x = rootNode.center.x + rightAdjNodeWidth / 2;
|
|
5712
|
+
for (const adjNode of adjacentNodesToRight) {
|
|
5713
|
+
this.absorbedNodeIds.add(adjNode.capacityMeshNodeId);
|
|
5217
5714
|
}
|
|
5715
|
+
rootNodeHasGrown = true;
|
|
5218
5716
|
}
|
|
5219
5717
|
}
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
if (!
|
|
5224
|
-
|
|
5225
|
-
|
|
5718
|
+
const adjacentNodesToTop = adjacentNodes.filter(
|
|
5719
|
+
(adjNode) => adjNode.center.y > rootNode.center.y && Math.abs(adjNode.center.x - rootNode.center.x) < rootNode.width / 2
|
|
5720
|
+
);
|
|
5721
|
+
if (adjacentNodesToTop.length > 0 && !rootNodeHasGrown) {
|
|
5722
|
+
const { width: topAdjNodeWidth, height: topAdjNodeHeight } = adjacentNodesToTop[0];
|
|
5723
|
+
const topAdjNodesAreAllSameSize = adjacentNodesToTop.every(
|
|
5724
|
+
(adjNode) => adjNode.width === topAdjNodeWidth && adjNode.height === topAdjNodeHeight
|
|
5226
5725
|
);
|
|
5726
|
+
const topAdjNodesTakeUpEntireWidth = Math.abs(
|
|
5727
|
+
adjacentNodesToTop.reduce((acc, adjNode) => {
|
|
5728
|
+
return acc + adjNode.width;
|
|
5729
|
+
}, 0) - rootNode.width
|
|
5730
|
+
) < EPSILON;
|
|
5731
|
+
if (topAdjNodesTakeUpEntireWidth && topAdjNodesAreAllSameSize) {
|
|
5732
|
+
rootNode.height += topAdjNodeHeight;
|
|
5733
|
+
rootNode.center.y = rootNode.center.y + topAdjNodeHeight / 2;
|
|
5734
|
+
for (const adjNode of adjacentNodesToTop) {
|
|
5735
|
+
this.absorbedNodeIds.add(adjNode.capacityMeshNodeId);
|
|
5736
|
+
}
|
|
5737
|
+
rootNodeHasGrown = true;
|
|
5738
|
+
}
|
|
5227
5739
|
}
|
|
5228
|
-
const
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5740
|
+
const adjacentNodesToBottom = adjacentNodes.filter(
|
|
5741
|
+
(adjNode) => adjNode.center.y < rootNode.center.y && Math.abs(adjNode.center.x - rootNode.center.x) < rootNode.width / 2
|
|
5742
|
+
);
|
|
5743
|
+
if (adjacentNodesToBottom.length > 0 && !rootNodeHasGrown) {
|
|
5744
|
+
const { width: bottomAdjNodeWidth, height: bottomAdjNodeHeight } = adjacentNodesToBottom[0];
|
|
5745
|
+
const bottomAdjNodesAreAllSameSize = adjacentNodesToBottom.every(
|
|
5746
|
+
(adjNode) => adjNode.width === bottomAdjNodeWidth && adjNode.height === bottomAdjNodeHeight
|
|
5747
|
+
);
|
|
5748
|
+
const bottomAdjNodesTakeUpEntireWidth = Math.abs(
|
|
5749
|
+
adjacentNodesToBottom.reduce((acc, adjNode) => {
|
|
5750
|
+
return acc + adjNode.width;
|
|
5751
|
+
}, 0) - rootNode.width
|
|
5752
|
+
) < EPSILON;
|
|
5753
|
+
if (bottomAdjNodesTakeUpEntireWidth && bottomAdjNodesAreAllSameSize) {
|
|
5754
|
+
rootNode.height += bottomAdjNodeHeight;
|
|
5755
|
+
rootNode.center.y = rootNode.center.y - bottomAdjNodeHeight / 2;
|
|
5756
|
+
for (const adjNode of adjacentNodesToBottom) {
|
|
5757
|
+
this.absorbedNodeIds.add(adjNode.capacityMeshNodeId);
|
|
5241
5758
|
}
|
|
5759
|
+
rootNodeHasGrown = true;
|
|
5242
5760
|
}
|
|
5243
5761
|
}
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5762
|
+
if (rootNodeHasGrown) {
|
|
5763
|
+
this.batchHadModifications = true;
|
|
5764
|
+
this.currentBatchNodeIds.push(rootNodeId);
|
|
5765
|
+
} else {
|
|
5766
|
+
this.nextBatchNodeIds.unshift(rootNodeId);
|
|
5767
|
+
}
|
|
5768
|
+
}
|
|
5769
|
+
visualize() {
|
|
5770
|
+
const graphics = {
|
|
5771
|
+
circles: [],
|
|
5772
|
+
lines: [],
|
|
5773
|
+
points: [],
|
|
5774
|
+
rects: [],
|
|
5775
|
+
coordinateSystem: "cartesian",
|
|
5776
|
+
title: "Same Layer Node Merger"
|
|
5777
|
+
};
|
|
5778
|
+
for (const node of this.newNodes) {
|
|
5779
|
+
graphics.rects.push(createRectFromCapacityNode(node));
|
|
5780
|
+
}
|
|
5781
|
+
const nextNodeIdInBatch = this.currentBatchNodeIds[this.currentBatchNodeIds.length - 1];
|
|
5782
|
+
let adjacentNodes;
|
|
5783
|
+
if (nextNodeIdInBatch) {
|
|
5784
|
+
adjacentNodes = this.getAdjacentSameLayerUnprocessedNodes(
|
|
5785
|
+
this.nodeMap.get(nextNodeIdInBatch)
|
|
5786
|
+
);
|
|
5787
|
+
}
|
|
5788
|
+
for (const nodeId of this.currentBatchNodeIds) {
|
|
5789
|
+
const node = this.nodeMap.get(nodeId);
|
|
5790
|
+
if (this.absorbedNodeIds.has(nodeId)) continue;
|
|
5791
|
+
if (node) {
|
|
5792
|
+
const rect = createRectFromCapacityNode(node, {
|
|
5793
|
+
rectMargin: 0.01
|
|
5794
|
+
});
|
|
5795
|
+
if (nodeId === nextNodeIdInBatch) {
|
|
5796
|
+
rect.stroke = "rgba(0, 255, 0, 0.8)";
|
|
5797
|
+
} else if (adjacentNodes?.some(
|
|
5798
|
+
(adjNode) => adjNode.capacityMeshNodeId === nodeId
|
|
5799
|
+
)) {
|
|
5800
|
+
rect.stroke = "rgba(128, 0, 128, 0.8)";
|
|
5801
|
+
} else {
|
|
5802
|
+
rect.stroke = "rgba(255, 165, 0, 0.8)";
|
|
5254
5803
|
}
|
|
5804
|
+
rect.layer = `z${node.availableZ.join(",")}`;
|
|
5805
|
+
rect.label = `${rect.label}
|
|
5806
|
+
(unprocessed)`;
|
|
5807
|
+
graphics.rects.push(rect);
|
|
5255
5808
|
}
|
|
5256
5809
|
}
|
|
5257
|
-
|
|
5810
|
+
for (const nodeId of this.nextBatchNodeIds) {
|
|
5811
|
+
const node = this.nodeMap.get(nodeId);
|
|
5812
|
+
if (this.absorbedNodeIds.has(nodeId)) continue;
|
|
5813
|
+
if (node) {
|
|
5814
|
+
const rect = createRectFromCapacityNode(node, {
|
|
5815
|
+
rectMargin: 0.01
|
|
5816
|
+
});
|
|
5817
|
+
rect.layer = `z${node.availableZ.join(",")}`;
|
|
5818
|
+
rect.stroke = "rgba(0, 217, 255, 0.8)";
|
|
5819
|
+
rect.label = `${rect.label}
|
|
5820
|
+
x: ${node.center.x}, y: ${node.center.y}
|
|
5821
|
+
${node.width}x${node.height}
|
|
5822
|
+
(next batch)`;
|
|
5823
|
+
graphics.rects.push(rect);
|
|
5824
|
+
}
|
|
5825
|
+
}
|
|
5826
|
+
return graphics;
|
|
5258
5827
|
}
|
|
5259
5828
|
};
|
|
5260
5829
|
|
|
@@ -5301,12 +5870,15 @@ var CapacityMeshSolver = class extends BaseSolver {
|
|
|
5301
5870
|
segmentToPointOptimizer;
|
|
5302
5871
|
highDensityRouteSolver;
|
|
5303
5872
|
highDensityStitchSolver;
|
|
5873
|
+
singleLayerNodeMerger;
|
|
5874
|
+
strawSolver;
|
|
5304
5875
|
startTimeOfPhase;
|
|
5305
5876
|
endTimeOfPhase;
|
|
5306
5877
|
timeSpentOnPhase;
|
|
5307
|
-
|
|
5878
|
+
activeSubSolver = null;
|
|
5308
5879
|
connMap;
|
|
5309
5880
|
srjWithPointPairs;
|
|
5881
|
+
capacityNodes = null;
|
|
5310
5882
|
pipelineDef = [
|
|
5311
5883
|
definePipelineStep(
|
|
5312
5884
|
"netToPointPairsSolver",
|
|
@@ -5322,39 +5894,65 @@ var CapacityMeshSolver = class extends BaseSolver {
|
|
|
5322
5894
|
}
|
|
5323
5895
|
}
|
|
5324
5896
|
),
|
|
5325
|
-
definePipelineStep("nodeSolver", CapacityMeshNodeSolver, (cms) => [
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
]),
|
|
5329
|
-
definePipelineStep("nodeTargetMerger", CapacityNodeTargetMerger, (cms) => [
|
|
5330
|
-
cms.nodeSolver?.finishedNodes || [],
|
|
5331
|
-
cms.srj.obstacles,
|
|
5332
|
-
cms.connMap
|
|
5333
|
-
]),
|
|
5334
|
-
definePipelineStep("edgeSolver", CapacityMeshEdgeSolver, (cms) => [
|
|
5335
|
-
cms.nodeTargetMerger?.newNodes || []
|
|
5336
|
-
]),
|
|
5897
|
+
// definePipelineStep("nodeSolver", CapacityMeshNodeSolver, (cms) => [
|
|
5898
|
+
// cms.netToPointPairsSolver?.getNewSimpleRouteJson() || cms.srj,
|
|
5899
|
+
// cms.opts,
|
|
5900
|
+
// ]),
|
|
5337
5901
|
definePipelineStep(
|
|
5338
|
-
"
|
|
5339
|
-
|
|
5902
|
+
"nodeSolver",
|
|
5903
|
+
CapacityMeshNodeSolver2_NodeUnderObstacle,
|
|
5340
5904
|
(cms) => [
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
nodes: cms.nodeTargetMerger?.newNodes || [],
|
|
5344
|
-
edges: cms.edgeSolver?.edges || [],
|
|
5345
|
-
colorMap: cms.colorMap,
|
|
5346
|
-
hyperParameters: {
|
|
5347
|
-
MAX_CAPACITY_FACTOR: 1
|
|
5348
|
-
}
|
|
5349
|
-
}
|
|
5905
|
+
cms.netToPointPairsSolver?.getNewSimpleRouteJson() || cms.srj,
|
|
5906
|
+
cms.opts
|
|
5350
5907
|
]
|
|
5351
5908
|
),
|
|
5909
|
+
// definePipelineStep("nodeTargetMerger", CapacityNodeTargetMerger, (cms) => [
|
|
5910
|
+
// cms.nodeSolver?.finishedNodes || [],
|
|
5911
|
+
// cms.srj.obstacles,
|
|
5912
|
+
// cms.connMap,
|
|
5913
|
+
// ]),
|
|
5914
|
+
// definePipelineStep("nodeTargetMerger", CapacityNodeTargetMerger2, (cms) => [
|
|
5915
|
+
// cms.nodeSolver?.finishedNodes || [],
|
|
5916
|
+
// cms.srj.obstacles,
|
|
5917
|
+
// cms.connMap,
|
|
5918
|
+
// cms.colorMap,
|
|
5919
|
+
// cms.srj.connections,
|
|
5920
|
+
// ]),
|
|
5921
|
+
definePipelineStep(
|
|
5922
|
+
"singleLayerNodeMerger",
|
|
5923
|
+
SingleLayerNodeMergerSolver,
|
|
5924
|
+
(cms) => [cms.nodeSolver?.finishedNodes]
|
|
5925
|
+
),
|
|
5926
|
+
definePipelineStep(
|
|
5927
|
+
"strawSolver",
|
|
5928
|
+
StrawSolver,
|
|
5929
|
+
(cms) => [{ nodes: cms.singleLayerNodeMerger?.newNodes }],
|
|
5930
|
+
{
|
|
5931
|
+
onSolved: (cms) => {
|
|
5932
|
+
cms.capacityNodes = cms.strawSolver?.getResultNodes();
|
|
5933
|
+
}
|
|
5934
|
+
}
|
|
5935
|
+
),
|
|
5936
|
+
definePipelineStep("edgeSolver", CapacityMeshEdgeSolver, (cms) => [
|
|
5937
|
+
cms.capacityNodes
|
|
5938
|
+
]),
|
|
5939
|
+
definePipelineStep("pathingSolver", CapacityPathingSolver5, (cms) => [
|
|
5940
|
+
{
|
|
5941
|
+
simpleRouteJson: cms.srjWithPointPairs,
|
|
5942
|
+
nodes: cms.capacityNodes,
|
|
5943
|
+
edges: cms.edgeSolver?.edges || [],
|
|
5944
|
+
colorMap: cms.colorMap,
|
|
5945
|
+
hyperParameters: {
|
|
5946
|
+
MAX_CAPACITY_FACTOR: 1
|
|
5947
|
+
}
|
|
5948
|
+
}
|
|
5949
|
+
]),
|
|
5352
5950
|
definePipelineStep(
|
|
5353
5951
|
"edgeToPortSegmentSolver",
|
|
5354
5952
|
CapacityEdgeToPortSegmentSolver,
|
|
5355
5953
|
(cms) => [
|
|
5356
5954
|
{
|
|
5357
|
-
nodes: cms.
|
|
5955
|
+
nodes: cms.capacityNodes,
|
|
5358
5956
|
edges: cms.edgeSolver?.edges || [],
|
|
5359
5957
|
capacityPaths: cms.pathingSolver?.getCapacityPaths() || [],
|
|
5360
5958
|
colorMap: cms.colorMap
|
|
@@ -5375,7 +5973,7 @@ var CapacityMeshSolver = class extends BaseSolver {
|
|
|
5375
5973
|
{
|
|
5376
5974
|
segments: allSegments,
|
|
5377
5975
|
colorMap: cms.colorMap,
|
|
5378
|
-
nodes: cms.
|
|
5976
|
+
nodes: cms.capacityNodes
|
|
5379
5977
|
}
|
|
5380
5978
|
];
|
|
5381
5979
|
}
|
|
@@ -5398,7 +5996,7 @@ var CapacityMeshSolver = class extends BaseSolver {
|
|
|
5398
5996
|
{
|
|
5399
5997
|
assignedSegments: cms.segmentToPointSolver?.solvedSegments || [],
|
|
5400
5998
|
colorMap: cms.colorMap,
|
|
5401
|
-
nodes: cms.
|
|
5999
|
+
nodes: cms.capacityNodes
|
|
5402
6000
|
}
|
|
5403
6001
|
]
|
|
5404
6002
|
),
|
|
@@ -5428,26 +6026,24 @@ var CapacityMeshSolver = class extends BaseSolver {
|
|
|
5428
6026
|
this.solved = true;
|
|
5429
6027
|
return;
|
|
5430
6028
|
}
|
|
5431
|
-
if (this.
|
|
5432
|
-
this.
|
|
5433
|
-
if (this.
|
|
6029
|
+
if (this.activeSubSolver) {
|
|
6030
|
+
this.activeSubSolver.step();
|
|
6031
|
+
if (this.activeSubSolver.solved) {
|
|
5434
6032
|
this.endTimeOfPhase[pipelineStepDef.solverName] = performance.now();
|
|
5435
6033
|
this.timeSpentOnPhase[pipelineStepDef.solverName] = this.endTimeOfPhase[pipelineStepDef.solverName] - this.startTimeOfPhase[pipelineStepDef.solverName];
|
|
5436
6034
|
pipelineStepDef.onSolved?.(this);
|
|
5437
|
-
this.
|
|
6035
|
+
this.activeSubSolver = null;
|
|
5438
6036
|
this.currentPipelineStepIndex++;
|
|
5439
|
-
} else if (this.
|
|
5440
|
-
this.error = this.
|
|
6037
|
+
} else if (this.activeSubSolver.failed) {
|
|
6038
|
+
this.error = this.activeSubSolver?.error;
|
|
5441
6039
|
this.failed = true;
|
|
5442
|
-
this.
|
|
6040
|
+
this.activeSubSolver = null;
|
|
5443
6041
|
}
|
|
5444
6042
|
return;
|
|
5445
6043
|
}
|
|
5446
6044
|
const constructorParams = pipelineStepDef.getConstructorParams(this);
|
|
5447
|
-
this.
|
|
5448
|
-
|
|
5449
|
-
);
|
|
5450
|
-
this[pipelineStepDef.solverName] = this.activeSolver;
|
|
6045
|
+
this.activeSubSolver = new pipelineStepDef.solverClass(...constructorParams);
|
|
6046
|
+
this[pipelineStepDef.solverName] = this.activeSubSolver;
|
|
5451
6047
|
this.timeSpentOnPhase[pipelineStepDef.solverName] = 0;
|
|
5452
6048
|
this.startTimeOfPhase[pipelineStepDef.solverName] = performance.now();
|
|
5453
6049
|
}
|
|
@@ -5455,9 +6051,13 @@ var CapacityMeshSolver = class extends BaseSolver {
|
|
|
5455
6051
|
return this.pipelineDef[this.currentPipelineStepIndex]?.solverName ?? "none";
|
|
5456
6052
|
}
|
|
5457
6053
|
visualize() {
|
|
5458
|
-
if (!this.solved && this.
|
|
6054
|
+
if (!this.solved && this.activeSubSolver)
|
|
6055
|
+
return this.activeSubSolver.visualize();
|
|
5459
6056
|
const netToPPSolver = this.netToPointPairsSolver?.visualize();
|
|
5460
6057
|
const nodeViz = this.nodeSolver?.visualize();
|
|
6058
|
+
const nodeTargetMergerViz = this.nodeTargetMerger?.visualize();
|
|
6059
|
+
const singleLayerNodeMergerViz = this.singleLayerNodeMerger?.visualize();
|
|
6060
|
+
const strawSolverViz = this.strawSolver?.visualize();
|
|
5461
6061
|
const edgeViz = this.edgeSolver?.visualize();
|
|
5462
6062
|
const pathingViz = this.pathingSolver?.visualize();
|
|
5463
6063
|
const edgeToPortSegmentViz = this.edgeToPortSegmentSolver?.visualize();
|
|
@@ -5470,14 +6070,37 @@ var CapacityMeshSolver = class extends BaseSolver {
|
|
|
5470
6070
|
rects: [
|
|
5471
6071
|
...(this.srj.obstacles ?? []).map((o) => ({
|
|
5472
6072
|
...o,
|
|
5473
|
-
fill: "rgba(255,0,0,0.25)"
|
|
6073
|
+
fill: o.layers?.includes("top") ? "rgba(255,0,0,0.25)" : o.layers?.includes("bottom") ? "rgba(0,0,255,0.25)" : "rgba(255,0,0,0.25)"
|
|
5474
6074
|
}))
|
|
6075
|
+
],
|
|
6076
|
+
lines: [
|
|
6077
|
+
{
|
|
6078
|
+
points: [
|
|
6079
|
+
// Add five points representing the bounds of the PCB
|
|
6080
|
+
{
|
|
6081
|
+
x: this.srj.bounds?.minX ?? -50,
|
|
6082
|
+
y: this.srj.bounds?.minY ?? -50
|
|
6083
|
+
},
|
|
6084
|
+
{ x: this.srj.bounds?.maxX ?? 50, y: this.srj.bounds?.minY ?? -50 },
|
|
6085
|
+
{ x: this.srj.bounds?.maxX ?? 50, y: this.srj.bounds?.maxY ?? 50 },
|
|
6086
|
+
{ x: this.srj.bounds?.minX ?? -50, y: this.srj.bounds?.maxY ?? 50 },
|
|
6087
|
+
{
|
|
6088
|
+
x: this.srj.bounds?.minX ?? -50,
|
|
6089
|
+
y: this.srj.bounds?.minY ?? -50
|
|
6090
|
+
}
|
|
6091
|
+
// Close the rectangle
|
|
6092
|
+
],
|
|
6093
|
+
strokeColor: "rgba(255,0,0,0.25)"
|
|
6094
|
+
}
|
|
5475
6095
|
]
|
|
5476
6096
|
};
|
|
5477
6097
|
const visualizations = [
|
|
5478
6098
|
problemViz,
|
|
5479
6099
|
netToPPSolver,
|
|
5480
6100
|
nodeViz,
|
|
6101
|
+
nodeTargetMergerViz,
|
|
6102
|
+
singleLayerNodeMergerViz,
|
|
6103
|
+
strawSolverViz,
|
|
5481
6104
|
edgeViz,
|
|
5482
6105
|
pathingViz,
|
|
5483
6106
|
edgeToPortSegmentViz,
|