@tscircuit/capacity-autorouter 0.0.26 → 0.0.28

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
@@ -1434,6 +1434,7 @@ var CapacityMeshNodeSolver2_NodeUnderObstacle = class extends CapacityMeshNodeSo
1434
1434
  this.srj = srj;
1435
1435
  this.opts = opts;
1436
1436
  }
1437
+ VIA_DIAMETER = 0.6;
1437
1438
  isNodeCompletelyOutsideBounds(node) {
1438
1439
  return node.center.x + node.width / 2 < this.srj.bounds.minX || node.center.x - node.width / 2 > this.srj.bounds.maxX || node.center.y + node.height / 2 < this.srj.bounds.minY || node.center.y - node.height / 2 > this.srj.bounds.maxY;
1439
1440
  }
@@ -1538,10 +1539,10 @@ var CapacityMeshNodeSolver2_NodeUnderObstacle = class extends CapacityMeshNodeSo
1538
1539
  const unfinishedNewNodes = [];
1539
1540
  for (const childNode of childNodes) {
1540
1541
  const shouldBeXYSubdivided = this.shouldNodeBeXYSubdivided(childNode);
1541
- const shouldBeZSubdivided = childNode.availableZ.length > 1 && !shouldBeXYSubdivided && childNode._containsObstacle;
1542
+ const shouldBeZSubdivided = childNode.availableZ.length > 1 && !shouldBeXYSubdivided && (childNode._containsObstacle || childNode.width < this.VIA_DIAMETER);
1542
1543
  if (shouldBeXYSubdivided) {
1543
1544
  unfinishedNewNodes.push(childNode);
1544
- } else if (!shouldBeXYSubdivided && !childNode._containsObstacle) {
1545
+ } else if (!shouldBeXYSubdivided && !childNode._containsObstacle && !shouldBeZSubdivided) {
1545
1546
  finishedNewNodes.push(childNode);
1546
1547
  } else if (!shouldBeXYSubdivided && childNode._containsTarget) {
1547
1548
  if (shouldBeZSubdivided) {
@@ -2503,7 +2504,6 @@ var IntraNodeRouteSolver = class extends BaseSolver {
2503
2504
  { x, y, z: z ?? 0 }
2504
2505
  ]);
2505
2506
  }
2506
- console.log({ unsolvedConnectionsMap });
2507
2507
  this.unsolvedConnections = Array.from(
2508
2508
  unsolvedConnectionsMap.entries().map(([connectionName, points]) => ({
2509
2509
  connectionName,
@@ -3128,6 +3128,9 @@ var getConnectivityMapFromSimpleRouteJson = (srj) => {
3128
3128
  }
3129
3129
  }
3130
3130
  }
3131
+ for (const obstacle of srj.obstacles) {
3132
+ connMap.addConnections([obstacle.connectedTo]);
3133
+ }
3131
3134
  return connMap;
3132
3135
  };
3133
3136
 
@@ -3333,13 +3336,13 @@ function buildMinimumSpanningTree(points) {
3333
3336
  if (point.x === neighbor.x && point.y === neighbor.y) {
3334
3337
  continue;
3335
3338
  }
3336
- const distance3 = Math.sqrt(
3339
+ const distance4 = Math.sqrt(
3337
3340
  (point.x - neighbor.x) ** 2 + (point.y - neighbor.y) ** 2
3338
3341
  );
3339
3342
  edges.push({
3340
3343
  from: point,
3341
3344
  to: neighbor,
3342
- weight: distance3
3345
+ weight: distance4
3343
3346
  });
3344
3347
  }
3345
3348
  }
@@ -5106,11 +5109,11 @@ var CapacityPathingSolver = class extends BaseSolver {
5106
5109
  let closestNode = this.nodes[0];
5107
5110
  let minDistance = Number.MAX_VALUE;
5108
5111
  for (const node of nodesWithTargets) {
5109
- const distance3 = Math.sqrt(
5112
+ const distance4 = Math.sqrt(
5110
5113
  (node.center.x - point.x) ** 2 + (node.center.y - point.y) ** 2
5111
5114
  );
5112
- if (distance3 < minDistance) {
5113
- minDistance = distance3;
5115
+ if (distance4 < minDistance) {
5116
+ minDistance = distance4;
5114
5117
  closestNode = node;
5115
5118
  }
5116
5119
  }
@@ -5828,6 +5831,694 @@ ${node.width}x${node.height}
5828
5831
  }
5829
5832
  };
5830
5833
 
5834
+ // lib/solvers/SimplifiedPathSolver/SingleSimplifiedPathSolver.ts
5835
+ var SingleSimplifiedPathSolver = class extends BaseSolver {
5836
+ newRoute;
5837
+ newVias;
5838
+ headIndex = 0;
5839
+ tailIndex = 0;
5840
+ inputRoute;
5841
+ otherHdRoutes;
5842
+ obstacles;
5843
+ connMap;
5844
+ colorMap;
5845
+ constructor(params) {
5846
+ super();
5847
+ this.inputRoute = params.inputRoute;
5848
+ this.otherHdRoutes = params.otherHdRoutes;
5849
+ this.obstacles = params.obstacles;
5850
+ this.connMap = params.connMap;
5851
+ this.colorMap = params.colorMap;
5852
+ this.newRoute = [];
5853
+ this.newVias = [];
5854
+ }
5855
+ get simplifiedRoute() {
5856
+ return {
5857
+ connectionName: this.inputRoute.connectionName,
5858
+ traceThickness: this.inputRoute.traceThickness,
5859
+ viaDiameter: this.inputRoute.viaDiameter,
5860
+ route: this.newRoute,
5861
+ vias: this.newVias
5862
+ };
5863
+ }
5864
+ isValidPath(pointsInRoute) {
5865
+ throw new Error("Not implemented");
5866
+ }
5867
+ _step() {
5868
+ throw new Error("Not implemented");
5869
+ }
5870
+ getVisualsForNewRouteAndObstacles() {
5871
+ const graphics = {
5872
+ lines: [],
5873
+ points: [],
5874
+ circles: [],
5875
+ rects: [],
5876
+ coordinateSystem: "cartesian",
5877
+ title: "Simplified Path Solver"
5878
+ };
5879
+ for (let i = 0; i < this.inputRoute.route.length - 1; i++) {
5880
+ graphics.lines.push({
5881
+ points: [
5882
+ { x: this.inputRoute.route[i].x, y: this.inputRoute.route[i].y },
5883
+ {
5884
+ x: this.inputRoute.route[i + 1].x,
5885
+ y: this.inputRoute.route[i + 1].y
5886
+ }
5887
+ ],
5888
+ strokeColor: "rgba(255, 0, 0, 0.8)",
5889
+ strokeDash: this.inputRoute.route[i].z === 1 ? "5, 5" : void 0,
5890
+ layer: this.inputRoute.route[i].z.toString()
5891
+ });
5892
+ }
5893
+ for (let i = 0; i < this.newRoute.length; i++) {
5894
+ if (i < this.newRoute.length - 1) {
5895
+ graphics.lines.push({
5896
+ points: [
5897
+ { x: this.newRoute[i].x, y: this.newRoute[i].y },
5898
+ { x: this.newRoute[i + 1].x, y: this.newRoute[i + 1].y }
5899
+ ],
5900
+ strokeWidth: 0.15,
5901
+ strokeColor: "rgba(0, 255, 0, 0.8)",
5902
+ strokeDash: this.newRoute[i].z === 1 ? [0.4, 0.4] : void 0,
5903
+ layer: this.newRoute[i].z.toString()
5904
+ });
5905
+ }
5906
+ graphics.points.push({
5907
+ x: this.newRoute[i].x,
5908
+ y: this.newRoute[i].y,
5909
+ color: "rgba(0, 255, 0, 0.8)",
5910
+ label: `z: ${this.newRoute[i].z}`,
5911
+ layer: this.newRoute[i].z.toString()
5912
+ });
5913
+ }
5914
+ for (const via of this.newVias) {
5915
+ graphics.circles.push({
5916
+ center: via,
5917
+ radius: this.inputRoute.viaDiameter / 2,
5918
+ fill: "rgba(0, 0, 255, 0.5)"
5919
+ });
5920
+ }
5921
+ for (const obstacle of this.obstacles) {
5922
+ graphics.rects.push({
5923
+ center: obstacle.center,
5924
+ width: obstacle.width,
5925
+ height: obstacle.height,
5926
+ 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)"
5927
+ });
5928
+ }
5929
+ for (const route of this.otherHdRoutes) {
5930
+ for (let i = 0; i < route.route.length - 1; i++) {
5931
+ graphics.lines.push({
5932
+ points: [
5933
+ { x: route.route[i].x, y: route.route[i].y },
5934
+ { x: route.route[i + 1].x, y: route.route[i + 1].y }
5935
+ ],
5936
+ strokeWidth: 0.15,
5937
+ 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)",
5938
+ // bottom layer (blue)
5939
+ layer: route.route[i].z.toString()
5940
+ });
5941
+ }
5942
+ }
5943
+ return graphics;
5944
+ }
5945
+ };
5946
+
5947
+ // lib/utils/calculate45DegreePaths.ts
5948
+ var calculate45DegreePaths = (pointA, pointB) => {
5949
+ const result = [];
5950
+ const dx = Math.abs(pointB.x - pointA.x);
5951
+ const dy = Math.abs(pointB.y - pointA.y);
5952
+ const signX = pointB.x > pointA.x ? 1 : -1;
5953
+ const signY = pointB.y > pointA.y ? 1 : -1;
5954
+ const midPoint1 = {
5955
+ x: pointB.x - signX * Math.abs(pointB.y - pointA.y),
5956
+ y: pointA.y
5957
+ };
5958
+ if ((midPoint1.x - pointA.x) * signX >= 0 && (midPoint1.x - pointB.x) * signX <= 0) {
5959
+ result.push([pointA, midPoint1, pointB]);
5960
+ }
5961
+ const midPoint2 = {
5962
+ x: pointA.x,
5963
+ y: pointB.y - signY * Math.abs(pointB.x - pointA.x)
5964
+ };
5965
+ if ((midPoint2.y - pointA.y) * signY >= 0 && (midPoint2.y - pointB.y) * signY <= 0) {
5966
+ result.push([pointA, midPoint2, pointB]);
5967
+ }
5968
+ const minDist = Math.min(dx, dy);
5969
+ const midPoint3 = {
5970
+ x: pointA.x + signX * minDist,
5971
+ y: pointA.y + signY * minDist
5972
+ };
5973
+ 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) {
5974
+ result.push([pointA, midPoint3, pointB]);
5975
+ }
5976
+ return result;
5977
+ };
5978
+
5979
+ // lib/utils/minimumDistanceBetweenSegments.ts
5980
+ function minimumDistanceBetweenSegments(A1, A2, B1, B2) {
5981
+ if (segmentsIntersect(A1, A2, B1, B2)) {
5982
+ return 0;
5983
+ }
5984
+ const distA1 = pointToSegmentDistance3(A1, B1, B2);
5985
+ const distA2 = pointToSegmentDistance3(A2, B1, B2);
5986
+ const distB1 = pointToSegmentDistance3(B1, A1, A2);
5987
+ const distB2 = pointToSegmentDistance3(B2, A1, A2);
5988
+ return Math.min(distA1, distA2, distB1, distB2);
5989
+ }
5990
+ function pointToSegmentDistance3(P, Q1, Q2) {
5991
+ const v = { x: Q2.x - Q1.x, y: Q2.y - Q1.y };
5992
+ const w = { x: P.x - Q1.x, y: P.y - Q1.y };
5993
+ const c1 = dotProduct(w, v);
5994
+ if (c1 <= 0) {
5995
+ return distance3(P, Q1);
5996
+ }
5997
+ const c2 = dotProduct(v, v);
5998
+ if (c2 <= c1) {
5999
+ return distance3(P, Q2);
6000
+ }
6001
+ const b = c1 / c2;
6002
+ const Pb = {
6003
+ x: Q1.x + b * v.x,
6004
+ y: Q1.y + b * v.y
6005
+ };
6006
+ return distance3(P, Pb);
6007
+ }
6008
+ function dotProduct(v1, v2) {
6009
+ return v1.x * v2.x + v1.y * v2.y;
6010
+ }
6011
+ function distance3(p1, p2) {
6012
+ const dx = p2.x - p1.x;
6013
+ const dy = p2.y - p1.y;
6014
+ return Math.sqrt(dx * dx + dy * dy);
6015
+ }
6016
+ function orientation3(p, q, r) {
6017
+ const val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
6018
+ if (val === 0) return 0;
6019
+ return val > 0 ? 1 : 2;
6020
+ }
6021
+ function onSegment3(p, q, r) {
6022
+ 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);
6023
+ }
6024
+ function segmentsIntersect(A1, A2, B1, B2) {
6025
+ const o1 = orientation3(A1, A2, B1);
6026
+ const o2 = orientation3(A1, A2, B2);
6027
+ const o3 = orientation3(B1, B2, A1);
6028
+ const o4 = orientation3(B1, B2, A2);
6029
+ if (o1 !== o2 && o3 !== o4) return true;
6030
+ if (o1 === 0 && onSegment3(A1, B1, A2)) return true;
6031
+ if (o2 === 0 && onSegment3(A1, B2, A2)) return true;
6032
+ if (o3 === 0 && onSegment3(B1, A1, B2)) return true;
6033
+ if (o4 === 0 && onSegment3(B1, A2, B2)) return true;
6034
+ return false;
6035
+ }
6036
+
6037
+ // lib/solvers/SimplifiedPathSolver/SingleSimplifiedPathSolver5_Deg45.ts
6038
+ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
6039
+ pathSegments = [];
6040
+ totalPathLength = 0;
6041
+ headDistanceAlongPath = 0;
6042
+ tailDistanceAlongPath = 0;
6043
+ stepSize = 0.25;
6044
+ // Default step size, can be adjusted
6045
+ lastValidPath = null;
6046
+ // Store the current valid path
6047
+ lastValidPathHeadDistance = 0;
6048
+ filteredObstacles = [];
6049
+ OBSTACLE_MARGIN = 0.15;
6050
+ TAIL_JUMP_RATIO = 0.8;
6051
+ constructor(params) {
6052
+ super(params);
6053
+ if (this.inputRoute.route.length <= 1) {
6054
+ this.newRoute = [...this.inputRoute.route];
6055
+ this.solved = true;
6056
+ return;
6057
+ }
6058
+ this.filteredObstacles = this.obstacles.filter(
6059
+ (obstacle) => !obstacle.connectedTo.some(
6060
+ (id) => this.connMap.areIdsConnected(this.inputRoute.connectionName, id)
6061
+ )
6062
+ );
6063
+ this.computePathSegments();
6064
+ }
6065
+ // Compute the path segments and their distances
6066
+ computePathSegments() {
6067
+ let cumulativeDistance = 0;
6068
+ for (let i = 0; i < this.inputRoute.route.length - 1; i++) {
6069
+ const start = this.inputRoute.route[i];
6070
+ const end = this.inputRoute.route[i + 1];
6071
+ const length = Math.sqrt((end.x - start.x) ** 2 + (end.y - start.y) ** 2) + i / 1e4;
6072
+ this.pathSegments.push({
6073
+ start,
6074
+ end,
6075
+ length,
6076
+ startDistance: cumulativeDistance,
6077
+ endDistance: cumulativeDistance + length
6078
+ });
6079
+ cumulativeDistance += length;
6080
+ }
6081
+ this.totalPathLength = cumulativeDistance;
6082
+ }
6083
+ // Helper to check if two points are the same
6084
+ arePointsEqual(p1, p2) {
6085
+ return p1.x === p2.x && p1.y === p2.y && p1.z === p2.z;
6086
+ }
6087
+ // Get point at a specific distance along the path
6088
+ getPointAtDistance(distance4) {
6089
+ distance4 = Math.max(0, Math.min(distance4, this.totalPathLength));
6090
+ const segment = this.pathSegments.find(
6091
+ (seg) => distance4 >= seg.startDistance && distance4 <= seg.endDistance
6092
+ );
6093
+ if (!segment) {
6094
+ return this.inputRoute.route[this.inputRoute.route.length - 1];
6095
+ }
6096
+ const factor = (distance4 - segment.startDistance) / segment.length;
6097
+ return {
6098
+ x: segment.start.x + factor * (segment.end.x - segment.start.x),
6099
+ y: segment.start.y + factor * (segment.end.y - segment.start.y),
6100
+ z: segment.start.z
6101
+ // Z doesn't interpolate - use the segment's start z value
6102
+ };
6103
+ }
6104
+ // Find nearest index in the original route for a given distance
6105
+ getNearestIndexForDistance(distance4) {
6106
+ if (distance4 <= 0) return 0;
6107
+ if (distance4 >= this.totalPathLength)
6108
+ return this.inputRoute.route.length - 1;
6109
+ const segmentIndex = this.pathSegments.findIndex(
6110
+ (seg) => distance4 >= seg.startDistance && distance4 <= seg.endDistance
6111
+ );
6112
+ if (segmentIndex === -1) return 0;
6113
+ const segment = this.pathSegments[segmentIndex];
6114
+ const midDistance = (segment.startDistance + segment.endDistance) / 2;
6115
+ return distance4 > midDistance ? segmentIndex + 1 : segmentIndex;
6116
+ }
6117
+ // Check if a path segment is valid
6118
+ isValidPathSegment(start, end) {
6119
+ for (const obstacle of this.filteredObstacles) {
6120
+ if (!obstacle.zLayers?.includes(start.z)) {
6121
+ continue;
6122
+ }
6123
+ const obstacleLeft = obstacle.center.x - obstacle.width / 2 - this.OBSTACLE_MARGIN;
6124
+ const obstacleRight = obstacle.center.x + obstacle.width / 2 + this.OBSTACLE_MARGIN;
6125
+ const obstacleTop = obstacle.center.y - obstacle.height / 2 - this.OBSTACLE_MARGIN;
6126
+ const obstacleBottom = obstacle.center.y + obstacle.height / 2 + this.OBSTACLE_MARGIN;
6127
+ if (doSegmentsIntersect(
6128
+ { x: start.x, y: start.y },
6129
+ { x: end.x, y: end.y },
6130
+ { x: obstacleLeft, y: obstacleTop },
6131
+ { x: obstacleRight, y: obstacleTop }
6132
+ ) || doSegmentsIntersect(
6133
+ { x: start.x, y: start.y },
6134
+ { x: end.x, y: end.y },
6135
+ { x: obstacleRight, y: obstacleTop },
6136
+ { x: obstacleRight, y: obstacleBottom }
6137
+ ) || doSegmentsIntersect(
6138
+ { x: start.x, y: start.y },
6139
+ { x: end.x, y: end.y },
6140
+ { x: obstacleRight, y: obstacleBottom },
6141
+ { x: obstacleLeft, y: obstacleBottom }
6142
+ ) || doSegmentsIntersect(
6143
+ { x: start.x, y: start.y },
6144
+ { x: end.x, y: end.y },
6145
+ { x: obstacleLeft, y: obstacleBottom },
6146
+ { x: obstacleLeft, y: obstacleTop }
6147
+ )) {
6148
+ return false;
6149
+ }
6150
+ }
6151
+ for (const route of this.otherHdRoutes) {
6152
+ if (this.connMap.areIdsConnected(
6153
+ this.inputRoute.connectionName,
6154
+ route.connectionName
6155
+ )) {
6156
+ continue;
6157
+ }
6158
+ for (let j = 0; j < route.route.length - 1; j++) {
6159
+ const routeStart = route.route[j];
6160
+ const routeEnd = route.route[j + 1];
6161
+ if (routeStart.z === start.z && routeEnd.z === start.z) {
6162
+ if (minimumDistanceBetweenSegments(
6163
+ { x: start.x, y: start.y },
6164
+ { x: end.x, y: end.y },
6165
+ { x: routeStart.x, y: routeStart.y },
6166
+ { x: routeEnd.x, y: routeEnd.y }
6167
+ ) < this.OBSTACLE_MARGIN) {
6168
+ return false;
6169
+ }
6170
+ }
6171
+ }
6172
+ for (const via of route.vias) {
6173
+ if (pointToSegmentDistance(via, start, end) < this.OBSTACLE_MARGIN + route.viaDiameter / 2) {
6174
+ return false;
6175
+ }
6176
+ }
6177
+ }
6178
+ return true;
6179
+ }
6180
+ // Check if a path with multiple points is valid
6181
+ isValidPath(pointsInRoute) {
6182
+ if (pointsInRoute.length < 2) return true;
6183
+ for (let i = 0; i < pointsInRoute.length - 1; i++) {
6184
+ if (pointsInRoute[i].z !== pointsInRoute[i + 1].z) {
6185
+ return false;
6186
+ }
6187
+ }
6188
+ for (let i = 0; i < pointsInRoute.length - 1; i++) {
6189
+ if (!this.isValidPathSegment(pointsInRoute[i], pointsInRoute[i + 1])) {
6190
+ return false;
6191
+ }
6192
+ }
6193
+ return true;
6194
+ }
6195
+ // Find a valid 45-degree path between two points
6196
+ find45DegreePath(start, end) {
6197
+ if (this.arePointsEqual(start, end)) {
6198
+ return [start];
6199
+ }
6200
+ if (start.z !== end.z) {
6201
+ return null;
6202
+ }
6203
+ const possiblePaths = calculate45DegreePaths(
6204
+ { x: start.x, y: start.y },
6205
+ { x: end.x, y: end.y }
6206
+ );
6207
+ for (const path of possiblePaths) {
6208
+ const fullPath = path.map((p) => ({ x: p.x, y: p.y, z: start.z }));
6209
+ if (this.isValidPath(fullPath)) {
6210
+ return fullPath;
6211
+ }
6212
+ }
6213
+ return null;
6214
+ }
6215
+ // Add a path to the result, skipping the first point if it's already added
6216
+ addPathToResult(path) {
6217
+ if (path.length === 0) return;
6218
+ for (let i = 0; i < path.length; i++) {
6219
+ if (i === 0 && this.newRoute.length > 0 && this.arePointsEqual(this.newRoute[this.newRoute.length - 1], path[i])) {
6220
+ continue;
6221
+ }
6222
+ this.newRoute.push(path[i]);
6223
+ }
6224
+ }
6225
+ _step() {
6226
+ const tailHasReachedEnd = this.tailDistanceAlongPath >= this.totalPathLength;
6227
+ const headHasReachedEnd = this.headDistanceAlongPath >= this.totalPathLength;
6228
+ if (tailHasReachedEnd) {
6229
+ const lastPoint = this.inputRoute.route[this.inputRoute.route.length - 1];
6230
+ if (this.newRoute.length === 0 || !this.arePointsEqual(this.newRoute[this.newRoute.length - 1], lastPoint)) {
6231
+ this.newRoute.push(lastPoint);
6232
+ }
6233
+ this.solved = true;
6234
+ return;
6235
+ }
6236
+ if (headHasReachedEnd) {
6237
+ const tailPoint2 = this.getPointAtDistance(this.tailDistanceAlongPath);
6238
+ const endPoint = this.inputRoute.route[this.inputRoute.route.length - 1];
6239
+ const path452 = this.find45DegreePath(tailPoint2, endPoint);
6240
+ if (path452) {
6241
+ this.addPathToResult(path452);
6242
+ this.solved = true;
6243
+ return;
6244
+ } else {
6245
+ if (this.lastValidPath) {
6246
+ this.addPathToResult(this.lastValidPath);
6247
+ this.lastValidPath = null;
6248
+ this.tailDistanceAlongPath = this.lastValidPathHeadDistance;
6249
+ } else {
6250
+ this.newRoute.push(endPoint);
6251
+ this.solved = true;
6252
+ }
6253
+ }
6254
+ }
6255
+ this.headDistanceAlongPath = Math.min(
6256
+ this.headDistanceAlongPath + this.stepSize,
6257
+ this.totalPathLength
6258
+ );
6259
+ const tailPoint = this.getPointAtDistance(this.tailDistanceAlongPath);
6260
+ const headPoint = this.getPointAtDistance(this.headDistanceAlongPath);
6261
+ const tailIndex = this.getNearestIndexForDistance(
6262
+ this.tailDistanceAlongPath
6263
+ );
6264
+ const headIndex = this.getNearestIndexForDistance(
6265
+ this.headDistanceAlongPath
6266
+ );
6267
+ let layerChangeBtwHeadAndTail = false;
6268
+ let layerChangeAtDistance = -1;
6269
+ for (let i = tailIndex; i < headIndex; i++) {
6270
+ if (i + 1 < this.inputRoute.route.length && this.inputRoute.route[i].z !== this.inputRoute.route[i + 1].z) {
6271
+ layerChangeBtwHeadAndTail = true;
6272
+ const changeSegmentIndex = i;
6273
+ layerChangeAtDistance = this.pathSegments[changeSegmentIndex].startDistance;
6274
+ break;
6275
+ }
6276
+ }
6277
+ if (layerChangeBtwHeadAndTail && layerChangeAtDistance > 0) {
6278
+ if (this.lastValidPath) {
6279
+ this.addPathToResult(this.lastValidPath);
6280
+ this.lastValidPath = null;
6281
+ }
6282
+ const pointBeforeChange = this.getPointAtDistance(layerChangeAtDistance);
6283
+ const pointAfterChange = this.inputRoute.route[this.getNearestIndexForDistance(layerChangeAtDistance) + 1];
6284
+ this.newVias.push({
6285
+ x: pointAfterChange.x,
6286
+ y: pointAfterChange.y
6287
+ });
6288
+ this.newRoute.push(pointAfterChange);
6289
+ const nextTailIndex = this.getNearestIndexForDistance(layerChangeAtDistance) + 1;
6290
+ if (this.pathSegments[nextTailIndex]) {
6291
+ this.tailDistanceAlongPath = this.pathSegments[nextTailIndex].startDistance;
6292
+ this.headDistanceAlongPath = this.tailDistanceAlongPath;
6293
+ } else {
6294
+ console.error("Creating via at end, this is probably not right");
6295
+ this.solved = true;
6296
+ return;
6297
+ }
6298
+ return;
6299
+ }
6300
+ const path45 = this.find45DegreePath(tailPoint, headPoint);
6301
+ if (!path45 && !this.lastValidPath) {
6302
+ this.tailDistanceAlongPath += this.stepSize;
6303
+ this.headDistanceAlongPath += this.stepSize;
6304
+ return;
6305
+ }
6306
+ if (path45) {
6307
+ this.lastValidPath = path45;
6308
+ this.lastValidPathHeadDistance = this.headDistanceAlongPath;
6309
+ return;
6310
+ }
6311
+ if (this.lastValidPath) {
6312
+ this.addPathToResult(this.lastValidPath);
6313
+ this.lastValidPath = null;
6314
+ this.tailDistanceAlongPath = this.lastValidPathHeadDistance;
6315
+ }
6316
+ this.headDistanceAlongPath = Math.min(
6317
+ this.headDistanceAlongPath + this.stepSize,
6318
+ this.totalPathLength
6319
+ );
6320
+ }
6321
+ visualize() {
6322
+ const graphics = this.getVisualsForNewRouteAndObstacles();
6323
+ const tailPoint = this.getPointAtDistance(this.tailDistanceAlongPath);
6324
+ const headPoint = this.getPointAtDistance(this.headDistanceAlongPath);
6325
+ graphics.points.push({
6326
+ x: tailPoint.x,
6327
+ y: tailPoint.y,
6328
+ color: "yellow",
6329
+ label: ["Tail", `z: ${tailPoint.z}`].join("\n")
6330
+ });
6331
+ graphics.points.push({
6332
+ x: headPoint.x,
6333
+ y: headPoint.y,
6334
+ color: "orange",
6335
+ label: ["Head", `z: ${headPoint.z}`].join("\n")
6336
+ });
6337
+ let distance4 = 0;
6338
+ while (distance4 < this.totalPathLength) {
6339
+ const point = this.getPointAtDistance(distance4);
6340
+ graphics.circles.push({
6341
+ center: {
6342
+ x: point.x,
6343
+ y: point.y
6344
+ },
6345
+ radius: 0.05,
6346
+ fill: "rgba(100, 100, 100, 0.5)"
6347
+ });
6348
+ distance4 += this.totalPathLength / 20;
6349
+ }
6350
+ if (this.lastValidPath && this.lastValidPath.length > 1) {
6351
+ for (let i = 0; i < this.lastValidPath.length - 1; i++) {
6352
+ graphics.lines.push({
6353
+ points: [
6354
+ { x: this.lastValidPath[i].x, y: this.lastValidPath[i].y },
6355
+ {
6356
+ x: this.lastValidPath[i + 1].x,
6357
+ y: this.lastValidPath[i + 1].y
6358
+ }
6359
+ ],
6360
+ strokeColor: "rgba(0, 255, 255, 0.9)",
6361
+ // Bright cyan
6362
+ strokeDash: "3, 3"
6363
+ // Dashed line to indicate it's a prospective path
6364
+ });
6365
+ }
6366
+ }
6367
+ return graphics;
6368
+ }
6369
+ };
6370
+
6371
+ // lib/solvers/SimplifiedPathSolver/MultiSimplifiedPathSolver.ts
6372
+ var MultiSimplifiedPathSolver = class extends BaseSolver {
6373
+ simplifiedHdRoutes;
6374
+ currentUnsimplifiedHdRouteIndex = 0;
6375
+ activeSubSolver = null;
6376
+ unsimplifiedHdRoutes;
6377
+ obstacles;
6378
+ connMap;
6379
+ colorMap;
6380
+ constructor(params) {
6381
+ super();
6382
+ this.MAX_ITERATIONS = 1e8;
6383
+ this.unsimplifiedHdRoutes = params.unsimplifiedHdRoutes;
6384
+ this.obstacles = params.obstacles;
6385
+ this.connMap = params.connMap || new ConnectivityMap({});
6386
+ this.colorMap = params.colorMap || {};
6387
+ this.simplifiedHdRoutes = [];
6388
+ }
6389
+ _step() {
6390
+ const hdRoute = this.unsimplifiedHdRoutes[this.currentUnsimplifiedHdRouteIndex];
6391
+ if (!this.activeSubSolver) {
6392
+ if (!hdRoute) {
6393
+ this.solved = true;
6394
+ return;
6395
+ }
6396
+ this.activeSubSolver = new SingleSimplifiedPathSolver5({
6397
+ inputRoute: hdRoute,
6398
+ otherHdRoutes: this.unsimplifiedHdRoutes.slice(this.currentUnsimplifiedHdRouteIndex + 1).concat(this.simplifiedHdRoutes),
6399
+ obstacles: this.obstacles,
6400
+ connMap: this.connMap,
6401
+ colorMap: this.colorMap
6402
+ });
6403
+ this.currentUnsimplifiedHdRouteIndex++;
6404
+ return;
6405
+ }
6406
+ this.activeSubSolver.step();
6407
+ if (this.activeSubSolver.solved) {
6408
+ this.simplifiedHdRoutes.push(this.activeSubSolver.simplifiedRoute);
6409
+ this.activeSubSolver = null;
6410
+ }
6411
+ }
6412
+ visualize() {
6413
+ if (this.activeSubSolver) {
6414
+ return this.activeSubSolver.visualize();
6415
+ }
6416
+ const graphics = {
6417
+ lines: [],
6418
+ points: [],
6419
+ circles: [],
6420
+ rects: [],
6421
+ coordinateSystem: "cartesian",
6422
+ title: "Multi Simplified Path Solver"
6423
+ };
6424
+ for (const route of this.unsimplifiedHdRoutes) {
6425
+ if (this.simplifiedHdRoutes.some(
6426
+ (r) => r.connectionName === route.connectionName
6427
+ )) {
6428
+ continue;
6429
+ }
6430
+ for (let i = 0; i < route.route.length - 1; i++) {
6431
+ graphics.lines.push({
6432
+ points: [
6433
+ { x: route.route[i].x, y: route.route[i].y },
6434
+ { x: route.route[i + 1].x, y: route.route[i + 1].y }
6435
+ ],
6436
+ strokeColor: route.route[i].z === 1 ? "rgba(0, 0, 255, 0.4)" : "rgba(255, 0, 0, 0.4)",
6437
+ strokeWidth: 0.15,
6438
+ strokeDash: route.route[i].z === 1 ? [0.5, 0.5] : void 0
6439
+ });
6440
+ }
6441
+ for (const via of route.vias || []) {
6442
+ graphics.circles.push({
6443
+ center: via,
6444
+ radius: route.viaDiameter / 2 || 0.3,
6445
+ // Default radius if viaDiameter not specified
6446
+ fill: "rgba(0, 0, 255, 0.4)"
6447
+ });
6448
+ }
6449
+ }
6450
+ for (const route of this.simplifiedHdRoutes) {
6451
+ const routeColor = this.colorMap?.[route.connectionName] || "rgba(128, 128, 128, 0.8)";
6452
+ for (let i = 0; i < route.route.length - 1; i++) {
6453
+ graphics.lines.push({
6454
+ points: [
6455
+ { x: route.route[i].x, y: route.route[i].y },
6456
+ { x: route.route[i + 1].x, y: route.route[i + 1].y }
6457
+ ],
6458
+ strokeWidth: 0.15,
6459
+ strokeColor: routeColor,
6460
+ strokeDash: route.route[i].z === 1 ? [0.5, 0.5] : void 0,
6461
+ step: 1
6462
+ });
6463
+ }
6464
+ for (const via of route.vias || []) {
6465
+ graphics.circles.push({
6466
+ center: via,
6467
+ radius: route.viaDiameter / 2,
6468
+ fill: "rgba(0, 0, 255, 0.5)",
6469
+ step: 1
6470
+ });
6471
+ }
6472
+ }
6473
+ for (const route of this.unsimplifiedHdRoutes) {
6474
+ for (let i = 0; i < route.route.length - 1; i++) {
6475
+ graphics.lines.push({
6476
+ points: [
6477
+ { x: route.route[i].x, y: route.route[i].y },
6478
+ { x: route.route[i + 1].x, y: route.route[i + 1].y }
6479
+ ],
6480
+ strokeWidth: 0.15,
6481
+ strokeColor: "rgba(255, 0, 0, 0.2)",
6482
+ strokeDash: [0.5, 0.5],
6483
+ step: 0,
6484
+ layer: route.route[i].z.toString()
6485
+ });
6486
+ }
6487
+ for (const point of route.vias) {
6488
+ graphics.circles.push({
6489
+ center: { x: point.x, y: point.y },
6490
+ radius: route.viaDiameter / 2,
6491
+ fill: "rgba(255, 0, 0, 0.2)",
6492
+ step: 0
6493
+ });
6494
+ }
6495
+ }
6496
+ for (const obstacle of this.obstacles) {
6497
+ graphics.rects.push({
6498
+ center: obstacle.center,
6499
+ width: obstacle.width,
6500
+ height: obstacle.height,
6501
+ 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)"
6502
+ });
6503
+ }
6504
+ if (this.currentUnsimplifiedHdRouteIndex < this.unsimplifiedHdRoutes.length) {
6505
+ const currentRoute = this.unsimplifiedHdRoutes[this.currentUnsimplifiedHdRouteIndex];
6506
+ if (currentRoute.route.length > 0) {
6507
+ graphics.circles.push({
6508
+ center: {
6509
+ x: currentRoute.route[0].x,
6510
+ y: currentRoute.route[0].y
6511
+ },
6512
+ radius: 0.2,
6513
+ fill: "yellow",
6514
+ label: "Current"
6515
+ });
6516
+ }
6517
+ }
6518
+ return graphics;
6519
+ }
6520
+ };
6521
+
5831
6522
  // lib/solvers/AutoroutingPipelineSolver.ts
