@tscircuit/capacity-autorouter 0.0.20 → 0.0.22
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 +118 -32
- package/dist/index.js +1411 -730
- 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])) {
|
|
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,30 +196,35 @@ var CapacityMeshEdgeSolver = class extends BaseSolver {
|
|
|
180
196
|
}
|
|
181
197
|
this.solved = true;
|
|
182
198
|
}
|
|
183
|
-
|
|
184
|
-
|
|
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;
|
|
199
|
+
doNodesHaveSharedLayer(node1, node2) {
|
|
200
|
+
return node1.availableZ.some((z) => node2.availableZ.includes(z));
|
|
196
201
|
}
|
|
197
202
|
visualize() {
|
|
198
203
|
const graphics = {
|
|
199
204
|
lines: [],
|
|
200
205
|
points: [],
|
|
201
|
-
rects: this.nodes.map((node) =>
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
206
|
+
rects: this.nodes.map((node) => {
|
|
207
|
+
const lowestZ = Math.min(...node.availableZ);
|
|
208
|
+
return {
|
|
209
|
+
width: Math.max(node.width - 2, node.width * 0.8),
|
|
210
|
+
height: Math.max(node.height - 2, node.height * 0.8),
|
|
211
|
+
center: {
|
|
212
|
+
x: node.center.x + lowestZ * node.width * 0.05,
|
|
213
|
+
y: node.center.y - lowestZ * node.width * 0.05
|
|
214
|
+
},
|
|
215
|
+
fill: node._containsObstacle ? "rgba(255,0,0,0.1)" : {
|
|
216
|
+
"0,1": "rgba(0,0,0,0.1)",
|
|
217
|
+
"0": "rgba(0,200,200, 0.1)",
|
|
218
|
+
"1": "rgba(0,0,200, 0.1)"
|
|
219
|
+
}[node.availableZ.join(",")] ?? "rgba(0,200,200,0.1)",
|
|
220
|
+
label: [
|
|
221
|
+
node.capacityMeshNodeId,
|
|
222
|
+
`availableZ: ${node.availableZ.join(",")}`,
|
|
223
|
+
`target? ${node._containsTarget ?? false}`,
|
|
224
|
+
`obs? ${node._containsObstacle ?? false}`
|
|
225
|
+
].join("\n")
|
|
226
|
+
};
|
|
227
|
+
}),
|
|
207
228
|
circles: []
|
|
208
229
|
};
|
|
209
230
|
for (const edge of this.edges) {
|
|
@@ -214,8 +235,19 @@ var CapacityMeshEdgeSolver = class extends BaseSolver {
|
|
|
214
235
|
(node) => node.capacityMeshNodeId === edge.nodeIds[1]
|
|
215
236
|
);
|
|
216
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
|
+
};
|
|
217
248
|
graphics.lines.push({
|
|
218
|
-
points: [
|
|
249
|
+
points: [nodeCenter1Adj, nodeCenter2Adj],
|
|
250
|
+
strokeDash: node1.availableZ.join(",") === node2.availableZ.join(",") ? void 0 : "10 5"
|
|
219
251
|
});
|
|
220
252
|
}
|
|
221
253
|
}
|
|
@@ -1074,7 +1106,14 @@ function doRectsOverlap(rect1, rect2) {
|
|
|
1074
1106
|
return rect1Left <= rect2Right && rect1Right >= rect2Left && rect1Top <= rect2Bottom && rect1Bottom >= rect2Top;
|
|
1075
1107
|
}
|
|
1076
1108
|
|
|
1077
|
-
// lib/
|
|
1109
|
+
// lib/utils/mapLayerNameToZ.ts
|
|
1110
|
+
var mapLayerNameToZ = (layerName, layerCount) => {
|
|
1111
|
+
if (layerName === "top") return 0;
|
|
1112
|
+
if (layerName === "bottom") return layerCount - 1;
|
|
1113
|
+
return parseInt(layerName.slice(5));
|
|
1114
|
+
};
|
|
1115
|
+
|
|
1116
|
+
// lib/solvers/CapacityMeshSolver/CapacityMeshNodeSolver1.ts
|
|
1078
1117
|
var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
1079
1118
|
constructor(srj, opts = {}) {
|
|
1080
1119
|
super();
|
|
@@ -1082,6 +1121,16 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1082
1121
|
this.opts = opts;
|
|
1083
1122
|
this.MAX_DEPTH = opts?.capacityDepth ?? this.MAX_DEPTH;
|
|
1084
1123
|
this.MAX_ITERATIONS = 1e5;
|
|
1124
|
+
this.layerCount = srj.layerCount ?? 2;
|
|
1125
|
+
for (const obstacle of srj.obstacles) {
|
|
1126
|
+
if (!obstacle.zLayers) {
|
|
1127
|
+
const zLayers = [];
|
|
1128
|
+
for (const layer of obstacle.layers) {
|
|
1129
|
+
zLayers.push(mapLayerNameToZ(layer, srj.layerCount));
|
|
1130
|
+
}
|
|
1131
|
+
obstacle.zLayers = zLayers;
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1085
1134
|
const boundsCenter = {
|
|
1086
1135
|
x: (srj.bounds.minX + srj.bounds.maxX) / 2,
|
|
1087
1136
|
y: (srj.bounds.minY + srj.bounds.maxY) / 2
|
|
@@ -1106,7 +1155,7 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1106
1155
|
}
|
|
1107
1156
|
];
|
|
1108
1157
|
this.finishedNodes = [];
|
|
1109
|
-
this.
|
|
1158
|
+
this.nodeToXYOverlappingObstaclesMap = /* @__PURE__ */ new Map();
|
|
1110
1159
|
this.targets = this.srj.connections.flatMap(
|
|
1111
1160
|
(c) => c.pointsToConnect.map((p) => ({
|
|
1112
1161
|
...p,
|
|
@@ -1117,7 +1166,8 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1117
1166
|
}
|
|
1118
1167
|
unfinishedNodes;
|
|
1119
1168
|
finishedNodes;
|
|
1120
|
-
|
|
1169
|
+
nodeToXYOverlappingObstaclesMap;
|
|
1170
|
+
layerCount;
|
|
1121
1171
|
// targetObstacleMap: Record<string, { obstacle: Obstacle, node: CapacityMeshNode }>
|
|
1122
1172
|
MAX_DEPTH = 4;
|
|
1123
1173
|
targets;
|
|
@@ -1129,8 +1179,9 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1129
1179
|
return (this.MAX_DEPTH - depth + 1) ** 2;
|
|
1130
1180
|
}
|
|
1131
1181
|
getTargetIfNodeContainsTarget(node) {
|
|
1132
|
-
const overlappingObstacles = this.
|
|
1182
|
+
const overlappingObstacles = this.getXYOverlappingObstacles(node);
|
|
1133
1183
|
for (const target of this.targets) {
|
|
1184
|
+
if (!target.availableZ.some((z) => node.availableZ.includes(z))) continue;
|
|
1134
1185
|
const targetObstacle = overlappingObstacles.find(
|
|
1135
1186
|
(o) => isPointInRect(target, o)
|
|
1136
1187
|
);
|
|
@@ -1145,8 +1196,8 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1145
1196
|
}
|
|
1146
1197
|
return null;
|
|
1147
1198
|
}
|
|
1148
|
-
|
|
1149
|
-
const cachedObstacles = this.
|
|
1199
|
+
getXYOverlappingObstacles(node) {
|
|
1200
|
+
const cachedObstacles = this.nodeToXYOverlappingObstaclesMap.get(
|
|
1150
1201
|
node.capacityMeshNodeId
|
|
1151
1202
|
);
|
|
1152
1203
|
if (cachedObstacles) {
|
|
@@ -1157,7 +1208,7 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1157
1208
|
const nodeRight = node.center.x + node.width / 2;
|
|
1158
1209
|
const nodeTop = node.center.y - node.height / 2;
|
|
1159
1210
|
const nodeBottom = node.center.y + node.height / 2;
|
|
1160
|
-
const obstacles = node._parent ? this.
|
|
1211
|
+
const obstacles = node._parent ? this.getXYOverlappingObstacles(node._parent) : this.srj.obstacles;
|
|
1161
1212
|
for (const obstacle of obstacles) {
|
|
1162
1213
|
const obsLeft = obstacle.center.x - obstacle.width / 2;
|
|
1163
1214
|
const obsRight = obstacle.center.x + obstacle.width / 2;
|
|
@@ -1165,20 +1216,38 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1165
1216
|
const obsBottom = obstacle.center.y + obstacle.height / 2;
|
|
1166
1217
|
if (nodeRight >= obsLeft && nodeLeft <= obsRight && nodeBottom >= obsTop && nodeTop <= obsBottom) {
|
|
1167
1218
|
overlappingObstacles.push(obstacle);
|
|
1219
|
+
continue;
|
|
1220
|
+
}
|
|
1221
|
+
if (nodeLeft >= obsLeft && nodeRight <= obsRight && nodeTop >= obsTop && nodeBottom <= obsBottom) {
|
|
1222
|
+
overlappingObstacles.push(obstacle);
|
|
1223
|
+
continue;
|
|
1224
|
+
}
|
|
1225
|
+
if (obsLeft >= nodeLeft && obsRight <= nodeRight && obsTop >= nodeTop && obsBottom <= nodeBottom) {
|
|
1226
|
+
overlappingObstacles.push(obstacle);
|
|
1168
1227
|
}
|
|
1169
1228
|
}
|
|
1170
|
-
this.
|
|
1229
|
+
this.nodeToXYOverlappingObstaclesMap.set(
|
|
1171
1230
|
node.capacityMeshNodeId,
|
|
1172
1231
|
overlappingObstacles
|
|
1173
1232
|
);
|
|
1174
1233
|
return overlappingObstacles;
|
|
1175
1234
|
}
|
|
1235
|
+
getXYZOverlappingObstacles(node) {
|
|
1236
|
+
const xyOverlappingObstacles = this.getXYOverlappingObstacles(node);
|
|
1237
|
+
const xyzOverlappingObstacles = [];
|
|
1238
|
+
for (const obstacle of xyOverlappingObstacles) {
|
|
1239
|
+
if (node.availableZ.some((z) => obstacle.zLayers.includes(z))) {
|
|
1240
|
+
xyzOverlappingObstacles.push(obstacle);
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
return xyzOverlappingObstacles;
|
|
1244
|
+
}
|
|
1176
1245
|
/**
|
|
1177
1246
|
* Checks if the given mesh node overlaps with any obstacle.
|
|
1178
1247
|
* We treat both obstacles and nodes as axis‐aligned rectangles.
|
|
1179
1248
|
*/
|
|
1180
1249
|
doesNodeOverlapObstacle(node) {
|
|
1181
|
-
const overlappingObstacles = this.
|
|
1250
|
+
const overlappingObstacles = this.getXYZOverlappingObstacles(node);
|
|
1182
1251
|
if (overlappingObstacles.length > 0) {
|
|
1183
1252
|
return true;
|
|
1184
1253
|
}
|
|
@@ -1195,7 +1264,7 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1195
1264
|
* Checks if the entire node is contained within any obstacle.
|
|
1196
1265
|
*/
|
|
1197
1266
|
isNodeCompletelyInsideObstacle(node) {
|
|
1198
|
-
const overlappingObstacles = this.
|
|
1267
|
+
const overlappingObstacles = this.getXYZOverlappingObstacles(node);
|
|
1199
1268
|
const nodeLeft = node.center.x - node.width / 2;
|
|
1200
1269
|
const nodeRight = node.center.x + node.width / 2;
|
|
1201
1270
|
const nodeTop = node.center.y - node.height / 2;
|
|
@@ -1209,9 +1278,6 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1209
1278
|
return true;
|
|
1210
1279
|
}
|
|
1211
1280
|
}
|
|
1212
|
-
if (nodeRight < this.srj.bounds.minX || nodeLeft > this.srj.bounds.maxX || nodeBottom < this.srj.bounds.minY || nodeTop > this.srj.bounds.maxY) {
|
|
1213
|
-
return true;
|
|
1214
|
-
}
|
|
1215
1281
|
return false;
|
|
1216
1282
|
}
|
|
1217
1283
|
getChildNodes(parent) {
|
|
@@ -1263,7 +1329,7 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1263
1329
|
}
|
|
1264
1330
|
return childNodes;
|
|
1265
1331
|
}
|
|
1266
|
-
|
|
1332
|
+
shouldNodeBeXYSubdivided(node) {
|
|
1267
1333
|
if (node._depth >= this.MAX_DEPTH) return false;
|
|
1268
1334
|
if (node._containsTarget) return true;
|
|
1269
1335
|
if (node._containsObstacle && !node._completelyInsideObstacle) return true;
|
|
@@ -1279,7 +1345,7 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1279
1345
|
const finishedNewNodes = [];
|
|
1280
1346
|
const unfinishedNewNodes = [];
|
|
1281
1347
|
for (const newNode of newNodes) {
|
|
1282
|
-
const shouldBeSubdivided = this.
|
|
1348
|
+
const shouldBeSubdivided = this.shouldNodeBeXYSubdivided(newNode);
|
|
1283
1349
|
if (shouldBeSubdivided) {
|
|
1284
1350
|
unfinishedNewNodes.push(newNode);
|
|
1285
1351
|
} else if (!shouldBeSubdivided && !newNode._containsObstacle) {
|
|
@@ -1315,22 +1381,37 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1315
1381
|
center: obstacle.center,
|
|
1316
1382
|
width: obstacle.width,
|
|
1317
1383
|
height: obstacle.height,
|
|
1318
|
-
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)",
|
|
1319
1385
|
stroke: "red",
|
|
1320
|
-
label: "obstacle"
|
|
1386
|
+
label: ["obstacle", `z: ${obstacle.zLayers.join(",")}`].join("\n")
|
|
1321
1387
|
});
|
|
1322
1388
|
}
|
|
1323
1389
|
const allNodes = [...this.finishedNodes, ...this.unfinishedNodes];
|
|
1324
1390
|
for (const node of allNodes) {
|
|
1391
|
+
const lowestZ = Math.min(...node.availableZ);
|
|
1392
|
+
const isNextToBeProcessed = this.unfinishedNodes.length > 0 && node === this.unfinishedNodes[this.unfinishedNodes.length - 1];
|
|
1325
1393
|
graphics.rects.push({
|
|
1326
|
-
center:
|
|
1394
|
+
center: {
|
|
1395
|
+
x: node.center.x + lowestZ * node.width * 0.05,
|
|
1396
|
+
y: node.center.y - lowestZ * node.width * 0.05
|
|
1397
|
+
},
|
|
1327
1398
|
width: Math.max(node.width - 2, node.width * 0.8),
|
|
1328
1399
|
height: Math.max(node.height - 2, node.height * 0.8),
|
|
1329
|
-
fill: node._containsObstacle ? "rgba(255,0,0,0.1)" :
|
|
1330
|
-
|
|
1331
|
-
|
|
1400
|
+
fill: node._containsObstacle ? "rgba(255,0,0,0.1)" : {
|
|
1401
|
+
"0,1": "rgba(0,0,0,0.1)",
|
|
1402
|
+
"0": "rgba(0,200,200, 0.1)",
|
|
1403
|
+
"1": "rgba(0,0,200, 0.1)"
|
|
1404
|
+
}[node.availableZ.join(",")] ?? "rgba(0,200,200,0.1)",
|
|
1405
|
+
stroke: isNextToBeProcessed ? "rgba(255,165,0,0.5)" : void 0,
|
|
1406
|
+
label: [
|
|
1407
|
+
node.capacityMeshNodeId,
|
|
1408
|
+
`availableZ: ${node.availableZ.join(",")}`,
|
|
1409
|
+
`target? ${node._containsTarget ?? false}`,
|
|
1410
|
+
`obs? ${node._containsObstacle ?? false}`
|
|
1411
|
+
].join("\n")
|
|
1332
1412
|
});
|
|
1333
1413
|
}
|
|
1414
|
+
graphics.rects.sort((a, b) => a.center.y - b.center.y);
|
|
1334
1415
|
this.srj.connections.forEach((connection, index) => {
|
|
1335
1416
|
const color = COLORS[index % COLORS.length];
|
|
1336
1417
|
for (const pt of connection.pointsToConnect) {
|
|
@@ -1346,6 +1427,146 @@ availableZ: ${node.availableZ.join(",")}`
|
|
|
1346
1427
|
}
|
|
1347
1428
|
};
|
|
1348
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
|
+
|
|
1349
1570
|
// lib/solvers/CapacityMeshSolver/getNodeEdgeMap.ts
|
|
1350
1571
|
function getNodeEdgeMap(edges) {
|
|
1351
1572
|
const nodeEdgeMap = /* @__PURE__ */ new Map();
|
|
@@ -2816,294 +3037,94 @@ var HighDensitySolver = class extends BaseSolver {
|
|
|
2816
3037
|
}
|
|
2817
3038
|
};
|
|
2818
3039
|
|
|
2819
|
-
//
|
|
2820
|
-
var
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
maxDepthOfNodes;
|
|
2832
|
-
activeCandidateStraightLineDistance;
|
|
2833
|
-
hyperParameters;
|
|
2834
|
-
constructor({
|
|
2835
|
-
simpleRouteJson,
|
|
2836
|
-
nodes,
|
|
2837
|
-
edges,
|
|
2838
|
-
colorMap,
|
|
2839
|
-
MAX_ITERATIONS = 1e6,
|
|
2840
|
-
hyperParameters = {}
|
|
2841
|
-
}) {
|
|
2842
|
-
super();
|
|
2843
|
-
this.MAX_ITERATIONS = MAX_ITERATIONS;
|
|
2844
|
-
this.simpleRouteJson = simpleRouteJson;
|
|
2845
|
-
this.nodes = nodes;
|
|
2846
|
-
this.edges = edges;
|
|
2847
|
-
this.colorMap = colorMap ?? {};
|
|
2848
|
-
const { connectionsWithNodes, connectionNameToGoalNodeIds } = this.getConnectionsWithNodes();
|
|
2849
|
-
this.connectionsWithNodes = connectionsWithNodes;
|
|
2850
|
-
this.connectionNameToGoalNodeIds = connectionNameToGoalNodeIds;
|
|
2851
|
-
this.hyperParameters = hyperParameters;
|
|
2852
|
-
this.usedNodeCapacityMap = new Map(
|
|
2853
|
-
this.nodes.map((node) => [node.capacityMeshNodeId, 0])
|
|
2854
|
-
);
|
|
2855
|
-
this.nodeMap = new Map(
|
|
2856
|
-
this.nodes.map((node) => [node.capacityMeshNodeId, node])
|
|
2857
|
-
);
|
|
2858
|
-
this.nodeEdgeMap = getNodeEdgeMap(this.edges);
|
|
2859
|
-
this.maxDepthOfNodes = Math.max(
|
|
2860
|
-
...this.nodes.map((node) => node._depth ?? 0)
|
|
2861
|
-
);
|
|
2862
|
-
}
|
|
2863
|
-
getTotalCapacity(node) {
|
|
2864
|
-
const depth = node._depth ?? 0;
|
|
2865
|
-
return (this.maxDepthOfNodes - depth + 1) ** 2;
|
|
3040
|
+
// node_modules/circuit-json-to-connectivity-map/dist/index.js
|
|
3041
|
+
var ConnectivityMap = class {
|
|
3042
|
+
netMap;
|
|
3043
|
+
idToNetMap;
|
|
3044
|
+
constructor(netMap) {
|
|
3045
|
+
this.netMap = netMap;
|
|
3046
|
+
this.idToNetMap = {};
|
|
3047
|
+
for (const [netId, ids] of Object.entries(netMap)) {
|
|
3048
|
+
for (const id of ids) {
|
|
3049
|
+
this.idToNetMap[id] = netId;
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
2866
3052
|
}
|
|
2867
|
-
|
|
2868
|
-
const
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
3053
|
+
addConnections(connections) {
|
|
3054
|
+
for (const connection of connections) {
|
|
3055
|
+
const existingNets = /* @__PURE__ */ new Set();
|
|
3056
|
+
for (const id of connection) {
|
|
3057
|
+
const existingNetId = this.idToNetMap[id];
|
|
3058
|
+
if (existingNetId) {
|
|
3059
|
+
existingNets.add(existingNetId);
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
3062
|
+
let targetNetId;
|
|
3063
|
+
if (existingNets.size === 0) {
|
|
3064
|
+
targetNetId = `connectivity_net${Object.keys(this.netMap).length}`;
|
|
3065
|
+
this.netMap[targetNetId] = [];
|
|
3066
|
+
} else if (existingNets.size === 1) {
|
|
3067
|
+
targetNetId = existingNets.values().next().value ?? `connectivity_net${Object.keys(this.netMap).length}`;
|
|
3068
|
+
} else {
|
|
3069
|
+
targetNetId = existingNets.values().next().value ?? `connectivity_net${Object.keys(this.netMap).length}`;
|
|
3070
|
+
for (const netId of existingNets) {
|
|
3071
|
+
if (netId !== targetNetId) {
|
|
3072
|
+
this.netMap[targetNetId].push(...this.netMap[netId]);
|
|
3073
|
+
this.netMap[netId] = this.netMap[targetNetId];
|
|
3074
|
+
for (const id of this.netMap[targetNetId]) {
|
|
3075
|
+
this.idToNetMap[id] = targetNetId;
|
|
3076
|
+
}
|
|
2883
3077
|
}
|
|
2884
3078
|
}
|
|
2885
|
-
nodesForConnection.push(closestNode);
|
|
2886
3079
|
}
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
3080
|
+
for (const id of connection) {
|
|
3081
|
+
if (!this.netMap[targetNetId].includes(id)) {
|
|
3082
|
+
this.netMap[targetNetId].push(id);
|
|
3083
|
+
}
|
|
3084
|
+
this.idToNetMap[id] = targetNetId;
|
|
2891
3085
|
}
|
|
2892
|
-
connectionNameToGoalNodeIds.set(
|
|
2893
|
-
connection.name,
|
|
2894
|
-
nodesForConnection.map((n) => n.capacityMeshNodeId)
|
|
2895
|
-
);
|
|
2896
|
-
connectionsWithNodes.push({
|
|
2897
|
-
connection,
|
|
2898
|
-
nodes: nodesForConnection,
|
|
2899
|
-
pathFound: false
|
|
2900
|
-
});
|
|
2901
3086
|
}
|
|
2902
|
-
return { connectionsWithNodes, connectionNameToGoalNodeIds };
|
|
2903
|
-
}
|
|
2904
|
-
currentConnectionIndex = 0;
|
|
2905
|
-
candidates;
|
|
2906
|
-
visitedNodes;
|
|
2907
|
-
computeG(prevCandidate, node, endGoal) {
|
|
2908
|
-
return prevCandidate.g + this.getDistanceBetweenNodes(prevCandidate.node, node);
|
|
2909
|
-
}
|
|
2910
|
-
computeH(prevCandidate, node, endGoal) {
|
|
2911
|
-
return this.getDistanceBetweenNodes(node, endGoal);
|
|
2912
3087
|
}
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
let currentCandidate = candidate;
|
|
2916
|
-
while (currentCandidate) {
|
|
2917
|
-
path.push(currentCandidate.node);
|
|
2918
|
-
currentCandidate = currentCandidate.prevCandidate;
|
|
2919
|
-
}
|
|
2920
|
-
return path;
|
|
3088
|
+
getIdsConnectedToNet(netId) {
|
|
3089
|
+
return this.netMap[netId] || [];
|
|
2921
3090
|
}
|
|
2922
|
-
|
|
2923
|
-
return this.
|
|
2924
|
-
(edge) => edge.nodeIds.filter((n) => n !== node.capacityMeshNodeId)
|
|
2925
|
-
).map((n) => this.nodeMap.get(n));
|
|
3091
|
+
getNetConnectedToId(id) {
|
|
3092
|
+
return this.idToNetMap[id];
|
|
2926
3093
|
}
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
connectionName: connection.connection.name,
|
|
2935
|
-
nodeIds: path.map((node) => node.capacityMeshNodeId)
|
|
2936
|
-
});
|
|
2937
|
-
}
|
|
2938
|
-
}
|
|
2939
|
-
return capacityPaths;
|
|
2940
|
-
}
|
|
2941
|
-
doesNodeHaveCapacityForTrace(node) {
|
|
2942
|
-
const usedCapacity = this.usedNodeCapacityMap.get(node.capacityMeshNodeId) ?? 0;
|
|
2943
|
-
const totalCapacity = this.getTotalCapacity(node);
|
|
2944
|
-
return usedCapacity < totalCapacity;
|
|
2945
|
-
}
|
|
2946
|
-
canTravelThroughObstacle(node, connectionName) {
|
|
2947
|
-
const goalNodeIds = this.connectionNameToGoalNodeIds.get(connectionName);
|
|
2948
|
-
return goalNodeIds?.includes(node.capacityMeshNodeId) ?? false;
|
|
2949
|
-
}
|
|
2950
|
-
getDistanceBetweenNodes(A, B) {
|
|
2951
|
-
return Math.sqrt(
|
|
2952
|
-
(A.center.x - B.center.x) ** 2 + (A.center.y - B.center.y) ** 2
|
|
2953
|
-
);
|
|
2954
|
-
}
|
|
2955
|
-
reduceCapacityAlongPath(nextConnection) {
|
|
2956
|
-
for (const node of nextConnection.path ?? []) {
|
|
2957
|
-
this.usedNodeCapacityMap.set(
|
|
2958
|
-
node.capacityMeshNodeId,
|
|
2959
|
-
this.usedNodeCapacityMap.get(node.capacityMeshNodeId) + 1
|
|
2960
|
-
);
|
|
2961
|
-
}
|
|
2962
|
-
}
|
|
2963
|
-
isConnectedToEndGoal(node, endGoal) {
|
|
2964
|
-
return this.nodeEdgeMap.get(node.capacityMeshNodeId).some((edge) => edge.nodeIds.includes(endGoal.capacityMeshNodeId));
|
|
3094
|
+
areIdsConnected(id1, id2) {
|
|
3095
|
+
if (id1 === id2) return true;
|
|
3096
|
+
const netId1 = this.getNetConnectedToId(id1);
|
|
3097
|
+
if (!netId1) return false;
|
|
3098
|
+
const netId2 = this.getNetConnectedToId(id2);
|
|
3099
|
+
if (!netId2) return false;
|
|
3100
|
+
return netId1 === netId2 || netId2 === id1 || netId2 === id1;
|
|
2965
3101
|
}
|
|
2966
|
-
|
|
2967
|
-
const
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
const [start, end] = nextConnection.nodes;
|
|
2973
|
-
if (!this.candidates) {
|
|
2974
|
-
this.candidates = [{ prevCandidate: null, node: start, f: 0, g: 0, h: 0 }];
|
|
2975
|
-
this.visitedNodes = /* @__PURE__ */ new Set([start.capacityMeshNodeId]);
|
|
2976
|
-
this.activeCandidateStraightLineDistance = distance(
|
|
2977
|
-
start.center,
|
|
2978
|
-
end.center
|
|
2979
|
-
);
|
|
2980
|
-
}
|
|
2981
|
-
this.candidates.sort((a, b) => a.f - b.f);
|
|
2982
|
-
const currentCandidate = this.candidates.shift();
|
|
2983
|
-
if (!currentCandidate) {
|
|
2984
|
-
console.error(
|
|
2985
|
-
`Ran out of candidates on connection ${nextConnection.connection.name}`
|
|
2986
|
-
);
|
|
2987
|
-
this.currentConnectionIndex++;
|
|
2988
|
-
this.candidates = null;
|
|
2989
|
-
this.visitedNodes = null;
|
|
2990
|
-
return;
|
|
2991
|
-
}
|
|
2992
|
-
if (this.isConnectedToEndGoal(currentCandidate.node, end)) {
|
|
2993
|
-
nextConnection.path = this.getBacktrackedPath({
|
|
2994
|
-
prevCandidate: currentCandidate,
|
|
2995
|
-
node: end,
|
|
2996
|
-
f: 0,
|
|
2997
|
-
g: 0,
|
|
2998
|
-
h: 0
|
|
2999
|
-
});
|
|
3000
|
-
this.reduceCapacityAlongPath(nextConnection);
|
|
3001
|
-
this.currentConnectionIndex++;
|
|
3002
|
-
this.candidates = null;
|
|
3003
|
-
this.visitedNodes = null;
|
|
3004
|
-
return;
|
|
3005
|
-
}
|
|
3006
|
-
const neighborNodes = this.getNeighboringNodes(currentCandidate.node);
|
|
3007
|
-
for (const neighborNode of neighborNodes) {
|
|
3008
|
-
if (this.visitedNodes?.has(neighborNode.capacityMeshNodeId)) {
|
|
3009
|
-
continue;
|
|
3010
|
-
}
|
|
3011
|
-
if (!this.doesNodeHaveCapacityForTrace(neighborNode)) {
|
|
3012
|
-
continue;
|
|
3102
|
+
areAllIdsConnected(ids) {
|
|
3103
|
+
const netId = this.getNetConnectedToId(ids[0]);
|
|
3104
|
+
for (const id of ids) {
|
|
3105
|
+
const nextNetId = this.getNetConnectedToId(id);
|
|
3106
|
+
if (nextNetId === void 0) {
|
|
3107
|
+
return false;
|
|
3013
3108
|
}
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
continue;
|
|
3109
|
+
if (nextNetId !== netId) {
|
|
3110
|
+
return false;
|
|
3017
3111
|
}
|
|
3018
|
-
const g = this.computeG(currentCandidate, neighborNode, end);
|
|
3019
|
-
const h = this.computeH(currentCandidate, neighborNode, end);
|
|
3020
|
-
const f = g + h * this.GREEDY_MULTIPLIER;
|
|
3021
|
-
const newCandidate = {
|
|
3022
|
-
prevCandidate: currentCandidate,
|
|
3023
|
-
node: neighborNode,
|
|
3024
|
-
f,
|
|
3025
|
-
g,
|
|
3026
|
-
h
|
|
3027
|
-
};
|
|
3028
|
-
this.candidates.push(newCandidate);
|
|
3029
3112
|
}
|
|
3030
|
-
|
|
3113
|
+
return true;
|
|
3031
3114
|
}
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
const conn = this.connectionsWithNodes[i];
|
|
3042
|
-
if (conn.path && conn.path.length > 0) {
|
|
3043
|
-
const pathPoints = conn.path.map(({ center: { x, y }, width }) => ({
|
|
3044
|
-
// slight offset to allow viewing overlapping paths
|
|
3045
|
-
x: x + (i % 10 + i % 19) * (0.01 * width),
|
|
3046
|
-
y: y + (i % 10 + i % 19) * (0.01 * width)
|
|
3047
|
-
}));
|
|
3048
|
-
graphics.lines.push({
|
|
3049
|
-
points: pathPoints,
|
|
3050
|
-
strokeColor: this.colorMap[conn.connection.name]
|
|
3051
|
-
});
|
|
3052
|
-
}
|
|
3053
|
-
}
|
|
3054
|
-
}
|
|
3055
|
-
for (const node of this.nodes) {
|
|
3056
|
-
graphics.rects.push({
|
|
3057
|
-
center: node.center,
|
|
3058
|
-
width: Math.max(node.width - 2, node.width * 0.8),
|
|
3059
|
-
height: Math.max(node.height - 2, node.height * 0.8),
|
|
3060
|
-
fill: node._containsObstacle ? "rgba(255,0,0,0.1)" : "rgba(0,0,0,0.1)",
|
|
3061
|
-
label: `${node.capacityMeshNodeId}
|
|
3062
|
-
${this.usedNodeCapacityMap.get(node.capacityMeshNodeId)}/${this.getTotalCapacity(node).toFixed(2)}
|
|
3063
|
-
${node.width.toFixed(2)}x${node.height.toFixed(2)}`
|
|
3064
|
-
});
|
|
3065
|
-
}
|
|
3066
|
-
if (this.connectionsWithNodes) {
|
|
3067
|
-
for (const conn of this.connectionsWithNodes) {
|
|
3068
|
-
if (conn.connection?.pointsToConnect) {
|
|
3069
|
-
for (const point of conn.connection.pointsToConnect) {
|
|
3070
|
-
graphics.points.push({
|
|
3071
|
-
x: point.x,
|
|
3072
|
-
y: point.y
|
|
3073
|
-
});
|
|
3074
|
-
}
|
|
3075
|
-
}
|
|
3115
|
+
};
|
|
3116
|
+
|
|
3117
|
+
// lib/utils/getConnectivityMapFromSimpleRouteJson.ts
|
|
3118
|
+
var getConnectivityMapFromSimpleRouteJson = (srj) => {
|
|
3119
|
+
const connMap = new ConnectivityMap({});
|
|
3120
|
+
for (const connection of srj.connections) {
|
|
3121
|
+
for (const point of connection.pointsToConnect) {
|
|
3122
|
+
if ("pcb_port_id" in point && point.pcb_port_id) {
|
|
3123
|
+
connMap.addConnections([[connection.name, point.pcb_port_id]]);
|
|
3076
3124
|
}
|
|
3077
3125
|
}
|
|
3078
|
-
const nextConnection = this.connectionsWithNodes[this.currentConnectionIndex];
|
|
3079
|
-
if (nextConnection) {
|
|
3080
|
-
const [start, end] = nextConnection.connection.pointsToConnect;
|
|
3081
|
-
graphics.lines.push({
|
|
3082
|
-
points: [
|
|
3083
|
-
{ x: start.x, y: start.y },
|
|
3084
|
-
{ x: end.x, y: end.y }
|
|
3085
|
-
],
|
|
3086
|
-
strokeColor: "red",
|
|
3087
|
-
strokeDash: "10 5"
|
|
3088
|
-
});
|
|
3089
|
-
}
|
|
3090
|
-
if (this.candidates) {
|
|
3091
|
-
const topCandidates = this.candidates.slice(0, 5);
|
|
3092
|
-
const connectionName = this.connectionsWithNodes[this.currentConnectionIndex].connection.name;
|
|
3093
|
-
topCandidates.forEach((candidate, index) => {
|
|
3094
|
-
const opacity = 0.5 * (1 - index / 5);
|
|
3095
|
-
const backtrackedPath = this.getBacktrackedPath(candidate);
|
|
3096
|
-
graphics.lines.push({
|
|
3097
|
-
points: backtrackedPath.map(({ center: { x, y } }) => ({ x, y })),
|
|
3098
|
-
strokeColor: safeTransparentize(
|
|
3099
|
-
this.colorMap[connectionName] ?? "red",
|
|
3100
|
-
1 - opacity
|
|
3101
|
-
)
|
|
3102
|
-
});
|
|
3103
|
-
});
|
|
3104
|
-
}
|
|
3105
|
-
return graphics;
|
|
3106
3126
|
}
|
|
3127
|
+
return connMap;
|
|
3107
3128
|
};
|
|
3108
3129
|
|
|
3109
3130
|
// lib/utils/getTunedTotalCapacity1.ts
|
|
@@ -3129,267 +3150,44 @@ var calculateOptimalCapacityDepth = (initialWidth, targetMinCapacity = 0.5, maxD
|
|
|
3129
3150
|
return Math.max(1, depth);
|
|
3130
3151
|
};
|
|
3131
3152
|
|
|
3132
|
-
// lib/solvers/
|
|
3133
|
-
var
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3153
|
+
// lib/solvers/NetToPointPairsSolver/buildMinimumSpanningTree.ts
|
|
3154
|
+
var KDNode = class {
|
|
3155
|
+
point;
|
|
3156
|
+
left = null;
|
|
3157
|
+
right = null;
|
|
3158
|
+
constructor(point) {
|
|
3159
|
+
this.point = point;
|
|
3138
3160
|
}
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3161
|
+
};
|
|
3162
|
+
var KDTree = class {
|
|
3163
|
+
root = null;
|
|
3164
|
+
constructor(points) {
|
|
3165
|
+
if (points.length > 0) {
|
|
3166
|
+
this.root = this.buildTree(points, 0);
|
|
3167
|
+
}
|
|
3144
3168
|
}
|
|
3145
|
-
|
|
3146
|
-
|
|
3169
|
+
buildTree(points, depth) {
|
|
3170
|
+
const axis = depth % 2 === 0 ? "x" : "y";
|
|
3171
|
+
points.sort((a, b) => a[axis] - b[axis]);
|
|
3172
|
+
const medianIndex = Math.floor(points.length / 2);
|
|
3173
|
+
const node = new KDNode(points[medianIndex]);
|
|
3174
|
+
if (medianIndex > 0) {
|
|
3175
|
+
node.left = this.buildTree(points.slice(0, medianIndex), depth + 1);
|
|
3176
|
+
}
|
|
3177
|
+
if (medianIndex < points.length - 1) {
|
|
3178
|
+
node.right = this.buildTree(points.slice(medianIndex + 1), depth + 1);
|
|
3179
|
+
}
|
|
3180
|
+
return node;
|
|
3147
3181
|
}
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
const totalCapacity = this.getTotalCapacity(node);
|
|
3153
|
-
const usedCapacity = this.usedNodeCapacityMap.get(node.capacityMeshNodeId) ?? 0;
|
|
3154
|
-
const remainingCapacity = totalCapacity - usedCapacity;
|
|
3155
|
-
const dist = this.activeCandidateStraightLineDistance;
|
|
3156
|
-
if (remainingCapacity <= 0) {
|
|
3157
|
-
const penalty = (-remainingCapacity + 1) / totalCapacity * dist * (this.NEGATIVE_CAPACITY_PENALTY_FACTOR / 4);
|
|
3158
|
-
return penalty ** 2;
|
|
3182
|
+
// Find the nearest neighbor to a query point
|
|
3183
|
+
findNearestNeighbor(queryPoint) {
|
|
3184
|
+
if (!this.root) {
|
|
3185
|
+
throw new Error("Tree is empty");
|
|
3159
3186
|
}
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
*
|
|
3165
|
-
* To minimize shortest path, you'd want to comment this out.
|
|
3166
|
-
*/
|
|
3167
|
-
getDistanceBetweenNodes(A, B) {
|
|
3168
|
-
const dx = A.center.x - B.center.x;
|
|
3169
|
-
const dy = A.center.y - B.center.y;
|
|
3170
|
-
const szx = Math.max(A.width, B.width);
|
|
3171
|
-
const szy = Math.max(A.height, B.height);
|
|
3172
|
-
const dist = Math.sqrt(dx ** 2 + dy ** 2) / (szx * szy);
|
|
3173
|
-
return dist;
|
|
3174
|
-
}
|
|
3175
|
-
computeG(prevCandidate, node, endGoal) {
|
|
3176
|
-
return prevCandidate.g + this.getDistanceBetweenNodes(prevCandidate.node, node) + this.getNodeCapacityPenalty(node);
|
|
3177
|
-
}
|
|
3178
|
-
computeH(prevCandidate, node, endGoal) {
|
|
3179
|
-
return this.getDistanceBetweenNodes(node, endGoal) + this.getNodeCapacityPenalty(node);
|
|
3180
|
-
}
|
|
3181
|
-
};
|
|
3182
|
-
|
|
3183
|
-
// node_modules/circuit-json-to-connectivity-map/dist/index.js
|
|
3184
|
-
var ConnectivityMap = class {
|
|
3185
|
-
netMap;
|
|
3186
|
-
idToNetMap;
|
|
3187
|
-
constructor(netMap) {
|
|
3188
|
-
this.netMap = netMap;
|
|
3189
|
-
this.idToNetMap = {};
|
|
3190
|
-
for (const [netId, ids] of Object.entries(netMap)) {
|
|
3191
|
-
for (const id of ids) {
|
|
3192
|
-
this.idToNetMap[id] = netId;
|
|
3193
|
-
}
|
|
3194
|
-
}
|
|
3195
|
-
}
|
|
3196
|
-
addConnections(connections) {
|
|
3197
|
-
for (const connection of connections) {
|
|
3198
|
-
const existingNets = /* @__PURE__ */ new Set();
|
|
3199
|
-
for (const id of connection) {
|
|
3200
|
-
const existingNetId = this.idToNetMap[id];
|
|
3201
|
-
if (existingNetId) {
|
|
3202
|
-
existingNets.add(existingNetId);
|
|
3203
|
-
}
|
|
3204
|
-
}
|
|
3205
|
-
let targetNetId;
|
|
3206
|
-
if (existingNets.size === 0) {
|
|
3207
|
-
targetNetId = `connectivity_net${Object.keys(this.netMap).length}`;
|
|
3208
|
-
this.netMap[targetNetId] = [];
|
|
3209
|
-
} else if (existingNets.size === 1) {
|
|
3210
|
-
targetNetId = existingNets.values().next().value ?? `connectivity_net${Object.keys(this.netMap).length}`;
|
|
3211
|
-
} else {
|
|
3212
|
-
targetNetId = existingNets.values().next().value ?? `connectivity_net${Object.keys(this.netMap).length}`;
|
|
3213
|
-
for (const netId of existingNets) {
|
|
3214
|
-
if (netId !== targetNetId) {
|
|
3215
|
-
this.netMap[targetNetId].push(...this.netMap[netId]);
|
|
3216
|
-
this.netMap[netId] = this.netMap[targetNetId];
|
|
3217
|
-
for (const id of this.netMap[targetNetId]) {
|
|
3218
|
-
this.idToNetMap[id] = targetNetId;
|
|
3219
|
-
}
|
|
3220
|
-
}
|
|
3221
|
-
}
|
|
3222
|
-
}
|
|
3223
|
-
for (const id of connection) {
|
|
3224
|
-
if (!this.netMap[targetNetId].includes(id)) {
|
|
3225
|
-
this.netMap[targetNetId].push(id);
|
|
3226
|
-
}
|
|
3227
|
-
this.idToNetMap[id] = targetNetId;
|
|
3228
|
-
}
|
|
3229
|
-
}
|
|
3230
|
-
}
|
|
3231
|
-
getIdsConnectedToNet(netId) {
|
|
3232
|
-
return this.netMap[netId] || [];
|
|
3233
|
-
}
|
|
3234
|
-
getNetConnectedToId(id) {
|
|
3235
|
-
return this.idToNetMap[id];
|
|
3236
|
-
}
|
|
3237
|
-
areIdsConnected(id1, id2) {
|
|
3238
|
-
if (id1 === id2) return true;
|
|
3239
|
-
const netId1 = this.getNetConnectedToId(id1);
|
|
3240
|
-
if (!netId1) return false;
|
|
3241
|
-
const netId2 = this.getNetConnectedToId(id2);
|
|
3242
|
-
if (!netId2) return false;
|
|
3243
|
-
return netId1 === netId2 || netId2 === id1 || netId2 === id1;
|
|
3244
|
-
}
|
|
3245
|
-
areAllIdsConnected(ids) {
|
|
3246
|
-
const netId = this.getNetConnectedToId(ids[0]);
|
|
3247
|
-
for (const id of ids) {
|
|
3248
|
-
const nextNetId = this.getNetConnectedToId(id);
|
|
3249
|
-
if (nextNetId === void 0) {
|
|
3250
|
-
return false;
|
|
3251
|
-
}
|
|
3252
|
-
if (nextNetId !== netId) {
|
|
3253
|
-
return false;
|
|
3254
|
-
}
|
|
3255
|
-
}
|
|
3256
|
-
return true;
|
|
3257
|
-
}
|
|
3258
|
-
};
|
|
3259
|
-
|
|
3260
|
-
// lib/utils/getConnectivityMapFromSimpleRouteJson.ts
|
|
3261
|
-
var getConnectivityMapFromSimpleRouteJson = (srj) => {
|
|
3262
|
-
const connMap = new ConnectivityMap({});
|
|
3263
|
-
for (const connection of srj.connections) {
|
|
3264
|
-
for (const point of connection.pointsToConnect) {
|
|
3265
|
-
if ("pcb_port_id" in point && point.pcb_port_id) {
|
|
3266
|
-
connMap.addConnections([[connection.name, point.pcb_port_id]]);
|
|
3267
|
-
}
|
|
3268
|
-
}
|
|
3269
|
-
}
|
|
3270
|
-
return connMap;
|
|
3271
|
-
};
|
|
3272
|
-
|
|
3273
|
-
// lib/solvers/CapacityMeshSolver/CapacityNodeTargetMerger.ts
|
|
3274
|
-
var CapacityNodeTargetMerger = class extends BaseSolver {
|
|
3275
|
-
constructor(nodes, obstacles, connMap) {
|
|
3276
|
-
super();
|
|
3277
|
-
this.nodes = nodes;
|
|
3278
|
-
this.connMap = connMap;
|
|
3279
|
-
this.MAX_ITERATIONS = 1e5;
|
|
3280
|
-
this.unprocessedObstacles = [...obstacles];
|
|
3281
|
-
this.newNodes = [];
|
|
3282
|
-
this.removedNodeIds = /* @__PURE__ */ new Set();
|
|
3283
|
-
}
|
|
3284
|
-
unprocessedObstacles;
|
|
3285
|
-
newNodes;
|
|
3286
|
-
removedNodeIds;
|
|
3287
|
-
_step() {
|
|
3288
|
-
const obstacle = this.unprocessedObstacles.pop();
|
|
3289
|
-
if (!obstacle) {
|
|
3290
|
-
for (const node of this.nodes) {
|
|
3291
|
-
if (this.removedNodeIds.has(node.capacityMeshNodeId)) continue;
|
|
3292
|
-
this.newNodes.push(node);
|
|
3293
|
-
}
|
|
3294
|
-
this.solved = true;
|
|
3295
|
-
return;
|
|
3296
|
-
}
|
|
3297
|
-
const connectedNodes = this.nodes.filter((n) => {
|
|
3298
|
-
if (!n._targetConnectionName) return false;
|
|
3299
|
-
const implicitlyConnected = doRectsOverlap(n, obstacle);
|
|
3300
|
-
return implicitlyConnected;
|
|
3301
|
-
});
|
|
3302
|
-
if (connectedNodes.length === 0) return;
|
|
3303
|
-
const connectionName = connectedNodes[0]._targetConnectionName;
|
|
3304
|
-
const bounds = {
|
|
3305
|
-
minX: Infinity,
|
|
3306
|
-
minY: Infinity,
|
|
3307
|
-
maxX: -Infinity,
|
|
3308
|
-
maxY: -Infinity
|
|
3309
|
-
};
|
|
3310
|
-
for (const node of connectedNodes) {
|
|
3311
|
-
bounds.minX = Math.min(bounds.minX, node.center.x - node.width / 2);
|
|
3312
|
-
bounds.minY = Math.min(bounds.minY, node.center.y - node.height / 2);
|
|
3313
|
-
bounds.maxX = Math.max(bounds.maxX, node.center.x + node.width / 2);
|
|
3314
|
-
bounds.maxY = Math.max(bounds.maxY, node.center.y + node.height / 2);
|
|
3315
|
-
}
|
|
3316
|
-
const newNode = {
|
|
3317
|
-
capacityMeshNodeId: connectedNodes[0].capacityMeshNodeId,
|
|
3318
|
-
center: {
|
|
3319
|
-
x: (bounds.minX + bounds.maxX) / 2,
|
|
3320
|
-
y: (bounds.minY + bounds.maxY) / 2
|
|
3321
|
-
},
|
|
3322
|
-
width: bounds.maxX - bounds.minX,
|
|
3323
|
-
height: bounds.maxY - bounds.minY,
|
|
3324
|
-
layer: connectedNodes[0].layer,
|
|
3325
|
-
availableZ: connectedNodes[0].availableZ,
|
|
3326
|
-
_completelyInsideObstacle: false,
|
|
3327
|
-
_containsObstacle: true,
|
|
3328
|
-
_containsTarget: true,
|
|
3329
|
-
_targetConnectionName: connectionName,
|
|
3330
|
-
_depth: connectedNodes[0]._depth,
|
|
3331
|
-
_parent: connectedNodes[0]._parent
|
|
3332
|
-
};
|
|
3333
|
-
this.newNodes.push(newNode);
|
|
3334
|
-
for (const node of connectedNodes) {
|
|
3335
|
-
this.removedNodeIds.add(node.capacityMeshNodeId);
|
|
3336
|
-
}
|
|
3337
|
-
}
|
|
3338
|
-
visualize() {
|
|
3339
|
-
const graphics = {
|
|
3340
|
-
rects: []
|
|
3341
|
-
};
|
|
3342
|
-
for (const node of this.newNodes) {
|
|
3343
|
-
graphics.rects.push({
|
|
3344
|
-
center: node.center,
|
|
3345
|
-
width: Math.max(node.width - 2, node.width * 0.8),
|
|
3346
|
-
height: Math.max(node.height - 2, node.height * 0.8),
|
|
3347
|
-
fill: node._containsObstacle ? "rgba(255,0,0,0.1)" : "rgba(0,0,0,0.1)",
|
|
3348
|
-
label: node.capacityMeshNodeId
|
|
3349
|
-
});
|
|
3350
|
-
}
|
|
3351
|
-
return graphics;
|
|
3352
|
-
}
|
|
3353
|
-
};
|
|
3354
|
-
|
|
3355
|
-
// lib/solvers/NetToPointPairsSolver/buildMinimumSpanningTree.ts
|
|
3356
|
-
var KDNode = class {
|
|
3357
|
-
point;
|
|
3358
|
-
left = null;
|
|
3359
|
-
right = null;
|
|
3360
|
-
constructor(point) {
|
|
3361
|
-
this.point = point;
|
|
3362
|
-
}
|
|
3363
|
-
};
|
|
3364
|
-
var KDTree = class {
|
|
3365
|
-
root = null;
|
|
3366
|
-
constructor(points) {
|
|
3367
|
-
if (points.length > 0) {
|
|
3368
|
-
this.root = this.buildTree(points, 0);
|
|
3369
|
-
}
|
|
3370
|
-
}
|
|
3371
|
-
buildTree(points, depth) {
|
|
3372
|
-
const axis = depth % 2 === 0 ? "x" : "y";
|
|
3373
|
-
points.sort((a, b) => a[axis] - b[axis]);
|
|
3374
|
-
const medianIndex = Math.floor(points.length / 2);
|
|
3375
|
-
const node = new KDNode(points[medianIndex]);
|
|
3376
|
-
if (medianIndex > 0) {
|
|
3377
|
-
node.left = this.buildTree(points.slice(0, medianIndex), depth + 1);
|
|
3378
|
-
}
|
|
3379
|
-
if (medianIndex < points.length - 1) {
|
|
3380
|
-
node.right = this.buildTree(points.slice(medianIndex + 1), depth + 1);
|
|
3381
|
-
}
|
|
3382
|
-
return node;
|
|
3383
|
-
}
|
|
3384
|
-
// Find the nearest neighbor to a query point
|
|
3385
|
-
findNearestNeighbor(queryPoint) {
|
|
3386
|
-
if (!this.root) {
|
|
3387
|
-
throw new Error("Tree is empty");
|
|
3388
|
-
}
|
|
3389
|
-
const best = this.root.point;
|
|
3390
|
-
const bestDistance = this.distance(queryPoint, best);
|
|
3391
|
-
this.nearestNeighborSearch(this.root, queryPoint, 0, best, bestDistance);
|
|
3392
|
-
return best;
|
|
3187
|
+
const best = this.root.point;
|
|
3188
|
+
const bestDistance = this.distance(queryPoint, best);
|
|
3189
|
+
this.nearestNeighborSearch(this.root, queryPoint, 0, best, bestDistance);
|
|
3190
|
+
return best;
|
|
3393
3191
|
}
|
|
3394
3192
|
nearestNeighborSearch(node, queryPoint, depth, best, bestDistance) {
|
|
3395
3193
|
if (!node) {
|
|
@@ -3717,13 +3515,6 @@ var convertHdRouteToSimplifiedRoute = (hdRoute, layerCount) => {
|
|
|
3717
3515
|
return result;
|
|
3718
3516
|
};
|
|
3719
3517
|
|
|
3720
|
-
// lib/utils/mapLayerNameToZ.ts
|
|
3721
|
-
var mapLayerNameToZ = (layerName, layerCount) => {
|
|
3722
|
-
if (layerName === "top") return 0;
|
|
3723
|
-
if (layerName === "bottom") return layerCount - 1;
|
|
3724
|
-
return parseInt(layerName.slice(5));
|
|
3725
|
-
};
|
|
3726
|
-
|
|
3727
3518
|
// lib/solvers/RouteStitchingSolver/SingleHighDensityRouteStitchSolver.ts
|
|
3728
3519
|
var SingleHighDensityRouteStitchSolver = class extends BaseSolver {
|
|
3729
3520
|
mergedHdRoute;
|
|
@@ -4286,6 +4077,7 @@ var createSegmentPointMap = (dedupedSegments, segmentIdToNodeIds) => {
|
|
|
4286
4077
|
var UnravelSectionSolver = class extends BaseSolver {
|
|
4287
4078
|
nodeMap;
|
|
4288
4079
|
dedupedSegments;
|
|
4080
|
+
dedupedSegmentMap;
|
|
4289
4081
|
MUTABLE_HOPS = 1;
|
|
4290
4082
|
unravelSection;
|
|
4291
4083
|
candidates = [];
|
|
@@ -4305,6 +4097,14 @@ var UnravelSectionSolver = class extends BaseSolver {
|
|
|
4305
4097
|
this.MUTABLE_HOPS = params.MUTABLE_HOPS ?? this.MUTABLE_HOPS;
|
|
4306
4098
|
this.nodeMap = params.nodeMap;
|
|
4307
4099
|
this.dedupedSegments = params.dedupedSegments;
|
|
4100
|
+
if (params.dedupedSegmentMap) {
|
|
4101
|
+
this.dedupedSegmentMap = params.dedupedSegmentMap;
|
|
4102
|
+
} else {
|
|
4103
|
+
this.dedupedSegmentMap = /* @__PURE__ */ new Map();
|
|
4104
|
+
for (const segment of this.dedupedSegments) {
|
|
4105
|
+
this.dedupedSegmentMap.set(segment.nodePortSegmentId, segment);
|
|
4106
|
+
}
|
|
4107
|
+
}
|
|
4308
4108
|
this.nodeIdToSegmentIds = params.nodeIdToSegmentIds;
|
|
4309
4109
|
this.segmentIdToNodeIds = params.segmentIdToNodeIds;
|
|
4310
4110
|
this.rootNodeId = params.rootNodeId;
|
|
@@ -4463,14 +4263,20 @@ var UnravelSectionSolver = class extends BaseSolver {
|
|
|
4463
4263
|
const [APointId, BPointId] = issue.segmentPoints;
|
|
4464
4264
|
const pointA = this.getPointInCandidate(candidate, APointId);
|
|
4465
4265
|
const pointB = this.getPointInCandidate(candidate, BPointId);
|
|
4466
|
-
|
|
4266
|
+
const aAvailableZ = this.dedupedSegmentMap.get(
|
|
4267
|
+
pointA.segmentId
|
|
4268
|
+
).availableZ;
|
|
4269
|
+
const bAvailableZ = this.dedupedSegmentMap.get(
|
|
4270
|
+
pointB.segmentId
|
|
4271
|
+
).availableZ;
|
|
4272
|
+
if (this.unravelSection.mutableSegmentIds.has(pointA.segmentId) && aAvailableZ.includes(pointB.z)) {
|
|
4467
4273
|
operations.push({
|
|
4468
4274
|
type: "change_layer",
|
|
4469
4275
|
newZ: pointB.z,
|
|
4470
4276
|
segmentPointIds: [APointId]
|
|
4471
4277
|
});
|
|
4472
4278
|
}
|
|
4473
|
-
if (this.unravelSection.mutableSegmentIds.has(pointB.segmentId)) {
|
|
4279
|
+
if (this.unravelSection.mutableSegmentIds.has(pointB.segmentId) && bAvailableZ.includes(pointA.z)) {
|
|
4474
4280
|
operations.push({
|
|
4475
4281
|
type: "change_layer",
|
|
4476
4282
|
newZ: pointA.z,
|
|
@@ -4925,6 +4731,7 @@ var calculateNodeProbabilityOfFailure = (node, numSameLayerCrossings, numEntryEx
|
|
|
4925
4731
|
// lib/solvers/UnravelSolver/UnravelMultiSectionSolver.ts
|
|
4926
4732
|
var UnravelMultiSectionSolver = class extends BaseSolver {
|
|
4927
4733
|
nodeMap;
|
|
4734
|
+
dedupedSegmentMap;
|
|
4928
4735
|
dedupedSegments;
|
|
4929
4736
|
nodeIdToSegmentIds;
|
|
4930
4737
|
segmentIdToNodeIds;
|
|
@@ -4948,6 +4755,10 @@ var UnravelMultiSectionSolver = class extends BaseSolver {
|
|
|
4948
4755
|
super();
|
|
4949
4756
|
this.MAX_ITERATIONS = 1e5;
|
|
4950
4757
|
this.dedupedSegments = getDedupedSegments(assignedSegments);
|
|
4758
|
+
this.dedupedSegmentMap = /* @__PURE__ */ new Map();
|
|
4759
|
+
for (const segment of this.dedupedSegments) {
|
|
4760
|
+
this.dedupedSegmentMap.set(segment.nodePortSegmentId, segment);
|
|
4761
|
+
}
|
|
4951
4762
|
this.nodeMap = /* @__PURE__ */ new Map();
|
|
4952
4763
|
for (const node of nodes) {
|
|
4953
4764
|
this.nodeMap.set(node.capacityMeshNodeId, node);
|
|
@@ -5026,6 +4837,7 @@ var UnravelMultiSectionSolver = class extends BaseSolver {
|
|
|
5026
4837
|
);
|
|
5027
4838
|
this.activeSolver = new UnravelSectionSolver({
|
|
5028
4839
|
dedupedSegments: this.dedupedSegments,
|
|
4840
|
+
dedupedSegmentMap: this.dedupedSegmentMap,
|
|
5029
4841
|
nodeMap: this.nodeMap,
|
|
5030
4842
|
nodeIdToSegmentIds: this.nodeIdToSegmentIds,
|
|
5031
4843
|
segmentIdToNodeIds: this.segmentIdToNodeIds,
|
|
@@ -5052,152 +4864,967 @@ var UnravelMultiSectionSolver = class extends BaseSolver {
|
|
|
5052
4864
|
segmentPoint.z = pointModification.z ?? segmentPoint.z;
|
|
5053
4865
|
}
|
|
5054
4866
|
}
|
|
5055
|
-
for (const nodeId of this.activeSolver.unravelSection.allNodeIds) {
|
|
5056
|
-
this.nodePfMap.set(
|
|
5057
|
-
nodeId,
|
|
5058
|
-
this.computeNodePf(this.nodeMap.get(nodeId))
|
|
5059
|
-
);
|
|
4867
|
+
for (const nodeId of this.activeSolver.unravelSection.allNodeIds) {
|
|
4868
|
+
this.nodePfMap.set(
|
|
4869
|
+
nodeId,
|
|
4870
|
+
this.computeNodePf(this.nodeMap.get(nodeId))
|
|
4871
|
+
);
|
|
4872
|
+
}
|
|
4873
|
+
this.activeSolver = null;
|
|
4874
|
+
}
|
|
4875
|
+
}
|
|
4876
|
+
visualize() {
|
|
4877
|
+
if (this.activeSolver) {
|
|
4878
|
+
return this.activeSolver.visualize();
|
|
4879
|
+
}
|
|
4880
|
+
const graphics = {
|
|
4881
|
+
lines: [],
|
|
4882
|
+
points: [],
|
|
4883
|
+
rects: [],
|
|
4884
|
+
circles: [],
|
|
4885
|
+
coordinateSystem: "cartesian",
|
|
4886
|
+
title: "Unravel Multi Section Solver"
|
|
4887
|
+
};
|
|
4888
|
+
for (const [nodeId, node] of this.nodeMap.entries()) {
|
|
4889
|
+
const probabilityOfFailure = this.nodePfMap.get(nodeId) || 0;
|
|
4890
|
+
const pf = Math.min(probabilityOfFailure, 1);
|
|
4891
|
+
const red = Math.floor(255 * pf);
|
|
4892
|
+
const green = Math.floor(255 * (1 - pf));
|
|
4893
|
+
const color = `rgb(${red}, ${green}, 0)`;
|
|
4894
|
+
if ((this.attemptsToFixNode.get(nodeId) ?? 0) === 0 && pf === 0) {
|
|
4895
|
+
continue;
|
|
4896
|
+
}
|
|
4897
|
+
graphics.rects.push({
|
|
4898
|
+
center: node.center,
|
|
4899
|
+
label: [
|
|
4900
|
+
nodeId,
|
|
4901
|
+
`${node.width.toFixed(2)}x${node.height.toFixed(2)}`,
|
|
4902
|
+
`Pf: ${probabilityOfFailure.toFixed(3)}`
|
|
4903
|
+
].join("\n"),
|
|
4904
|
+
color,
|
|
4905
|
+
width: node.width / 8,
|
|
4906
|
+
height: node.height / 8
|
|
4907
|
+
});
|
|
4908
|
+
}
|
|
4909
|
+
for (const segmentPoint of this.segmentPointMap.values()) {
|
|
4910
|
+
const segment = this.dedupedSegmentMap.get(segmentPoint.segmentId);
|
|
4911
|
+
graphics.points.push({
|
|
4912
|
+
x: segmentPoint.x,
|
|
4913
|
+
y: segmentPoint.y,
|
|
4914
|
+
label: [
|
|
4915
|
+
segmentPoint.segmentPointId,
|
|
4916
|
+
segmentPoint.segmentId,
|
|
4917
|
+
`z: ${segmentPoint.z}`,
|
|
4918
|
+
`segment.availableZ: ${segment?.availableZ.join(",")}`
|
|
4919
|
+
].join("\n"),
|
|
4920
|
+
color: this.colorMap[segmentPoint.connectionName] || "#000"
|
|
4921
|
+
});
|
|
4922
|
+
}
|
|
4923
|
+
const pointsBySegment = /* @__PURE__ */ new Map();
|
|
4924
|
+
for (const point of this.segmentPointMap.values()) {
|
|
4925
|
+
if (!pointsBySegment.has(point.segmentId)) {
|
|
4926
|
+
pointsBySegment.set(point.segmentId, []);
|
|
4927
|
+
}
|
|
4928
|
+
pointsBySegment.get(point.segmentId).push(point);
|
|
4929
|
+
}
|
|
4930
|
+
for (const [segmentId, points] of pointsBySegment.entries()) {
|
|
4931
|
+
if (points.length < 2) continue;
|
|
4932
|
+
const sortedPoints = [...points].sort(
|
|
4933
|
+
(a, b) => a.x !== b.x ? a.x - b.x : a.y - b.y
|
|
4934
|
+
);
|
|
4935
|
+
for (let i = 0; i < sortedPoints.length - 1; i++) {
|
|
4936
|
+
graphics.lines.push({
|
|
4937
|
+
points: [
|
|
4938
|
+
{ x: sortedPoints[i].x, y: sortedPoints[i].y },
|
|
4939
|
+
{ x: sortedPoints[i + 1].x, y: sortedPoints[i + 1].y }
|
|
4940
|
+
],
|
|
4941
|
+
strokeColor: this.colorMap[segmentId] || "#000"
|
|
4942
|
+
});
|
|
4943
|
+
}
|
|
4944
|
+
}
|
|
4945
|
+
const processedConnections = /* @__PURE__ */ new Set();
|
|
4946
|
+
const allPoints = Array.from(this.segmentPointMap.values());
|
|
4947
|
+
for (let i = 0; i < allPoints.length; i++) {
|
|
4948
|
+
const point1 = allPoints[i];
|
|
4949
|
+
for (let j = i + 1; j < allPoints.length; j++) {
|
|
4950
|
+
const point2 = allPoints[j];
|
|
4951
|
+
if (point1.connectionName !== point2.connectionName || point1.segmentId === point2.segmentId) {
|
|
4952
|
+
continue;
|
|
4953
|
+
}
|
|
4954
|
+
const hasSharedNode = point1.capacityMeshNodeIds.some(
|
|
4955
|
+
(nodeId) => point2.capacityMeshNodeIds.includes(nodeId)
|
|
4956
|
+
);
|
|
4957
|
+
if (hasSharedNode) {
|
|
4958
|
+
const connectionKey = `${point1.segmentPointId}-${point2.segmentPointId}`;
|
|
4959
|
+
if (processedConnections.has(connectionKey)) continue;
|
|
4960
|
+
processedConnections.add(connectionKey);
|
|
4961
|
+
const sameLayer = point1.z === point2.z;
|
|
4962
|
+
const layer = point1.z;
|
|
4963
|
+
let strokeDash;
|
|
4964
|
+
if (sameLayer) {
|
|
4965
|
+
strokeDash = layer === 0 ? void 0 : "10 5";
|
|
4966
|
+
} else {
|
|
4967
|
+
strokeDash = "3 3 10";
|
|
4968
|
+
}
|
|
4969
|
+
graphics.lines.push({
|
|
4970
|
+
points: [
|
|
4971
|
+
{ x: point1.x, y: point1.y },
|
|
4972
|
+
{ x: point2.x, y: point2.y }
|
|
4973
|
+
],
|
|
4974
|
+
strokeDash,
|
|
4975
|
+
strokeColor: this.colorMap[point1.connectionName] || "#666"
|
|
4976
|
+
});
|
|
4977
|
+
}
|
|
4978
|
+
}
|
|
4979
|
+
}
|
|
4980
|
+
return graphics;
|
|
4981
|
+
}
|
|
4982
|
+
getNodesWithPortPoints() {
|
|
4983
|
+
if (!this.solved) {
|
|
4984
|
+
throw new Error(
|
|
4985
|
+
"CapacitySegmentToPointSolver not solved, can't give port points yet"
|
|
4986
|
+
);
|
|
4987
|
+
}
|
|
4988
|
+
const nodeWithPortPointsMap = /* @__PURE__ */ new Map();
|
|
4989
|
+
for (const segment of this.dedupedSegments) {
|
|
4990
|
+
const segId = segment.nodePortSegmentId;
|
|
4991
|
+
for (const nodeId of this.segmentIdToNodeIds.get(segId)) {
|
|
4992
|
+
const node = this.nodeMap.get(nodeId);
|
|
4993
|
+
if (!nodeWithPortPointsMap.has(nodeId)) {
|
|
4994
|
+
nodeWithPortPointsMap.set(nodeId, {
|
|
4995
|
+
capacityMeshNodeId: nodeId,
|
|
4996
|
+
portPoints: [],
|
|
4997
|
+
center: node.center,
|
|
4998
|
+
width: node.width,
|
|
4999
|
+
height: node.height
|
|
5000
|
+
});
|
|
5001
|
+
}
|
|
5002
|
+
}
|
|
5003
|
+
}
|
|
5004
|
+
for (const segmentPoint of this.segmentPointMap.values()) {
|
|
5005
|
+
for (const nodeId of segmentPoint.capacityMeshNodeIds) {
|
|
5006
|
+
const nodeWithPortPoints = nodeWithPortPointsMap.get(nodeId);
|
|
5007
|
+
if (nodeWithPortPoints) {
|
|
5008
|
+
nodeWithPortPoints.portPoints.push({
|
|
5009
|
+
x: segmentPoint.x,
|
|
5010
|
+
y: segmentPoint.y,
|
|
5011
|
+
z: segmentPoint.z,
|
|
5012
|
+
connectionName: segmentPoint.connectionName
|
|
5013
|
+
});
|
|
5014
|
+
}
|
|
5015
|
+
}
|
|
5016
|
+
}
|
|
5017
|
+
return Array.from(nodeWithPortPointsMap.values());
|
|
5018
|
+
}
|
|
5019
|
+
};
|
|
5020
|
+
|
|
5021
|
+
// lib/utils/createRectFromCapacityNode.ts
|
|
5022
|
+
var createRectFromCapacityNode = (node, opts = {}) => {
|
|
5023
|
+
const lowestZ = Math.min(...node.availableZ);
|
|
5024
|
+
return {
|
|
5025
|
+
center: !opts.rectMargin ? {
|
|
5026
|
+
x: node.center.x + lowestZ * node.width * 0.05,
|
|
5027
|
+
y: node.center.y - lowestZ * node.width * 0.05
|
|
5028
|
+
} : node.center,
|
|
5029
|
+
width: opts.rectMargin ? node.width - opts.rectMargin * 2 : Math.max(node.width - 0.5, node.width * 0.8),
|
|
5030
|
+
height: opts.rectMargin ? node.height - opts.rectMargin * 2 : Math.max(node.height - 0.5, node.height * 0.8),
|
|
5031
|
+
fill: node._containsObstacle ? "rgba(255,0,0,0.1)" : {
|
|
5032
|
+
"0,1": "rgba(0,0,0,0.1)",
|
|
5033
|
+
"0": "rgba(0,200,200, 0.1)",
|
|
5034
|
+
"1": "rgba(0,0,200, 0.1)"
|
|
5035
|
+
}[node.availableZ.join(",")] ?? "rgba(0,200,200,0.1)",
|
|
5036
|
+
label: [
|
|
5037
|
+
node.capacityMeshNodeId,
|
|
5038
|
+
`availableZ: ${node.availableZ.join(",")}`,
|
|
5039
|
+
`${node._containsTarget ? "containsTarget" : ""}`,
|
|
5040
|
+
`${node._containsObstacle ? "containsObstacle" : ""}`
|
|
5041
|
+
].filter(Boolean).join("\n")
|
|
5042
|
+
};
|
|
5043
|
+
};
|
|
5044
|
+
|
|
5045
|
+
// lib/solvers/CapacityPathingSolver/CapacityPathingSolver.ts
|
|
5046
|
+
var CapacityPathingSolver = class extends BaseSolver {
|
|
5047
|
+
connectionsWithNodes;
|
|
5048
|
+
usedNodeCapacityMap;
|
|
5049
|
+
simpleRouteJson;
|
|
5050
|
+
nodes;
|
|
5051
|
+
edges;
|
|
5052
|
+
GREEDY_MULTIPLIER = 1.1;
|
|
5053
|
+
nodeMap;
|
|
5054
|
+
nodeEdgeMap;
|
|
5055
|
+
connectionNameToGoalNodeIds;
|
|
5056
|
+
colorMap;
|
|
5057
|
+
maxDepthOfNodes;
|
|
5058
|
+
activeCandidateStraightLineDistance;
|
|
5059
|
+
debug_lastNodeCostMap;
|
|
5060
|
+
hyperParameters;
|
|
5061
|
+
constructor({
|
|
5062
|
+
simpleRouteJson,
|
|
5063
|
+
nodes,
|
|
5064
|
+
edges,
|
|
5065
|
+
colorMap,
|
|
5066
|
+
MAX_ITERATIONS = 1e6,
|
|
5067
|
+
hyperParameters = {}
|
|
5068
|
+
}) {
|
|
5069
|
+
super();
|
|
5070
|
+
this.MAX_ITERATIONS = MAX_ITERATIONS;
|
|
5071
|
+
this.simpleRouteJson = simpleRouteJson;
|
|
5072
|
+
this.nodes = nodes;
|
|
5073
|
+
this.edges = edges;
|
|
5074
|
+
this.colorMap = colorMap ?? {};
|
|
5075
|
+
const { connectionsWithNodes, connectionNameToGoalNodeIds } = this.getConnectionsWithNodes();
|
|
5076
|
+
this.connectionsWithNodes = connectionsWithNodes;
|
|
5077
|
+
this.connectionNameToGoalNodeIds = connectionNameToGoalNodeIds;
|
|
5078
|
+
this.hyperParameters = hyperParameters;
|
|
5079
|
+
this.usedNodeCapacityMap = new Map(
|
|
5080
|
+
this.nodes.map((node) => [node.capacityMeshNodeId, 0])
|
|
5081
|
+
);
|
|
5082
|
+
this.nodeMap = new Map(
|
|
5083
|
+
this.nodes.map((node) => [node.capacityMeshNodeId, node])
|
|
5084
|
+
);
|
|
5085
|
+
this.nodeEdgeMap = getNodeEdgeMap(this.edges);
|
|
5086
|
+
this.maxDepthOfNodes = Math.max(
|
|
5087
|
+
...this.nodes.map((node) => node._depth ?? 0)
|
|
5088
|
+
);
|
|
5089
|
+
this.debug_lastNodeCostMap = /* @__PURE__ */ new Map();
|
|
5090
|
+
}
|
|
5091
|
+
getTotalCapacity(node) {
|
|
5092
|
+
const depth = node._depth ?? 0;
|
|
5093
|
+
return (this.maxDepthOfNodes - depth + 1) ** 2;
|
|
5094
|
+
}
|
|
5095
|
+
getConnectionsWithNodes() {
|
|
5096
|
+
const connectionsWithNodes = [];
|
|
5097
|
+
const nodesWithTargets = this.nodes.filter((node) => node._containsTarget);
|
|
5098
|
+
const connectionNameToGoalNodeIds = /* @__PURE__ */ new Map();
|
|
5099
|
+
for (const connection of this.simpleRouteJson.connections) {
|
|
5100
|
+
const nodesForConnection = [];
|
|
5101
|
+
for (const point of connection.pointsToConnect) {
|
|
5102
|
+
let closestNode = this.nodes[0];
|
|
5103
|
+
let minDistance = Number.MAX_VALUE;
|
|
5104
|
+
for (const node of nodesWithTargets) {
|
|
5105
|
+
const distance3 = Math.sqrt(
|
|
5106
|
+
(node.center.x - point.x) ** 2 + (node.center.y - point.y) ** 2
|
|
5107
|
+
);
|
|
5108
|
+
if (distance3 < minDistance) {
|
|
5109
|
+
minDistance = distance3;
|
|
5110
|
+
closestNode = node;
|
|
5111
|
+
}
|
|
5112
|
+
}
|
|
5113
|
+
nodesForConnection.push(closestNode);
|
|
5114
|
+
}
|
|
5115
|
+
if (nodesForConnection.length < 2) {
|
|
5116
|
+
throw new Error(
|
|
5117
|
+
`Not enough nodes for connection "${connection.name}", only ${nodesForConnection.length} found`
|
|
5118
|
+
);
|
|
5119
|
+
}
|
|
5120
|
+
connectionNameToGoalNodeIds.set(
|
|
5121
|
+
connection.name,
|
|
5122
|
+
nodesForConnection.map((n) => n.capacityMeshNodeId)
|
|
5123
|
+
);
|
|
5124
|
+
connectionsWithNodes.push({
|
|
5125
|
+
connection,
|
|
5126
|
+
nodes: nodesForConnection,
|
|
5127
|
+
pathFound: false
|
|
5128
|
+
});
|
|
5129
|
+
}
|
|
5130
|
+
return { connectionsWithNodes, connectionNameToGoalNodeIds };
|
|
5131
|
+
}
|
|
5132
|
+
currentConnectionIndex = 0;
|
|
5133
|
+
candidates;
|
|
5134
|
+
visitedNodes;
|
|
5135
|
+
computeG(prevCandidate, node, endGoal) {
|
|
5136
|
+
return prevCandidate.g + this.getDistanceBetweenNodes(prevCandidate.node, node);
|
|
5137
|
+
}
|
|
5138
|
+
computeH(prevCandidate, node, endGoal) {
|
|
5139
|
+
return this.getDistanceBetweenNodes(node, endGoal);
|
|
5140
|
+
}
|
|
5141
|
+
getBacktrackedPath(candidate) {
|
|
5142
|
+
const path = [];
|
|
5143
|
+
let currentCandidate = candidate;
|
|
5144
|
+
while (currentCandidate) {
|
|
5145
|
+
path.push(currentCandidate.node);
|
|
5146
|
+
currentCandidate = currentCandidate.prevCandidate;
|
|
5147
|
+
}
|
|
5148
|
+
return path;
|
|
5149
|
+
}
|
|
5150
|
+
getNeighboringNodes(node) {
|
|
5151
|
+
return this.nodeEdgeMap.get(node.capacityMeshNodeId).flatMap(
|
|
5152
|
+
(edge) => edge.nodeIds.filter((n) => n !== node.capacityMeshNodeId)
|
|
5153
|
+
).map((n) => this.nodeMap.get(n));
|
|
5154
|
+
}
|
|
5155
|
+
getCapacityPaths() {
|
|
5156
|
+
const capacityPaths = [];
|
|
5157
|
+
for (const connection of this.connectionsWithNodes) {
|
|
5158
|
+
const path = connection.path;
|
|
5159
|
+
if (path) {
|
|
5160
|
+
capacityPaths.push({
|
|
5161
|
+
capacityPathId: connection.connection.name,
|
|
5162
|
+
connectionName: connection.connection.name,
|
|
5163
|
+
nodeIds: path.map((node) => node.capacityMeshNodeId)
|
|
5164
|
+
});
|
|
5165
|
+
}
|
|
5166
|
+
}
|
|
5167
|
+
return capacityPaths;
|
|
5168
|
+
}
|
|
5169
|
+
doesNodeHaveCapacityForTrace(node) {
|
|
5170
|
+
const usedCapacity = this.usedNodeCapacityMap.get(node.capacityMeshNodeId) ?? 0;
|
|
5171
|
+
const totalCapacity = this.getTotalCapacity(node);
|
|
5172
|
+
if (node.availableZ.length === 1 && !node._containsTarget && usedCapacity > 0)
|
|
5173
|
+
return false;
|
|
5174
|
+
return usedCapacity < totalCapacity;
|
|
5175
|
+
}
|
|
5176
|
+
canTravelThroughObstacle(node, connectionName) {
|
|
5177
|
+
const goalNodeIds = this.connectionNameToGoalNodeIds.get(connectionName);
|
|
5178
|
+
return goalNodeIds?.includes(node.capacityMeshNodeId) ?? false;
|
|
5179
|
+
}
|
|
5180
|
+
getDistanceBetweenNodes(A, B) {
|
|
5181
|
+
return Math.sqrt(
|
|
5182
|
+
(A.center.x - B.center.x) ** 2 + (A.center.y - B.center.y) ** 2
|
|
5183
|
+
);
|
|
5184
|
+
}
|
|
5185
|
+
reduceCapacityAlongPath(nextConnection) {
|
|
5186
|
+
for (const node of nextConnection.path ?? []) {
|
|
5187
|
+
this.usedNodeCapacityMap.set(
|
|
5188
|
+
node.capacityMeshNodeId,
|
|
5189
|
+
this.usedNodeCapacityMap.get(node.capacityMeshNodeId) + 1
|
|
5190
|
+
);
|
|
5191
|
+
}
|
|
5192
|
+
}
|
|
5193
|
+
isConnectedToEndGoal(node, endGoal) {
|
|
5194
|
+
return this.nodeEdgeMap.get(node.capacityMeshNodeId).some((edge) => edge.nodeIds.includes(endGoal.capacityMeshNodeId));
|
|
5195
|
+
}
|
|
5196
|
+
_step() {
|
|
5197
|
+
const nextConnection = this.connectionsWithNodes[this.currentConnectionIndex];
|
|
5198
|
+
if (!nextConnection) {
|
|
5199
|
+
this.solved = true;
|
|
5200
|
+
return;
|
|
5201
|
+
}
|
|
5202
|
+
const [start, end] = nextConnection.nodes;
|
|
5203
|
+
if (!this.candidates) {
|
|
5204
|
+
this.candidates = [{ prevCandidate: null, node: start, f: 0, g: 0, h: 0 }];
|
|
5205
|
+
this.debug_lastNodeCostMap = /* @__PURE__ */ new Map();
|
|
5206
|
+
this.visitedNodes = /* @__PURE__ */ new Set([start.capacityMeshNodeId]);
|
|
5207
|
+
this.activeCandidateStraightLineDistance = distance(
|
|
5208
|
+
start.center,
|
|
5209
|
+
end.center
|
|
5210
|
+
);
|
|
5211
|
+
}
|
|
5212
|
+
this.candidates.sort((a, b) => a.f - b.f);
|
|
5213
|
+
const currentCandidate = this.candidates.shift();
|
|
5214
|
+
if (!currentCandidate) {
|
|
5215
|
+
console.error(
|
|
5216
|
+
`Ran out of candidates on connection ${nextConnection.connection.name}`
|
|
5217
|
+
);
|
|
5218
|
+
this.currentConnectionIndex++;
|
|
5219
|
+
this.candidates = null;
|
|
5220
|
+
this.visitedNodes = null;
|
|
5221
|
+
return;
|
|
5222
|
+
}
|
|
5223
|
+
if (this.isConnectedToEndGoal(currentCandidate.node, end)) {
|
|
5224
|
+
nextConnection.path = this.getBacktrackedPath({
|
|
5225
|
+
prevCandidate: currentCandidate,
|
|
5226
|
+
node: end,
|
|
5227
|
+
f: 0,
|
|
5228
|
+
g: 0,
|
|
5229
|
+
h: 0
|
|
5230
|
+
});
|
|
5231
|
+
this.reduceCapacityAlongPath(nextConnection);
|
|
5232
|
+
this.currentConnectionIndex++;
|
|
5233
|
+
this.candidates = null;
|
|
5234
|
+
this.visitedNodes = null;
|
|
5235
|
+
return;
|
|
5236
|
+
}
|
|
5237
|
+
const neighborNodes = this.getNeighboringNodes(currentCandidate.node);
|
|
5238
|
+
for (const neighborNode of neighborNodes) {
|
|
5239
|
+
if (this.visitedNodes?.has(neighborNode.capacityMeshNodeId)) {
|
|
5240
|
+
continue;
|
|
5241
|
+
}
|
|
5242
|
+
if (!this.doesNodeHaveCapacityForTrace(neighborNode)) {
|
|
5243
|
+
continue;
|
|
5244
|
+
}
|
|
5245
|
+
const connectionName = this.connectionsWithNodes[this.currentConnectionIndex].connection.name;
|
|
5246
|
+
if (neighborNode._containsObstacle && !this.canTravelThroughObstacle(neighborNode, connectionName)) {
|
|
5247
|
+
continue;
|
|
5248
|
+
}
|
|
5249
|
+
const g = this.computeG(currentCandidate, neighborNode, end);
|
|
5250
|
+
const h = this.computeH(currentCandidate, neighborNode, end);
|
|
5251
|
+
const f = g + h * this.GREEDY_MULTIPLIER;
|
|
5252
|
+
this.debug_lastNodeCostMap.set(neighborNode.capacityMeshNodeId, {
|
|
5253
|
+
f,
|
|
5254
|
+
g,
|
|
5255
|
+
h
|
|
5256
|
+
});
|
|
5257
|
+
const newCandidate = {
|
|
5258
|
+
prevCandidate: currentCandidate,
|
|
5259
|
+
node: neighborNode,
|
|
5260
|
+
f,
|
|
5261
|
+
g,
|
|
5262
|
+
h
|
|
5263
|
+
};
|
|
5264
|
+
this.candidates.push(newCandidate);
|
|
5265
|
+
}
|
|
5266
|
+
this.visitedNodes.add(currentCandidate.node.capacityMeshNodeId);
|
|
5267
|
+
}
|
|
5268
|
+
visualize() {
|
|
5269
|
+
const graphics = {
|
|
5270
|
+
lines: [],
|
|
5271
|
+
points: [],
|
|
5272
|
+
rects: [],
|
|
5273
|
+
circles: []
|
|
5274
|
+
};
|
|
5275
|
+
if (this.connectionsWithNodes) {
|
|
5276
|
+
for (let i = 0; i < this.connectionsWithNodes.length; i++) {
|
|
5277
|
+
const conn = this.connectionsWithNodes[i];
|
|
5278
|
+
if (conn.path && conn.path.length > 0) {
|
|
5279
|
+
const pathPoints = conn.path.map(({ center: { x, y }, width }) => ({
|
|
5280
|
+
// slight offset to allow viewing overlapping paths
|
|
5281
|
+
x: x + (i % 10 + i % 19) * (0.01 * width),
|
|
5282
|
+
y: y + (i % 10 + i % 19) * (0.01 * width)
|
|
5283
|
+
}));
|
|
5284
|
+
graphics.lines.push({
|
|
5285
|
+
points: pathPoints,
|
|
5286
|
+
strokeColor: this.colorMap[conn.connection.name]
|
|
5287
|
+
});
|
|
5288
|
+
}
|
|
5289
|
+
}
|
|
5290
|
+
}
|
|
5291
|
+
for (const node of this.nodes) {
|
|
5292
|
+
const nodeCosts = this.debug_lastNodeCostMap.get(node.capacityMeshNodeId);
|
|
5293
|
+
graphics.rects.push({
|
|
5294
|
+
...createRectFromCapacityNode(node),
|
|
5295
|
+
label: [
|
|
5296
|
+
`${node.capacityMeshNodeId}`,
|
|
5297
|
+
`${this.usedNodeCapacityMap.get(node.capacityMeshNodeId)}/${this.getTotalCapacity(node).toFixed(2)}`,
|
|
5298
|
+
`${node.width.toFixed(2)}x${node.height.toFixed(2)}`,
|
|
5299
|
+
`g: ${nodeCosts?.g !== void 0 ? nodeCosts.g.toFixed(2) : "?"}`,
|
|
5300
|
+
`h: ${nodeCosts?.h !== void 0 ? nodeCosts.h.toFixed(2) : "?"}`,
|
|
5301
|
+
`f: ${nodeCosts?.f !== void 0 ? nodeCosts.f.toFixed(2) : "?"}`
|
|
5302
|
+
].join("\n")
|
|
5303
|
+
});
|
|
5304
|
+
}
|
|
5305
|
+
if (this.connectionsWithNodes) {
|
|
5306
|
+
for (const conn of this.connectionsWithNodes) {
|
|
5307
|
+
if (conn.connection?.pointsToConnect) {
|
|
5308
|
+
for (const point of conn.connection.pointsToConnect) {
|
|
5309
|
+
graphics.points.push({
|
|
5310
|
+
x: point.x,
|
|
5311
|
+
y: point.y
|
|
5312
|
+
});
|
|
5313
|
+
}
|
|
5314
|
+
}
|
|
5315
|
+
}
|
|
5316
|
+
}
|
|
5317
|
+
const nextConnection = this.connectionsWithNodes[this.currentConnectionIndex];
|
|
5318
|
+
if (nextConnection) {
|
|
5319
|
+
const [start, end] = nextConnection.connection.pointsToConnect;
|
|
5320
|
+
graphics.lines.push({
|
|
5321
|
+
points: [
|
|
5322
|
+
{ x: start.x, y: start.y },
|
|
5323
|
+
{ x: end.x, y: end.y }
|
|
5324
|
+
],
|
|
5325
|
+
strokeColor: "red",
|
|
5326
|
+
strokeDash: "10 5"
|
|
5327
|
+
});
|
|
5328
|
+
}
|
|
5329
|
+
if (this.candidates) {
|
|
5330
|
+
const topCandidates = this.candidates.slice(0, 5);
|
|
5331
|
+
const connectionName = this.connectionsWithNodes[this.currentConnectionIndex].connection.name;
|
|
5332
|
+
topCandidates.forEach((candidate, index) => {
|
|
5333
|
+
const opacity = 0.5 * (1 - index / 5);
|
|
5334
|
+
const backtrackedPath = this.getBacktrackedPath(candidate);
|
|
5335
|
+
graphics.lines.push({
|
|
5336
|
+
points: backtrackedPath.map(({ center: { x, y } }) => ({ x, y })),
|
|
5337
|
+
strokeColor: safeTransparentize(
|
|
5338
|
+
this.colorMap[connectionName] ?? "red",
|
|
5339
|
+
1 - opacity
|
|
5340
|
+
)
|
|
5341
|
+
});
|
|
5342
|
+
});
|
|
5343
|
+
}
|
|
5344
|
+
return graphics;
|
|
5345
|
+
}
|
|
5346
|
+
};
|
|
5347
|
+
|
|
5348
|
+
// lib/solvers/CapacityPathingSolver/CapacityPathingSolver5.ts
|
|
5349
|
+
var CapacityPathingSolver5 = class extends CapacityPathingSolver {
|
|
5350
|
+
NEGATIVE_CAPACITY_PENALTY_FACTOR = 1;
|
|
5351
|
+
REDUCED_CAPACITY_PENALTY_FACTOR = 1;
|
|
5352
|
+
constructor(...args) {
|
|
5353
|
+
super(...args);
|
|
5354
|
+
this.GREEDY_MULTIPLIER = 2.5;
|
|
5355
|
+
}
|
|
5356
|
+
get maxCapacityFactor() {
|
|
5357
|
+
return this.hyperParameters.MAX_CAPACITY_FACTOR ?? 1;
|
|
5358
|
+
}
|
|
5359
|
+
getTotalCapacity(node) {
|
|
5360
|
+
return getTunedTotalCapacity1(node, this.maxCapacityFactor);
|
|
5361
|
+
}
|
|
5362
|
+
/**
|
|
5363
|
+
* Penalty you pay for using this node
|
|
5364
|
+
*/
|
|
5365
|
+
getNodeCapacityPenalty(node) {
|
|
5366
|
+
return 0.05;
|
|
5367
|
+
if (node.availableZ.length === 1) {
|
|
5368
|
+
return 0;
|
|
5369
|
+
}
|
|
5370
|
+
const totalCapacity = this.getTotalCapacity(node);
|
|
5371
|
+
const usedCapacity = this.usedNodeCapacityMap.get(node.capacityMeshNodeId) ?? 0;
|
|
5372
|
+
const remainingCapacity = totalCapacity - usedCapacity;
|
|
5373
|
+
const dist = this.activeCandidateStraightLineDistance;
|
|
5374
|
+
if (remainingCapacity <= 0) {
|
|
5375
|
+
const penalty = (-remainingCapacity + 1) / totalCapacity * dist * (this.NEGATIVE_CAPACITY_PENALTY_FACTOR / 4);
|
|
5376
|
+
return penalty ** 2;
|
|
5377
|
+
}
|
|
5378
|
+
return 1 / remainingCapacity * dist * this.REDUCED_CAPACITY_PENALTY_FACTOR / 8;
|
|
5379
|
+
}
|
|
5380
|
+
/**
|
|
5381
|
+
* We're rewarding travel into big nodes.
|
|
5382
|
+
*
|
|
5383
|
+
* To minimize shortest path, you'd want to comment this out.
|
|
5384
|
+
*/
|
|
5385
|
+
getDistanceBetweenNodes(A, B) {
|
|
5386
|
+
const dx = A.center.x - B.center.x;
|
|
5387
|
+
const dy = A.center.y - B.center.y;
|
|
5388
|
+
return Math.sqrt(dx ** 2 + dy ** 2);
|
|
5389
|
+
}
|
|
5390
|
+
computeG(prevCandidate, node, endGoal) {
|
|
5391
|
+
return prevCandidate.g + this.getDistanceBetweenNodes(prevCandidate.node, node) + this.getNodeCapacityPenalty(node);
|
|
5392
|
+
}
|
|
5393
|
+
computeH(prevCandidate, node, endGoal) {
|
|
5394
|
+
return this.getDistanceBetweenNodes(node, endGoal);
|
|
5395
|
+
}
|
|
5396
|
+
};
|
|
5397
|
+
|
|
5398
|
+
// lib/solvers/StrawSolver/StrawSolver.ts
|
|
5399
|
+
var StrawSolver = class extends BaseSolver {
|
|
5400
|
+
multiLayerNodes;
|
|
5401
|
+
strawNodes;
|
|
5402
|
+
skippedNodes;
|
|
5403
|
+
unprocessedNodes;
|
|
5404
|
+
strawSize;
|
|
5405
|
+
nodeIdCounter;
|
|
5406
|
+
constructor(params) {
|
|
5407
|
+
super();
|
|
5408
|
+
this.strawSize = params.strawSize ?? 0.5;
|
|
5409
|
+
this.multiLayerNodes = [];
|
|
5410
|
+
this.strawNodes = [];
|
|
5411
|
+
this.skippedNodes = [];
|
|
5412
|
+
this.nodeIdCounter = 0;
|
|
5413
|
+
this.unprocessedNodes = [];
|
|
5414
|
+
for (const node of params.nodes) {
|
|
5415
|
+
if (node.availableZ.length === 1) {
|
|
5416
|
+
this.unprocessedNodes.push(node);
|
|
5417
|
+
} else {
|
|
5418
|
+
this.multiLayerNodes.push(node);
|
|
5419
|
+
}
|
|
5420
|
+
}
|
|
5421
|
+
}
|
|
5422
|
+
getCapacityOfMultiLayerNodesWithinBounds(bounds) {
|
|
5423
|
+
let totalCapacity = 0;
|
|
5424
|
+
for (const node of this.multiLayerNodes) {
|
|
5425
|
+
const nodeMinX = node.center.x - node.width / 2;
|
|
5426
|
+
const nodeMaxX = node.center.x + node.width / 2;
|
|
5427
|
+
const nodeMinY = node.center.y - node.height / 2;
|
|
5428
|
+
const nodeMaxY = node.center.y + node.height / 2;
|
|
5429
|
+
const overlapMinX = Math.max(bounds.minX, nodeMinX);
|
|
5430
|
+
const overlapMaxX = Math.min(bounds.maxX, nodeMaxX);
|
|
5431
|
+
const overlapMinY = Math.max(bounds.minY, nodeMinY);
|
|
5432
|
+
const overlapMaxY = Math.min(bounds.maxY, nodeMaxY);
|
|
5433
|
+
if (overlapMinX < overlapMaxX && overlapMinY < overlapMaxY) {
|
|
5434
|
+
const overlapWidth = overlapMaxX - overlapMinX;
|
|
5435
|
+
const overlapHeight = overlapMaxY - overlapMinY;
|
|
5436
|
+
const overlapArea = overlapWidth * overlapHeight;
|
|
5437
|
+
const nodeArea = node.width * node.height;
|
|
5438
|
+
const proportion = overlapArea / nodeArea;
|
|
5439
|
+
totalCapacity += getTunedTotalCapacity1(node) * proportion;
|
|
5440
|
+
}
|
|
5441
|
+
}
|
|
5442
|
+
return totalCapacity;
|
|
5443
|
+
}
|
|
5444
|
+
getSurroundingCapacities(node) {
|
|
5445
|
+
const searchDistance = Math.min(node.width, node.height);
|
|
5446
|
+
const leftSurroundingCapacity = this.getCapacityOfMultiLayerNodesWithinBounds({
|
|
5447
|
+
minX: node.center.x - node.width / 2 - searchDistance,
|
|
5448
|
+
maxX: node.center.x - node.width / 2,
|
|
5449
|
+
minY: node.center.y - node.height / 2,
|
|
5450
|
+
maxY: node.center.y + node.height / 2
|
|
5451
|
+
});
|
|
5452
|
+
const rightSurroundingCapacity = this.getCapacityOfMultiLayerNodesWithinBounds({
|
|
5453
|
+
minX: node.center.x + node.width / 2,
|
|
5454
|
+
maxX: node.center.x + node.width / 2 + searchDistance,
|
|
5455
|
+
minY: node.center.y - node.height / 2,
|
|
5456
|
+
maxY: node.center.y + node.height / 2
|
|
5457
|
+
});
|
|
5458
|
+
const topSurroundingCapacity = this.getCapacityOfMultiLayerNodesWithinBounds({
|
|
5459
|
+
minX: node.center.x - node.width / 2,
|
|
5460
|
+
maxX: node.center.x + node.width / 2,
|
|
5461
|
+
minY: node.center.y - node.height / 2 - searchDistance,
|
|
5462
|
+
maxY: node.center.y - node.height / 2
|
|
5463
|
+
});
|
|
5464
|
+
const bottomSurroundingCapacity = this.getCapacityOfMultiLayerNodesWithinBounds({
|
|
5465
|
+
minX: node.center.x - node.width / 2,
|
|
5466
|
+
maxX: node.center.x + node.width / 2,
|
|
5467
|
+
minY: node.center.y + node.height / 2,
|
|
5468
|
+
maxY: node.center.y + node.height / 2 + searchDistance
|
|
5469
|
+
});
|
|
5470
|
+
return {
|
|
5471
|
+
leftSurroundingCapacity,
|
|
5472
|
+
rightSurroundingCapacity,
|
|
5473
|
+
topSurroundingCapacity,
|
|
5474
|
+
bottomSurroundingCapacity
|
|
5475
|
+
};
|
|
5476
|
+
}
|
|
5477
|
+
/**
|
|
5478
|
+
* Creates straw nodes from a single-layer node based on surrounding capacities
|
|
5479
|
+
*/
|
|
5480
|
+
createStrawsForNode(node) {
|
|
5481
|
+
const result = [];
|
|
5482
|
+
const {
|
|
5483
|
+
leftSurroundingCapacity,
|
|
5484
|
+
rightSurroundingCapacity,
|
|
5485
|
+
topSurroundingCapacity,
|
|
5486
|
+
bottomSurroundingCapacity
|
|
5487
|
+
} = this.getSurroundingCapacities(node);
|
|
5488
|
+
const horizontalCapacity = leftSurroundingCapacity + rightSurroundingCapacity;
|
|
5489
|
+
const verticalCapacity = topSurroundingCapacity + bottomSurroundingCapacity;
|
|
5490
|
+
const layerPrefersFactor = 1;
|
|
5491
|
+
const effectiveHorizontalCapacity = horizontalCapacity * layerPrefersFactor;
|
|
5492
|
+
if (effectiveHorizontalCapacity > verticalCapacity) {
|
|
5493
|
+
const numStraws = Math.floor(node.height / this.strawSize);
|
|
5494
|
+
const strawHeight = node.height / numStraws;
|
|
5495
|
+
for (let i = 0; i < numStraws; i++) {
|
|
5496
|
+
const strawCenterY = node.center.y - node.height / 2 + i * strawHeight + strawHeight / 2;
|
|
5497
|
+
result.push({
|
|
5498
|
+
capacityMeshNodeId: `${node.capacityMeshNodeId}_straw${i}`,
|
|
5499
|
+
center: { x: node.center.x, y: strawCenterY },
|
|
5500
|
+
width: node.width,
|
|
5501
|
+
height: strawHeight,
|
|
5502
|
+
layer: node.layer,
|
|
5503
|
+
availableZ: [...node.availableZ],
|
|
5504
|
+
_depth: node._depth,
|
|
5505
|
+
_strawNode: true
|
|
5506
|
+
});
|
|
5507
|
+
}
|
|
5508
|
+
} else {
|
|
5509
|
+
const numStraws = Math.floor(node.width / this.strawSize);
|
|
5510
|
+
const strawWidth = node.width / numStraws;
|
|
5511
|
+
for (let i = 0; i < numStraws; i++) {
|
|
5512
|
+
const strawCenterX = node.center.x - node.width / 2 + i * strawWidth + strawWidth / 2;
|
|
5513
|
+
result.push({
|
|
5514
|
+
capacityMeshNodeId: `${node.capacityMeshNodeId}_straw${i}`,
|
|
5515
|
+
center: { x: strawCenterX, y: node.center.y },
|
|
5516
|
+
width: strawWidth,
|
|
5517
|
+
height: node.height,
|
|
5518
|
+
layer: node.layer,
|
|
5519
|
+
availableZ: [...node.availableZ],
|
|
5520
|
+
_depth: node._depth,
|
|
5521
|
+
_strawNode: true
|
|
5522
|
+
});
|
|
5060
5523
|
}
|
|
5061
|
-
this.activeSolver = null;
|
|
5062
5524
|
}
|
|
5525
|
+
return result;
|
|
5063
5526
|
}
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5527
|
+
getResultNodes() {
|
|
5528
|
+
return [...this.multiLayerNodes, ...this.strawNodes, ...this.skippedNodes];
|
|
5529
|
+
}
|
|
5530
|
+
_step() {
|
|
5531
|
+
const rootNode = this.unprocessedNodes.pop();
|
|
5532
|
+
if (!rootNode) {
|
|
5533
|
+
this.solved = true;
|
|
5534
|
+
return;
|
|
5535
|
+
}
|
|
5536
|
+
if (rootNode.width < this.strawSize * 5 && rootNode.height < this.strawSize * 5) {
|
|
5537
|
+
this.skippedNodes.push(rootNode);
|
|
5538
|
+
return;
|
|
5539
|
+
}
|
|
5540
|
+
if (rootNode._containsTarget) {
|
|
5541
|
+
this.skippedNodes.push(rootNode);
|
|
5542
|
+
return;
|
|
5067
5543
|
}
|
|
5544
|
+
const strawNodes = this.createStrawsForNode(rootNode);
|
|
5545
|
+
this.strawNodes.push(...strawNodes);
|
|
5546
|
+
}
|
|
5547
|
+
visualize() {
|
|
5068
5548
|
const graphics = {
|
|
5549
|
+
rects: [],
|
|
5069
5550
|
lines: [],
|
|
5070
5551
|
points: [],
|
|
5071
|
-
rects: [],
|
|
5072
5552
|
circles: [],
|
|
5073
|
-
|
|
5074
|
-
title: "Unravel Multi Section Solver"
|
|
5553
|
+
title: "Straw Solver"
|
|
5075
5554
|
};
|
|
5076
|
-
for (const
|
|
5077
|
-
const probabilityOfFailure = this.nodePfMap.get(nodeId) || 0;
|
|
5078
|
-
const pf = Math.min(probabilityOfFailure, 1);
|
|
5079
|
-
const red = Math.floor(255 * pf);
|
|
5080
|
-
const green = Math.floor(255 * (1 - pf));
|
|
5081
|
-
const color = `rgb(${red}, ${green}, 0)`;
|
|
5555
|
+
for (const node of this.unprocessedNodes) {
|
|
5082
5556
|
graphics.rects.push({
|
|
5083
5557
|
center: node.center,
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5558
|
+
width: node.width,
|
|
5559
|
+
height: node.height,
|
|
5560
|
+
fill: "rgba(200, 200, 200, 0.5)",
|
|
5561
|
+
stroke: "rgba(0, 0, 0, 0.5)",
|
|
5562
|
+
label: `${node.capacityMeshNodeId}
|
|
5563
|
+
Unprocessed
|
|
5564
|
+
${node.width}x${node.height}`
|
|
5090
5565
|
});
|
|
5091
5566
|
}
|
|
5092
|
-
for (const
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5567
|
+
for (const node of this.strawNodes) {
|
|
5568
|
+
const color = node.availableZ[0] === 0 ? "rgba(0, 150, 255, 0.5)" : "rgba(255, 100, 0, 0.5)";
|
|
5569
|
+
graphics.rects.push({
|
|
5570
|
+
center: node.center,
|
|
5571
|
+
width: node.width,
|
|
5572
|
+
height: node.height,
|
|
5573
|
+
fill: color,
|
|
5574
|
+
stroke: "rgba(0, 0, 0, 0.5)",
|
|
5575
|
+
label: `${node.capacityMeshNodeId}
|
|
5576
|
+
Layer: ${node.availableZ[0]}
|
|
5577
|
+
${node.width}x${node.height}`
|
|
5100
5578
|
});
|
|
5101
5579
|
}
|
|
5102
|
-
const
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5580
|
+
for (const node of this.multiLayerNodes) {
|
|
5581
|
+
graphics.rects.push({
|
|
5582
|
+
center: node.center,
|
|
5583
|
+
width: node.width * 0.9,
|
|
5584
|
+
height: node.height * 0.9,
|
|
5585
|
+
fill: "rgba(100, 255, 100, 0.5)",
|
|
5586
|
+
stroke: "rgba(0, 0, 0, 0.5)",
|
|
5587
|
+
layer: `z${node.availableZ.join(",")}`,
|
|
5588
|
+
label: `${node.capacityMeshNodeId}
|
|
5589
|
+
Layers: ${node.availableZ.join(",")}
|
|
5590
|
+
${node.width}x${node.height}`
|
|
5591
|
+
});
|
|
5592
|
+
}
|
|
5593
|
+
return graphics;
|
|
5594
|
+
}
|
|
5595
|
+
};
|
|
5596
|
+
|
|
5597
|
+
// lib/solvers/SingleLayerNodeMerger/SingleLayerNodeMergerSolver.ts
|
|
5598
|
+
var EPSILON = 5e-3;
|
|
5599
|
+
var SingleLayerNodeMergerSolver = class extends BaseSolver {
|
|
5600
|
+
nodeMap;
|
|
5601
|
+
currentBatchNodeIds;
|
|
5602
|
+
absorbedNodeIds;
|
|
5603
|
+
nextBatchNodeIds;
|
|
5604
|
+
batchHadModifications;
|
|
5605
|
+
newNodes;
|
|
5606
|
+
constructor(nodes) {
|
|
5607
|
+
super();
|
|
5608
|
+
this.nodeMap = /* @__PURE__ */ new Map();
|
|
5609
|
+
this.MAX_ITERATIONS = 1e5;
|
|
5610
|
+
for (const node of nodes) {
|
|
5611
|
+
this.nodeMap.set(node.capacityMeshNodeId, node);
|
|
5612
|
+
}
|
|
5613
|
+
this.newNodes = [];
|
|
5614
|
+
this.absorbedNodeIds = /* @__PURE__ */ new Set();
|
|
5615
|
+
const nodeWithArea = [];
|
|
5616
|
+
for (const node of nodes) {
|
|
5617
|
+
if (node.availableZ.length > 1) {
|
|
5618
|
+
this.newNodes.push(node);
|
|
5619
|
+
this.absorbedNodeIds.add(node.capacityMeshNodeId);
|
|
5620
|
+
} else {
|
|
5621
|
+
nodeWithArea.push([node.capacityMeshNodeId, node.width * node.height]);
|
|
5106
5622
|
}
|
|
5107
|
-
pointsBySegment.get(point.segmentId).push(point);
|
|
5108
5623
|
}
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5624
|
+
nodeWithArea.sort((a, b) => a[1] - b[1]);
|
|
5625
|
+
this.currentBatchNodeIds = nodeWithArea.map((n) => n[0]);
|
|
5626
|
+
this.nextBatchNodeIds = [];
|
|
5627
|
+
this.batchHadModifications = false;
|
|
5628
|
+
}
|
|
5629
|
+
getAdjacentSameLayerUnprocessedNodes(rootNode) {
|
|
5630
|
+
const adjacentNodes = [];
|
|
5631
|
+
for (const unprocessedNodeId of this.currentBatchNodeIds) {
|
|
5632
|
+
if (this.absorbedNodeIds.has(unprocessedNodeId)) continue;
|
|
5633
|
+
const unprocessedNode = this.nodeMap.get(unprocessedNodeId);
|
|
5634
|
+
if (unprocessedNode.availableZ[0] !== rootNode.availableZ[0]) continue;
|
|
5635
|
+
if (unprocessedNode._containsTarget && unprocessedNode._targetConnectionName !== rootNode._targetConnectionName)
|
|
5636
|
+
continue;
|
|
5637
|
+
if (!areNodesBordering(rootNode, unprocessedNode)) continue;
|
|
5638
|
+
adjacentNodes.push(unprocessedNode);
|
|
5639
|
+
}
|
|
5640
|
+
return adjacentNodes;
|
|
5641
|
+
}
|
|
5642
|
+
_step() {
|
|
5643
|
+
let rootNodeId = this.currentBatchNodeIds.pop();
|
|
5644
|
+
while (rootNodeId && this.absorbedNodeIds.has(rootNodeId)) {
|
|
5645
|
+
rootNodeId = this.currentBatchNodeIds.pop();
|
|
5646
|
+
}
|
|
5647
|
+
if (!rootNodeId) {
|
|
5648
|
+
if (this.batchHadModifications) {
|
|
5649
|
+
this.currentBatchNodeIds = this.nextBatchNodeIds.sort((a, b) => {
|
|
5650
|
+
const A = this.nodeMap.get(a);
|
|
5651
|
+
const B = this.nodeMap.get(b);
|
|
5652
|
+
return A.width * A.height - B.width * B.height;
|
|
5121
5653
|
});
|
|
5654
|
+
this.nextBatchNodeIds = [];
|
|
5655
|
+
this.batchHadModifications = false;
|
|
5656
|
+
return;
|
|
5122
5657
|
}
|
|
5658
|
+
this.solved = true;
|
|
5659
|
+
this.newNodes.push(
|
|
5660
|
+
...this.nextBatchNodeIds.map((id) => this.nodeMap.get(id))
|
|
5661
|
+
);
|
|
5662
|
+
return;
|
|
5123
5663
|
}
|
|
5124
|
-
const
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5664
|
+
const rootNode = this.nodeMap.get(rootNodeId);
|
|
5665
|
+
let rootNodeHasGrown = false;
|
|
5666
|
+
const adjacentNodes = this.getAdjacentSameLayerUnprocessedNodes(rootNode);
|
|
5667
|
+
if (adjacentNodes.length === 0) {
|
|
5668
|
+
this.nextBatchNodeIds.push(rootNodeId);
|
|
5669
|
+
return;
|
|
5670
|
+
}
|
|
5671
|
+
const adjacentNodesToLeft = adjacentNodes.filter(
|
|
5672
|
+
(adjNode) => adjNode.center.x < rootNode.center.x && Math.abs(adjNode.center.y - rootNode.center.y) < rootNode.height / 2
|
|
5673
|
+
);
|
|
5674
|
+
if (adjacentNodesToLeft.length > 0) {
|
|
5675
|
+
const { width: leftAdjNodeWidth, height: leftAdjNodeHeight } = adjacentNodesToLeft[0];
|
|
5676
|
+
const leftAdjNodesAreAllSameSize = adjacentNodesToLeft.every(
|
|
5677
|
+
(adjNode) => adjNode.width === leftAdjNodeWidth && adjNode.height === leftAdjNodeHeight
|
|
5678
|
+
);
|
|
5679
|
+
const leftAdjNodesTakeUpEntireHeight = Math.abs(
|
|
5680
|
+
adjacentNodesToLeft.reduce((acc, adjNode) => {
|
|
5681
|
+
return acc + adjNode.height;
|
|
5682
|
+
}, 0) - rootNode.height
|
|
5683
|
+
) < EPSILON;
|
|
5684
|
+
if (leftAdjNodesTakeUpEntireHeight && leftAdjNodesAreAllSameSize) {
|
|
5685
|
+
rootNode.width += leftAdjNodeWidth;
|
|
5686
|
+
rootNode.center.x = rootNode.center.x - leftAdjNodeWidth / 2;
|
|
5687
|
+
for (const adjNode of adjacentNodesToLeft) {
|
|
5688
|
+
this.absorbedNodeIds.add(adjNode.capacityMeshNodeId);
|
|
5132
5689
|
}
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
strokeColor: this.colorMap[point1.connectionName] || "#666"
|
|
5155
|
-
});
|
|
5690
|
+
rootNodeHasGrown = true;
|
|
5691
|
+
}
|
|
5692
|
+
}
|
|
5693
|
+
const adjacentNodesToRight = adjacentNodes.filter(
|
|
5694
|
+
(adjNode) => adjNode.center.x > rootNode.center.x && Math.abs(adjNode.center.y - rootNode.center.y) < rootNode.height / 2
|
|
5695
|
+
);
|
|
5696
|
+
if (adjacentNodesToRight.length > 0 && !rootNodeHasGrown) {
|
|
5697
|
+
const { width: rightAdjNodeWidth, height: rightAdjNodeHeight } = adjacentNodesToRight[0];
|
|
5698
|
+
const rightAdjNodesAreAllSameSize = adjacentNodesToRight.every(
|
|
5699
|
+
(adjNode) => adjNode.width === rightAdjNodeWidth && adjNode.height === rightAdjNodeHeight
|
|
5700
|
+
);
|
|
5701
|
+
const rightAdjNodesTakeUpEntireHeight = Math.abs(
|
|
5702
|
+
adjacentNodesToRight.reduce((acc, adjNode) => {
|
|
5703
|
+
return acc + adjNode.height;
|
|
5704
|
+
}, 0) - rootNode.height
|
|
5705
|
+
) < EPSILON;
|
|
5706
|
+
if (rightAdjNodesTakeUpEntireHeight && rightAdjNodesAreAllSameSize) {
|
|
5707
|
+
rootNode.width += rightAdjNodeWidth;
|
|
5708
|
+
rootNode.center.x = rootNode.center.x + rightAdjNodeWidth / 2;
|
|
5709
|
+
for (const adjNode of adjacentNodesToRight) {
|
|
5710
|
+
this.absorbedNodeIds.add(adjNode.capacityMeshNodeId);
|
|
5156
5711
|
}
|
|
5712
|
+
rootNodeHasGrown = true;
|
|
5157
5713
|
}
|
|
5158
5714
|
}
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
if (!
|
|
5163
|
-
|
|
5164
|
-
|
|
5715
|
+
const adjacentNodesToTop = adjacentNodes.filter(
|
|
5716
|
+
(adjNode) => adjNode.center.y > rootNode.center.y && Math.abs(adjNode.center.x - rootNode.center.x) < rootNode.width / 2
|
|
5717
|
+
);
|
|
5718
|
+
if (adjacentNodesToTop.length > 0 && !rootNodeHasGrown) {
|
|
5719
|
+
const { width: topAdjNodeWidth, height: topAdjNodeHeight } = adjacentNodesToTop[0];
|
|
5720
|
+
const topAdjNodesAreAllSameSize = adjacentNodesToTop.every(
|
|
5721
|
+
(adjNode) => adjNode.width === topAdjNodeWidth && adjNode.height === topAdjNodeHeight
|
|
5165
5722
|
);
|
|
5723
|
+
const topAdjNodesTakeUpEntireWidth = Math.abs(
|
|
5724
|
+
adjacentNodesToTop.reduce((acc, adjNode) => {
|
|
5725
|
+
return acc + adjNode.width;
|
|
5726
|
+
}, 0) - rootNode.width
|
|
5727
|
+
) < EPSILON;
|
|
5728
|
+
if (topAdjNodesTakeUpEntireWidth && topAdjNodesAreAllSameSize) {
|
|
5729
|
+
rootNode.height += topAdjNodeHeight;
|
|
5730
|
+
rootNode.center.y = rootNode.center.y + topAdjNodeHeight / 2;
|
|
5731
|
+
for (const adjNode of adjacentNodesToTop) {
|
|
5732
|
+
this.absorbedNodeIds.add(adjNode.capacityMeshNodeId);
|
|
5733
|
+
}
|
|
5734
|
+
rootNodeHasGrown = true;
|
|
5735
|
+
}
|
|
5166
5736
|
}
|
|
5167
|
-
const
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5737
|
+
const adjacentNodesToBottom = adjacentNodes.filter(
|
|
5738
|
+
(adjNode) => adjNode.center.y < rootNode.center.y && Math.abs(adjNode.center.x - rootNode.center.x) < rootNode.width / 2
|
|
5739
|
+
);
|
|
5740
|
+
if (adjacentNodesToBottom.length > 0 && !rootNodeHasGrown) {
|
|
5741
|
+
const { width: bottomAdjNodeWidth, height: bottomAdjNodeHeight } = adjacentNodesToBottom[0];
|
|
5742
|
+
const bottomAdjNodesAreAllSameSize = adjacentNodesToBottom.every(
|
|
5743
|
+
(adjNode) => adjNode.width === bottomAdjNodeWidth && adjNode.height === bottomAdjNodeHeight
|
|
5744
|
+
);
|
|
5745
|
+
const bottomAdjNodesTakeUpEntireWidth = Math.abs(
|
|
5746
|
+
adjacentNodesToBottom.reduce((acc, adjNode) => {
|
|
5747
|
+
return acc + adjNode.width;
|
|
5748
|
+
}, 0) - rootNode.width
|
|
5749
|
+
) < EPSILON;
|
|
5750
|
+
if (bottomAdjNodesTakeUpEntireWidth && bottomAdjNodesAreAllSameSize) {
|
|
5751
|
+
rootNode.height += bottomAdjNodeHeight;
|
|
5752
|
+
rootNode.center.y = rootNode.center.y - bottomAdjNodeHeight / 2;
|
|
5753
|
+
for (const adjNode of adjacentNodesToBottom) {
|
|
5754
|
+
this.absorbedNodeIds.add(adjNode.capacityMeshNodeId);
|
|
5180
5755
|
}
|
|
5756
|
+
rootNodeHasGrown = true;
|
|
5181
5757
|
}
|
|
5182
5758
|
}
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5759
|
+
if (rootNodeHasGrown) {
|
|
5760
|
+
this.batchHadModifications = true;
|
|
5761
|
+
this.currentBatchNodeIds.push(rootNodeId);
|
|
5762
|
+
} else {
|
|
5763
|
+
this.nextBatchNodeIds.unshift(rootNodeId);
|
|
5764
|
+
}
|
|
5765
|
+
}
|
|
5766
|
+
visualize() {
|
|
5767
|
+
const graphics = {
|
|
5768
|
+
circles: [],
|
|
5769
|
+
lines: [],
|
|
5770
|
+
points: [],
|
|
5771
|
+
rects: [],
|
|
5772
|
+
coordinateSystem: "cartesian",
|
|
5773
|
+
title: "Same Layer Node Merger"
|
|
5774
|
+
};
|
|
5775
|
+
for (const node of this.newNodes) {
|
|
5776
|
+
graphics.rects.push(createRectFromCapacityNode(node));
|
|
5777
|
+
}
|
|
5778
|
+
const nextNodeIdInBatch = this.currentBatchNodeIds[this.currentBatchNodeIds.length - 1];
|
|
5779
|
+
let adjacentNodes;
|
|
5780
|
+
if (nextNodeIdInBatch) {
|
|
5781
|
+
adjacentNodes = this.getAdjacentSameLayerUnprocessedNodes(
|
|
5782
|
+
this.nodeMap.get(nextNodeIdInBatch)
|
|
5783
|
+
);
|
|
5784
|
+
}
|
|
5785
|
+
for (const nodeId of this.currentBatchNodeIds) {
|
|
5786
|
+
const node = this.nodeMap.get(nodeId);
|
|
5787
|
+
if (this.absorbedNodeIds.has(nodeId)) continue;
|
|
5788
|
+
if (node) {
|
|
5789
|
+
const rect = createRectFromCapacityNode(node, {
|
|
5790
|
+
rectMargin: 0.01
|
|
5791
|
+
});
|
|
5792
|
+
if (nodeId === nextNodeIdInBatch) {
|
|
5793
|
+
rect.stroke = "rgba(0, 255, 0, 0.8)";
|
|
5794
|
+
} else if (adjacentNodes?.some(
|
|
5795
|
+
(adjNode) => adjNode.capacityMeshNodeId === nodeId
|
|
5796
|
+
)) {
|
|
5797
|
+
rect.stroke = "rgba(128, 0, 128, 0.8)";
|
|
5798
|
+
} else {
|
|
5799
|
+
rect.stroke = "rgba(255, 165, 0, 0.8)";
|
|
5193
5800
|
}
|
|
5801
|
+
rect.layer = `z${node.availableZ.join(",")}`;
|
|
5802
|
+
rect.label = `${rect.label}
|
|
5803
|
+
(unprocessed)`;
|
|
5804
|
+
graphics.rects.push(rect);
|
|
5194
5805
|
}
|
|
5195
5806
|
}
|
|
5196
|
-
|
|
5807
|
+
for (const nodeId of this.nextBatchNodeIds) {
|
|
5808
|
+
const node = this.nodeMap.get(nodeId);
|
|
5809
|
+
if (this.absorbedNodeIds.has(nodeId)) continue;
|
|
5810
|
+
if (node) {
|
|
5811
|
+
const rect = createRectFromCapacityNode(node, {
|
|
5812
|
+
rectMargin: 0.01
|
|
5813
|
+
});
|
|
5814
|
+
rect.layer = `z${node.availableZ.join(",")}`;
|
|
5815
|
+
rect.stroke = "rgba(0, 217, 255, 0.8)";
|
|
5816
|
+
rect.label = `${rect.label}
|
|
5817
|
+
x: ${node.center.x}, y: ${node.center.y}
|
|
5818
|
+
${node.width}x${node.height}
|
|
5819
|
+
(next batch)`;
|
|
5820
|
+
graphics.rects.push(rect);
|
|
5821
|
+
}
|
|
5822
|
+
}
|
|
5823
|
+
return graphics;
|
|
5197
5824
|
}
|
|
5198
5825
|
};
|
|
5199
5826
|
|
|
5200
|
-
// lib/solvers/
|
|
5827
|
+
// lib/solvers/AutoroutingPipelineSolver.ts
|
|
5201
5828
|
function definePipelineStep(solverName, solverClass, getConstructorParams, opts = {}) {
|
|
5202
5829
|
return {
|
|
5203
5830
|
solverName,
|
|
@@ -5240,12 +5867,15 @@ var CapacityMeshSolver = class extends BaseSolver {
|
|
|
5240
5867
|
segmentToPointOptimizer;
|
|
5241
5868
|
highDensityRouteSolver;
|
|
5242
5869
|
highDensityStitchSolver;
|
|
5870
|
+
singleLayerNodeMerger;
|
|
5871
|
+
strawSolver;
|
|
5243
5872
|
startTimeOfPhase;
|
|
5244
5873
|
endTimeOfPhase;
|
|
5245
5874
|
timeSpentOnPhase;
|
|
5246
|
-
|
|
5875
|
+
activeSubSolver = null;
|
|
5247
5876
|
connMap;
|
|
5248
5877
|
srjWithPointPairs;
|
|
5878
|
+
capacityNodes = null;
|
|
5249
5879
|
pipelineDef = [
|
|
5250
5880
|
definePipelineStep(
|
|
5251
5881
|
"netToPointPairsSolver",
|
|
@@ -5261,39 +5891,65 @@ var CapacityMeshSolver = class extends BaseSolver {
|
|
|
5261
5891
|
}
|
|
5262
5892
|
}
|
|
5263
5893
|
),
|
|
5264
|
-
definePipelineStep("nodeSolver", CapacityMeshNodeSolver, (cms) => [
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
]),
|
|
5268
|
-
definePipelineStep("nodeTargetMerger", CapacityNodeTargetMerger, (cms) => [
|
|
5269
|
-
cms.nodeSolver?.finishedNodes || [],
|
|
5270
|
-
cms.srj.obstacles,
|
|
5271
|
-
cms.connMap
|
|
5272
|
-
]),
|
|
5273
|
-
definePipelineStep("edgeSolver", CapacityMeshEdgeSolver, (cms) => [
|
|
5274
|
-
cms.nodeTargetMerger?.newNodes || []
|
|
5275
|
-
]),
|
|
5894
|
+
// definePipelineStep("nodeSolver", CapacityMeshNodeSolver, (cms) => [
|
|
5895
|
+
// cms.netToPointPairsSolver?.getNewSimpleRouteJson() || cms.srj,
|
|
5896
|
+
// cms.opts,
|
|
5897
|
+
// ]),
|
|
5276
5898
|
definePipelineStep(
|
|
5277
|
-
"
|
|
5278
|
-
|
|
5899
|
+
"nodeSolver",
|
|
5900
|
+
CapacityMeshNodeSolver2_NodeUnderObstacle,
|
|
5279
5901
|
(cms) => [
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
nodes: cms.nodeTargetMerger?.newNodes || [],
|
|
5283
|
-
edges: cms.edgeSolver?.edges || [],
|
|
5284
|
-
colorMap: cms.colorMap,
|
|
5285
|
-
hyperParameters: {
|
|
5286
|
-
MAX_CAPACITY_FACTOR: 1
|
|
5287
|
-
}
|
|
5288
|
-
}
|
|
5902
|
+
cms.netToPointPairsSolver?.getNewSimpleRouteJson() || cms.srj,
|
|
5903
|
+
cms.opts
|
|
5289
5904
|
]
|
|
5290
5905
|
),
|
|
5906
|
+
// definePipelineStep("nodeTargetMerger", CapacityNodeTargetMerger, (cms) => [
|
|
5907
|
+
// cms.nodeSolver?.finishedNodes || [],
|
|
5908
|
+
// cms.srj.obstacles,
|
|
5909
|
+
// cms.connMap,
|
|
5910
|
+
// ]),
|
|
5911
|
+
// definePipelineStep("nodeTargetMerger", CapacityNodeTargetMerger2, (cms) => [
|
|
5912
|
+
// cms.nodeSolver?.finishedNodes || [],
|
|
5913
|
+
// cms.srj.obstacles,
|
|
5914
|
+
// cms.connMap,
|
|
5915
|
+
// cms.colorMap,
|
|
5916
|
+
// cms.srj.connections,
|
|
5917
|
+
// ]),
|
|
5918
|
+
definePipelineStep(
|
|
5919
|
+
"singleLayerNodeMerger",
|
|
5920
|
+
SingleLayerNodeMergerSolver,
|
|
5921
|
+
(cms) => [cms.nodeSolver?.finishedNodes]
|
|
5922
|
+
),
|
|
5923
|
+
definePipelineStep(
|
|
5924
|
+
"strawSolver",
|
|
5925
|
+
StrawSolver,
|
|
5926
|
+
(cms) => [{ nodes: cms.singleLayerNodeMerger?.newNodes }],
|
|
5927
|
+
{
|
|
5928
|
+
onSolved: (cms) => {
|
|
5929
|
+
cms.capacityNodes = cms.strawSolver?.getResultNodes();
|
|
5930
|
+
}
|
|
5931
|
+
}
|
|
5932
|
+
),
|
|
5933
|
+
definePipelineStep("edgeSolver", CapacityMeshEdgeSolver, (cms) => [
|
|
5934
|
+
cms.capacityNodes
|
|
5935
|
+
]),
|
|
5936
|
+
definePipelineStep("pathingSolver", CapacityPathingSolver5, (cms) => [
|
|
5937
|
+
{
|
|
5938
|
+
simpleRouteJson: cms.srjWithPointPairs,
|
|
5939
|
+
nodes: cms.capacityNodes,
|
|
5940
|
+
edges: cms.edgeSolver?.edges || [],
|
|
5941
|
+
colorMap: cms.colorMap,
|
|
5942
|
+
hyperParameters: {
|
|
5943
|
+
MAX_CAPACITY_FACTOR: 1
|
|
5944
|
+
}
|
|
5945
|
+
}
|
|
5946
|
+
]),
|
|
5291
5947
|
definePipelineStep(
|
|
5292
5948
|
"edgeToPortSegmentSolver",
|
|
5293
5949
|
CapacityEdgeToPortSegmentSolver,
|
|
5294
5950
|
(cms) => [
|
|
5295
5951
|
{
|
|
5296
|
-
nodes: cms.
|
|
5952
|
+
nodes: cms.capacityNodes,
|
|
5297
5953
|
edges: cms.edgeSolver?.edges || [],
|
|
5298
5954
|
capacityPaths: cms.pathingSolver?.getCapacityPaths() || [],
|
|
5299
5955
|
colorMap: cms.colorMap
|
|
@@ -5314,7 +5970,7 @@ var CapacityMeshSolver = class extends BaseSolver {
|
|
|
5314
5970
|
{
|
|
5315
5971
|
segments: allSegments,
|
|
5316
5972
|
colorMap: cms.colorMap,
|
|
5317
|
-
nodes: cms.
|
|
5973
|
+
nodes: cms.capacityNodes
|
|
5318
5974
|
}
|
|
5319
5975
|
];
|
|
5320
5976
|
}
|
|
@@ -5337,7 +5993,7 @@ var CapacityMeshSolver = class extends BaseSolver {
|
|
|
5337
5993
|
{
|
|
5338
5994
|
assignedSegments: cms.segmentToPointSolver?.solvedSegments || [],
|
|
5339
5995
|
colorMap: cms.colorMap,
|
|
5340
|
-
nodes: cms.
|
|
5996
|
+
nodes: cms.capacityNodes
|
|
5341
5997
|
}
|
|
5342
5998
|
]
|
|
5343
5999
|
),
|
|
@@ -5367,26 +6023,24 @@ var CapacityMeshSolver = class extends BaseSolver {
|
|
|
5367
6023
|
this.solved = true;
|
|
5368
6024
|
return;
|
|
5369
6025
|
}
|
|
5370
|
-
if (this.
|
|
5371
|
-
this.
|
|
5372
|
-
if (this.
|
|
6026
|
+
if (this.activeSubSolver) {
|
|
6027
|
+
this.activeSubSolver.step();
|
|
6028
|
+
if (this.activeSubSolver.solved) {
|
|
5373
6029
|
this.endTimeOfPhase[pipelineStepDef.solverName] = performance.now();
|
|
5374
6030
|
this.timeSpentOnPhase[pipelineStepDef.solverName] = this.endTimeOfPhase[pipelineStepDef.solverName] - this.startTimeOfPhase[pipelineStepDef.solverName];
|
|
5375
6031
|
pipelineStepDef.onSolved?.(this);
|
|
5376
|
-
this.
|
|
6032
|
+
this.activeSubSolver = null;
|
|
5377
6033
|
this.currentPipelineStepIndex++;
|
|
5378
|
-
} else if (this.
|
|
5379
|
-
this.error = this.
|
|
6034
|
+
} else if (this.activeSubSolver.failed) {
|
|
6035
|
+
this.error = this.activeSubSolver?.error;
|
|
5380
6036
|
this.failed = true;
|
|
5381
|
-
this.
|
|
6037
|
+
this.activeSubSolver = null;
|
|
5382
6038
|
}
|
|
5383
6039
|
return;
|
|
5384
6040
|
}
|
|
5385
6041
|
const constructorParams = pipelineStepDef.getConstructorParams(this);
|
|
5386
|
-
this.
|
|
5387
|
-
|
|
5388
|
-
);
|
|
5389
|
-
this[pipelineStepDef.solverName] = this.activeSolver;
|
|
6042
|
+
this.activeSubSolver = new pipelineStepDef.solverClass(...constructorParams);
|
|
6043
|
+
this[pipelineStepDef.solverName] = this.activeSubSolver;
|
|
5390
6044
|
this.timeSpentOnPhase[pipelineStepDef.solverName] = 0;
|
|
5391
6045
|
this.startTimeOfPhase[pipelineStepDef.solverName] = performance.now();
|
|
5392
6046
|
}
|
|
@@ -5394,9 +6048,13 @@ var CapacityMeshSolver = class extends BaseSolver {
|
|
|
5394
6048
|
return this.pipelineDef[this.currentPipelineStepIndex]?.solverName ?? "none";
|
|
5395
6049
|
}
|
|
5396
6050
|
visualize() {
|
|
5397
|
-
if (!this.solved && this.
|
|
6051
|
+
if (!this.solved && this.activeSubSolver)
|
|
6052
|
+
return this.activeSubSolver.visualize();
|
|
5398
6053
|
const netToPPSolver = this.netToPointPairsSolver?.visualize();
|
|
5399
6054
|
const nodeViz = this.nodeSolver?.visualize();
|
|
6055
|
+
const nodeTargetMergerViz = this.nodeTargetMerger?.visualize();
|
|
6056
|
+
const singleLayerNodeMergerViz = this.singleLayerNodeMerger?.visualize();
|
|
6057
|
+
const strawSolverViz = this.strawSolver?.visualize();
|
|
5400
6058
|
const edgeViz = this.edgeSolver?.visualize();
|
|
5401
6059
|
const pathingViz = this.pathingSolver?.visualize();
|
|
5402
6060
|
const edgeToPortSegmentViz = this.edgeToPortSegmentSolver?.visualize();
|
|
@@ -5409,14 +6067,37 @@ var CapacityMeshSolver = class extends BaseSolver {
|
|
|
5409
6067
|
rects: [
|
|
5410
6068
|
...(this.srj.obstacles ?? []).map((o) => ({
|
|
5411
6069
|
...o,
|
|
5412
|
-
fill: "rgba(255,0,0,0.25)"
|
|
6070
|
+
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)"
|
|
5413
6071
|
}))
|
|
6072
|
+
],
|
|
6073
|
+
lines: [
|
|
6074
|
+
{
|
|
6075
|
+
points: [
|
|
6076
|
+
// Add five points representing the bounds of the PCB
|
|
6077
|
+
{
|
|
6078
|
+
x: this.srj.bounds?.minX ?? -50,
|
|
6079
|
+
y: this.srj.bounds?.minY ?? -50
|
|
6080
|
+
},
|
|
6081
|
+
{ x: this.srj.bounds?.maxX ?? 50, y: this.srj.bounds?.minY ?? -50 },
|
|
6082
|
+
{ x: this.srj.bounds?.maxX ?? 50, y: this.srj.bounds?.maxY ?? 50 },
|
|
6083
|
+
{ x: this.srj.bounds?.minX ?? -50, y: this.srj.bounds?.maxY ?? 50 },
|
|
6084
|
+
{
|
|
6085
|
+
x: this.srj.bounds?.minX ?? -50,
|
|
6086
|
+
y: this.srj.bounds?.minY ?? -50
|
|
6087
|
+
}
|
|
6088
|
+
// Close the rectangle
|
|
6089
|
+
],
|
|
6090
|
+
strokeColor: "rgba(255,0,0,0.25)"
|
|
6091
|
+
}
|
|
5414
6092
|
]
|
|
5415
6093
|
};
|
|
5416
6094
|
const visualizations = [
|
|
5417
6095
|
problemViz,
|
|
5418
6096
|
netToPPSolver,
|
|
5419
6097
|
nodeViz,
|
|
6098
|
+
nodeTargetMergerViz,
|
|
6099
|
+
singleLayerNodeMergerViz,
|
|
6100
|
+
strawSolverViz,
|
|
5420
6101
|
edgeViz,
|
|
5421
6102
|
pathingViz,
|
|
5422
6103
|
edgeToPortSegmentViz,
|