@tscircuit/capacity-autorouter 0.0.28 → 0.0.30

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
@@ -85,176 +85,6 @@ var BaseSolver = class {
85
85
  }
86
86
  };
87
87
 
88
- // node_modules/@tscircuit/math-utils/dist/chunk-CHQOCSFB.js
89
- function doSegmentsIntersect(p1, q1, p2, q2) {
90
- const o1 = orientation(p1, q1, p2);
91
- const o2 = orientation(p1, q1, q2);
92
- const o3 = orientation(p2, q2, p1);
93
- const o4 = orientation(p2, q2, q1);
94
- if (o1 !== o2 && o3 !== o4) {
95
- return true;
96
- }
97
- if (o1 === 0 && onSegment(p1, p2, q1)) return true;
98
- if (o2 === 0 && onSegment(p1, q2, q1)) return true;
99
- if (o3 === 0 && onSegment(p2, p1, q2)) return true;
100
- if (o4 === 0 && onSegment(p2, q1, q2)) return true;
101
- return false;
102
- }
103
- function orientation(p, q, r) {
104
- const val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
105
- if (val === 0) return 0;
106
- return val > 0 ? 1 : 2;
107
- }
108
- function onSegment(p, q, r) {
109
- 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);
110
- }
111
- function pointToSegmentDistance(p, v, w) {
112
- const l2 = (w.x - v.x) ** 2 + (w.y - v.y) ** 2;
113
- if (l2 === 0) return distance(p, v);
114
- let t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
115
- t = Math.max(0, Math.min(1, t));
116
- const projection = {
117
- x: v.x + t * (w.x - v.x),
118
- y: v.y + t * (w.y - v.y)
119
- };
120
- return distance(p, projection);
121
- }
122
- function distance(p1, p2) {
123
- const dx = p1.x - p2.x;
124
- const dy = p1.y - p2.y;
125
- return Math.sqrt(dx * dx + dy * dy);
126
- }
127
-
128
- // lib/utils/areNodesBordering.ts
129
- function areNodesBordering(node1, node2) {
130
- const n1Left = node1.center.x - node1.width / 2;
131
- const n1Right = node1.center.x + node1.width / 2;
132
- const n1Top = node1.center.y - node1.height / 2;
133
- const n1Bottom = node1.center.y + node1.height / 2;
134
- const n2Left = node2.center.x - node2.width / 2;
135
- const n2Right = node2.center.x + node2.width / 2;
136
- const n2Top = node2.center.y - node2.height / 2;
137
- const n2Bottom = node2.center.y + node2.height / 2;
138
- const epsilon = 1e-3;
139
- const shareVerticalBorder = (Math.abs(n1Right - n2Left) < epsilon || Math.abs(n1Left - n2Right) < epsilon) && Math.min(n1Bottom, n2Bottom) - Math.max(n1Top, n2Top) >= epsilon;
140
- const shareHorizontalBorder = (Math.abs(n1Bottom - n2Top) < epsilon || Math.abs(n1Top - n2Bottom) < epsilon) && Math.min(n1Right, n2Right) - Math.max(n1Left, n2Left) >= epsilon;
141
- return shareVerticalBorder || shareHorizontalBorder;
142
- }
143
-
144
- // lib/solvers/CapacityMeshSolver/CapacityMeshEdgeSolver.ts
145
- var CapacityMeshEdgeSolver = class extends BaseSolver {
146
- constructor(nodes) {
147
- super();
148
- this.nodes = nodes;
149
- this.edges = [];
150
- }
151
- edges;
152
- getNextCapacityMeshEdgeId() {
153
- return `ce${this.edges.length}`;
154
- }
155
- step() {
156
- this.edges = [];
157
- for (let i = 0; i < this.nodes.length; i++) {
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])) {
160
- this.edges.push({
161
- capacityMeshEdgeId: this.getNextCapacityMeshEdgeId(),
162
- nodeIds: [
163
- this.nodes[i].capacityMeshNodeId,
164
- this.nodes[j].capacityMeshNodeId
165
- ]
166
- });
167
- }
168
- }
169
- }
170
- const targetNodes = this.nodes.filter((node) => node._containsTarget);
171
- for (const targetNode of targetNodes) {
172
- const hasEdge = this.edges.some(
173
- (edge) => edge.nodeIds.includes(targetNode.capacityMeshNodeId)
174
- );
175
- if (hasEdge) continue;
176
- let nearestNode = null;
177
- let nearestDistance = Infinity;
178
- for (const node of this.nodes) {
179
- if (node._containsObstacle) continue;
180
- if (node._containsTarget) continue;
181
- const dist = distance(targetNode.center, node.center);
182
- if (dist < nearestDistance) {
183
- nearestDistance = dist;
184
- nearestNode = node;
185
- }
186
- }
187
- if (nearestNode) {
188
- this.edges.push({
189
- capacityMeshEdgeId: this.getNextCapacityMeshEdgeId(),
190
- nodeIds: [
191
- targetNode.capacityMeshNodeId,
192
- nearestNode.capacityMeshNodeId
193
- ]
194
- });
195
- }
196
- }
197
- this.solved = true;
198
- }
199
- doNodesHaveSharedLayer(node1, node2) {
200
- return node1.availableZ.some((z) => node2.availableZ.includes(z));
201
- }
202
- visualize() {
203
- const graphics = {
204
- lines: [],
205
- points: [],
206
- rects: this.nodes.map((node) => {
207
- const lowestZ = Math.min(...node.availableZ);
208
- return {
209
- width: Math.max(node.width - 2, node.width * 0.8),
210
- height: Math.max(node.height - 2, node.height * 0.8),
211
- center: {
212
- x: node.center.x + lowestZ * node.width * 0.05,
213
- y: node.center.y - lowestZ * node.width * 0.05
214
- },
215
- fill: node._containsObstacle ? "rgba(255,0,0,0.1)" : {
216
- "0,1": "rgba(0,0,0,0.1)",
217
- "0": "rgba(0,200,200, 0.1)",
218
- "1": "rgba(0,0,200, 0.1)"
219
- }[node.availableZ.join(",")] ?? "rgba(0,200,200,0.1)",
220
- label: [
221
- node.capacityMeshNodeId,
222
- `availableZ: ${node.availableZ.join(",")}`,
223
- `target? ${node._containsTarget ?? false}`,
224
- `obs? ${node._containsObstacle ?? false}`
225
- ].join("\n")
226
- };
227
- }),
228
- circles: []
229
- };
230
- for (const edge of this.edges) {
231
- const node1 = this.nodes.find(
232
- (node) => node.capacityMeshNodeId === edge.nodeIds[0]
233
- );
234
- const node2 = this.nodes.find(
235
- (node) => node.capacityMeshNodeId === edge.nodeIds[1]
236
- );
237
- if (node1?.center && node2?.center) {
238
- const lowestZ1 = Math.min(...node1.availableZ);
239
- const lowestZ2 = Math.min(...node2.availableZ);
240
- const nodeCenter1Adj = {
241
- x: node1.center.x + lowestZ1 * node1.width * 0.05,
242
- y: node1.center.y - lowestZ1 * node1.width * 0.05
243
- };
244
- const nodeCenter2Adj = {
245
- x: node2.center.x + lowestZ2 * node2.width * 0.05,
246
- y: node2.center.y - lowestZ2 * node2.width * 0.05
247
- };
248
- graphics.lines.push({
249
- points: [nodeCenter1Adj, nodeCenter2Adj],
250
- strokeDash: node1.availableZ.join(",") === node2.availableZ.join(",") ? void 0 : "10 5"
251
- });
252
- }
253
- }
254
- return graphics;
255
- }
256
- };
257
-
258
88
  // node_modules/@babel/runtime/helpers/esm/extends.js
