@tscircuit/capacity-autorouter 0.0.29 → 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,177 +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
- 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])) {
161
- this.edges.push({
162
- capacityMeshEdgeId: this.getNextCapacityMeshEdgeId(),
163
- nodeIds: [
164
- this.nodes[i].capacityMeshNodeId,
165
- this.nodes[j].capacityMeshNodeId
166
- ]
167
- });
168
- }
169
- }
170
- }
171
- const targetNodes = this.nodes.filter((node) => node._containsTarget);
172
- for (const targetNode of targetNodes) {
173
- const hasEdge = this.edges.some(
174
- (edge) => edge.nodeIds.includes(targetNode.capacityMeshNodeId)
175
- );
176
- if (hasEdge) continue;
177
- let nearestNode = null;
178
- let nearestDistance = Infinity;
179
- for (const node of this.nodes) {
180
- if (node._containsObstacle) continue;
181
- if (node._containsTarget) continue;
182
- const dist = distance(targetNode.center, node.center);
183
- if (dist < nearestDistance) {
184
- nearestDistance = dist;
185
- nearestNode = node;
186
- }
187
- }
188
- if (nearestNode) {
189
- this.edges.push({
190
- capacityMeshEdgeId: this.getNextCapacityMeshEdgeId(),
191
- nodeIds: [
192
- targetNode.capacityMeshNodeId,
193
- nearestNode.capacityMeshNodeId
194
- ]
195
- });
196
- }
197
- }
198
- this.solved = true;
199
- }
200
- doNodesHaveSharedLayer(node1, node2) {
201
- return node1.availableZ.some((z) => node2.availableZ.includes(z));
202
- }
203
- visualize() {
204
- const graphics = {
205
- lines: [],
206
- points: [],
207
- rects: this.nodes.map((node) => {
208
- const lowestZ = Math.min(...node.availableZ);
209
- return {
210
- width: Math.max(node.width - 2, node.width * 0.8),
211
- height: Math.max(node.height - 2, node.height * 0.8),
212
- center: {
213
- x: node.center.x + lowestZ * node.width * 0.05,
214
- y: node.center.y - lowestZ * node.width * 0.05
215
- },
216
- fill: node._containsObstacle ? "rgba(255,0,0,0.1)" : {
217
- "0,1": "rgba(0,0,0,0.1)",
218
- "0": "rgba(0,200,200, 0.1)",
219
- "1": "rgba(0,0,200, 0.1)"
220
- }[node.availableZ.join(",")] ?? "rgba(0,200,200,0.1)",
221
- label: [
222
- node.capacityMeshNodeId,
223
- `availableZ: ${node.availableZ.join(",")}`,
224
- `target? ${node._containsTarget ?? false}`,
225
- `obs? ${node._containsObstacle ?? false}`
226
- ].join("\n")
227
- };
228
- }),
229
- circles: []
230
- };
231
- for (const edge of this.edges) {
232
- const node1 = this.nodes.find(
233
- (node) => node.capacityMeshNodeId === edge.nodeIds[0]
234
- );
235
- const node2 = this.nodes.find(
236
- (node) => node.capacityMeshNodeId === edge.nodeIds[1]
237
- );
238
- if (node1?.center && node2?.center) {
239
- const lowestZ1 = Math.min(...node1.availableZ);
240
- const lowestZ2 = Math.min(...node2.availableZ);
241
- const nodeCenter1Adj = {
242
- x: node1.center.x + lowestZ1 * node1.width * 0.05,
243
- y: node1.center.y - lowestZ1 * node1.width * 0.05
244
- };
245
- const nodeCenter2Adj = {
246
- x: node2.center.x + lowestZ2 * node2.width * 0.05,
247
- y: node2.center.y - lowestZ2 * node2.width * 0.05
248
- };
249
- graphics.lines.push({
250
- points: [nodeCenter1Adj, nodeCenter2Adj],
251
- strokeDash: node1.availableZ.join(",") === node2.availableZ.join(",") ? void 0 : "10 5"
252
- });
253
- }
254
- }
255
- return graphics;
256
- }
257
- };
258
-
259
88
  // node_modules/@babel/runtime/helpers/esm/extends.js
