@tscircuit/capacity-autorouter 0.0.27 → 0.0.29
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 +53 -2
- package/dist/index.js +814 -68
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -156,7 +156,8 @@ var CapacityMeshEdgeSolver = class extends BaseSolver {
|
|
|
156
156
|
this.edges = [];
|
|
157
157
|
for (let i = 0; i < this.nodes.length; i++) {
|
|
158
158
|
for (let j = i + 1; j < this.nodes.length; j++) {
|
|
159
|
-
|
|
159
|
+
const strawNodesWithSameParent = this.nodes[i]._strawNode && this.nodes[j]._strawNode && this.nodes[i]._strawParentCapacityMeshNodeId === this.nodes[j]._strawParentCapacityMeshNodeId;
|
|
160
|
+
if (!strawNodesWithSameParent && areNodesBordering(this.nodes[i], this.nodes[j]) && this.doNodesHaveSharedLayer(this.nodes[i], this.nodes[j])) {
|
|
160
161
|
this.edges.push({
|
|
161
162
|
capacityMeshEdgeId: this.getNextCapacityMeshEdgeId(),
|
|
162
163
|
nodeIds: [
|
|
@@ -1113,6 +1114,29 @@ var mapLayerNameToZ = (layerName, layerCount) => {
|
|
|
1113
1114
|
return parseInt(layerName.slice(5));
|
|
1114
1115
|
};
|
|
1115
1116
|
|
|
1117
|
+
// lib/utils/getTunedTotalCapacity1.ts
|
|
1118
|
+
var getTunedTotalCapacity1 = (nodeOrWidth, maxCapacityFactor = 1) => {
|
|
1119
|
+
const VIA_DIAMETER = 0.6;
|
|
1120
|
+
const TRACE_WIDTH = 0.15;
|
|
1121
|
+
const obstacleMargin = 0.2;
|
|
1122
|
+
const width = "width" in nodeOrWidth ? nodeOrWidth.width : nodeOrWidth;
|
|
1123
|
+
const viaLengthAcross = width / (VIA_DIAMETER / 2 + obstacleMargin);
|
|
1124
|
+
return (viaLengthAcross / 2) ** 1.1 * maxCapacityFactor;
|
|
1125
|
+
};
|
|
1126
|
+
var calculateOptimalCapacityDepth = (initialWidth, targetMinCapacity = 0.5, maxDepth = 16) => {
|
|
1127
|
+
let depth = 0;
|
|
1128
|
+
let width = initialWidth;
|
|
1129
|
+
while (depth < maxDepth) {
|
|
1130
|
+
const capacity = getTunedTotalCapacity1({ width });
|
|
1131
|
+
if (capacity <= targetMinCapacity) {
|
|
1132
|
+
break;
|
|
1133
|
+
}
|
|
1134
|
+
width /= 2;
|
|
1135
|
+
depth++;
|
|
1136
|
+
}
|
|
1137
|
+
return Math.max(1, depth);
|
|
1138
|
+
};
|
|
1139
|
+
|
|
1116
1140
|
// lib/solvers/CapacityMeshSolver/CapacityMeshNodeSolver1.ts
|
|
1117
1141
|
var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
1118
1142
|
constructor(srj, opts = {}) {
|
|
@@ -1407,7 +1431,9 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1407
1431
|
node.capacityMeshNodeId,
|
|
1408
1432
|
`availableZ: ${node.availableZ.join(",")}`,
|
|
1409
1433
|
`target? ${node._containsTarget ?? false}`,
|
|
1410
|
-
`obs? ${node._containsObstacle ?? false}
|
|
1434
|
+
`obs? ${node._containsObstacle ?? false}`,
|
|
1435
|
+
`${node.width.toFixed(2)}x${node.height.toFixed(2)}`,
|
|
1436
|
+
`capacity: ${getTunedTotalCapacity1(node).toFixed(2)}`
|
|
1411
1437
|
].join("\n")
|
|
1412
1438
|
});
|
|
1413
1439
|
}
|
|
@@ -1434,6 +1460,7 @@ var CapacityMeshNodeSolver2_NodeUnderObstacle = class extends CapacityMeshNodeSo
|
|
|
1434
1460
|
this.srj = srj;
|
|
1435
1461
|
this.opts = opts;
|
|
1436
1462
|
}
|
|
1463
|
+
VIA_DIAMETER = 0.6;
|
|
1437
1464
|
isNodeCompletelyOutsideBounds(node) {
|
|
1438
1465
|
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
1466
|
}
|
|
@@ -1538,10 +1565,10 @@ var CapacityMeshNodeSolver2_NodeUnderObstacle = class extends CapacityMeshNodeSo
|
|
|
1538
1565
|
const unfinishedNewNodes = [];
|
|
1539
1566
|
for (const childNode of childNodes) {
|
|
1540
1567
|
const shouldBeXYSubdivided = this.shouldNodeBeXYSubdivided(childNode);
|
|
1541
|
-
const shouldBeZSubdivided = childNode.availableZ.length > 1 && !shouldBeXYSubdivided && childNode._containsObstacle;
|
|
1568
|
+
const shouldBeZSubdivided = childNode.availableZ.length > 1 && !shouldBeXYSubdivided && (childNode._containsObstacle || childNode.width < this.VIA_DIAMETER);
|
|
1542
1569
|
if (shouldBeXYSubdivided) {
|
|
1543
1570
|
unfinishedNewNodes.push(childNode);
|
|
1544
|
-
} else if (!shouldBeXYSubdivided && !childNode._containsObstacle) {
|
|
1571
|
+
} else if (!shouldBeXYSubdivided && !childNode._containsObstacle && !shouldBeZSubdivided) {
|
|
1545
1572
|
finishedNewNodes.push(childNode);
|
|
1546
1573
|
} else if (!shouldBeXYSubdivided && childNode._containsTarget) {
|
|
1547
1574
|
if (shouldBeZSubdivided) {
|
|
@@ -2057,7 +2084,7 @@ var SingleHighDensityRouteSolver = class extends BaseSolver {
|
|
|
2057
2084
|
return false;
|
|
2058
2085
|
}
|
|
2059
2086
|
isNodeTooCloseToEdge(node, isVia) {
|
|
2060
|
-
const margin = isVia ? this.viaDiameter / 2 : this.obstacleMargin;
|
|
2087
|
+
const margin = isVia ? this.viaDiameter / 2 + this.obstacleMargin / 2 : this.obstacleMargin / 2;
|
|
2061
2088
|
const tooClose = node.x < this.bounds.minX + margin || node.x > this.bounds.maxX - margin || node.y < this.bounds.minY + margin || node.y > this.bounds.maxY - margin;
|
|
2062
2089
|
if (tooClose && !isVia) {
|
|
2063
2090
|
if (distance(node, this.B) < margin * 2 || distance(node, this.A) < margin * 2) {
|
|
@@ -3127,30 +3154,10 @@ var getConnectivityMapFromSimpleRouteJson = (srj) => {
|
|
|
3127
3154
|
}
|
|
3128
3155
|
}
|
|
3129
3156
|
}
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
// lib/utils/getTunedTotalCapacity1.ts
|
|
3134
|
-
var getTunedTotalCapacity1 = (nodeOrWidth, maxCapacityFactor = 1) => {
|
|
3135
|
-
const VIA_DIAMETER = 0.6;
|
|
3136
|
-
const TRACE_WIDTH = 0.15;
|
|
3137
|
-
const obstacleMargin = 0.2;
|
|
3138
|
-
const width = "width" in nodeOrWidth ? nodeOrWidth.width : nodeOrWidth;
|
|
3139
|
-
const viaLengthAcross = width / (VIA_DIAMETER / 2 + obstacleMargin);
|
|
3140
|
-
return (viaLengthAcross / 2) ** 1.1 * maxCapacityFactor;
|
|
3141
|
-
};
|
|
3142
|
-
var calculateOptimalCapacityDepth = (initialWidth, targetMinCapacity = 0.5, maxDepth = 16) => {
|
|
3143
|
-
let depth = 0;
|
|
3144
|
-
let width = initialWidth;
|
|
3145
|
-
while (depth < maxDepth) {
|
|
3146
|
-
const capacity = getTunedTotalCapacity1({ width });
|
|
3147
|
-
if (capacity <= targetMinCapacity) {
|
|
3148
|
-
break;
|
|
3149
|
-
}
|
|
3150
|
-
width /= 2;
|
|
3151
|
-
depth++;
|
|
3157
|
+
for (const obstacle of srj.obstacles) {
|
|
3158
|
+
connMap.addConnections([obstacle.connectedTo]);
|
|
3152
3159
|
}
|
|
3153
|
-
return
|
|
3160
|
+
return connMap;
|
|
3154
3161
|
};
|
|
3155
3162
|
|
|
3156
3163
|
// lib/solvers/NetToPointPairsSolver/buildMinimumSpanningTree.ts
|
|
@@ -3332,13 +3339,13 @@ function buildMinimumSpanningTree(points) {
|
|
|
3332
3339
|
if (point.x === neighbor.x && point.y === neighbor.y) {
|
|
3333
3340
|
continue;
|
|
3334
3341
|
}
|
|
3335
|
-
const
|
|
3342
|
+
const distance4 = Math.sqrt(
|
|
3336
3343
|
(point.x - neighbor.x) ** 2 + (point.y - neighbor.y) ** 2
|
|
3337
3344
|
);
|
|
3338
3345
|
edges.push({
|
|
3339
3346
|
from: point,
|
|
3340
3347
|
to: neighbor,
|
|
3341
|
-
weight:
|
|
3348
|
+
weight: distance4
|
|
3342
3349
|
});
|
|
3343
3350
|
}
|
|
3344
3351
|
}
|
|
@@ -5025,9 +5032,9 @@ var UnravelMultiSectionSolver = class extends BaseSolver {
|
|
|
5025
5032
|
var createRectFromCapacityNode = (node, opts = {}) => {
|
|
5026
5033
|
const lowestZ = Math.min(...node.availableZ);
|
|
5027
5034
|
return {
|
|
5028
|
-
center: !opts.rectMargin ? {
|
|
5029
|
-
x: node.center.x + lowestZ * node.width * 0.05,
|
|
5030
|
-
y: node.center.y - lowestZ * node.width * 0.05
|
|
5035
|
+
center: !opts.rectMargin || opts.zOffset ? {
|
|
5036
|
+
x: node.center.x + lowestZ * node.width * (opts.zOffset ?? 0.05),
|
|
5037
|
+
y: node.center.y - lowestZ * node.width * (opts.zOffset ?? 0.05)
|
|
5031
5038
|
} : node.center,
|
|
5032
5039
|
width: opts.rectMargin ? node.width - opts.rectMargin * 2 : Math.max(node.width - 0.5, node.width * 0.8),
|
|
5033
5040
|
height: opts.rectMargin ? node.height - opts.rectMargin * 2 : Math.max(node.height - 0.5, node.height * 0.8),
|
|
@@ -5105,11 +5112,11 @@ var CapacityPathingSolver = class extends BaseSolver {
|
|
|
5105
5112
|
let closestNode = this.nodes[0];
|
|
5106
5113
|
let minDistance = Number.MAX_VALUE;
|
|
5107
5114
|
for (const node of nodesWithTargets) {
|
|
5108
|
-
const
|
|
5115
|
+
const distance4 = Math.sqrt(
|
|
5109
5116
|
(node.center.x - point.x) ** 2 + (node.center.y - point.y) ** 2
|
|
5110
5117
|
);
|
|
5111
|
-
if (
|
|
5112
|
-
minDistance =
|
|
5118
|
+
if (distance4 < minDistance) {
|
|
5119
|
+
minDistance = distance4;
|
|
5113
5120
|
closestNode = node;
|
|
5114
5121
|
}
|
|
5115
5122
|
}
|
|
@@ -5169,12 +5176,16 @@ var CapacityPathingSolver = class extends BaseSolver {
|
|
|
5169
5176
|
}
|
|
5170
5177
|
return capacityPaths;
|
|
5171
5178
|
}
|
|
5172
|
-
doesNodeHaveCapacityForTrace(node) {
|
|
5179
|
+
doesNodeHaveCapacityForTrace(node, prevNode) {
|
|
5173
5180
|
const usedCapacity = this.usedNodeCapacityMap.get(node.capacityMeshNodeId) ?? 0;
|
|
5174
5181
|
const totalCapacity = this.getTotalCapacity(node);
|
|
5175
5182
|
if (node.availableZ.length === 1 && !node._containsTarget && usedCapacity > 0)
|
|
5176
5183
|
return false;
|
|
5177
|
-
|
|
5184
|
+
let additionalCapacityRequirement = 0;
|
|
5185
|
+
if (node.availableZ.length > 1 && prevNode.availableZ.length === 1) {
|
|
5186
|
+
additionalCapacityRequirement += 0.5;
|
|
5187
|
+
}
|
|
5188
|
+
return usedCapacity + additionalCapacityRequirement < totalCapacity;
|
|
5178
5189
|
}
|
|
5179
5190
|
canTravelThroughObstacle(node, connectionName) {
|
|
5180
5191
|
const goalNodeIds = this.connectionNameToGoalNodeIds.get(connectionName);
|
|
@@ -5242,7 +5253,7 @@ var CapacityPathingSolver = class extends BaseSolver {
|
|
|
5242
5253
|
if (this.visitedNodes?.has(neighborNode.capacityMeshNodeId)) {
|
|
5243
5254
|
continue;
|
|
5244
5255
|
}
|
|
5245
|
-
if (!this.doesNodeHaveCapacityForTrace(neighborNode)) {
|
|
5256
|
+
if (!this.doesNodeHaveCapacityForTrace(neighborNode, currentCandidate.node)) {
|
|
5246
5257
|
continue;
|
|
5247
5258
|
}
|
|
5248
5259
|
const connectionName = this.connectionsWithNodes[this.currentConnectionIndex].connection.name;
|
|
@@ -5281,27 +5292,42 @@ var CapacityPathingSolver = class extends BaseSolver {
|
|
|
5281
5292
|
if (conn.path && conn.path.length > 0) {
|
|
5282
5293
|
const pathPoints = conn.path.map(({ center: { x, y }, width }) => ({
|
|
5283
5294
|
// slight offset to allow viewing overlapping paths
|
|
5284
|
-
x: x + (i % 10 + i % 19) * (
|
|
5285
|
-
y: y + (i % 10 + i % 19) * (
|
|
5295
|
+
x: x + (i % 10 + i % 19) * (5e-3 * width),
|
|
5296
|
+
y: y + (i % 10 + i % 19) * (5e-3 * width)
|
|
5286
5297
|
}));
|
|
5287
5298
|
graphics.lines.push({
|
|
5288
5299
|
points: pathPoints,
|
|
5289
5300
|
strokeColor: this.colorMap[conn.connection.name]
|
|
5290
5301
|
});
|
|
5302
|
+
for (let u = 0; u < pathPoints.length; u++) {
|
|
5303
|
+
const point = pathPoints[u];
|
|
5304
|
+
graphics.points.push({
|
|
5305
|
+
x: point.x,
|
|
5306
|
+
y: point.y,
|
|
5307
|
+
label: [
|
|
5308
|
+
`conn: ${conn.connection.name}`,
|
|
5309
|
+
`node: ${conn.path[u].capacityMeshNodeId}`
|
|
5310
|
+
].join("\n")
|
|
5311
|
+
});
|
|
5312
|
+
}
|
|
5291
5313
|
}
|
|
5292
5314
|
}
|
|
5293
5315
|
}
|
|
5294
5316
|
for (const node of this.nodes) {
|
|
5295
5317
|
const nodeCosts = this.debug_lastNodeCostMap.get(node.capacityMeshNodeId);
|
|
5296
5318
|
graphics.rects.push({
|
|
5297
|
-
...createRectFromCapacityNode(node
|
|
5319
|
+
...createRectFromCapacityNode(node, {
|
|
5320
|
+
rectMargin: 0.025,
|
|
5321
|
+
zOffset: 0.01
|
|
5322
|
+
}),
|
|
5298
5323
|
label: [
|
|
5299
5324
|
`${node.capacityMeshNodeId}`,
|
|
5300
5325
|
`${this.usedNodeCapacityMap.get(node.capacityMeshNodeId)}/${this.getTotalCapacity(node).toFixed(2)}`,
|
|
5301
5326
|
`${node.width.toFixed(2)}x${node.height.toFixed(2)}`,
|
|
5302
5327
|
`g: ${nodeCosts?.g !== void 0 ? nodeCosts.g.toFixed(2) : "?"}`,
|
|
5303
5328
|
`h: ${nodeCosts?.h !== void 0 ? nodeCosts.h.toFixed(2) : "?"}`,
|
|
5304
|
-
`f: ${nodeCosts?.f !== void 0 ? nodeCosts.f.toFixed(2) : "?"}
|
|
5329
|
+
`f: ${nodeCosts?.f !== void 0 ? nodeCosts.f.toFixed(2) : "?"}`,
|
|
5330
|
+
`z: ${node.availableZ.join(", ")}`
|
|
5305
5331
|
].join("\n")
|
|
5306
5332
|
});
|
|
5307
5333
|
}
|
|
@@ -5311,7 +5337,8 @@ var CapacityPathingSolver = class extends BaseSolver {
|
|
|
5311
5337
|
for (const point of conn.connection.pointsToConnect) {
|
|
5312
5338
|
graphics.points.push({
|
|
5313
5339
|
x: point.x,
|
|
5314
|
-
y: point.y
|
|
5340
|
+
y: point.y,
|
|
5341
|
+
label: [`pointsToConnect ${conn.connection.name}`].join("\n")
|
|
5315
5342
|
});
|
|
5316
5343
|
}
|
|
5317
5344
|
}
|
|
@@ -5367,18 +5394,6 @@ var CapacityPathingSolver5 = class extends CapacityPathingSolver {
|
|
|
5367
5394
|
*/
|
|
5368
5395
|
getNodeCapacityPenalty(node) {
|
|
5369
5396
|
return 0.05;
|
|
5370
|
-
if (node.availableZ.length === 1) {
|
|
5371
|
-
return 0;
|
|
5372
|
-
}
|
|
5373
|
-
const totalCapacity = this.getTotalCapacity(node);
|
|
5374
|
-
const usedCapacity = this.usedNodeCapacityMap.get(node.capacityMeshNodeId) ?? 0;
|
|
5375
|
-
const remainingCapacity = totalCapacity - usedCapacity;
|
|
5376
|
-
const dist = this.activeCandidateStraightLineDistance;
|
|
5377
|
-
if (remainingCapacity <= 0) {
|
|
5378
|
-
const penalty = (-remainingCapacity + 1) / totalCapacity * dist * (this.NEGATIVE_CAPACITY_PENALTY_FACTOR / 4);
|
|
5379
|
-
return penalty ** 2;
|
|
5380
|
-
}
|
|
5381
|
-
return 1 / remainingCapacity * dist * this.REDUCED_CAPACITY_PENALTY_FACTOR / 8;
|
|
5382
5397
|
}
|
|
5383
5398
|
/**
|
|
5384
5399
|
* We're rewarding travel into big nodes.
|
|
@@ -5505,7 +5520,8 @@ var StrawSolver = class extends BaseSolver {
|
|
|
5505
5520
|
layer: node.layer,
|
|
5506
5521
|
availableZ: [...node.availableZ],
|
|
5507
5522
|
_depth: node._depth,
|
|
5508
|
-
_strawNode: true
|
|
5523
|
+
_strawNode: true,
|
|
5524
|
+
_strawParentCapacityMeshNodeId: node.capacityMeshNodeId
|
|
5509
5525
|
});
|
|
5510
5526
|
}
|
|
5511
5527
|
} else {
|
|
@@ -5521,7 +5537,8 @@ var StrawSolver = class extends BaseSolver {
|
|
|
5521
5537
|
layer: node.layer,
|
|
5522
5538
|
availableZ: [...node.availableZ],
|
|
5523
5539
|
_depth: node._depth,
|
|
5524
|
-
_strawNode: true
|
|
5540
|
+
_strawNode: true,
|
|
5541
|
+
_strawParentCapacityMeshNodeId: node.capacityMeshNodeId
|
|
5525
5542
|
});
|
|
5526
5543
|
}
|
|
5527
5544
|
}
|
|
@@ -5577,7 +5594,8 @@ ${node.width}x${node.height}`
|
|
|
5577
5594
|
stroke: "rgba(0, 0, 0, 0.5)",
|
|
5578
5595
|
label: `${node.capacityMeshNodeId}
|
|
5579
5596
|
Layer: ${node.availableZ[0]}
|
|
5580
|
-
${node.width}x${node.height}
|
|
5597
|
+
${node.width}x${node.height}`,
|
|
5598
|
+
layer: `z${node.availableZ.join(",")}`
|
|
5581
5599
|
});
|
|
5582
5600
|
}
|
|
5583
5601
|
for (const node of this.multiLayerNodes) {
|
|
@@ -5625,6 +5643,13 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
|
|
|
5625
5643
|
}
|
|
5626
5644
|
}
|
|
5627
5645
|
nodeWithArea.sort((a, b) => a[1] - b[1]);
|
|
5646
|
+
for (const [nodeId, area] of nodeWithArea) {
|
|
5647
|
+
const node = this.nodeMap.get(nodeId);
|
|
5648
|
+
this.nodeMap.set(nodeId, {
|
|
5649
|
+
...node,
|
|
5650
|
+
center: { ...node.center }
|
|
5651
|
+
});
|
|
5652
|
+
}
|
|
5628
5653
|
this.currentBatchNodeIds = nodeWithArea.map((n) => n[0]);
|
|
5629
5654
|
this.nextBatchNodeIds = [];
|
|
5630
5655
|
this.batchHadModifications = false;
|
|
@@ -5827,6 +5852,694 @@ ${node.width}x${node.height}
|
|
|
5827
5852
|
}
|
|
5828
5853
|
};
|
|
5829
5854
|
|
|
5855
|
+
// lib/solvers/SimplifiedPathSolver/SingleSimplifiedPathSolver.ts
|
|
5856
|
+
var SingleSimplifiedPathSolver = class extends BaseSolver {
|
|
5857
|
+
newRoute;
|
|
5858
|
+
newVias;
|
|
5859
|
+
headIndex = 0;
|
|
5860
|
+
tailIndex = 0;
|
|
5861
|
+
inputRoute;
|
|
5862
|
+
otherHdRoutes;
|
|
5863
|
+
obstacles;
|
|
5864
|
+
connMap;
|
|
5865
|
+
colorMap;
|
|
5866
|
+
constructor(params) {
|
|
5867
|
+
super();
|
|
5868
|
+
this.inputRoute = params.inputRoute;
|
|
5869
|
+
this.otherHdRoutes = params.otherHdRoutes;
|
|
5870
|
+
this.obstacles = params.obstacles;
|
|
5871
|
+
this.connMap = params.connMap;
|
|
5872
|
+
this.colorMap = params.colorMap;
|
|
5873
|
+
this.newRoute = [];
|
|
5874
|
+
this.newVias = [];
|
|
5875
|
+
}
|
|
5876
|
+
get simplifiedRoute() {
|
|
5877
|
+
return {
|
|
5878
|
+
connectionName: this.inputRoute.connectionName,
|
|
5879
|
+
traceThickness: this.inputRoute.traceThickness,
|
|
5880
|
+
viaDiameter: this.inputRoute.viaDiameter,
|
|
5881
|
+
route: this.newRoute,
|
|
5882
|
+
vias: this.newVias
|
|
5883
|
+
};
|
|
5884
|
+
}
|
|
5885
|
+
isValidPath(pointsInRoute) {
|
|
5886
|
+
throw new Error("Not implemented");
|
|
5887
|
+
}
|
|
5888
|
+
_step() {
|
|
5889
|
+
throw new Error("Not implemented");
|
|
5890
|
+
}
|
|
5891
|
+
getVisualsForNewRouteAndObstacles() {
|
|
5892
|
+
const graphics = {
|
|
5893
|
+
lines: [],
|
|
5894
|
+
points: [],
|
|
5895
|
+
circles: [],
|
|
5896
|
+
rects: [],
|
|
5897
|
+
coordinateSystem: "cartesian",
|
|
5898
|
+
title: "Simplified Path Solver"
|
|
5899
|
+
};
|
|
5900
|
+
for (let i = 0; i < this.inputRoute.route.length - 1; i++) {
|
|
5901
|
+
graphics.lines.push({
|
|
5902
|
+
points: [
|
|
5903
|
+
{ x: this.inputRoute.route[i].x, y: this.inputRoute.route[i].y },
|
|
5904
|
+
{
|
|
5905
|
+
x: this.inputRoute.route[i + 1].x,
|
|
5906
|
+
y: this.inputRoute.route[i + 1].y
|
|
5907
|
+
}
|
|
5908
|
+
],
|
|
5909
|
+
strokeColor: "rgba(255, 0, 0, 0.8)",
|
|
5910
|
+
strokeDash: this.inputRoute.route[i].z === 1 ? "5, 5" : void 0,
|
|
5911
|
+
layer: `z${this.inputRoute.route[i].z.toString()}`
|
|
5912
|
+
});
|
|
5913
|
+
}
|
|
5914
|
+
for (let i = 0; i < this.newRoute.length; i++) {
|
|
5915
|
+
if (i < this.newRoute.length - 1) {
|
|
5916
|
+
graphics.lines.push({
|
|
5917
|
+
points: [
|
|
5918
|
+
{ x: this.newRoute[i].x, y: this.newRoute[i].y },
|
|
5919
|
+
{ x: this.newRoute[i + 1].x, y: this.newRoute[i + 1].y }
|
|
5920
|
+
],
|
|
5921
|
+
strokeWidth: 0.15,
|
|
5922
|
+
strokeColor: "rgba(0, 255, 0, 0.8)",
|
|
5923
|
+
strokeDash: this.newRoute[i].z === 1 ? [0.4, 0.4] : void 0,
|
|
5924
|
+
layer: `z${this.newRoute[i].z.toString()}`
|
|
5925
|
+
});
|
|
5926
|
+
}
|
|
5927
|
+
graphics.points.push({
|
|
5928
|
+
x: this.newRoute[i].x,
|
|
5929
|
+
y: this.newRoute[i].y,
|
|
5930
|
+
color: "rgba(0, 255, 0, 0.8)",
|
|
5931
|
+
label: `z: ${this.newRoute[i].z}`,
|
|
5932
|
+
layer: `z${this.newRoute[i].z.toString()}`
|
|
5933
|
+
});
|
|
5934
|
+
}
|
|
5935
|
+
for (const via of this.newVias) {
|
|
5936
|
+
graphics.circles.push({
|
|
5937
|
+
center: via,
|
|
5938
|
+
radius: this.inputRoute.viaDiameter / 2,
|
|
5939
|
+
fill: "rgba(0, 0, 255, 0.5)"
|
|
5940
|
+
});
|
|
5941
|
+
}
|
|
5942
|
+
for (const obstacle of this.obstacles) {
|
|
5943
|
+
graphics.rects.push({
|
|
5944
|
+
center: obstacle.center,
|
|
5945
|
+
width: obstacle.width,
|
|
5946
|
+
height: obstacle.height,
|
|
5947
|
+
fill: obstacle.layers?.includes("top") ? "rgba(255, 0, 0, 0.3)" : obstacle.layers?.includes("bottom") ? "rgba(0, 0, 255, 0.3)" : "rgba(128, 128, 128, 0.3)"
|
|
5948
|
+
});
|
|
5949
|
+
}
|
|
5950
|
+
for (const route of this.otherHdRoutes) {
|
|
5951
|
+
for (let i = 0; i < route.route.length - 1; i++) {
|
|
5952
|
+
graphics.lines.push({
|
|
5953
|
+
points: [
|
|
5954
|
+
{ x: route.route[i].x, y: route.route[i].y },
|
|
5955
|
+
{ x: route.route[i + 1].x, y: route.route[i + 1].y }
|
|
5956
|
+
],
|
|
5957
|
+
strokeWidth: 0.15,
|
|
5958
|
+
strokeColor: route.route[i].z === 0 ? "rgba(255, 0, 255, 0.5)" : route.route[i].z === 1 ? "rgba(128, 0, 128, 0.5)" : "rgba(0, 0, 255, 0.5)",
|
|
5959
|
+
// bottom layer (blue)
|
|
5960
|
+
layer: `z${route.route[i].z.toString()}`
|
|
5961
|
+
});
|
|
5962
|
+
}
|
|
5963
|
+
}
|
|
5964
|
+
return graphics;
|
|
5965
|
+
}
|
|
5966
|
+
};
|
|
5967
|
+
|
|
5968
|
+
// lib/utils/calculate45DegreePaths.ts
|
|
5969
|
+
var calculate45DegreePaths = (pointA, pointB) => {
|
|
5970
|
+
const result = [];
|
|
5971
|
+
const dx = Math.abs(pointB.x - pointA.x);
|
|
5972
|
+
const dy = Math.abs(pointB.y - pointA.y);
|
|
5973
|
+
const signX = pointB.x > pointA.x ? 1 : -1;
|
|
5974
|
+
const signY = pointB.y > pointA.y ? 1 : -1;
|
|
5975
|
+
const midPoint1 = {
|
|
5976
|
+
x: pointB.x - signX * Math.abs(pointB.y - pointA.y),
|
|
5977
|
+
y: pointA.y
|
|
5978
|
+
};
|
|
5979
|
+
if ((midPoint1.x - pointA.x) * signX >= 0 && (midPoint1.x - pointB.x) * signX <= 0) {
|
|
5980
|
+
result.push([pointA, midPoint1, pointB]);
|
|
5981
|
+
}
|
|
5982
|
+
const midPoint2 = {
|
|
5983
|
+
x: pointA.x,
|
|
5984
|
+
y: pointB.y - signY * Math.abs(pointB.x - pointA.x)
|
|
5985
|
+
};
|
|
5986
|
+
if ((midPoint2.y - pointA.y) * signY >= 0 && (midPoint2.y - pointB.y) * signY <= 0) {
|
|
5987
|
+
result.push([pointA, midPoint2, pointB]);
|
|
5988
|
+
}
|
|
5989
|
+
const minDist = Math.min(dx, dy);
|
|
5990
|
+
const midPoint3 = {
|
|
5991
|
+
x: pointA.x + signX * minDist,
|
|
5992
|
+
y: pointA.y + signY * minDist
|
|
5993
|
+
};
|
|
5994
|
+
if ((midPoint3.x - pointA.x) * signX >= 0 && (midPoint3.x - pointB.x) * signX <= 0 && (midPoint3.y - pointA.y) * signY >= 0 && (midPoint3.y - pointB.y) * signY <= 0) {
|
|
5995
|
+
result.push([pointA, midPoint3, pointB]);
|
|
5996
|
+
}
|
|
5997
|
+
return result;
|
|
5998
|
+
};
|
|
5999
|
+
|
|
6000
|
+
// lib/utils/minimumDistanceBetweenSegments.ts
|
|
6001
|
+
function minimumDistanceBetweenSegments(A1, A2, B1, B2) {
|
|
6002
|
+
if (segmentsIntersect(A1, A2, B1, B2)) {
|
|
6003
|
+
return 0;
|
|
6004
|
+
}
|
|
6005
|
+
const distA1 = pointToSegmentDistance3(A1, B1, B2);
|
|
6006
|
+
const distA2 = pointToSegmentDistance3(A2, B1, B2);
|
|
6007
|
+
const distB1 = pointToSegmentDistance3(B1, A1, A2);
|
|
6008
|
+
const distB2 = pointToSegmentDistance3(B2, A1, A2);
|
|
6009
|
+
return Math.min(distA1, distA2, distB1, distB2);
|
|
6010
|
+
}
|
|
6011
|
+
function pointToSegmentDistance3(P, Q1, Q2) {
|
|
6012
|
+
const v = { x: Q2.x - Q1.x, y: Q2.y - Q1.y };
|
|
6013
|
+
const w = { x: P.x - Q1.x, y: P.y - Q1.y };
|
|
6014
|
+
const c1 = dotProduct(w, v);
|
|
6015
|
+
if (c1 <= 0) {
|
|
6016
|
+
return distance3(P, Q1);
|
|
6017
|
+
}
|
|
6018
|
+
const c2 = dotProduct(v, v);
|
|
6019
|
+
if (c2 <= c1) {
|
|
6020
|
+
return distance3(P, Q2);
|
|
6021
|
+
}
|
|
6022
|
+
const b = c1 / c2;
|
|
6023
|
+
const Pb = {
|
|
6024
|
+
x: Q1.x + b * v.x,
|
|
6025
|
+
y: Q1.y + b * v.y
|
|
6026
|
+
};
|
|
6027
|
+
return distance3(P, Pb);
|
|
6028
|
+
}
|
|
6029
|
+
function dotProduct(v1, v2) {
|
|
6030
|
+
return v1.x * v2.x + v1.y * v2.y;
|
|
6031
|
+
}
|
|
6032
|
+
function distance3(p1, p2) {
|
|
6033
|
+
const dx = p2.x - p1.x;
|
|
6034
|
+
const dy = p2.y - p1.y;
|
|
6035
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
6036
|
+
}
|
|
6037
|
+
function orientation3(p, q, r) {
|
|
6038
|
+
const val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
|
|
6039
|
+
if (val === 0) return 0;
|
|
6040
|
+
return val > 0 ? 1 : 2;
|
|
6041
|
+
}
|
|
6042
|
+
function onSegment3(p, q, r) {
|
|
6043
|
+
return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y);
|
|
6044
|
+
}
|
|
6045
|
+
function segmentsIntersect(A1, A2, B1, B2) {
|
|
6046
|
+
const o1 = orientation3(A1, A2, B1);
|
|
6047
|
+
const o2 = orientation3(A1, A2, B2);
|
|
6048
|
+
const o3 = orientation3(B1, B2, A1);
|
|
6049
|
+
const o4 = orientation3(B1, B2, A2);
|
|
6050
|
+
if (o1 !== o2 && o3 !== o4) return true;
|
|
6051
|
+
if (o1 === 0 && onSegment3(A1, B1, A2)) return true;
|
|
6052
|
+
if (o2 === 0 && onSegment3(A1, B2, A2)) return true;
|
|
6053
|
+
if (o3 === 0 && onSegment3(B1, A1, B2)) return true;
|
|
6054
|
+
if (o4 === 0 && onSegment3(B1, A2, B2)) return true;
|
|
6055
|
+
return false;
|
|
6056
|
+
}
|
|
6057
|
+
|
|
6058
|
+
// lib/solvers/SimplifiedPathSolver/SingleSimplifiedPathSolver5_Deg45.ts
|
|
6059
|
+
var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
|
|
6060
|
+
pathSegments = [];
|
|
6061
|
+
totalPathLength = 0;
|
|
6062
|
+
headDistanceAlongPath = 0;
|
|
6063
|
+
tailDistanceAlongPath = 0;
|
|
6064
|
+
stepSize = 0.25;
|
|
6065
|
+
// Default step size, can be adjusted
|
|
6066
|
+
lastValidPath = null;
|
|
6067
|
+
// Store the current valid path
|
|
6068
|
+
lastValidPathHeadDistance = 0;
|
|
6069
|
+
filteredObstacles = [];
|
|
6070
|
+
OBSTACLE_MARGIN = 0.15;
|
|
6071
|
+
TAIL_JUMP_RATIO = 0.8;
|
|
6072
|
+
constructor(params) {
|
|
6073
|
+
super(params);
|
|
6074
|
+
if (this.inputRoute.route.length <= 1) {
|
|
6075
|
+
this.newRoute = [...this.inputRoute.route];
|
|
6076
|
+
this.solved = true;
|
|
6077
|
+
return;
|
|
6078
|
+
}
|
|
6079
|
+
this.filteredObstacles = this.obstacles.filter(
|
|
6080
|
+
(obstacle) => !obstacle.connectedTo.some(
|
|
6081
|
+
(id) => this.connMap.areIdsConnected(this.inputRoute.connectionName, id)
|
|
6082
|
+
)
|
|
6083
|
+
);
|
|
6084
|
+
this.computePathSegments();
|
|
6085
|
+
}
|
|
6086
|
+
// Compute the path segments and their distances
|
|
6087
|
+
computePathSegments() {
|
|
6088
|
+
let cumulativeDistance = 0;
|
|
6089
|
+
for (let i = 0; i < this.inputRoute.route.length - 1; i++) {
|
|
6090
|
+
const start = this.inputRoute.route[i];
|
|
6091
|
+
const end = this.inputRoute.route[i + 1];
|
|
6092
|
+
const length = Math.sqrt((end.x - start.x) ** 2 + (end.y - start.y) ** 2) + i / 1e4;
|
|
6093
|
+
this.pathSegments.push({
|
|
6094
|
+
start,
|
|
6095
|
+
end,
|
|
6096
|
+
length,
|
|
6097
|
+
startDistance: cumulativeDistance,
|
|
6098
|
+
endDistance: cumulativeDistance + length
|
|
6099
|
+
});
|
|
6100
|
+
cumulativeDistance += length;
|
|
6101
|
+
}
|
|
6102
|
+
this.totalPathLength = cumulativeDistance;
|
|
6103
|
+
}
|
|
6104
|
+
// Helper to check if two points are the same
|
|
6105
|
+
arePointsEqual(p1, p2) {
|
|
6106
|
+
return p1.x === p2.x && p1.y === p2.y && p1.z === p2.z;
|
|
6107
|
+
}
|
|
6108
|
+
// Get point at a specific distance along the path
|
|
6109
|
+
getPointAtDistance(distance4) {
|
|
6110
|
+
distance4 = Math.max(0, Math.min(distance4, this.totalPathLength));
|
|
6111
|
+
const segment = this.pathSegments.find(
|
|
6112
|
+
(seg) => distance4 >= seg.startDistance && distance4 <= seg.endDistance
|
|
6113
|
+
);
|
|
6114
|
+
if (!segment) {
|
|
6115
|
+
return this.inputRoute.route[this.inputRoute.route.length - 1];
|
|
6116
|
+
}
|
|
6117
|
+
const factor = (distance4 - segment.startDistance) / segment.length;
|
|
6118
|
+
return {
|
|
6119
|
+
x: segment.start.x + factor * (segment.end.x - segment.start.x),
|
|
6120
|
+
y: segment.start.y + factor * (segment.end.y - segment.start.y),
|
|
6121
|
+
z: segment.start.z
|
|
6122
|
+
// Z doesn't interpolate - use the segment's start z value
|
|
6123
|
+
};
|
|
6124
|
+
}
|
|
6125
|
+
// Find nearest index in the original route for a given distance
|
|
6126
|
+
getNearestIndexForDistance(distance4) {
|
|
6127
|
+
if (distance4 <= 0) return 0;
|
|
6128
|
+
if (distance4 >= this.totalPathLength)
|
|
6129
|
+
return this.inputRoute.route.length - 1;
|
|
6130
|
+
const segmentIndex = this.pathSegments.findIndex(
|
|
6131
|
+
(seg) => distance4 >= seg.startDistance && distance4 <= seg.endDistance
|
|
6132
|
+
);
|
|
6133
|
+
if (segmentIndex === -1) return 0;
|
|
6134
|
+
const segment = this.pathSegments[segmentIndex];
|
|
6135
|
+
const midDistance = (segment.startDistance + segment.endDistance) / 2;
|
|
6136
|
+
return distance4 > midDistance ? segmentIndex + 1 : segmentIndex;
|
|
6137
|
+
}
|
|
6138
|
+
// Check if a path segment is valid
|
|
6139
|
+
isValidPathSegment(start, end) {
|
|
6140
|
+
for (const obstacle of this.filteredObstacles) {
|
|
6141
|
+
if (!obstacle.zLayers?.includes(start.z)) {
|
|
6142
|
+
continue;
|
|
6143
|
+
}
|
|
6144
|
+
const obstacleLeft = obstacle.center.x - obstacle.width / 2 - this.OBSTACLE_MARGIN;
|
|
6145
|
+
const obstacleRight = obstacle.center.x + obstacle.width / 2 + this.OBSTACLE_MARGIN;
|
|
6146
|
+
const obstacleTop = obstacle.center.y - obstacle.height / 2 - this.OBSTACLE_MARGIN;
|
|
6147
|
+
const obstacleBottom = obstacle.center.y + obstacle.height / 2 + this.OBSTACLE_MARGIN;
|
|
6148
|
+
if (doSegmentsIntersect(
|
|
6149
|
+
{ x: start.x, y: start.y },
|
|
6150
|
+
{ x: end.x, y: end.y },
|
|
6151
|
+
{ x: obstacleLeft, y: obstacleTop },
|
|
6152
|
+
{ x: obstacleRight, y: obstacleTop }
|
|
6153
|
+
) || doSegmentsIntersect(
|
|
6154
|
+
{ x: start.x, y: start.y },
|
|
6155
|
+
{ x: end.x, y: end.y },
|
|
6156
|
+
{ x: obstacleRight, y: obstacleTop },
|
|
6157
|
+
{ x: obstacleRight, y: obstacleBottom }
|
|
6158
|
+
) || doSegmentsIntersect(
|
|
6159
|
+
{ x: start.x, y: start.y },
|
|
6160
|
+
{ x: end.x, y: end.y },
|
|
6161
|
+
{ x: obstacleRight, y: obstacleBottom },
|
|
6162
|
+
{ x: obstacleLeft, y: obstacleBottom }
|
|
6163
|
+
) || doSegmentsIntersect(
|
|
6164
|
+
{ x: start.x, y: start.y },
|
|
6165
|
+
{ x: end.x, y: end.y },
|
|
6166
|
+
{ x: obstacleLeft, y: obstacleBottom },
|
|
6167
|
+
{ x: obstacleLeft, y: obstacleTop }
|
|
6168
|
+
)) {
|
|
6169
|
+
return false;
|
|
6170
|
+
}
|
|
6171
|
+
}
|
|
6172
|
+
for (const route of this.otherHdRoutes) {
|
|
6173
|
+
if (this.connMap.areIdsConnected(
|
|
6174
|
+
this.inputRoute.connectionName,
|
|
6175
|
+
route.connectionName
|
|
6176
|
+
)) {
|
|
6177
|
+
continue;
|
|
6178
|
+
}
|
|
6179
|
+
for (let j = 0; j < route.route.length - 1; j++) {
|
|
6180
|
+
const routeStart = route.route[j];
|
|
6181
|
+
const routeEnd = route.route[j + 1];
|
|
6182
|
+
if (routeStart.z === start.z && routeEnd.z === start.z) {
|
|
6183
|
+
if (minimumDistanceBetweenSegments(
|
|
6184
|
+
{ x: start.x, y: start.y },
|
|
6185
|
+
{ x: end.x, y: end.y },
|
|
6186
|
+
{ x: routeStart.x, y: routeStart.y },
|
|
6187
|
+
{ x: routeEnd.x, y: routeEnd.y }
|
|
6188
|
+
) < this.OBSTACLE_MARGIN) {
|
|
6189
|
+
return false;
|
|
6190
|
+
}
|
|
6191
|
+
}
|
|
6192
|
+
}
|
|
6193
|
+
for (const via of route.vias) {
|
|
6194
|
+
if (pointToSegmentDistance(via, start, end) < this.OBSTACLE_MARGIN + route.viaDiameter / 2) {
|
|
6195
|
+
return false;
|
|
6196
|
+
}
|
|
6197
|
+
}
|
|
6198
|
+
}
|
|
6199
|
+
return true;
|
|
6200
|
+
}
|
|
6201
|
+
// Check if a path with multiple points is valid
|
|
6202
|
+
isValidPath(pointsInRoute) {
|
|
6203
|
+
if (pointsInRoute.length < 2) return true;
|
|
6204
|
+
for (let i = 0; i < pointsInRoute.length - 1; i++) {
|
|
6205
|
+
if (pointsInRoute[i].z !== pointsInRoute[i + 1].z) {
|
|
6206
|
+
return false;
|
|
6207
|
+
}
|
|
6208
|
+
}
|
|
6209
|
+
for (let i = 0; i < pointsInRoute.length - 1; i++) {
|
|
6210
|
+
if (!this.isValidPathSegment(pointsInRoute[i], pointsInRoute[i + 1])) {
|
|
6211
|
+
return false;
|
|
6212
|
+
}
|
|
6213
|
+
}
|
|
6214
|
+
return true;
|
|
6215
|
+
}
|
|
6216
|
+
// Find a valid 45-degree path between two points
|
|
6217
|
+
find45DegreePath(start, end) {
|
|
6218
|
+
if (this.arePointsEqual(start, end)) {
|
|
6219
|
+
return [start];
|
|
6220
|
+
}
|
|
6221
|
+
if (start.z !== end.z) {
|
|
6222
|
+
return null;
|
|
6223
|
+
}
|
|
6224
|
+
const possiblePaths = calculate45DegreePaths(
|
|
6225
|
+
{ x: start.x, y: start.y },
|
|
6226
|
+
{ x: end.x, y: end.y }
|
|
6227
|
+
);
|
|
6228
|
+
for (const path of possiblePaths) {
|
|
6229
|
+
const fullPath = path.map((p) => ({ x: p.x, y: p.y, z: start.z }));
|
|
6230
|
+
if (this.isValidPath(fullPath)) {
|
|
6231
|
+
return fullPath;
|
|
6232
|
+
}
|
|
6233
|
+
}
|
|
6234
|
+
return null;
|
|
6235
|
+
}
|
|
6236
|
+
// Add a path to the result, skipping the first point if it's already added
|
|
6237
|
+
addPathToResult(path) {
|
|
6238
|
+
if (path.length === 0) return;
|
|
6239
|
+
for (let i = 0; i < path.length; i++) {
|
|
6240
|
+
if (i === 0 && this.newRoute.length > 0 && this.arePointsEqual(this.newRoute[this.newRoute.length - 1], path[i])) {
|
|
6241
|
+
continue;
|
|
6242
|
+
}
|
|
6243
|
+
this.newRoute.push(path[i]);
|
|
6244
|
+
}
|
|
6245
|
+
}
|
|
6246
|
+
_step() {
|
|
6247
|
+
const tailHasReachedEnd = this.tailDistanceAlongPath >= this.totalPathLength;
|
|
6248
|
+
const headHasReachedEnd = this.headDistanceAlongPath >= this.totalPathLength;
|
|
6249
|
+
if (tailHasReachedEnd) {
|
|
6250
|
+
const lastPoint = this.inputRoute.route[this.inputRoute.route.length - 1];
|
|
6251
|
+
if (this.newRoute.length === 0 || !this.arePointsEqual(this.newRoute[this.newRoute.length - 1], lastPoint)) {
|
|
6252
|
+
this.newRoute.push(lastPoint);
|
|
6253
|
+
}
|
|
6254
|
+
this.solved = true;
|
|
6255
|
+
return;
|
|
6256
|
+
}
|
|
6257
|
+
if (headHasReachedEnd) {
|
|
6258
|
+
const tailPoint2 = this.getPointAtDistance(this.tailDistanceAlongPath);
|
|
6259
|
+
const endPoint = this.inputRoute.route[this.inputRoute.route.length - 1];
|
|
6260
|
+
const path452 = this.find45DegreePath(tailPoint2, endPoint);
|
|
6261
|
+
if (path452) {
|
|
6262
|
+
this.addPathToResult(path452);
|
|
6263
|
+
this.solved = true;
|
|
6264
|
+
return;
|
|
6265
|
+
} else {
|
|
6266
|
+
if (this.lastValidPath) {
|
|
6267
|
+
this.addPathToResult(this.lastValidPath);
|
|
6268
|
+
this.lastValidPath = null;
|
|
6269
|
+
this.tailDistanceAlongPath = this.lastValidPathHeadDistance;
|
|
6270
|
+
} else {
|
|
6271
|
+
this.newRoute.push(endPoint);
|
|
6272
|
+
this.solved = true;
|
|
6273
|
+
}
|
|
6274
|
+
}
|
|
6275
|
+
}
|
|
6276
|
+
this.headDistanceAlongPath = Math.min(
|
|
6277
|
+
this.headDistanceAlongPath + this.stepSize,
|
|
6278
|
+
this.totalPathLength
|
|
6279
|
+
);
|
|
6280
|
+
const tailPoint = this.getPointAtDistance(this.tailDistanceAlongPath);
|
|
6281
|
+
const headPoint = this.getPointAtDistance(this.headDistanceAlongPath);
|
|
6282
|
+
const tailIndex = this.getNearestIndexForDistance(
|
|
6283
|
+
this.tailDistanceAlongPath
|
|
6284
|
+
);
|
|
6285
|
+
const headIndex = this.getNearestIndexForDistance(
|
|
6286
|
+
this.headDistanceAlongPath
|
|
6287
|
+
);
|
|
6288
|
+
let layerChangeBtwHeadAndTail = false;
|
|
6289
|
+
let layerChangeAtDistance = -1;
|
|
6290
|
+
for (let i = tailIndex; i < headIndex; i++) {
|
|
6291
|
+
if (i + 1 < this.inputRoute.route.length && this.inputRoute.route[i].z !== this.inputRoute.route[i + 1].z) {
|
|
6292
|
+
layerChangeBtwHeadAndTail = true;
|
|
6293
|
+
const changeSegmentIndex = i;
|
|
6294
|
+
layerChangeAtDistance = this.pathSegments[changeSegmentIndex].startDistance;
|
|
6295
|
+
break;
|
|
6296
|
+
}
|
|
6297
|
+
}
|
|
6298
|
+
if (layerChangeBtwHeadAndTail && layerChangeAtDistance > 0) {
|
|
6299
|
+
if (this.lastValidPath) {
|
|
6300
|
+
this.addPathToResult(this.lastValidPath);
|
|
6301
|
+
this.lastValidPath = null;
|
|
6302
|
+
}
|
|
6303
|
+
const pointBeforeChange = this.getPointAtDistance(layerChangeAtDistance);
|
|
6304
|
+
const pointAfterChange = this.inputRoute.route[this.getNearestIndexForDistance(layerChangeAtDistance) + 1];
|
|
6305
|
+
this.newVias.push({
|
|
6306
|
+
x: pointAfterChange.x,
|
|
6307
|
+
y: pointAfterChange.y
|
|
6308
|
+
});
|
|
6309
|
+
this.newRoute.push(pointAfterChange);
|
|
6310
|
+
const nextTailIndex = this.getNearestIndexForDistance(layerChangeAtDistance) + 1;
|
|
6311
|
+
if (this.pathSegments[nextTailIndex]) {
|
|
6312
|
+
this.tailDistanceAlongPath = this.pathSegments[nextTailIndex].startDistance;
|
|
6313
|
+
this.headDistanceAlongPath = this.tailDistanceAlongPath;
|
|
6314
|
+
} else {
|
|
6315
|
+
console.error("Creating via at end, this is probably not right");
|
|
6316
|
+
this.solved = true;
|
|
6317
|
+
return;
|
|
6318
|
+
}
|
|
6319
|
+
return;
|
|
6320
|
+
}
|
|
6321
|
+
const path45 = this.find45DegreePath(tailPoint, headPoint);
|
|
6322
|
+
if (!path45 && !this.lastValidPath) {
|
|
6323
|
+
this.tailDistanceAlongPath += this.stepSize;
|
|
6324
|
+
this.headDistanceAlongPath += this.stepSize;
|
|
6325
|
+
return;
|
|
6326
|
+
}
|
|
6327
|
+
if (path45) {
|
|
6328
|
+
this.lastValidPath = path45;
|
|
6329
|
+
this.lastValidPathHeadDistance = this.headDistanceAlongPath;
|
|
6330
|
+
return;
|
|
6331
|
+
}
|
|
6332
|
+
if (this.lastValidPath) {
|
|
6333
|
+
this.addPathToResult(this.lastValidPath);
|
|
6334
|
+
this.lastValidPath = null;
|
|
6335
|
+
this.tailDistanceAlongPath = this.lastValidPathHeadDistance;
|
|
6336
|
+
}
|
|
6337
|
+
this.headDistanceAlongPath = Math.min(
|
|
6338
|
+
this.headDistanceAlongPath + this.stepSize,
|
|
6339
|
+
this.totalPathLength
|
|
6340
|
+
);
|
|
6341
|
+
}
|
|
6342
|
+
visualize() {
|
|
6343
|
+
const graphics = this.getVisualsForNewRouteAndObstacles();
|
|
6344
|
+
const tailPoint = this.getPointAtDistance(this.tailDistanceAlongPath);
|
|
6345
|
+
const headPoint = this.getPointAtDistance(this.headDistanceAlongPath);
|
|
6346
|
+
graphics.points.push({
|
|
6347
|
+
x: tailPoint.x,
|
|
6348
|
+
y: tailPoint.y,
|
|
6349
|
+
color: "yellow",
|
|
6350
|
+
label: ["Tail", `z: ${tailPoint.z}`].join("\n")
|
|
6351
|
+
});
|
|
6352
|
+
graphics.points.push({
|
|
6353
|
+
x: headPoint.x,
|
|
6354
|
+
y: headPoint.y,
|
|
6355
|
+
color: "orange",
|
|
6356
|
+
label: ["Head", `z: ${headPoint.z}`].join("\n")
|
|
6357
|
+
});
|
|
6358
|
+
let distance4 = 0;
|
|
6359
|
+
while (distance4 < this.totalPathLength) {
|
|
6360
|
+
const point = this.getPointAtDistance(distance4);
|
|
6361
|
+
graphics.circles.push({
|
|
6362
|
+
center: {
|
|
6363
|
+
x: point.x,
|
|
6364
|
+
y: point.y
|
|
6365
|
+
},
|
|
6366
|
+
radius: 0.05,
|
|
6367
|
+
fill: "rgba(100, 100, 100, 0.5)"
|
|
6368
|
+
});
|
|
6369
|
+
distance4 += this.totalPathLength / 20;
|
|
6370
|
+
}
|
|
6371
|
+
if (this.lastValidPath && this.lastValidPath.length > 1) {
|
|
6372
|
+
for (let i = 0; i < this.lastValidPath.length - 1; i++) {
|
|
6373
|
+
graphics.lines.push({
|
|
6374
|
+
points: [
|
|
6375
|
+
{ x: this.lastValidPath[i].x, y: this.lastValidPath[i].y },
|
|
6376
|
+
{
|
|
6377
|
+
x: this.lastValidPath[i + 1].x,
|
|
6378
|
+
y: this.lastValidPath[i + 1].y
|
|
6379
|
+
}
|
|
6380
|
+
],
|
|
6381
|
+
strokeColor: "rgba(0, 255, 255, 0.9)",
|
|
6382
|
+
// Bright cyan
|
|
6383
|
+
strokeDash: "3, 3"
|
|
6384
|
+
// Dashed line to indicate it's a prospective path
|
|
6385
|
+
});
|
|
6386
|
+
}
|
|
6387
|
+
}
|
|
6388
|
+
return graphics;
|
|
6389
|
+
}
|
|
6390
|
+
};
|
|
6391
|
+
|
|
6392
|
+
// lib/solvers/SimplifiedPathSolver/MultiSimplifiedPathSolver.ts
|
|
6393
|
+
var MultiSimplifiedPathSolver = class extends BaseSolver {
|
|
6394
|
+
simplifiedHdRoutes;
|
|
6395
|
+
currentUnsimplifiedHdRouteIndex = 0;
|
|
6396
|
+
activeSubSolver = null;
|
|
6397
|
+
unsimplifiedHdRoutes;
|
|
6398
|
+
obstacles;
|
|
6399
|
+
connMap;
|
|
6400
|
+
colorMap;
|
|
6401
|
+
constructor(params) {
|
|
6402
|
+
super();
|
|
6403
|
+
this.MAX_ITERATIONS = 1e8;
|
|
6404
|
+
this.unsimplifiedHdRoutes = params.unsimplifiedHdRoutes;
|
|
6405
|
+
this.obstacles = params.obstacles;
|
|
6406
|
+
this.connMap = params.connMap || new ConnectivityMap({});
|
|
6407
|
+
this.colorMap = params.colorMap || {};
|
|
6408
|
+
this.simplifiedHdRoutes = [];
|
|
6409
|
+
}
|
|
6410
|
+
_step() {
|
|
6411
|
+
const hdRoute = this.unsimplifiedHdRoutes[this.currentUnsimplifiedHdRouteIndex];
|
|
6412
|
+
if (!this.activeSubSolver) {
|
|
6413
|
+
if (!hdRoute) {
|
|
6414
|
+
this.solved = true;
|
|
6415
|
+
return;
|
|
6416
|
+
}
|
|
6417
|
+
this.activeSubSolver = new SingleSimplifiedPathSolver5({
|
|
6418
|
+
inputRoute: hdRoute,
|
|
6419
|
+
otherHdRoutes: this.unsimplifiedHdRoutes.slice(this.currentUnsimplifiedHdRouteIndex + 1).concat(this.simplifiedHdRoutes),
|
|
6420
|
+
obstacles: this.obstacles,
|
|
6421
|
+
connMap: this.connMap,
|
|
6422
|
+
colorMap: this.colorMap
|
|
6423
|
+
});
|
|
6424
|
+
this.currentUnsimplifiedHdRouteIndex++;
|
|
6425
|
+
return;
|
|
6426
|
+
}
|
|
6427
|
+
this.activeSubSolver.step();
|
|
6428
|
+
if (this.activeSubSolver.solved) {
|
|
6429
|
+
this.simplifiedHdRoutes.push(this.activeSubSolver.simplifiedRoute);
|
|
6430
|
+
this.activeSubSolver = null;
|
|
6431
|
+
}
|
|
6432
|
+
}
|
|
6433
|
+
visualize() {
|
|
6434
|
+
if (this.activeSubSolver) {
|
|
6435
|
+
return this.activeSubSolver.visualize();
|
|
6436
|
+
}
|
|
6437
|
+
const graphics = {
|
|
6438
|
+
lines: [],
|
|
6439
|
+
points: [],
|
|
6440
|
+
circles: [],
|
|
6441
|
+
rects: [],
|
|
6442
|
+
coordinateSystem: "cartesian",
|
|
6443
|
+
title: "Multi Simplified Path Solver"
|
|
6444
|
+
};
|
|
6445
|
+
for (const route of this.unsimplifiedHdRoutes) {
|
|
6446
|
+
if (this.simplifiedHdRoutes.some(
|
|
6447
|
+
(r) => r.connectionName === route.connectionName
|
|
6448
|
+
)) {
|
|
6449
|
+
continue;
|
|
6450
|
+
}
|
|
6451
|
+
for (let i = 0; i < route.route.length - 1; i++) {
|
|
6452
|
+
graphics.lines.push({
|
|
6453
|
+
points: [
|
|
6454
|
+
{ x: route.route[i].x, y: route.route[i].y },
|
|
6455
|
+
{ x: route.route[i + 1].x, y: route.route[i + 1].y }
|
|
6456
|
+
],
|
|
6457
|
+
strokeColor: route.route[i].z === 1 ? "rgba(0, 0, 255, 0.4)" : "rgba(255, 0, 0, 0.4)",
|
|
6458
|
+
strokeWidth: 0.15,
|
|
6459
|
+
strokeDash: route.route[i].z === 1 ? [0.5, 0.5] : void 0
|
|
6460
|
+
});
|
|
6461
|
+
}
|
|
6462
|
+
for (const via of route.vias || []) {
|
|
6463
|
+
graphics.circles.push({
|
|
6464
|
+
center: via,
|
|
6465
|
+
radius: route.viaDiameter / 2 || 0.3,
|
|
6466
|
+
// Default radius if viaDiameter not specified
|
|
6467
|
+
fill: "rgba(0, 0, 255, 0.4)"
|
|
6468
|
+
});
|
|
6469
|
+
}
|
|
6470
|
+
}
|
|
6471
|
+
for (const route of this.simplifiedHdRoutes) {
|
|
6472
|
+
const routeColor = this.colorMap?.[route.connectionName] || "rgba(128, 128, 128, 0.8)";
|
|
6473
|
+
for (let i = 0; i < route.route.length - 1; i++) {
|
|
6474
|
+
graphics.lines.push({
|
|
6475
|
+
points: [
|
|
6476
|
+
{ x: route.route[i].x, y: route.route[i].y },
|
|
6477
|
+
{ x: route.route[i + 1].x, y: route.route[i + 1].y }
|
|
6478
|
+
],
|
|
6479
|
+
strokeWidth: 0.15,
|
|
6480
|
+
strokeColor: routeColor,
|
|
6481
|
+
strokeDash: route.route[i].z === 1 ? [0.5, 0.5] : void 0,
|
|
6482
|
+
step: 1
|
|
6483
|
+
});
|
|
6484
|
+
}
|
|
6485
|
+
for (const via of route.vias || []) {
|
|
6486
|
+
graphics.circles.push({
|
|
6487
|
+
center: via,
|
|
6488
|
+
radius: route.viaDiameter / 2,
|
|
6489
|
+
fill: "rgba(0, 0, 255, 0.5)",
|
|
6490
|
+
step: 1
|
|
6491
|
+
});
|
|
6492
|
+
}
|
|
6493
|
+
}
|
|
6494
|
+
for (const route of this.unsimplifiedHdRoutes) {
|
|
6495
|
+
for (let i = 0; i < route.route.length - 1; i++) {
|
|
6496
|
+
graphics.lines.push({
|
|
6497
|
+
points: [
|
|
6498
|
+
{ x: route.route[i].x, y: route.route[i].y },
|
|
6499
|
+
{ x: route.route[i + 1].x, y: route.route[i + 1].y }
|
|
6500
|
+
],
|
|
6501
|
+
strokeWidth: 0.15,
|
|
6502
|
+
strokeColor: "rgba(255, 0, 0, 0.2)",
|
|
6503
|
+
strokeDash: [0.5, 0.5],
|
|
6504
|
+
step: 0,
|
|
6505
|
+
layer: `z${route.route[i].z.toString()}`
|
|
6506
|
+
});
|
|
6507
|
+
}
|
|
6508
|
+
for (const point of route.vias) {
|
|
6509
|
+
graphics.circles.push({
|
|
6510
|
+
center: { x: point.x, y: point.y },
|
|
6511
|
+
radius: route.viaDiameter / 2,
|
|
6512
|
+
fill: "rgba(255, 0, 0, 0.2)",
|
|
6513
|
+
step: 0
|
|
6514
|
+
});
|
|
6515
|
+
}
|
|
6516
|
+
}
|
|
6517
|
+
for (const obstacle of this.obstacles) {
|
|
6518
|
+
graphics.rects.push({
|
|
6519
|
+
center: obstacle.center,
|
|
6520
|
+
width: obstacle.width,
|
|
6521
|
+
height: obstacle.height,
|
|
6522
|
+
fill: obstacle.layers?.includes("top") ? "rgba(255, 0, 0, 0.3)" : obstacle.layers?.includes("bottom") ? "rgba(0, 0, 255, 0.3)" : "rgba(128, 128, 128, 0.3)"
|
|
6523
|
+
});
|
|
6524
|
+
}
|
|
6525
|
+
if (this.currentUnsimplifiedHdRouteIndex < this.unsimplifiedHdRoutes.length) {
|
|
6526
|
+
const currentRoute = this.unsimplifiedHdRoutes[this.currentUnsimplifiedHdRouteIndex];
|
|
6527
|
+
if (currentRoute.route.length > 0) {
|
|
6528
|
+
graphics.circles.push({
|
|
6529
|
+
center: {
|
|
6530
|
+
x: currentRoute.route[0].x,
|
|
6531
|
+
y: currentRoute.route[0].y
|
|
6532
|
+
},
|
|
6533
|
+
radius: 0.2,
|
|
6534
|
+
fill: "yellow",
|
|
6535
|
+
label: "Current"
|
|
6536
|
+
});
|
|
6537
|
+
}
|
|
6538
|
+
}
|
|
6539
|
+
return graphics;
|
|
6540
|
+
}
|
|
6541
|
+
};
|
|
6542
|
+
|
|
5830
6543
|
// lib/solvers/AutoroutingPipelineSolver.ts
|
|
5831
6544
|
function definePipelineStep(solverName, solverClass, getConstructorParams, opts = {}) {
|
|
5832
6545
|
return {
|
|
@@ -5872,6 +6585,7 @@ var CapacityMeshSolver = class extends BaseSolver {
|
|
|
5872
6585
|
highDensityStitchSolver;
|
|
5873
6586
|
singleLayerNodeMerger;
|
|
5874
6587
|
strawSolver;
|
|
6588
|
+
multiSimplifiedPathSolver;
|
|
5875
6589
|
startTimeOfPhase;
|
|
5876
6590
|
endTimeOfPhase;
|
|
5877
6591
|
timeSpentOnPhase;
|
|
@@ -5894,17 +6608,18 @@ var CapacityMeshSolver = class extends BaseSolver {
|
|
|
5894
6608
|
}
|
|
5895
6609
|
}
|
|
5896
6610
|
),
|
|
5897
|
-
// definePipelineStep("nodeSolver", CapacityMeshNodeSolver, (cms) => [
|
|
5898
|
-
// cms.netToPointPairsSolver?.getNewSimpleRouteJson() || cms.srj,
|
|
5899
|
-
// cms.opts,
|
|
5900
|
-
// ]),
|
|
5901
6611
|
definePipelineStep(
|
|
5902
6612
|
"nodeSolver",
|
|
5903
6613
|
CapacityMeshNodeSolver2_NodeUnderObstacle,
|
|
5904
6614
|
(cms) => [
|
|
5905
6615
|
cms.netToPointPairsSolver?.getNewSimpleRouteJson() || cms.srj,
|
|
5906
6616
|
cms.opts
|
|
5907
|
-
]
|
|
6617
|
+
],
|
|
6618
|
+
{
|
|
6619
|
+
onSolved: (cms) => {
|
|
6620
|
+
cms.capacityNodes = cms.nodeSolver?.finishedNodes;
|
|
6621
|
+
}
|
|
6622
|
+
}
|
|
5908
6623
|
),
|
|
5909
6624
|
// definePipelineStep("nodeTargetMerger", CapacityNodeTargetMerger, (cms) => [
|
|
5910
6625
|
// cms.nodeSolver?.finishedNodes || [],
|
|
@@ -5921,7 +6636,12 @@ var CapacityMeshSolver = class extends BaseSolver {
|
|
|
5921
6636
|
definePipelineStep(
|
|
5922
6637
|
"singleLayerNodeMerger",
|
|
5923
6638
|
SingleLayerNodeMergerSolver,
|
|
5924
|
-
(cms) => [cms.nodeSolver?.finishedNodes]
|
|
6639
|
+
(cms) => [cms.nodeSolver?.finishedNodes],
|
|
6640
|
+
{
|
|
6641
|
+
onSolved: (cms) => {
|
|
6642
|
+
cms.capacityNodes = cms.singleLayerNodeMerger?.newNodes;
|
|
6643
|
+
}
|
|
6644
|
+
}
|
|
5925
6645
|
),
|
|
5926
6646
|
definePipelineStep(
|
|
5927
6647
|
"strawSolver",
|
|
@@ -6017,6 +6737,18 @@ var CapacityMeshSolver = class extends BaseSolver {
|
|
|
6017
6737
|
layerCount: cms.srj.layerCount
|
|
6018
6738
|
}
|
|
6019
6739
|
]
|
|
6740
|
+
),
|
|
6741
|
+
definePipelineStep(
|
|
6742
|
+
"multiSimplifiedPathSolver",
|
|
6743
|
+
MultiSimplifiedPathSolver,
|
|
6744
|
+
(cms) => [
|
|
6745
|
+
{
|
|
6746
|
+
unsimplifiedHdRoutes: cms.highDensityStitchSolver.mergedHdRoutes,
|
|
6747
|
+
obstacles: cms.srj.obstacles,
|
|
6748
|
+
connMap: cms.connMap,
|
|
6749
|
+
colorMap: cms.colorMap
|
|
6750
|
+
}
|
|
6751
|
+
]
|
|
6020
6752
|
)
|
|
6021
6753
|
];
|
|
6022
6754
|
currentPipelineStepIndex = 0;
|
|
@@ -6065,12 +6797,21 @@ var CapacityMeshSolver = class extends BaseSolver {
|
|
|
6065
6797
|
const segmentOptimizationViz = this.unravelMultiSectionSolver?.visualize() ?? this.segmentToPointOptimizer?.visualize();
|
|
6066
6798
|
const highDensityViz = this.highDensityRouteSolver?.visualize();
|
|
6067
6799
|
const highDensityStitchViz = this.highDensityStitchSolver?.visualize();
|
|
6800
|
+
const simplifiedPathSolverViz = this.multiSimplifiedPathSolver?.visualize();
|
|
6068
6801
|
const problemViz = {
|
|
6069
|
-
points: [
|
|
6802
|
+
points: [
|
|
6803
|
+
...this.srj.connections.flatMap(
|
|
6804
|
+
(c) => c.pointsToConnect.map((p) => ({
|
|
6805
|
+
...p,
|
|
6806
|
+
label: `${c.name} ${p.pcb_port_id ?? ""}`
|
|
6807
|
+
}))
|
|
6808
|
+
)
|
|
6809
|
+
],
|
|
6070
6810
|
rects: [
|
|
6071
6811
|
...(this.srj.obstacles ?? []).map((o) => ({
|
|
6072
6812
|
...o,
|
|
6073
|
-
fill: o.layers?.includes("top") ? "rgba(255,0,0,0.25)" : o.layers?.includes("bottom") ? "rgba(0,0,255,0.25)" : "rgba(255,0,0,0.25)"
|
|
6813
|
+
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)",
|
|
6814
|
+
label: o.layers?.join(", ")
|
|
6074
6815
|
}))
|
|
6075
6816
|
],
|
|
6076
6817
|
lines: [
|
|
@@ -6108,6 +6849,7 @@ var CapacityMeshSolver = class extends BaseSolver {
|
|
|
6108
6849
|
segmentOptimizationViz,
|
|
6109
6850
|
highDensityViz ? combineVisualizations(problemViz, highDensityViz) : null,
|
|
6110
6851
|
highDensityStitchViz,
|
|
6852
|
+
simplifiedPathSolverViz,
|
|
6111
6853
|
this.solved ? combineVisualizations(
|
|
6112
6854
|
problemViz,
|
|
6113
6855
|
convertSrjToGraphicsObject(this.getOutputSimpleRouteJson())
|
|
@@ -6124,6 +6866,9 @@ var CapacityMeshSolver = class extends BaseSolver {
|
|
|
6124
6866
|
const match = mstConnectionName.match(/^(.+?)_mst\d+$/);
|
|
6125
6867
|
return match ? match[1] : mstConnectionName;
|
|
6126
6868
|
}
|
|
6869
|
+
_getOutputHdRoutes() {
|
|
6870
|
+
return this.multiSimplifiedPathSolver?.simplifiedHdRoutes ?? this.highDensityStitchSolver.mergedHdRoutes;
|
|
6871
|
+
}
|
|
6127
6872
|
/**
|
|
6128
6873
|
* Returns the SimpleRouteJson with routes converted to SimplifiedPcbTraces
|
|
6129
6874
|
*/
|
|
@@ -6132,11 +6877,12 @@ var CapacityMeshSolver = class extends BaseSolver {
|
|
|
6132
6877
|
throw new Error("Cannot get output before solving is complete");
|
|
6133
6878
|
}
|
|
6134
6879
|
const traces = [];
|
|
6880
|
+
const allHdRoutes = this._getOutputHdRoutes();
|
|
6135
6881
|
for (const connection of this.netToPointPairsSolver?.newConnections ?? []) {
|
|
6136
6882
|
const netConnection = this.srj.connections.find(
|
|
6137
6883
|
(c) => c.name === connection.netConnectionName
|
|
6138
6884
|
);
|
|
6139
|
-
const hdRoutes =
|
|
6885
|
+
const hdRoutes = allHdRoutes.filter(
|
|
6140
6886
|
(r) => r.connectionName === connection.name
|
|
6141
6887
|
);
|
|
6142
6888
|
for (let i = 0; i < hdRoutes.length; i++) {
|