259
89
  function _extends() {
260
90
  return _extends = Object.assign ? Object.assign.bind() : function(n) {
@@ -1113,6 +943,29 @@ var mapLayerNameToZ = (layerName, layerCount) => {
1113
943
  return parseInt(layerName.slice(5));
1114
944
  };
1115
945
 
946
+ // lib/utils/getTunedTotalCapacity1.ts
947
+ var getTunedTotalCapacity1 = (nodeOrWidth, maxCapacityFactor = 1) => {
948
+ const VIA_DIAMETER = 0.6;
949
+ const TRACE_WIDTH = 0.15;
950
+ const obstacleMargin = 0.2;
951
+ const width = "width" in nodeOrWidth ? nodeOrWidth.width : nodeOrWidth;
952
+ const viaLengthAcross = width / (VIA_DIAMETER / 2 + obstacleMargin);
953
+ return (viaLengthAcross / 2) ** 1.1 * maxCapacityFactor;
954
+ };
955
+ var calculateOptimalCapacityDepth = (initialWidth, targetMinCapacity = 0.5, maxDepth = 16) => {
956
+ let depth = 0;
957
+ let width = initialWidth;
958
+ while (depth < maxDepth) {
959
+ const capacity = getTunedTotalCapacity1({ width });
960
+ if (capacity <= targetMinCapacity) {
961
+ break;
962
+ }
963
+ width /= 2;
964
+ depth++;
965
+ }
966
+ return Math.max(1, depth);
967
+ };
968
+
1116
969
  // lib/solvers/CapacityMeshSolver/CapacityMeshNodeSolver1.ts
1117
970
  var CapacityMeshNodeSolver = class extends BaseSolver {
1118
971
  constructor(srj, opts = {}) {
@@ -1407,7 +1260,9 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
1407
1260
  node.capacityMeshNodeId,
1408
1261
  `availableZ: ${node.availableZ.join(",")}`,
1409
1262
  `target? ${node._containsTarget ?? false}`,
1410
- `obs? ${node._containsObstacle ?? false}`
1263
+ `obs? ${node._containsObstacle ?? false}`,
1264
+ `${node.width.toFixed(2)}x${node.height.toFixed(2)}`,
1265
+ `capacity: ${getTunedTotalCapacity1(node).toFixed(2)}`
1411
1266
  ].join("\n")
1412
1267
  });
1413
1268
  }
@@ -1916,6 +1771,46 @@ var CapacitySegmentToPointSolver = class extends BaseSolver {
1916
1771
  }
1917
1772
  };
1918
1773
 
1774
+ // node_modules/@tscircuit/math-utils/dist/chunk-CHQOCSFB.js
1775
+ function doSegmentsIntersect(p1, q1, p2, q2) {
1776
+ const o1 = orientation(p1, q1, p2);
1777
+ const o2 = orientation(p1, q1, q2);
1778
+ const o3 = orientation(p2, q2, p1);
1779
+ const o4 = orientation(p2, q2, q1);
1780
+ if (o1 !== o2 && o3 !== o4) {
1781
+ return true;
1782
+ }
1783
+ if (o1 === 0 && onSegment(p1, p2, q1)) return true;
1784
+ if (o2 === 0 && onSegment(p1, q2, q1)) return true;
1785
+ if (o3 === 0 && onSegment(p2, p1, q2)) return true;
1786
+ if (o4 === 0 && onSegment(p2, q1, q2)) return true;
1787
+ return false;
1788
+ }
1789
+ function orientation(p, q, r) {
1790
+ const val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
1791
+ if (val === 0) return 0;
1792
+ return val > 0 ? 1 : 2;
1793
+ }
1794
+ function onSegment(p, q, r) {
1795
+ 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);
1796
+ }
1797
+ function pointToSegmentDistance(p, v, w) {
1798
+ const l2 = (w.x - v.x) ** 2 + (w.y - v.y) ** 2;
1799
+ if (l2 === 0) return distance(p, v);
1800
+ let t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
1801
+ t = Math.max(0, Math.min(1, t));
1802
+ const projection = {
1803
+ x: v.x + t * (w.x - v.x),
1804
+ y: v.y + t * (w.y - v.y)
1805
+ };
1806
+ return distance(p, projection);
1807
+ }
1808
+ function distance(p1, p2) {
1809
+ const dx = p1.x - p2.x;
1810
+ const dy = p1.y - p2.y;
1811
+ return Math.sqrt(dx * dx + dy * dy);
1812
+ }
1813
+
1919
1814
  // lib/solvers/HighDensitySolver/SingleHighDensityRouteSolver.ts