260
89
  function _extends() {
261
90
  return _extends = Object.assign ? Object.assign.bind() : function(n) {
@@ -1942,6 +1771,46 @@ var CapacitySegmentToPointSolver = class extends BaseSolver {
1942
1771
  }
1943
1772
  };
1944
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
+
1945
1814
  // lib/solvers/HighDensitySolver/SingleHighDensityRouteSolver.ts
1946
1815
  var SingleHighDensityRouteSolver = class extends BaseSolver {
1947
1816
  obstacleRoutes;
@@ -3685,6 +3554,7 @@ var MultipleHighDensityRouteStitchSolver = class extends BaseSolver {
3685
3554
  z: mapLayerNameToZ(c.pointsToConnect[1].layer, opts.layerCount)
3686
3555
  }
3687
3556
  }));
3557
+ this.MAX_ITERATIONS = 1e5;
3688
3558
  }
3689
3559
  _step() {
3690
3560
  if (this.activeSolver) {
@@ -4169,16 +4039,16 @@ var UnravelSectionSolver = class extends BaseSolver {
4169
4039
  segmentPoint.segmentPointId
4170
4040
  ]);
4171
4041
  }
4172
- for (let i = 0; i < segmentPoints.length; i++) {
4173
- const A = segmentPoints[i];
4174
- for (let j = i + 1; j < segmentPoints.length; j++) {
4175
- const B = segmentPoints[j];
4176
- if (B.segmentPointId === A.segmentPointId) continue;
4177
- if (B.segmentId === A.segmentId) continue;
4178
- if (B.connectionName !== A.connectionName) continue;
4179
- if (A.capacityMeshNodeIds.some(
4180
- (nId) => B.capacityMeshNodeIds.includes(nId)
4181
- )) {
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;
4182
4052
  A.directlyConnectedSegmentPointIds.push(B.segmentPointId);
4183
4053
  B.directlyConnectedSegmentPointIds.push(A.segmentPointId);
4184
4054
  }
@@ -5423,6 +5293,7 @@ var StrawSolver = class extends BaseSolver {
5423
5293
  nodeIdCounter;
5424
5294
  constructor(params) {
5425
5295
  super();
5296
+ this.MAX_ITERATIONS = 1e5;
5426
5297
  this.strawSize = params.strawSize ?? 0.5;
5427
5298
  this.multiLayerNodes = [];
5428
5299
  this.strawNodes = [];
@@ -5615,6 +5486,72 @@ ${node.width}x${node.height}`
5615
5486
  }
5616
5487
  };
5617
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
+
5618
5555
  // lib/solvers/SingleLayerNodeMerger/SingleLayerNodeMergerSolver.ts
5619
5556
  var EPSILON = 5e-3;
5620
5557
  var SingleLayerNodeMergerSolver = class extends BaseSolver {
@@ -5623,6 +5560,7 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
5623
5560
  absorbedNodeIds;
5624
5561
  nextBatchNodeIds;
5625
5562
  batchHadModifications;
5563
+ hasComputedAdjacentNodeIds = false;
5626
5564
  newNodes;
5627
5565
  constructor(nodes) {
5628
5566
  super();
@@ -5633,41 +5571,94 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
5633
5571
  }
5634
5572
  this.newNodes = [];
5635
5573
  this.absorbedNodeIds = /* @__PURE__ */ new Set();
5636
- const nodeWithArea = [];
5574
+ const unprocessedNodesWithArea = [];
5637
5575
  for (const node of nodes) {
5638
5576
  if (node.availableZ.length > 1) {
5639
5577
  this.newNodes.push(node);
5640
5578
  this.absorbedNodeIds.add(node.capacityMeshNodeId);
5641
5579
  } else {
5642
- nodeWithArea.push([node.capacityMeshNodeId, node.width * node.height]);
5580
+ unprocessedNodesWithArea.push([node, node.width * node.height]);
5643
5581
  }
5644
5582
  }
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, {
5583
+ unprocessedNodesWithArea.sort((a, b) => a[1] - b[1]);
5584
+ for (const [node, area] of unprocessedNodesWithArea) {
5585
+ const unprocessedNode = {
5649
5586
  ...node,
5650
5587
  center: { ...node.center }
5651
- });
5588
+ };
5589
+ this.nodeMap.set(node.capacityMeshNodeId, unprocessedNode);
5652
5590
  }
5653
- this.currentBatchNodeIds = nodeWithArea.map((n) => n[0]);
5591
+ this.currentBatchNodeIds = unprocessedNodesWithArea.map(
5592
+ ([node]) => node.capacityMeshNodeId
5593
+ );
5654
5594
  this.nextBatchNodeIds = [];
5655
5595
  this.batchHadModifications = false;
5656
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
+ // }
5657
5638
  getAdjacentSameLayerUnprocessedNodes(rootNode) {
5639
+ return this.getAdjacentSameLayerUnprocessedNodes2(rootNode);
5640
+ }
5641
+ getAdjacentSameLayerUnprocessedNodes2(rootNode) {
5658
5642
  const adjacentNodes = [];
5659
- for (const unprocessedNodeId of this.currentBatchNodeIds) {
5660
- if (this.absorbedNodeIds.has(unprocessedNodeId)) continue;
5661
- const unprocessedNode = this.nodeMap.get(unprocessedNodeId);
5662
- if (unprocessedNode.availableZ[0] !== rootNode.availableZ[0]) continue;
5663
- if (unprocessedNode._containsTarget && unprocessedNode._targetConnectionName !== rootNode._targetConnectionName)
5664
- continue;
5665
- 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;
5666
5651
  adjacentNodes.push(unprocessedNode);
5667
5652
  }
5668
5653
  return adjacentNodes;
5669
5654
  }
5670
5655
  _step() {
5656
+ if (!this.hasComputedAdjacentNodeIds) {
5657
+ this.computeAdjacentNodeIdsForFirstBatch(
5658
+ this.currentBatchNodeIds.map((id) => this.nodeMap.get(id))
5659
+ );
5660
+ this.hasComputedAdjacentNodeIds = true;
5661
+ }
5671
5662
  let rootNodeId = this.currentBatchNodeIds.pop();
5672
5663
  while (rootNodeId && this.absorbedNodeIds.has(rootNodeId)) {
5673
5664
  rootNodeId = this.currentBatchNodeIds.pop();
@@ -5696,6 +5687,19 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
5696
5687
  this.nextBatchNodeIds.push(rootNodeId);
5697
5688
  return;
5698
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
+ };
5699
5703
  const adjacentNodesToLeft = adjacentNodes.filter(
5700
5704
  (adjNode) => adjNode.center.x < rootNode.center.x && Math.abs(adjNode.center.y - rootNode.center.y) < rootNode.height / 2
5701
5705
  );
@@ -5712,9 +5716,7 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
5712
5716
  if (leftAdjNodesTakeUpEntireHeight && leftAdjNodesAreAllSameSize) {
5713
5717
  rootNode.width += leftAdjNodeWidth;
5714
5718
  rootNode.center.x = rootNode.center.x - leftAdjNodeWidth / 2;
5715
- for (const adjNode of adjacentNodesToLeft) {
5716
- this.absorbedNodeIds.add(adjNode.capacityMeshNodeId);
5717
- }
5719
+ absorbAdjacentNodeIds(adjacentNodesToLeft);
5718
5720
  rootNodeHasGrown = true;
5719
5721
  }
5720
5722
  }
@@ -5734,9 +5736,7 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
5734
5736
  if (rightAdjNodesTakeUpEntireHeight && rightAdjNodesAreAllSameSize) {
5735
5737
  rootNode.width += rightAdjNodeWidth;
5736
5738
  rootNode.center.x = rootNode.center.x + rightAdjNodeWidth / 2;
5737
- for (const adjNode of adjacentNodesToRight) {
5738
- this.absorbedNodeIds.add(adjNode.capacityMeshNodeId);
5739
- }
5739
+ absorbAdjacentNodeIds(adjacentNodesToRight);
5740
5740
  rootNodeHasGrown = true;
5741
5741
  }
5742
5742
  }
@@ -5756,9 +5756,7 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
5756
5756
  if (topAdjNodesTakeUpEntireWidth && topAdjNodesAreAllSameSize) {
5757
5757
  rootNode.height += topAdjNodeHeight;
5758
5758
  rootNode.center.y = rootNode.center.y + topAdjNodeHeight / 2;
5759
- for (const adjNode of adjacentNodesToTop) {
5760
- this.absorbedNodeIds.add(adjNode.capacityMeshNodeId);
5761
- }
5759
+ absorbAdjacentNodeIds(adjacentNodesToTop);
5762
5760
  rootNodeHasGrown = true;
5763
5761
  }
5764
5762
  }
@@ -5778,9 +5776,7 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
5778
5776
  if (bottomAdjNodesTakeUpEntireWidth && bottomAdjNodesAreAllSameSize) {
5779
5777
  rootNode.height += bottomAdjNodeHeight;
5780
5778
  rootNode.center.y = rootNode.center.y - bottomAdjNodeHeight / 2;
5781
- for (const adjNode of adjacentNodesToBottom) {
5782
- this.absorbedNodeIds.add(adjNode.capacityMeshNodeId);
5783
- }
5779
+ absorbAdjacentNodeIds(adjacentNodesToBottom);
5784
5780
  rootNodeHasGrown = true;
5785
5781
  }
5786
5782
  }
@@ -5870,7 +5866,7 @@ var SingleSimplifiedPathSolver = class extends BaseSolver {
5870
5866
  this.obstacles = params.obstacles;
5871
5867
  this.connMap = params.connMap;
5872
5868
  this.colorMap = params.colorMap;
5873
- this.newRoute = [];
5869
+ this.newRoute = [this.inputRoute.route[0]];
5874
5870
  this.newVias = [];
5875
5871
  }
5876
5872
  get simplifiedRoute() {
@@ -6055,32 +6051,169 @@ function segmentsIntersect(A1, A2, B1, B2) {
6055
6051
  return false;
6056
6052
  }
6057
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
+
6058
6115
  // lib/solvers/SimplifiedPathSolver/SingleSimplifiedPathSolver5_Deg45.ts
6059
6116
  var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
6060
6117
  pathSegments = [];
6061
6118
  totalPathLength = 0;
6062
6119
  headDistanceAlongPath = 0;
6063
6120
  tailDistanceAlongPath = 0;
6064
- stepSize = 0.25;
6121
+ minStepSize = 0.25;
6065
6122
  // Default step size, can be adjusted
6066
6123
  lastValidPath = null;
6067
6124
  // Store the current valid path
6068
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;
6069
6132
  filteredObstacles = [];
6133
+ filteredObstaclePathSegments = [];
6134
+ filteredVias = [];
6135
+ segmentTree;
6070
6136
  OBSTACLE_MARGIN = 0.15;
6071
6137
  TAIL_JUMP_RATIO = 0.8;
6072
6138
  constructor(params) {
6073
6139
  super(params);
6140
+ this.cachedValidPathSegments = /* @__PURE__ */ new Set();
6074
6141
  if (this.inputRoute.route.length <= 1) {
6075
6142
  this.newRoute = [...this.inputRoute.route];
6076
6143
  this.solved = true;
6077
6144
  return;
6078
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
+ );
6079
6156
  this.filteredObstacles = this.obstacles.filter(
6080
6157
  (obstacle) => !obstacle.connectedTo.some(
6081
6158
  (id) => this.connMap.areIdsConnected(this.inputRoute.connectionName, id)
6082
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
+ }
6083
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
+ });
6084
6217
  this.computePathSegments();
6085
6218
  }
6086
6219
  // Compute the path segments and their distances
@@ -6118,7 +6251,7 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
6118
6251
  return {
6119
6252
  x: segment.start.x + factor * (segment.end.x - segment.start.x),
6120
6253
  y: segment.start.y + factor * (segment.end.y - segment.start.y),
6121
- z: segment.start.z
6254
+ z: factor < 0.5 ? segment.start.z : segment.end.z
6122
6255
  // Z doesn't interpolate - use the segment's start z value
6123
6256
  };
6124
6257
  }
@@ -6169,33 +6302,24 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
6169
6302
  return false;
6170
6303
  }
6171
6304
  }
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) {
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) {
6195
6314
  return false;
6196
6315
  }
6197
6316
  }
6198
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
+ }
6199
6323
  return true;
6200
6324
  }
6201
6325
  // Check if a path with multiple points is valid
@@ -6242,6 +6366,24 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
6242
6366
  }
6243
6367
  this.newRoute.push(path[i]);
6244
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
+ );
6245
6387
  }
6246
6388
  _step() {
6247
6389
  const tailHasReachedEnd = this.tailDistanceAlongPath >= this.totalPathLength;
@@ -6273,10 +6415,7 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
6273
6415
  }
6274
6416
  }
6275
6417
  }
6276
- this.headDistanceAlongPath = Math.min(
6277
- this.headDistanceAlongPath + this.stepSize,
6278
- this.totalPathLength
6279
- );
6418
+ this.moveHead(this.currentStepSize);
6280
6419
  const tailPoint = this.getPointAtDistance(this.tailDistanceAlongPath);
6281
6420
  const headPoint = this.getPointAtDistance(this.headDistanceAlongPath);
6282
6421
  const tailIndex = this.getNearestIndexForDistance(
@@ -6295,21 +6434,26 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
6295
6434
  break;
6296
6435
  }
6297
6436
  }
6437
+ if (layerChangeBtwHeadAndTail && this.lastHeadMoveDistance > this.minStepSize) {
6438
+ this.stepBackAndReduceStepSize();
6439
+ return;
6440
+ }
6298
6441
  if (layerChangeBtwHeadAndTail && layerChangeAtDistance > 0) {
6442
+ const pointBeforeChange = this.getPointAtDistance(layerChangeAtDistance);
6299
6443
  if (this.lastValidPath) {
6300
6444
  this.addPathToResult(this.lastValidPath);
6301
6445
  this.lastValidPath = null;
6302
6446
  }
6303
- const pointBeforeChange = this.getPointAtDistance(layerChangeAtDistance);
6304
- const pointAfterChange = this.inputRoute.route[this.getNearestIndexForDistance(layerChangeAtDistance) + 1];
6447
+ const indexAfterLayerChange = this.getNearestIndexForDistance(layerChangeAtDistance) + 1;
6448
+ const pointAfterChange = this.inputRoute.route[indexAfterLayerChange];
6305
6449
  this.newVias.push({
6306
6450
  x: pointAfterChange.x,
6307
6451
  y: pointAfterChange.y
6308
6452
  });
6309
6453
  this.newRoute.push(pointAfterChange);
6310
- const nextTailIndex = this.getNearestIndexForDistance(layerChangeAtDistance) + 1;
6311
- if (this.pathSegments[nextTailIndex]) {
6312
- this.tailDistanceAlongPath = this.pathSegments[nextTailIndex].startDistance;
6454
+ this.currentStepSize = this.maxStepSize;
6455
+ if (this.pathSegments[indexAfterLayerChange]) {
6456
+ this.tailDistanceAlongPath = this.pathSegments[indexAfterLayerChange].startDistance;
6313
6457
  this.headDistanceAlongPath = this.tailDistanceAlongPath;
6314
6458
  } else {
6315
6459
  console.error("Creating via at end, this is probably not right");
@@ -6319,9 +6463,13 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
6319
6463
  return;
6320
6464
  }
6321
6465
  const path45 = this.find45DegreePath(tailPoint, headPoint);
6466
+ if (!path45 && this.lastHeadMoveDistance > this.minStepSize) {
6467
+ this.stepBackAndReduceStepSize();
6468
+ return;
6469
+ }
6322
6470
  if (!path45 && !this.lastValidPath) {
6323
- this.tailDistanceAlongPath += this.stepSize;
6324
- this.headDistanceAlongPath += this.stepSize;
6471
+ this.tailDistanceAlongPath += this.minStepSize;
6472
+ this.moveHead(this.minStepSize);
6325
6473
  return;
6326
6474
  }
6327
6475
  if (path45) {
@@ -6333,11 +6481,8 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
6333
6481
  this.addPathToResult(this.lastValidPath);
6334
6482
  this.lastValidPath = null;
6335
6483
  this.tailDistanceAlongPath = this.lastValidPathHeadDistance;
6484
+ this.moveHead(this.minStepSize);
6336
6485
  }
6337
- this.headDistanceAlongPath = Math.min(
6338
- this.headDistanceAlongPath + this.stepSize,
6339
- this.totalPathLength
6340
- );
6341
6486
  }
6342
6487
  visualize() {
6343
6488
  const graphics = this.getVisualsForNewRouteAndObstacles();
@@ -6355,6 +6500,15 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
6355
6500
  color: "orange",
6356
6501
  label: ["Head", `z: ${headPoint.z}`].join("\n")
6357
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
+ });
6358
6512
  let distance4 = 0;
6359
6513
  while (distance4 < this.totalPathLength) {
6360
6514
  const point = this.getPointAtDistance(distance4);
@@ -6540,6 +6694,157 @@ var MultiSimplifiedPathSolver = class extends BaseSolver {
6540
6694
  }
6541
6695
  };
6542
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
+
6543
6848
  // lib/solvers/AutoroutingPipelineSolver.ts
6544
6849
  function definePipelineStep(solverName, solverClass, getConstructorParams, opts = {}) {
6545
6850
  return {
@@ -6653,9 +6958,11 @@ var CapacityMeshSolver = class extends BaseSolver {
6653
6958
  }
6654
6959
  }
6655
6960
  ),
6656
- definePipelineStep("edgeSolver", CapacityMeshEdgeSolver, (cms) => [
6657
- cms.capacityNodes
6658
- ]),
6961
+ definePipelineStep(
6962
+ "edgeSolver",
6963
+ CapacityMeshEdgeSolver2_NodeTreeOptimization,
6964
+ (cms) => [cms.capacityNodes]
6965
+ ),
6659
6966
  definePipelineStep("pathingSolver", CapacityPathingSolver5, (cms) => [
6660
6967
  {
6661
6968
  simpleRouteJson: cms.srjWithPointPairs,