5832
6523
  function definePipelineStep(solverName, solverClass, getConstructorParams, opts = {}) {
5833
6524
  return {
@@ -5873,6 +6564,7 @@ var CapacityMeshSolver = class extends BaseSolver {
5873
6564
  highDensityStitchSolver;
5874
6565
  singleLayerNodeMerger;
5875
6566
  strawSolver;
6567
+ multiSimplifiedPathSolver;
5876
6568
  startTimeOfPhase;
5877
6569
  endTimeOfPhase;
5878
6570
  timeSpentOnPhase;
@@ -6018,6 +6710,18 @@ var CapacityMeshSolver = class extends BaseSolver {
6018
6710
  layerCount: cms.srj.layerCount
6019
6711
  }
6020
6712
  ]
6713
+ ),
6714
+ definePipelineStep(
6715
+ "multiSimplifiedPathSolver",
6716
+ MultiSimplifiedPathSolver,
6717
+ (cms) => [
6718
+ {
6719
+ unsimplifiedHdRoutes: cms.highDensityStitchSolver.mergedHdRoutes,
6720
+ obstacles: cms.srj.obstacles,
6721
+ connMap: cms.connMap,
6722
+ colorMap: cms.colorMap
6723
+ }
6724
+ ]
6021
6725
  )
6022
6726
  ];