1920
1815
  var SingleHighDensityRouteSolver = class extends BaseSolver {
1921
1816
  obstacleRoutes;
@@ -2058,7 +1953,7 @@ var SingleHighDensityRouteSolver = class extends BaseSolver {
2058
1953
  return false;
2059
1954
  }
2060
1955
  isNodeTooCloseToEdge(node, isVia) {
2061
- const margin = isVia ? this.viaDiameter / 2 : this.obstacleMargin;
1956
+ const margin = isVia ? this.viaDiameter / 2 + this.obstacleMargin / 2 : this.obstacleMargin / 2;
2062
1957
  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;
2063
1958
  if (tooClose && !isVia) {
2064
1959
  if (distance(node, this.B) < margin * 2 || distance(node, this.A) < margin * 2) {
@@ -3134,29 +3029,6 @@ var getConnectivityMapFromSimpleRouteJson = (srj) => {
3134
3029
  return connMap;
3135
3030
  };
3136
3031
 
3137
- // lib/utils/getTunedTotalCapacity1.ts
3138
- var getTunedTotalCapacity1 = (nodeOrWidth, maxCapacityFactor = 1) => {
3139
- const VIA_DIAMETER = 0.6;
3140
- const TRACE_WIDTH = 0.15;
3141
- const obstacleMargin = 0.2;
3142
- const width = "width" in nodeOrWidth ? nodeOrWidth.width : nodeOrWidth;
3143
- const viaLengthAcross = width / (VIA_DIAMETER / 2 + obstacleMargin);
3144
- return (viaLengthAcross / 2) ** 1.1 * maxCapacityFactor;
3145
- };
3146
- var calculateOptimalCapacityDepth = (initialWidth, targetMinCapacity = 0.5, maxDepth = 16) => {
3147
- let depth = 0;
3148
- let width = initialWidth;
3149
- while (depth < maxDepth) {
3150
- const capacity = getTunedTotalCapacity1({ width });
3151
- if (capacity <= targetMinCapacity) {
3152
- break;
3153
- }
3154
- width /= 2;
3155
- depth++;
3156
- }
3157
- return Math.max(1, depth);
3158
- };
3159
-
3160
3032
  // lib/solvers/NetToPointPairsSolver/buildMinimumSpanningTree.ts
3161
3033
  var KDNode = class {
3162
3034
  point;
@@ -3682,6 +3554,7 @@ var MultipleHighDensityRouteStitchSolver = class extends BaseSolver {
3682
3554
  z: mapLayerNameToZ(c.pointsToConnect[1].layer, opts.layerCount)
3683
3555
  }
3684
3556
  }));
3557
+ this.MAX_ITERATIONS = 1e5;
3685
3558
  }
3686
3559
  _step() {
3687
3560
  if (this.activeSolver) {
@@ -4166,16 +4039,16 @@ var UnravelSectionSolver = class extends BaseSolver {
4166
4039
  segmentPoint.segmentPointId
4167
4040
  ]);
4168
4041
  }
4169
- for (let i = 0; i < segmentPoints.length; i++) {
4170
- const A = segmentPoints[i];
4171
- for (let j = i + 1; j < segmentPoints.length; j++) {
4172
- const B = segmentPoints[j];
4173
- if (B.segmentPointId === A.segmentPointId) continue;
4174
- if (B.segmentId === A.segmentId) continue;
4175
- if (B.connectionName !== A.connectionName) continue;
4176
- if (A.capacityMeshNodeIds.some(
4177
- (nId) => B.capacityMeshNodeIds.includes(nId)
4178
- )) {
4042
+ for (const [nodeId, segmentPoints2] of segmentPointsInNode.entries()) {
4043
+ for (let i = 0; i < segmentPoints2.length; i++) {
4044
+ const A = segmentPointMap.get(segmentPoints2[i]);
4045
+ for (let j = i + 1; j < segmentPoints2.length; j++) {
4046
+ const B = segmentPointMap.get(segmentPoints2[j]);
4047
+ if (B.segmentPointId === A.segmentPointId) continue;
4048
+ if (B.segmentId === A.segmentId) continue;
4049
+ if (B.connectionName !== A.connectionName) continue;
4050
+ if (B.directlyConnectedSegmentPointIds.includes(A.segmentPointId))
4051
+ continue;
4179
4052
  A.directlyConnectedSegmentPointIds.push(B.segmentPointId);
4180
4053
  B.directlyConnectedSegmentPointIds.push(A.segmentPointId);
4181
4054
  }
@@ -5029,9 +4902,9 @@ var UnravelMultiSectionSolver = class extends BaseSolver {
5029
4902
  var createRectFromCapacityNode = (node, opts = {}) => {
5030
4903
  const lowestZ = Math.min(...node.availableZ);
5031
4904
  return {
5032
- center: !opts.rectMargin ? {
5033
- x: node.center.x + lowestZ * node.width * 0.05,
5034
- y: node.center.y - lowestZ * node.width * 0.05
4905
+ center: !opts.rectMargin || opts.zOffset ? {
4906
+ x: node.center.x + lowestZ * node.width * (opts.zOffset ?? 0.05),
4907
+ y: node.center.y - lowestZ * node.width * (opts.zOffset ?? 0.05)
5035
4908
  } : node.center,
5036
4909
  width: opts.rectMargin ? node.width - opts.rectMargin * 2 : Math.max(node.width - 0.5, node.width * 0.8),
5037
4910
  height: opts.rectMargin ? node.height - opts.rectMargin * 2 : Math.max(node.height - 0.5, node.height * 0.8),
@@ -5173,12 +5046,16 @@ var CapacityPathingSolver = class extends BaseSolver {
5173
5046
  }
5174
5047
  return capacityPaths;
5175
5048
  }
5176
- doesNodeHaveCapacityForTrace(node) {
5049
+ doesNodeHaveCapacityForTrace(node, prevNode) {
5177
5050
  const usedCapacity = this.usedNodeCapacityMap.get(node.capacityMeshNodeId) ?? 0;
5178
5051
  const totalCapacity = this.getTotalCapacity(node);
5179
5052
  if (node.availableZ.length === 1 && !node._containsTarget && usedCapacity > 0)
5180
5053
  return false;
5181
- return usedCapacity < totalCapacity;
5054
+ let additionalCapacityRequirement = 0;
5055
+ if (node.availableZ.length > 1 && prevNode.availableZ.length === 1) {
5056
+ additionalCapacityRequirement += 0.5;
5057
+ }
5058
+ return usedCapacity + additionalCapacityRequirement < totalCapacity;
5182
5059
  }
5183
5060
  canTravelThroughObstacle(node, connectionName) {
5184
5061
  const goalNodeIds = this.connectionNameToGoalNodeIds.get(connectionName);
@@ -5246,7 +5123,7 @@ var CapacityPathingSolver = class extends BaseSolver {
5246
5123
  if (this.visitedNodes?.has(neighborNode.capacityMeshNodeId)) {
5247
5124
  continue;
5248
5125
  }
5249
- if (!this.doesNodeHaveCapacityForTrace(neighborNode)) {
5126
+ if (!this.doesNodeHaveCapacityForTrace(neighborNode, currentCandidate.node)) {
5250
5127
  continue;
5251
5128
  }
5252
5129
  const connectionName = this.connectionsWithNodes[this.currentConnectionIndex].connection.name;
@@ -5285,27 +5162,42 @@ var CapacityPathingSolver = class extends BaseSolver {
5285
5162
  if (conn.path && conn.path.length > 0) {
5286
5163
  const pathPoints = conn.path.map(({ center: { x, y }, width }) => ({
5287
5164
  // slight offset to allow viewing overlapping paths
5288
- x: x + (i % 10 + i % 19) * (0.01 * width),
5289
- y: y + (i % 10 + i % 19) * (0.01 * width)
5165
+ x: x + (i % 10 + i % 19) * (5e-3 * width),
5166
+ y: y + (i % 10 + i % 19) * (5e-3 * width)
5290
5167
  }));
5291
5168
  graphics.lines.push({
5292
5169
  points: pathPoints,
5293
5170
  strokeColor: this.colorMap[conn.connection.name]
5294
5171
  });
5172
+ for (let u = 0; u < pathPoints.length; u++) {
5173
+ const point = pathPoints[u];
5174
+ graphics.points.push({
5175
+ x: point.x,
5176
+ y: point.y,
5177
+ label: [
5178
+ `conn: ${conn.connection.name}`,
5179
+ `node: ${conn.path[u].capacityMeshNodeId}`
5180
+ ].join("\n")
5181
+ });
5182
+ }
5295
5183
  }
5296
5184
  }
5297
5185
  }
5298
5186
  for (const node of this.nodes) {
5299
5187
  const nodeCosts = this.debug_lastNodeCostMap.get(node.capacityMeshNodeId);
5300
5188
  graphics.rects.push({
5301
- ...createRectFromCapacityNode(node),
5189
+ ...createRectFromCapacityNode(node, {
5190
+ rectMargin: 0.025,
5191
+ zOffset: 0.01
5192
+ }),
5302
5193
  label: [
5303
5194
  `${node.capacityMeshNodeId}`,
5304
5195
  `${this.usedNodeCapacityMap.get(node.capacityMeshNodeId)}/${this.getTotalCapacity(node).toFixed(2)}`,
5305
5196
  `${node.width.toFixed(2)}x${node.height.toFixed(2)}`,
5306
5197
  `g: ${nodeCosts?.g !== void 0 ? nodeCosts.g.toFixed(2) : "?"}`,
5307
5198
  `h: ${nodeCosts?.h !== void 0 ? nodeCosts.h.toFixed(2) : "?"}`,
5308
- `f: ${nodeCosts?.f !== void 0 ? nodeCosts.f.toFixed(2) : "?"}`
5199
+ `f: ${nodeCosts?.f !== void 0 ? nodeCosts.f.toFixed(2) : "?"}`,
5200
+ `z: ${node.availableZ.join(", ")}`
5309
5201
  ].join("\n")
5310
5202
  });
5311
5203
  }
@@ -5315,7 +5207,8 @@ var CapacityPathingSolver = class extends BaseSolver {
5315
5207
  for (const point of conn.connection.pointsToConnect) {
5316
5208
  graphics.points.push({
5317
5209
  x: point.x,
5318
- y: point.y
5210
+ y: point.y,
5211
+ label: [`pointsToConnect ${conn.connection.name}`].join("\n")
5319
5212
  });
5320
5213
  }
5321
5214
  }
@@ -5371,18 +5264,6 @@ var CapacityPathingSolver5 = class extends CapacityPathingSolver {
5371
5264
  */
5372
5265
  getNodeCapacityPenalty(node) {
5373
5266
  return 0.05;
5374
- if (node.availableZ.length === 1) {
5375
- return 0;
5376
- }
5377
- const totalCapacity = this.getTotalCapacity(node);
5378
- const usedCapacity = this.usedNodeCapacityMap.get(node.capacityMeshNodeId) ?? 0;
5379
- const remainingCapacity = totalCapacity - usedCapacity;
5380
- const dist = this.activeCandidateStraightLineDistance;
5381
- if (remainingCapacity <= 0) {
5382
- const penalty = (-remainingCapacity + 1) / totalCapacity * dist * (this.NEGATIVE_CAPACITY_PENALTY_FACTOR / 4);
5383
- return penalty ** 2;
5384
- }
5385
- return 1 / remainingCapacity * dist * this.REDUCED_CAPACITY_PENALTY_FACTOR / 8;
5386
5267
  }
5387
5268
  /**
5388
5269
  * We're rewarding travel into big nodes.
@@ -5412,6 +5293,7 @@ var StrawSolver = class extends BaseSolver {
5412
5293
  nodeIdCounter;
5413
5294
  constructor(params) {
5414
5295
  super();
5296
+ this.MAX_ITERATIONS = 1e5;
5415
5297
  this.strawSize = params.strawSize ?? 0.5;
5416
5298
  this.multiLayerNodes = [];
5417
5299
  this.strawNodes = [];
@@ -5509,7 +5391,8 @@ var StrawSolver = class extends BaseSolver {
5509
5391
  layer: node.layer,
5510
5392
  availableZ: [...node.availableZ],
5511
5393
  _depth: node._depth,
5512
- _strawNode: true
5394
+ _strawNode: true,
5395
+ _strawParentCapacityMeshNodeId: node.capacityMeshNodeId
5513
5396
  });
5514
5397
  }
5515
5398
  } else {
@@ -5525,7 +5408,8 @@ var StrawSolver = class extends BaseSolver {
5525
5408
  layer: node.layer,
5526
5409
  availableZ: [...node.availableZ],
5527
5410
  _depth: node._depth,
5528
- _strawNode: true
5411
+ _strawNode: true,
5412
+ _strawParentCapacityMeshNodeId: node.capacityMeshNodeId
5529
5413
  });
5530
5414
  }
5531
5415
  }
@@ -5581,7 +5465,8 @@ ${node.width}x${node.height}`
5581
5465
  stroke: "rgba(0, 0, 0, 0.5)",
5582
5466
  label: `${node.capacityMeshNodeId}
5583
5467
  Layer: ${node.availableZ[0]}
5584
- ${node.width}x${node.height}`
5468
+ ${node.width}x${node.height}`,
5469
+ layer: `z${node.availableZ.join(",")}`
5585
5470
  });
5586
5471
  }
5587
5472
  for (const node of this.multiLayerNodes) {
@@ -5601,6 +5486,72 @@ ${node.width}x${node.height}`
5601
5486
  }
5602
5487
  };
5603
5488
 
5489
+ // lib/utils/areNodesBordering.ts
5490
+ function areNodesBordering(node1, node2) {
5491
+ const n1Left = node1.center.x - node1.width / 2;
5492
+ const n1Right = node1.center.x + node1.width / 2;
5493
+ const n1Top = node1.center.y - node1.height / 2;
5494
+ const n1Bottom = node1.center.y + node1.height / 2;
5495
+ const n2Left = node2.center.x - node2.width / 2;
5496
+ const n2Right = node2.center.x + node2.width / 2;
5497
+ const n2Top = node2.center.y - node2.height / 2;
5498
+ const n2Bottom = node2.center.y + node2.height / 2;
5499
+ const epsilon = 1e-3;
5500
+ const shareVerticalBorder = (Math.abs(n1Right - n2Left) < epsilon || Math.abs(n1Left - n2Right) < epsilon) && Math.min(n1Bottom, n2Bottom) - Math.max(n1Top, n2Top) >= epsilon;
5501
+ const shareHorizontalBorder = (Math.abs(n1Bottom - n2Top) < epsilon || Math.abs(n1Top - n2Bottom) < epsilon) && Math.min(n1Right, n2Right) - Math.max(n1Left, n2Left) >= epsilon;
5502
+ return shareVerticalBorder || shareHorizontalBorder;
5503
+ }
5504
+
5505
+ // lib/data-structures/CapacityNodeTree.ts
5506
+ var CapacityNodeTree = class {
5507
+ constructor(nodes) {
5508
+ this.nodes = nodes;
5509
+ this.buckets = /* @__PURE__ */ new Map();
5510
+ for (const node of nodes) {
5511
+ const nodeMinX = node.center.x - node.width / 2;
5512
+ const nodeMinY = node.center.y - node.height / 2;
5513
+ const nodeMaxX = node.center.x + node.width / 2;
5514
+ const nodeMaxY = node.center.y + node.height / 2;
5515
+ for (let x = nodeMinX; x <= nodeMaxX; x += this.CELL_SIZE) {
5516
+ for (let y = nodeMinY; y <= nodeMaxY; y += this.CELL_SIZE) {
5517
+ const bucketKey = this.getBucketKey(x, y);
5518
+ const bucket = this.buckets.get(bucketKey);
5519
+ if (!bucket) {
5520
+ this.buckets.set(bucketKey, [node]);
5521
+ } else {
5522
+ bucket.push(node);
5523
+ }
5524
+ }
5525
+ }
5526
+ }
5527
+ }
5528
+ buckets;
5529
+ CELL_SIZE = 0.4;
5530
+ getBucketKey(x, y) {
5531
+ return `${Math.floor(x / this.CELL_SIZE)}x${Math.floor(y / this.CELL_SIZE)}`;
5532
+ }
5533
+ getNodesInArea(centerX, centerY, width, height) {
5534
+ const nodes = [];
5535
+ const alreadyAddedNodes = /* @__PURE__ */ new Set();
5536
+ const minX = centerX - width / 2;
5537
+ const minY = centerY - height / 2;
5538
+ const maxX = centerX + width / 2;
5539
+ const maxY = centerY + height / 2;
5540
+ for (let x = minX; x <= maxX; x += this.CELL_SIZE) {
5541
+ for (let y = minY; y <= maxY; y += this.CELL_SIZE) {
5542
+ const bucketKey = this.getBucketKey(x, y);
5543
+ const bucket = this.buckets.get(bucketKey) || [];
5544
+ for (const node of bucket) {
5545
+ if (alreadyAddedNodes.has(node.capacityMeshNodeId)) continue;
5546
+ alreadyAddedNodes.add(node.capacityMeshNodeId);
5547
+ nodes.push(node);
5548
+ }
5549
+ }
5550
+ }
5551
+ return nodes;
5552
+ }
5553
+ };
5554
+
5604
5555
  // lib/solvers/SingleLayerNodeMerger/SingleLayerNodeMergerSolver.ts
5605
5556
  var EPSILON = 5e-3;
5606
5557
  var SingleLayerNodeMergerSolver = class extends BaseSolver {
@@ -5609,6 +5560,7 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
5609
5560
  absorbedNodeIds;
5610
5561
  nextBatchNodeIds;
5611
5562
  batchHadModifications;
5563
+ hasComputedAdjacentNodeIds = false;
5612
5564
  newNodes;
5613
5565
  constructor(nodes) {
5614
5566
  super();
@@ -5619,34 +5571,94 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
5619
5571
  }
5620
5572
  this.newNodes = [];
5621
5573
  this.absorbedNodeIds = /* @__PURE__ */ new Set();
5622
- const nodeWithArea = [];
5574
+ const unprocessedNodesWithArea = [];
5623
5575
  for (const node of nodes) {
5624
5576
  if (node.availableZ.length > 1) {
5625
5577
  this.newNodes.push(node);
5626
5578
  this.absorbedNodeIds.add(node.capacityMeshNodeId);
5627
5579
  } else {
5628
- nodeWithArea.push([node.capacityMeshNodeId, node.width * node.height]);
5580
+ unprocessedNodesWithArea.push([node, node.width * node.height]);
5629
5581
  }
5630
5582
  }
5631
- nodeWithArea.sort((a, b) => a[1] - b[1]);
5632
- this.currentBatchNodeIds = nodeWithArea.map((n) => n[0]);
5583
+ unprocessedNodesWithArea.sort((a, b) => a[1] - b[1]);
5584
+ for (const [node, area] of unprocessedNodesWithArea) {
5585
+ const unprocessedNode = {
5586
+ ...node,
5587
+ center: { ...node.center }
5588
+ };
5589
+ this.nodeMap.set(node.capacityMeshNodeId, unprocessedNode);
5590
+ }
5591
+ this.currentBatchNodeIds = unprocessedNodesWithArea.map(
5592
+ ([node]) => node.capacityMeshNodeId
5593
+ );
5633
5594
  this.nextBatchNodeIds = [];
5634
5595
  this.batchHadModifications = false;
5635
5596
  }
5597
+ computeAdjacentNodeIdsForFirstBatch(nodes) {
5598
+ const nodeTrees = [
5599
+ new CapacityNodeTree(nodes.filter((n) => n.availableZ[0] === 0)),
5600
+ new CapacityNodeTree(nodes.filter((n) => n.availableZ[0] === 1))
5601
+ ];
5602
+ for (const node of nodes) {
5603
+ const adjacentNodes = [];
5604
+ const z = node.availableZ[0];
5605
+ const nodesInArea = nodeTrees[z].getNodesInArea(
5606
+ node.center.x,
5607
+ node.center.y,
5608
+ node.width * 4,
5609
+ node.height * 4
5610
+ );
5611
+ for (const unprocessedNode of nodesInArea) {
5612
+ if (unprocessedNode._containsTarget && unprocessedNode._targetConnectionName !== node._targetConnectionName)
5613
+ continue;
5614
+ if (unprocessedNode.capacityMeshNodeId === node.capacityMeshNodeId)
5615
+ continue;
5616
+ if (!areNodesBordering(node, unprocessedNode)) continue;
5617
+ adjacentNodes.push(unprocessedNode);
5618
+ }
5619
+ node._adjacentNodeIds = adjacentNodes.map((n) => n.capacityMeshNodeId);
5620
+ }
5621
+ }
5622
+ // getAdjacentSameLayerUnprocessedNodes1(rootNode: CapacityMeshNode) {
5623
+ // const adjacentNodes: CapacityMeshNode[] = []
5624
+ // for (const unprocessedNodeId of this.currentBatchNodeIds) {
5625
+ // const unprocessedNode = this.nodeMap.get(unprocessedNodeId)!
5626
+ // if (!areNodesBordering(rootNode, unprocessedNode)) continue
5627
+ // if (unprocessedNode.availableZ[0] !== rootNode.availableZ[0]) continue
5628
+ // if (
5629
+ // unprocessedNode._containsTarget &&
5630
+ // unprocessedNode._targetConnectionName !== rootNode._targetConnectionName
5631
+ // )
5632
+ // continue
5633
+ // if (this.absorbedNodeIds.has(unprocessedNodeId)) continue
5634
+ // adjacentNodes.push(unprocessedNode)
5635
+ // }
5636
+ // return adjacentNodes
5637
+ // }
5636
5638
  getAdjacentSameLayerUnprocessedNodes(rootNode) {
5639
+ return this.getAdjacentSameLayerUnprocessedNodes2(rootNode);
5640
+ }
5641
+ getAdjacentSameLayerUnprocessedNodes2(rootNode) {
5637
5642
  const adjacentNodes = [];
5638
- for (const unprocessedNodeId of this.currentBatchNodeIds) {
5639
- if (this.absorbedNodeIds.has(unprocessedNodeId)) continue;
5640
- const unprocessedNode = this.nodeMap.get(unprocessedNodeId);
5641
- if (unprocessedNode.availableZ[0] !== rootNode.availableZ[0]) continue;
5642
- if (unprocessedNode._containsTarget && unprocessedNode._targetConnectionName !== rootNode._targetConnectionName)
5643
- continue;
5644
- if (!areNodesBordering(rootNode, unprocessedNode)) continue;
5643
+ const unprocessedAdjNodes = Array.from(
5644
+ new Set(
5645
+ (rootNode._adjacentNodeIds ?? []).map((a) => this.nodeMap.get(a))
5646
+ )
5647
+ );
5648
+ unprocessedAdjNodes.sort((a, b) => a.width * a.height - b.width * b.height);
5649
+ for (const unprocessedNode of unprocessedAdjNodes) {
5650
+ if (this.absorbedNodeIds.has(unprocessedNode.capacityMeshNodeId)) continue;
5645
5651
  adjacentNodes.push(unprocessedNode);
5646
5652
  }
5647
5653
  return adjacentNodes;
5648
5654
  }
5649
5655
  _step() {
5656
+ if (!this.hasComputedAdjacentNodeIds) {
5657
+ this.computeAdjacentNodeIdsForFirstBatch(
5658
+ this.currentBatchNodeIds.map((id) => this.nodeMap.get(id))
5659
+ );
5660
+ this.hasComputedAdjacentNodeIds = true;
5661
+ }
5650
5662
  let rootNodeId = this.currentBatchNodeIds.pop();
5651
5663
  while (rootNodeId && this.absorbedNodeIds.has(rootNodeId)) {
5652
5664
  rootNodeId = this.currentBatchNodeIds.pop();
@@ -5675,6 +5687,19 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
5675
5687
  this.nextBatchNodeIds.push(rootNodeId);
5676
5688
  return;
5677
5689
  }
5690
+ const absorbAdjacentNodeIds = (nodesToAbsorb) => {
5691
+ for (const adjNode of nodesToAbsorb) {
5692
+ this.absorbedNodeIds.add(adjNode.capacityMeshNodeId);
5693
+ }
5694
+ rootNode._adjacentNodeIds = Array.from(
5695
+ new Set(
5696
+ [
5697
+ ...rootNode._adjacentNodeIds ?? [],
5698
+ ...nodesToAbsorb.flatMap((n) => n._adjacentNodeIds ?? [])
5699
+ ].filter((id) => !this.absorbedNodeIds.has(id))
5700
+ )
5701
+ );
5702
+ };
5678
5703
  const adjacentNodesToLeft = adjacentNodes.filter(
5679
5704
  (adjNode) => adjNode.center.x < rootNode.center.x && Math.abs(adjNode.center.y - rootNode.center.y) < rootNode.height / 2
5680
5705
  );
@@ -5691,9 +5716,7 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
5691
5716
  if (leftAdjNodesTakeUpEntireHeight && leftAdjNodesAreAllSameSize) {
5692
5717
  rootNode.width += leftAdjNodeWidth;
5693
5718
  rootNode.center.x = rootNode.center.x - leftAdjNodeWidth / 2;
5694
- for (const adjNode of adjacentNodesToLeft) {
5695
- this.absorbedNodeIds.add(adjNode.capacityMeshNodeId);
5696
- }
5719
+ absorbAdjacentNodeIds(adjacentNodesToLeft);
5697
5720
  rootNodeHasGrown = true;
5698
5721
  }
5699
5722
  }
@@ -5713,9 +5736,7 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
5713
5736
  if (rightAdjNodesTakeUpEntireHeight && rightAdjNodesAreAllSameSize) {
5714
5737
  rootNode.width += rightAdjNodeWidth;
5715
5738
  rootNode.center.x = rootNode.center.x + rightAdjNodeWidth / 2;
5716
- for (const adjNode of adjacentNodesToRight) {
5717
- this.absorbedNodeIds.add(adjNode.capacityMeshNodeId);
5718
- }
5739
+ absorbAdjacentNodeIds(adjacentNodesToRight);
5719
5740
  rootNodeHasGrown = true;
5720
5741
  }
5721
5742
  }
@@ -5735,9 +5756,7 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
5735
5756
  if (topAdjNodesTakeUpEntireWidth && topAdjNodesAreAllSameSize) {
5736
5757
  rootNode.height += topAdjNodeHeight;
5737
5758
  rootNode.center.y = rootNode.center.y + topAdjNodeHeight / 2;
5738
- for (const adjNode of adjacentNodesToTop) {
5739
- this.absorbedNodeIds.add(adjNode.capacityMeshNodeId);
5740
- }
5759
+ absorbAdjacentNodeIds(adjacentNodesToTop);
5741
5760
  rootNodeHasGrown = true;
5742
5761
  }
5743
5762
  }
@@ -5757,9 +5776,7 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
5757
5776
  if (bottomAdjNodesTakeUpEntireWidth && bottomAdjNodesAreAllSameSize) {
5758
5777
  rootNode.height += bottomAdjNodeHeight;
5759
5778
  rootNode.center.y = rootNode.center.y - bottomAdjNodeHeight / 2;
5760
- for (const adjNode of adjacentNodesToBottom) {
5761
- this.absorbedNodeIds.add(adjNode.capacityMeshNodeId);
5762
- }
5779
+ absorbAdjacentNodeIds(adjacentNodesToBottom);
5763
5780
  rootNodeHasGrown = true;
5764
5781
  }
5765
5782
  }
@@ -5849,7 +5866,7 @@ var SingleSimplifiedPathSolver = class extends BaseSolver {
5849
5866
  this.obstacles = params.obstacles;
5850
5867
  this.connMap = params.connMap;
5851
5868
  this.colorMap = params.colorMap;
5852
- this.newRoute = [];
5869
+ this.newRoute = [this.inputRoute.route[0]];
5853
5870
  this.newVias = [];
5854
5871
  }
5855
5872
  get simplifiedRoute() {
@@ -5887,7 +5904,7 @@ var SingleSimplifiedPathSolver = class extends BaseSolver {
5887
5904
  ],
5888
5905
  strokeColor: "rgba(255, 0, 0, 0.8)",
5889
5906
  strokeDash: this.inputRoute.route[i].z === 1 ? "5, 5" : void 0,
5890
- layer: this.inputRoute.route[i].z.toString()
5907
+ layer: `z${this.inputRoute.route[i].z.toString()}`
5891
5908
  });
5892
5909
  }
5893
5910
  for (let i = 0; i < this.newRoute.length; i++) {
@@ -5900,7 +5917,7 @@ var SingleSimplifiedPathSolver = class extends BaseSolver {
5900
5917
  strokeWidth: 0.15,
5901
5918
  strokeColor: "rgba(0, 255, 0, 0.8)",
5902
5919
  strokeDash: this.newRoute[i].z === 1 ? [0.4, 0.4] : void 0,
5903
- layer: this.newRoute[i].z.toString()
5920
+ layer: `z${this.newRoute[i].z.toString()}`
5904
5921
  });
5905
5922
  }
5906
5923
  graphics.points.push({
@@ -5908,7 +5925,7 @@ var SingleSimplifiedPathSolver = class extends BaseSolver {
5908
5925
  y: this.newRoute[i].y,
5909
5926
  color: "rgba(0, 255, 0, 0.8)",
5910
5927
  label: `z: ${this.newRoute[i].z}`,
5911
- layer: this.newRoute[i].z.toString()
5928
+ layer: `z${this.newRoute[i].z.toString()}`
5912
5929
  });
5913
5930
  }
5914
5931
  for (const via of this.newVias) {
@@ -5936,7 +5953,7 @@ var SingleSimplifiedPathSolver = class extends BaseSolver {
5936
5953
  strokeWidth: 0.15,
5937
5954
  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
5955
  // bottom layer (blue)
5939
- layer: route.route[i].z.toString()
5956
+ layer: `z${route.route[i].z.toString()}`
5940
5957
  });
5941
5958
  }
5942
5959
  }
@@ -6034,32 +6051,169 @@ function segmentsIntersect(A1, A2, B1, B2) {
6034
6051
  return false;
6035
6052
  }
6036
6053
 
6054
+ // lib/data-structures/SegmentTree.ts
6055
+ var getSegmentBounds = (segment) => {
6056
+ return {
6057
+ minX: Math.min(segment[0].x, segment[1].x),
6058
+ maxX: Math.max(segment[0].x, segment[1].x),
6059
+ minY: Math.min(segment[0].y, segment[1].y),
6060
+ maxY: Math.max(segment[0].y, segment[1].y)
6061
+ };
6062
+ };
6063
+ var SegmentTree = class {
6064
+ constructor(segments) {
6065
+ this.segments = segments;
6066
+ this.buckets = /* @__PURE__ */ new Map();
6067
+ for (const segment of segments) {
6068
+ const bounds = getSegmentBounds(segment);
6069
+ for (let x = bounds.minX; x <= bounds.maxX; x += this.CELL_SIZE) {
6070
+ for (let y = bounds.minY; y <= bounds.maxY; y += this.CELL_SIZE) {
6071
+ const bucketKey = this.getBucketKey(x, y);
6072
+ const bucket = this.buckets.get(bucketKey);
6073
+ if (!bucket) {
6074
+ this.buckets.set(bucketKey, [
6075
+ [segment[0], segment[1], this.getSegmentKey(segment)]
6076
+ ]);
6077
+ } else {
6078
+ bucket.push([segment[0], segment[1], this.getSegmentKey(segment)]);
6079
+ }
6080
+ }
6081
+ }
6082
+ }
6083
+ }
6084
+ buckets;
6085
+ CELL_SIZE = 0.4;
6086
+ getBucketKey(x, y) {
6087
+ return `${Math.floor(x / this.CELL_SIZE)}x${Math.floor(y / this.CELL_SIZE)}`;
6088
+ }
6089
+ getSegmentKey(segment) {
6090
+ return `${segment[0].x}-${segment[0].y}-${segment[0].z}-${segment[1].x}-${segment[1].y}-${segment[1].z}`;
6091
+ }
6092
+ getSegmentsThatCouldIntersect(A, B) {
6093
+ const segments = [];
6094
+ const alreadyAddedSegments = /* @__PURE__ */ new Set();
6095
+ const minX = Math.min(A.x, B.x) - this.CELL_SIZE;
6096
+ const minY = Math.min(A.y, B.y) - this.CELL_SIZE;
6097
+ const maxX = Math.max(A.x, B.x) + this.CELL_SIZE;
6098
+ const maxY = Math.max(A.y, B.y) + this.CELL_SIZE;
6099
+ for (let x = minX; x <= maxX; x += this.CELL_SIZE) {
6100
+ for (let y = minY; y <= maxY; y += this.CELL_SIZE) {
6101
+ const bucketKey = this.getBucketKey(x, y);
6102
+ const bucket = this.buckets.get(bucketKey) || [];
6103
+ for (const segment of bucket) {
6104
+ const key = segment[2];
6105
+ if (alreadyAddedSegments.has(key)) continue;
6106
+ alreadyAddedSegments.add(key);
6107
+ segments.push(segment);
6108
+ }
6109
+ }
6110
+ }
6111
+ return segments;
6112
+ }
6113
+ };
6114
+
6037
6115
  // lib/solvers/SimplifiedPathSolver/SingleSimplifiedPathSolver5_Deg45.ts
6038
6116
  var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
6039
6117
  pathSegments = [];
6040
6118
  totalPathLength = 0;
6041
6119
  headDistanceAlongPath = 0;
6042
6120
  tailDistanceAlongPath = 0;
6043
- stepSize = 0.25;
6121
+ minStepSize = 0.25;
6044
6122
  // Default step size, can be adjusted
6045
6123
  lastValidPath = null;
6046
6124
  // Store the current valid path
6047
6125
  lastValidPathHeadDistance = 0;
6126
+ /** Amount the step size is reduced when the step isn't possible */
6127
+ STEP_SIZE_REDUCTION_FACTOR = 0.25;
6128
+ maxStepSize = 4;
6129
+ currentStepSize = this.maxStepSize;
6130
+ lastHeadMoveDistance = 0;
6131
+ cachedValidPathSegments;
6048
6132
  filteredObstacles = [];
6133
+ filteredObstaclePathSegments = [];
6134
+ filteredVias = [];
6135
+ segmentTree;
6049
6136
  OBSTACLE_MARGIN = 0.15;
6050
6137
  TAIL_JUMP_RATIO = 0.8;
6051
6138
  constructor(params) {
6052
6139
  super(params);
6140
+ this.cachedValidPathSegments = /* @__PURE__ */ new Set();
6053
6141
  if (this.inputRoute.route.length <= 1) {
6054
6142
  this.newRoute = [...this.inputRoute.route];
6055
6143
  this.solved = true;
6056
6144
  return;
6057
6145
  }
6146
+ const bounds = this.inputRoute.route.reduce(
6147
+ (acc, point) => {
6148
+ acc.minX = Math.min(acc.minX, point.x);
6149
+ acc.maxX = Math.max(acc.maxX, point.x);
6150
+ acc.minY = Math.min(acc.minY, point.y);
6151
+ acc.maxY = Math.max(acc.maxY, point.y);
6152
+ return acc;
6153
+ },
6154
+ { minX: Infinity, maxX: -Infinity, minY: Infinity, maxY: -Infinity }
6155
+ );
6058
6156
  this.filteredObstacles = this.obstacles.filter(
6059
6157
  (obstacle) => !obstacle.connectedTo.some(
6060
6158
  (id) => this.connMap.areIdsConnected(this.inputRoute.connectionName, id)
6061
6159
  )
6160
+ ).filter((obstacle) => {
6161
+ if (obstacle.connectedTo.some(
6162
+ (obsId) => this.connMap.areIdsConnected(this.inputRoute.connectionName, obsId)
6163
+ )) {
6164
+ return false;
6165
+ }
6166
+ const obstacleMinX = obstacle.center.x - obstacle.width / 2 - this.OBSTACLE_MARGIN;
6167
+ const obstacleMaxX = obstacle.center.x + obstacle.width / 2 + this.OBSTACLE_MARGIN;
6168
+ const obstacleMinY = obstacle.center.y - obstacle.height / 2 - this.OBSTACLE_MARGIN;
6169
+ const obstacleMaxY = obstacle.center.y + obstacle.height / 2 + this.OBSTACLE_MARGIN;
6170
+ return obstacleMinX <= bounds.maxX && obstacleMaxX >= bounds.minX && obstacleMinY <= bounds.maxY && obstacleMaxY >= bounds.minY;
6171
+ });
6172
+ this.filteredObstaclePathSegments = this.otherHdRoutes.flatMap(
6173
+ (hdRoute) => {
6174
+ if (this.connMap.areIdsConnected(
6175
+ this.inputRoute.connectionName,
6176
+ hdRoute.connectionName
6177
+ )) {
6178
+ return [];
6179
+ }
6180
+ const route = hdRoute.route;
6181
+ const segments = [];
6182
+ for (let i = 0; i < route.length - 1; i++) {
6183
+ const start = route[i];
6184
+ const end = route[i + 1];
6185
+ const minX = Math.min(start.x, end.x);
6186
+ const maxX = Math.max(start.x, end.x);
6187
+ const minY = Math.min(start.y, end.y);
6188
+ const maxY = Math.max(start.y, end.y);
6189
+ if (minX <= bounds.maxX && maxX >= bounds.minX && minY <= bounds.maxY && maxY >= bounds.minY) {
6190
+ segments.push([start, end]);
6191
+ }
6192
+ }
6193
+ return segments;
6194
+ }
6062
6195
  );
6196
+ this.segmentTree = new SegmentTree(this.filteredObstaclePathSegments);
6197
+ this.filteredVias = this.otherHdRoutes.flatMap((hdRoute) => {
6198
+ if (this.connMap.areIdsConnected(
6199
+ this.inputRoute.connectionName,
6200
+ hdRoute.connectionName
6201
+ )) {
6202
+ return [];
6203
+ }
6204
+ const vias = hdRoute.vias;
6205
+ const filteredVias = [];
6206
+ for (const via of vias) {
6207
+ const minX = via.x - hdRoute.viaDiameter / 2;
6208
+ const maxX = via.x + hdRoute.viaDiameter / 2;
6209
+ const minY = via.y - hdRoute.viaDiameter / 2;
6210
+ const maxY = via.y + hdRoute.viaDiameter / 2;
6211
+ if (minX <= bounds.maxX && maxX >= bounds.minX && minY <= bounds.maxY && maxY >= bounds.minY) {
6212
+ filteredVias.push({ ...via, diameter: hdRoute.viaDiameter });
6213
+ }
6214
+ }
6215
+ return filteredVias;
6216
+ });
6063
6217
  this.computePathSegments();
6064
6218
  }
6065
6219
  // Compute the path segments and their distances
@@ -6097,7 +6251,7 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
6097
6251
  return {
6098
6252
  x: segment.start.x + factor * (segment.end.x - segment.start.x),
6099
6253
  y: segment.start.y + factor * (segment.end.y - segment.start.y),
6100
- z: segment.start.z
6254
+ z: factor < 0.5 ? segment.start.z : segment.end.z
6101
6255
  // Z doesn't interpolate - use the segment's start z value
6102
6256
  };
6103
6257
  }
@@ -6148,33 +6302,24 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
6148
6302
  return false;
6149
6303
  }
6150
6304
  }
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) {
6305
+ const segmentsThatCouldIntersect = this.segmentTree.getSegmentsThatCouldIntersect(start, end);
6306
+ for (const [otherSegA, otherSegB] of segmentsThatCouldIntersect) {
6307
+ if (otherSegA.z === start.z && otherSegB.z === start.z) {
6308
+ if (minimumDistanceBetweenSegments(
6309
+ { x: start.x, y: start.y },
6310
+ { x: end.x, y: end.y },
6311
+ { x: otherSegA.x, y: otherSegA.y },
6312
+ { x: otherSegB.x, y: otherSegB.y }
6313
+ ) < this.OBSTACLE_MARGIN) {
6174
6314
  return false;
6175
6315
  }
6176
6316
  }
6177
6317
  }
6318
+ for (const via of this.filteredVias) {
6319
+ if (pointToSegmentDistance(via, start, end) < this.OBSTACLE_MARGIN + via.diameter / 2) {
6320
+ return false;
6321
+ }
6322
+ }
6178
6323
  return true;
6179
6324
  }
