@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.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
- if (!(this.nodes[i]._strawNode && this.nodes[j]._strawNode) && areNodesBordering(this.nodes[i], this.nodes[j]) && this.doNodesHaveSharedLayer(this.nodes[i], this.nodes[j])) {
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
- return connMap;
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 Math.max(1, depth);
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 distance3 = Math.sqrt(
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: distance3
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 distance3 = Math.sqrt(
5115
+ const distance4 = Math.sqrt(
5109
5116
  (node.center.x - point.x) ** 2 + (node.center.y - point.y) ** 2
5110
5117
  );
5111
- if (distance3 < minDistance) {
5112
- minDistance = distance3;
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
- return usedCapacity < totalCapacity;
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) * (0.01 * width),
5285
- y: y + (i % 10 + i % 19) * (0.01 * width)
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: [...this.srj.connections.flatMap((c) => c.pointsToConnect)],
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 = this.highDensityStitchSolver.mergedHdRoutes.filter(
6885
+ const hdRoutes = allHdRoutes.filter(
6140
6886
  (r) => r.connectionName === connection.name
6141
6887
  );
6142
6888
  for (let i = 0; i < hdRoutes.length; i++) {