6023
6727
  currentPipelineStepIndex = 0;
@@ -6066,12 +6770,21 @@ var CapacityMeshSolver = class extends BaseSolver {
6066
6770
  const segmentOptimizationViz = this.unravelMultiSectionSolver?.visualize() ?? this.segmentToPointOptimizer?.visualize();
6067
6771
  const highDensityViz = this.highDensityRouteSolver?.visualize();
6068
6772
  const highDensityStitchViz = this.highDensityStitchSolver?.visualize();
6773
+ const simplifiedPathSolverViz = this.multiSimplifiedPathSolver?.visualize();
6069
6774
  const problemViz = {
6070
- points: [...this.srj.connections.flatMap((c) => c.pointsToConnect)],
6775
+ points: [
6776
+ ...this.srj.connections.flatMap(
6777
+ (c) => c.pointsToConnect.map((p) => ({
6778
+ ...p,
6779
+ label: `${c.name} ${p.pcb_port_id ?? ""}`
6780
+ }))
6781
+ )
6782
+ ],
6071
6783
  rects: [
6072
6784
  ...(this.srj.obstacles ?? []).map((o) => ({
6073
6785
  ...o,
6074
- 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)"
6786
+ 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)",
6787
+ label: o.layers?.join(", ")
6075
6788
  }))
6076
6789
  ],