6180
6325
  // Check if a path with multiple points is valid
@@ -6221,6 +6366,24 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
6221
6366
  }
6222
6367
  this.newRoute.push(path[i]);
6223
6368
  }
6369
+ this.currentStepSize = this.maxStepSize;
6370
+ }
6371
+ moveHead(distance4) {
6372
+ this.lastHeadMoveDistance = distance4;
6373
+ this.headDistanceAlongPath = Math.min(
6374
+ this.headDistanceAlongPath + distance4,
6375
+ this.totalPathLength
6376
+ );
6377
+ }
6378
+ stepBackAndReduceStepSize() {
6379
+ this.headDistanceAlongPath = Math.max(
6380
+ this.tailDistanceAlongPath,
6381
+ this.headDistanceAlongPath - this.lastHeadMoveDistance
6382
+ );
6383
+ this.currentStepSize = Math.max(
6384
+ this.minStepSize,
6385
+ this.currentStepSize * this.STEP_SIZE_REDUCTION_FACTOR
6386
+ );
6224
6387
  }
6225
6388
  _step() {
6226
6389
  const tailHasReachedEnd = this.tailDistanceAlongPath >= this.totalPathLength;
@@ -6252,10 +6415,7 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
6252
6415
  }
6253
6416
  }
6254
6417
  }
6255
- this.headDistanceAlongPath = Math.min(
6256
- this.headDistanceAlongPath + this.stepSize,
6257
- this.totalPathLength
6258
- );
6418
+ this.moveHead(this.currentStepSize);
6259
6419
  const tailPoint = this.getPointAtDistance(this.tailDistanceAlongPath);
6260
6420
  const headPoint = this.getPointAtDistance(this.headDistanceAlongPath);
6261
6421
  const tailIndex = this.getNearestIndexForDistance(
@@ -6274,21 +6434,26 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
6274
6434
  break;
6275
6435
  }
6276
6436
  }
6437
+ if (layerChangeBtwHeadAndTail && this.lastHeadMoveDistance > this.minStepSize) {
6438
+ this.stepBackAndReduceStepSize();
6439
+ return;
6440
+ }
6277
6441
  if (layerChangeBtwHeadAndTail && layerChangeAtDistance > 0) {
6442
+ const pointBeforeChange = this.getPointAtDistance(layerChangeAtDistance);
6278
6443
  if (this.lastValidPath) {
6279
6444
  this.addPathToResult(this.lastValidPath);
6280
6445
  this.lastValidPath = null;
6281
6446
  }
6282
- const pointBeforeChange = this.getPointAtDistance(layerChangeAtDistance);
6283
- const pointAfterChange = this.inputRoute.route[this.getNearestIndexForDistance(layerChangeAtDistance) + 1];
6447
+ const indexAfterLayerChange = this.getNearestIndexForDistance(layerChangeAtDistance) + 1;
6448
+ const pointAfterChange = this.inputRoute.route[indexAfterLayerChange];
6284
6449
  this.newVias.push({
6285
6450
  x: pointAfterChange.x,
6286
6451
  y: pointAfterChange.y
6287
6452
  });
6288
6453
  this.newRoute.push(pointAfterChange);
6289
- const nextTailIndex = this.getNearestIndexForDistance(layerChangeAtDistance) + 1;
6290
- if (this.pathSegments[nextTailIndex]) {
6291
- this.tailDistanceAlongPath = this.pathSegments[nextTailIndex].startDistance;
6454
+ this.currentStepSize = this.maxStepSize;
6455
+ if (this.pathSegments[indexAfterLayerChange]) {
6456
+ this.tailDistanceAlongPath = this.pathSegments[indexAfterLayerChange].startDistance;
6292
6457
  this.headDistanceAlongPath = this.tailDistanceAlongPath;
6293
6458
  } else {
6294
6459
  console.error("Creating via at end, this is probably not right");
@@ -6298,9 +6463,13 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
6298
6463
  return;
6299
6464
  }
6300
6465
  const path45 = this.find45DegreePath(tailPoint, headPoint);
6466
+ if (!path45 && this.lastHeadMoveDistance > this.minStepSize) {
6467
+ this.stepBackAndReduceStepSize();
6468
+ return;
6469
+ }
6301
6470
  if (!path45 && !this.lastValidPath) {
6302
- this.tailDistanceAlongPath += this.stepSize;
6303
- this.headDistanceAlongPath += this.stepSize;
6471
+ this.tailDistanceAlongPath += this.minStepSize;
6472
+ this.moveHead(this.minStepSize);
6304
6473
  return;
6305
6474
  }
6306
6475
  if (path45) {
@@ -6312,11 +6481,8 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
6312
6481
  this.addPathToResult(this.lastValidPath);
6313
6482
  this.lastValidPath = null;
6314
6483
  this.tailDistanceAlongPath = this.lastValidPathHeadDistance;
6484
+ this.moveHead(this.minStepSize);
6315
6485
  }
6316
- this.headDistanceAlongPath = Math.min(
6317
- this.headDistanceAlongPath + this.stepSize,
6318
- this.totalPathLength
6319
- );
6320
6486
  }
6321
6487
  visualize() {
6322
6488
  const graphics = this.getVisualsForNewRouteAndObstacles();
@@ -6334,6 +6500,15 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
6334
6500
  color: "orange",
6335
6501
  label: ["Head", `z: ${headPoint.z}`].join("\n")
6336
6502
  });