6077
6790
  lines: [
@@ -6109,6 +6822,7 @@ var CapacityMeshSolver = class extends BaseSolver {
6109
6822
  segmentOptimizationViz,
6110
6823
  highDensityViz ? combineVisualizations(problemViz, highDensityViz) : null,
6111
6824
  highDensityStitchViz,
6825
+ simplifiedPathSolverViz,
6112
6826
  this.solved ? combineVisualizations(
6113
6827
  problemViz,
6114
6828
  convertSrjToGraphicsObject(this.getOutputSimpleRouteJson())
@@ -6125,6 +6839,9 @@ var CapacityMeshSolver = class extends BaseSolver {
6125
6839
  const match = mstConnectionName.match(/^(.+?)_mst\d+$/);
6126
6840
  return match ? match[1] : mstConnectionName;
6127
6841
  }
6842
+ _getOutputHdRoutes() {
6843
+ return this.multiSimplifiedPathSolver?.simplifiedHdRoutes ?? this.highDensityStitchSolver.mergedHdRoutes;
6844
+ }
6128
6845
  /**
6129
6846
  * Returns the SimpleRouteJson with routes converted to SimplifiedPcbTraces
6130
6847
  */
@@ -6133,11 +6850,12 @@ var CapacityMeshSolver = class extends BaseSolver {
6133
6850
  throw new Error("Cannot get output before solving is complete");
6134
6851
  }
6135
6852
  const traces = [];
6853
+ const allHdRoutes = this._getOutputHdRoutes();
6136
6854
  for (const connection of this.netToPointPairsSolver?.newConnections ?? []) {
6137
6855
  const netConnection = this.srj.connections.find(
6138
6856
  (c) => c.name === connection.netConnectionName
6139
6857
  );
6140
- const hdRoutes = this.highDensityStitchSolver.mergedHdRoutes.filter(
6858
+ const hdRoutes = allHdRoutes.filter(
6141
6859
  (r) => r.connectionName === connection.name
6142
6860
  );
6143
6861
  for (let i = 0; i < hdRoutes.length; i++) {