6503
+ const tentativeHead = this.getPointAtDistance(
6504
+ this.headDistanceAlongPath + this.currentStepSize
6505
+ );
6506
+ graphics.points.push({
6507
+ x: tentativeHead.x,
6508
+ y: tentativeHead.y,
6509
+ color: "red",
6510
+ label: ["Tentative Head", `z: ${tentativeHead.z}`].join("\n")
6511
+ });
6337
6512
  let distance4 = 0;
6338
6513
  while (distance4 < this.totalPathLength) {
6339
6514
  const point = this.getPointAtDistance(distance4);
@@ -6481,7 +6656,7 @@ var MultiSimplifiedPathSolver = class extends BaseSolver {
6481
6656
  strokeColor: "rgba(255, 0, 0, 0.2)",
6482
6657
  strokeDash: [0.5, 0.5],
6483
6658
  step: 0,
6484
- layer: route.route[i].z.toString()
6659
+ layer: `z${route.route[i].z.toString()}`
6485
6660
  });
6486
6661
  }
6487
6662
  for (const point of route.vias) {
@@ -6519,6 +6694,157 @@ var MultiSimplifiedPathSolver = class extends BaseSolver {
6519
6694
  }
6520
6695
  };
6521
6696
 
6697
+ // lib/solvers/CapacityMeshSolver/CapacityMeshEdgeSolver.ts
6698
+ var CapacityMeshEdgeSolver = class extends BaseSolver {
6699
+ constructor(nodes) {
6700
+ super();
6701
+ this.nodes = nodes;
6702
+ this.edges = [];
6703
+ }
6704
+ edges;
6705
+ getNextCapacityMeshEdgeId() {
6706
+ return `ce${this.edges.length}`;
6707
+ }
6708
+ step() {
6709
+ this.edges = [];
6710
+ for (let i = 0; i < this.nodes.length; i++) {
6711
+ for (let j = i + 1; j < this.nodes.length; j++) {
6712
+ const strawNodesWithSameParent = this.nodes[i]._strawNode && this.nodes[j]._strawNode && this.nodes[i]._strawParentCapacityMeshNodeId === this.nodes[j]._strawParentCapacityMeshNodeId;
6713
+ if (!strawNodesWithSameParent && areNodesBordering(this.nodes[i], this.nodes[j]) && this.doNodesHaveSharedLayer(this.nodes[i], this.nodes[j])) {
6714
+ this.edges.push({
6715
+ capacityMeshEdgeId: this.getNextCapacityMeshEdgeId(),
6716
+ nodeIds: [
6717
+ this.nodes[i].capacityMeshNodeId,
6718
+ this.nodes[j].capacityMeshNodeId
6719
+ ]
6720
+ });
6721
+ }
6722
+ }
6723
+ }
6724
+ this.handleTargetNodes();
6725
+ this.solved = true;
6726
+ }
6727
+ handleTargetNodes() {
6728
+ const targetNodes = this.nodes.filter((node) => node._containsTarget);
6729
+ for (const targetNode of targetNodes) {
6730
+ const hasEdge = this.edges.some(
6731
+ (edge) => edge.nodeIds.includes(targetNode.capacityMeshNodeId)
6732
+ );
6733
+ if (hasEdge) continue;
6734
+ let nearestNode = null;
6735
+ let nearestDistance = Infinity;
6736
+ for (const node of this.nodes) {
6737
+ if (node._containsObstacle) continue;
6738
+ if (node._containsTarget) continue;
6739
+ const dist = distance(targetNode.center, node.center);
6740
+ if (dist < nearestDistance) {
6741
+ nearestDistance = dist;
6742
+ nearestNode = node;
6743
+ }
6744
+ }
6745
+ if (nearestNode) {
6746
+ this.edges.push({
6747
+ capacityMeshEdgeId: this.getNextCapacityMeshEdgeId(),
6748
+ nodeIds: [
6749
+ targetNode.capacityMeshNodeId,
6750
+ nearestNode.capacityMeshNodeId
6751
+ ]
6752
+ });
6753
+ }
6754
+ }
6755
+ }
6756
+ doNodesHaveSharedLayer(node1, node2) {
6757
+ return node1.availableZ.some((z) => node2.availableZ.includes(z));
6758
+ }
6759
+ visualize() {
6760
+ const graphics = {
6761
+ lines: [],
6762
+ points: [],
6763
+ rects: this.nodes.map((node) => {
6764
+ const lowestZ = Math.min(...node.availableZ);
6765
+ return {
6766
+ width: Math.max(node.width - 2, node.width * 0.8),
6767
+ height: Math.max(node.height - 2, node.height * 0.8),
6768
+ center: {
6769
+ x: node.center.x + lowestZ * node.width * 0.05,
6770
+ y: node.center.y - lowestZ * node.width * 0.05
6771
+ },
6772
+ fill: node._containsObstacle ? "rgba(255,0,0,0.1)" : {
6773
+ "0,1": "rgba(0,0,0,0.1)",
6774
+ "0": "rgba(0,200,200, 0.1)",
6775
+ "1": "rgba(0,0,200, 0.1)"
6776
+ }[node.availableZ.join(",")] ?? "rgba(0,200,200,0.1)",
6777
+ label: [
6778
+ node.capacityMeshNodeId,
6779
+ `availableZ: ${node.availableZ.join(",")}`,
6780
+ `target? ${node._containsTarget ?? false}`,
6781
+ `obs? ${node._containsObstacle ?? false}`
6782
+ ].join("\n")
6783
+ };
6784
+ }),
6785
+ circles: []
6786
+ };
6787
+ for (const edge of this.edges) {
6788
+ const node1 = this.nodes.find(
6789
+ (node) => node.capacityMeshNodeId === edge.nodeIds[0]
6790
+ );
6791
+ const node2 = this.nodes.find(
6792
+ (node) => node.capacityMeshNodeId === edge.nodeIds[1]
6793
+ );
6794
+ if (node1?.center && node2?.center) {
6795
+ const lowestZ1 = Math.min(...node1.availableZ);
6796
+ const lowestZ2 = Math.min(...node2.availableZ);
6797
+ const nodeCenter1Adj = {
6798
+ x: node1.center.x + lowestZ1 * node1.width * 0.05,
6799
+ y: node1.center.y - lowestZ1 * node1.width * 0.05
6800
+ };
6801
+ const nodeCenter2Adj = {
6802
+ x: node2.center.x + lowestZ2 * node2.width * 0.05,
6803
+ y: node2.center.y - lowestZ2 * node2.width * 0.05
6804
+ };
6805
+ graphics.lines.push({
6806
+ points: [nodeCenter1Adj, nodeCenter2Adj],
6807
+ strokeDash: node1.availableZ.join(",") === node2.availableZ.join(",") ? void 0 : "10 5"
6808
+ });
6809
+ }
6810
+ }
6811
+ return graphics;
6812
+ }
6813
+ };
6814
+
6815
+ // lib/solvers/CapacityMeshSolver/CapacityMeshEdgeSolver2_NodeTreeOptimization.ts
6816
+ var CapacityMeshEdgeSolver2_NodeTreeOptimization = class extends CapacityMeshEdgeSolver {
6817
+ step() {
6818
+ this.edges = [];
6819
+ const edgeSet = /* @__PURE__ */ new Set();
6820
+ const nodeTree = new CapacityNodeTree(this.nodes);
6821
+ for (let i = 0; i < this.nodes.length; i++) {
6822
+ const A = this.nodes[i];
6823
+ const maybeAdjNodes = nodeTree.getNodesInArea(
6824
+ A.center.x,
6825
+ A.center.y,
6826
+ A.width * 2,
6827
+ A.height * 2
6828
+ );
6829
+ for (const B of maybeAdjNodes) {
6830
+ const areBordering = areNodesBordering(A, B);
6831
+ if (!areBordering) continue;
6832
+ const strawNodesWithSameParent = A._strawNode && B._strawNode && A._strawParentCapacityMeshNodeId === B._strawParentCapacityMeshNodeId;
6833
+ if (!strawNodesWithSameParent && this.doNodesHaveSharedLayer(A, B) && !edgeSet.has(`${A.capacityMeshNodeId}-${B.capacityMeshNodeId}`)) {
6834
+ edgeSet.add(`${A.capacityMeshNodeId}-${B.capacityMeshNodeId}`);
6835
+ edgeSet.add(`${B.capacityMeshNodeId}-${A.capacityMeshNodeId}`);
6836
+ this.edges.push({
6837
+ capacityMeshEdgeId: this.getNextCapacityMeshEdgeId(),
6838
+ nodeIds: [A.capacityMeshNodeId, B.capacityMeshNodeId]
6839
+ });
6840
+ }
6841
+ }
6842
+ }
6843
+ this.handleTargetNodes();
6844
+ this.solved = true;
6845
+ }
6846
+ };
6847
+
6522
6848
  // lib/solvers/AutoroutingPipelineSolver.ts
6523
6849
  function definePipelineStep(solverName, solverClass, getConstructorParams, opts = {}) {
6524
6850
  return {
@@ -6587,17 +6913,18 @@ var CapacityMeshSolver = class extends BaseSolver {
6587
6913
  }
6588
6914
  }
6589
6915
  ),
6590
- // definePipelineStep("nodeSolver", CapacityMeshNodeSolver, (cms) => [
6591
- // cms.netToPointPairsSolver?.getNewSimpleRouteJson() || cms.srj,
6592
- // cms.opts,
6593
- // ]),
6594
6916
  definePipelineStep(
6595
6917
  "nodeSolver",
6596
6918
  CapacityMeshNodeSolver2_NodeUnderObstacle,
6597
6919
  (cms) => [
6598
6920
  cms.netToPointPairsSolver?.getNewSimpleRouteJson() || cms.srj,
6599
6921
  cms.opts
6600
- ]
6922
+ ],
6923
+ {
6924
+ onSolved: (cms) => {
6925
+ cms.capacityNodes = cms.nodeSolver?.finishedNodes;
6926
+ }
6927
+ }
6601
6928
  ),
6602
6929
  // definePipelineStep("nodeTargetMerger", CapacityNodeTargetMerger, (cms) => [
6603
6930
  // cms.nodeSolver?.finishedNodes || [],
@@ -6614,7 +6941,12 @@ var CapacityMeshSolver = class extends BaseSolver {
6614
6941
  definePipelineStep(
6615
6942
  "singleLayerNodeMerger",
6616
6943
  SingleLayerNodeMergerSolver,
6617
- (cms) => [cms.nodeSolver?.finishedNodes]
6944
+ (cms) => [cms.nodeSolver?.finishedNodes],
6945
+ {
6946
+ onSolved: (cms) => {
6947
+ cms.capacityNodes = cms.singleLayerNodeMerger?.newNodes;
6948
+ }
6949
+ }
6618
6950
  ),
6619
6951
  definePipelineStep(
6620
6952
  "strawSolver",
@@ -6626,9 +6958,11 @@ var CapacityMeshSolver = class extends BaseSolver {
6626
6958
  }
6627
6959
  }
6628
6960
  ),
6629
- definePipelineStep("edgeSolver", CapacityMeshEdgeSolver, (cms) => [
6630
- cms.capacityNodes
6631
- ]),
6961
+ definePipelineStep(
6962
+ "edgeSolver",
6963
+ CapacityMeshEdgeSolver2_NodeTreeOptimization,
6964
+ (cms) => [cms.capacityNodes]
6965
+ ),
6632
6966
  definePipelineStep("pathingSolver", CapacityPathingSolver5, (cms) => [
6633
6967
  {
6634
6968
  simpleRouteJson: cms.srjWithPointPairs,