@tscircuit/capacity-autorouter 0.0.43 → 0.0.44

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
@@ -1642,30 +1642,40 @@ var CapacityEdgeToPortSegmentSolver = class extends BaseSolver {
1642
1642
  const node = this.nodeMap.get(nodeId);
1643
1643
  segments.forEach((segment) => {
1644
1644
  const isVertical = segment.start.x === segment.end.x;
1645
- const THICKNESS = 0.1 / segment.connectionNames.length;
1645
+ const THICKNESS = 0.05;
1646
1646
  for (let i = 0; i < segment.connectionNames.length; i++) {
1647
- const offsetAmount = (i / (segment.connectionNames.length - 1 + 1e-6) - 0.5) * THICKNESS;
1648
1647
  const offset = {
1649
- x: isVertical ? offsetAmount : 0,
1650
- y: isVertical ? 0 : offsetAmount
1648
+ x: 0.05 * Math.max(...segment.availableZ),
1649
+ y: 0.05 * Math.max(...segment.availableZ)
1651
1650
  };
1652
1651
  const trueSegmentCenter = {
1653
- x: (segment.start.x + segment.end.x) / 2 + offset.x,
1654
- y: (segment.start.y + segment.end.y) / 2 + offset.y
1652
+ x: (segment.start.x + segment.end.x) / 2,
1653
+ y: (segment.start.y + segment.end.y) / 2
1655
1654
  };
1656
- graphics.rects.push({
1657
- center: {
1658
- x: (trueSegmentCenter.x * 6 + node.center.x) / 7,
1659
- y: (trueSegmentCenter.y * 6 + node.center.y) / 7
1660
- },
1661
- width: isVertical ? THICKNESS : Math.abs(segment.end.x - segment.start.x),
1662
- height: isVertical ? Math.abs(segment.end.y - segment.start.y) : THICKNESS,
1663
- fill: safeTransparentize(
1655
+ const segmentCenter = {
1656
+ x: trueSegmentCenter.x + offset.x,
1657
+ y: trueSegmentCenter.y + offset.y
1658
+ };
1659
+ if (offset.x > 0) {
1660
+ graphics.lines.push({
1661
+ points: [trueSegmentCenter, segmentCenter],
1662
+ strokeColor: "rgba(0, 0, 0, 0.25)",
1663
+ strokeDash: "5 5"
1664
+ });
1665
+ }
1666
+ graphics.points.push({
1667
+ x: segmentCenter.x,
1668
+ y: segmentCenter.y,
1669
+ label: `${nodeId}: ${segment.connectionNames.join(", ")}
1670
+ availableZ: ${segment.availableZ.join(",")}
1671
+ nodePortSegmentId: ${segment.nodePortSegmentId}`
1672
+ });
1673
+ graphics.lines.push({
1674
+ points: [segment.start, segment.end],
1675
+ strokeColor: safeTransparentize(
1664
1676
  this.colorMap[segment.connectionNames[i]],
1665
1677
  0.6
1666
- ),
1667
- label: `${nodeId}: ${segment.connectionNames.join(", ")}
1668
- availableZ: ${segment.availableZ.join(",")}`
1678
+ )
1669
1679
  });
1670
1680
  }
1671
1681
  });
@@ -1710,19 +1720,50 @@ function findOverlappingSegment(node, adjNode) {
1710
1720
  };
1711
1721
  }
1712
1722
  }
1723
+ var EPSILON = 1e-9;
1724
+ function coordsAreEqual(p1, p2) {
1725
+ return Math.abs(p1.x - p2.x) < EPSILON && Math.abs(p1.y - p2.y) < EPSILON;
1726
+ }
1727
+ function availableZAreEqual(zA1, zA2) {
1728
+ if (zA1.length !== zA2.length) {
1729
+ return false;
1730
+ }
1731
+ for (let i = 0; i < zA1.length; i++) {
1732
+ if (zA1[i] !== zA2[i]) {
1733
+ return false;
1734
+ }
1735
+ }
1736
+ return true;
1737
+ }
1713
1738
  function combineSegments(segments) {
1714
1739
  const mergedSegments = [];
1715
- const remainingSegments = [...segments];
1740
+ const remainingSegments = segments.map((s) => ({
1741
+ ...s,
1742
+ connectionNames: [...s.connectionNames],
1743
+ availableZ: [...s.availableZ].sort((a, b) => a - b)
1744
+ // Ensure Z is sorted for comparison
1745
+ }));
1716
1746
  while (remainingSegments.length > 0) {
1717
1747
  const segmentUnderTest = remainingSegments.pop();
1718
- const overlappingMergedSegment = mergedSegments.find((segment) => {
1719
- return segment.start.x === segmentUnderTest.start.x && segment.start.y === segmentUnderTest.start.y && segment.end.x === segmentUnderTest.end.x && segment.end.y === segmentUnderTest.end.y;
1720
- });
1721
- if (overlappingMergedSegment) {
1722
- overlappingMergedSegment.connectionNames.push(
1723
- ...segmentUnderTest.connectionNames
1748
+ let foundMatch = false;
1749
+ for (let i = 0; i < mergedSegments.length; i++) {
1750
+ const mergedSegment = mergedSegments[i];
1751
+ const geometryMatch = coordsAreEqual(mergedSegment.start, segmentUnderTest.start) && coordsAreEqual(mergedSegment.end, segmentUnderTest.end) || coordsAreEqual(mergedSegment.start, segmentUnderTest.end) && coordsAreEqual(mergedSegment.end, segmentUnderTest.start);
1752
+ const zMatch = availableZAreEqual(
1753
+ mergedSegment.availableZ,
1754
+ segmentUnderTest.availableZ
1724
1755
  );
1725
- } else {
1756
+ if (geometryMatch && zMatch) {
1757
+ const currentConnections = new Set(mergedSegment.connectionNames);
1758
+ segmentUnderTest.connectionNames.forEach(
1759
+ (cn) => currentConnections.add(cn)
1760
+ );
1761
+ mergedSegment.connectionNames = Array.from(currentConnections);
1762
+ foundMatch = true;
1763
+ break;
1764
+ }
1765
+ }
1766
+ if (!foundMatch) {
1726
1767
  mergedSegments.push(segmentUnderTest);
1727
1768
  }
1728
1769
  }
@@ -1847,25 +1888,49 @@ var CapacitySegmentToPointSolver = class extends BaseSolver {
1847
1888
  */
1848
1889
  visualize() {
1849
1890
  const graphics = {
1850
- points: this.solvedSegments.flatMap(
1851
- (seg) => seg.assignedPoints.map((ap) => ({
1852
- x: ap.point.x,
1853
- y: ap.point.y,
1854
- label: [
1855
- `${seg.capacityMeshNodeId}-${ap.connectionName}`,
1856
- `z: ${seg.availableZ.join(",")}`
1857
- ].join("\n"),
1858
- color: this.colorMap[ap.connectionName],
1859
- step: 4
1860
- }))
1861
- ),
1891
+ points: [],
1862
1892
  lines: this.solvedSegments.map((seg) => ({
1863
1893
  points: [seg.start, seg.end],
1864
1894
  step: 4
1865
1895
  })),
1866
1896
  rects: [],
1867
- circles: []
1897
+ circles: [],
1898
+ coordinateSystem: "cartesian",
1899
+ title: "Capacity Segment to Point Solver"
1868
1900
  };
1901
+ for (let i = 0; i < this.solvedSegments.length; i++) {
1902
+ const seg = this.solvedSegments[i];
1903
+ for (let j = 0; j < seg.assignedPoints.length; j++) {
1904
+ const ap = seg.assignedPoints[j];
1905
+ const truePoint = {
1906
+ x: ap.point.x,
1907
+ y: ap.point.y
1908
+ };
1909
+ const offsetPoint = {
1910
+ x: ap.point.x + ap.point.z * 0.05,
1911
+ y: ap.point.y + ap.point.z * 0.05
1912
+ };
1913
+ if (ap.point.z !== 0) {
1914
+ graphics.lines.push({
1915
+ points: [truePoint, offsetPoint],
1916
+ strokeColor: "rgba(0, 0, 0, 0.25)",
1917
+ strokeDash: "5 5",
1918
+ step: 4
1919
+ });
1920
+ }
1921
+ graphics.points.push({
1922
+ x: offsetPoint.x,
1923
+ y: offsetPoint.y,
1924
+ label: [
1925
+ `${seg.capacityMeshNodeId}-${ap.connectionName}`,
1926
+ `z: ${seg.availableZ.join(",")}`,
1927
+ `nodePortSegmentId: ${seg.nodePortSegmentId}`
1928
+ ].join("\n"),
1929
+ color: this.colorMap[ap.connectionName],
1930
+ step: 4
1931
+ });
1932
+ }
1933
+ }
1869
1934
  const dashedLines = [];
1870
1935
  const nodeConnections = {};
1871
1936
  for (const seg of this.solvedSegments) {
@@ -2075,6 +2140,12 @@ var SingleRouteCandidatePriorityQueue = class {
2075
2140
  this.heapifyDown();
2076
2141
  return item;
2077
2142
  }
2143
+ peek() {
2144
+ if (this.heap.length === 0) {
2145
+ return null;
2146
+ }
2147
+ return this.heap[0];
2148
+ }
2078
2149
  enqueue(item) {
2079
2150
  this.heap.push(item);
2080
2151
  this.heapifyUp();
@@ -2157,16 +2228,6 @@ var SingleHighDensityRouteSolver = class extends BaseSolver {
2157
2228
  this.obstacleMargin = opts.obstacleMargin ?? 0.2;
2158
2229
  this.layerCount = opts.layerCount ?? 2;
2159
2230
  this.exploredNodes = /* @__PURE__ */ new Set();
2160
- this.candidates = new SingleRouteCandidatePriorityQueue([
2161
- {
2162
- ...opts.A,
2163
- z: opts.A.z ?? 0,
2164
- g: 0,
2165
- h: 0,
2166
- f: 0,
2167
- parent: null
2168
- }
2169
- ]);
2170
2231
  this.straightLineDistance = distance(this.A, this.B);
2171
2232
  this.futureConnections = opts.futureConnections ?? [];
2172
2233
  this.MAX_ITERATIONS = 5e3;
@@ -2189,6 +2250,25 @@ var SingleHighDensityRouteSolver = class extends BaseSolver {
2189
2250
  if (this.futureConnections && this.futureConnections.length === 0 && this.obstacleRoutes.length === 0) {
2190
2251
  this.handleSimpleCases();
2191
2252
  }
2253
+ this.candidates = new SingleRouteCandidatePriorityQueue([
2254
+ {
2255
+ ...opts.A,
2256
+ x: Math.floor(opts.A.x / this.cellStep) * this.cellStep,
2257
+ y: Math.floor(opts.A.y / this.cellStep) * this.cellStep,
2258
+ z: opts.A.z ?? 0,
2259
+ g: 0,
2260
+ h: 0,
2261
+ f: 0,
2262
+ parent: {
2263
+ ...opts.A,
2264
+ z: opts.A.z ?? 0,
2265
+ g: 0,
2266
+ h: 0,
2267
+ f: 0,
2268
+ parent: null
2269
+ }
2270
+ }
2271
+ ]);
2192
2272
  }
2193
2273
  handleSimpleCases() {
2194
2274
  this.solved = true;
@@ -2468,6 +2548,19 @@ z: ${this.B.z}`,
2468
2548
  label: `Explored (z=${z})`
2469
2549
  });
2470
2550
  }
2551
+ if (this.candidates.peek()) {
2552
+ const nextNode = this.candidates.peek();
2553
+ graphics.rects.push({
2554
+ center: {
2555
+ x: nextNode.x + nextNode.z * this.cellStep / 20,
2556
+ y: nextNode.y + nextNode.z * this.cellStep / 20
2557
+ },
2558
+ fill: "rgba(0, 255, 0, 0.8)",
2559
+ width: this.cellStep * 0.9,
2560
+ height: this.cellStep * 0.9,
2561
+ label: `Next (z=${nextNode.z})`
2562
+ });
2563
+ }
2471
2564
  for (const route of this.obstacleRoutes) {
2472
2565
  for (const via of route.vias) {
2473
2566
  graphics.circles.push({
@@ -3922,14 +4015,14 @@ function distance2(p1, p2) {
3922
4015
  }
3923
4016
 
3924
4017
  // lib/solvers/HighDensitySolver/TwoRouteHighDensitySolver/calculateSideTraversal.ts
3925
- var EPSILON = 1e-9;
4018
+ var EPSILON2 = 1e-9;
3926
4019
  function calculateSegmentTraversal(startPoint, endPoint, bounds) {
3927
4020
  const startAngle = pointToAngle(startPoint, bounds);
3928
4021
  let endAngle = pointToAngle(endPoint, bounds);
3929
4022
  if (endAngle < startAngle) {
3930
4023
  endAngle += 2 * Math.PI;
3931
4024
  }
3932
- if (Math.abs(endAngle - startAngle) < EPSILON) {
4025
+ if (Math.abs(endAngle - startAngle) < EPSILON2) {
3933
4026
  return { left: 0, top: 0, right: 0, bottom: 0 };
3934
4027
  }
3935
4028
  return calculateSidePercentages(startAngle, endAngle, bounds);
@@ -3944,7 +4037,7 @@ function calculateTraversalPercentages(A, B, C, bounds) {
3944
4037
  bottom: Math.min(1, percentagesAB.bottom + percentagesBC.bottom)
3945
4038
  };
3946
4039
  for (const key in totalPercentages) {
3947
- if (Math.abs(totalPercentages[key]) < EPSILON) {
4040
+ if (Math.abs(totalPercentages[key]) < EPSILON2) {
3948
4041
  totalPercentages[key] = 0;
3949
4042
  }
3950
4043
  }
@@ -3953,17 +4046,17 @@ function calculateTraversalPercentages(A, B, C, bounds) {
3953
4046
  function pointToAngle(point, bounds) {
3954
4047
  const width = bounds.maxX - bounds.minX;
3955
4048
  const height = bounds.maxY - bounds.minY;
3956
- if (width < EPSILON && height < EPSILON) return 0;
4049
+ if (width < EPSILON2 && height < EPSILON2) return 0;
3957
4050
  const perimeter = 2 * (width + height);
3958
- if (perimeter < EPSILON) return 0;
4051
+ if (perimeter < EPSILON2) return 0;
3959
4052
  let distance6 = 0;
3960
- if (Math.abs(point.y - bounds.maxY) < EPSILON && point.x >= bounds.minX - EPSILON && point.x <= bounds.maxX + EPSILON) {
4053
+ if (Math.abs(point.y - bounds.maxY) < EPSILON2 && point.x >= bounds.minX - EPSILON2 && point.x <= bounds.maxX + EPSILON2) {
3961
4054
  distance6 = Math.max(0, Math.min(width, point.x - bounds.minX));
3962
- } else if (Math.abs(point.x - bounds.maxX) < EPSILON && point.y >= bounds.minY - EPSILON && point.y <= bounds.maxY + EPSILON) {
4055
+ } else if (Math.abs(point.x - bounds.maxX) < EPSILON2 && point.y >= bounds.minY - EPSILON2 && point.y <= bounds.maxY + EPSILON2) {
3963
4056
  distance6 = width + Math.max(0, Math.min(height, bounds.maxY - point.y));
3964
- } else if (Math.abs(point.y - bounds.minY) < EPSILON && point.x >= bounds.minX - EPSILON && point.x <= bounds.maxX + EPSILON) {
4057
+ } else if (Math.abs(point.y - bounds.minY) < EPSILON2 && point.x >= bounds.minX - EPSILON2 && point.x <= bounds.maxX + EPSILON2) {
3965
4058
  distance6 = width + height + Math.max(0, Math.min(width, bounds.maxX - point.x));
3966
- } else if (Math.abs(point.x - bounds.minX) < EPSILON && point.y >= bounds.minY - EPSILON && point.y <= bounds.maxY + EPSILON) {
4059
+ } else if (Math.abs(point.x - bounds.minX) < EPSILON2 && point.y >= bounds.minY - EPSILON2 && point.y <= bounds.maxY + EPSILON2) {
3967
4060
  distance6 = width + height + width + Math.max(0, Math.min(height, point.y - bounds.minY));
3968
4061
  } else {
3969
4062
  throw new Error(
@@ -3971,15 +4064,15 @@ function pointToAngle(point, bounds) {
3971
4064
  );
3972
4065
  }
3973
4066
  distance6 = Math.max(0, Math.min(perimeter, distance6));
3974
- return perimeter > EPSILON ? distance6 / perimeter * (2 * Math.PI) : 0;
4067
+ return perimeter > EPSILON2 ? distance6 / perimeter * (2 * Math.PI) : 0;
3975
4068
  }
3976
4069
  function calculateSidePercentages(startAngle, endAngle, bounds) {
3977
4070
  const width = bounds.maxX - bounds.minX;
3978
4071
  const height = bounds.maxY - bounds.minY;
3979
- if (width < EPSILON && height < EPSILON)
4072
+ if (width < EPSILON2 && height < EPSILON2)
3980
4073
  return { left: 0, top: 0, right: 0, bottom: 0 };
3981
4074
  const perimeter = 2 * (width + height);
3982
- if (perimeter < EPSILON) return { left: 0, top: 0, right: 0, bottom: 0 };
4075
+ if (perimeter < EPSILON2) return { left: 0, top: 0, right: 0, bottom: 0 };
3983
4076
  const angleTopEnd = width / perimeter * (2 * Math.PI);
3984
4077
  const angleRightEnd = (width + height) / perimeter * (2 * Math.PI);
3985
4078
  const angleBottomEnd = (width + width + height) / perimeter * (2 * Math.PI);
@@ -3998,13 +4091,13 @@ function calculateSidePercentages(startAngle, endAngle, bounds) {
3998
4091
  ];
3999
4092
  const result = { left: 0, top: 0, right: 0, bottom: 0 };
4000
4093
  const totalAngleTraversal = endAngle - startAngle;
4001
- if (totalAngleTraversal < EPSILON) return result;
4094
+ if (totalAngleTraversal < EPSILON2) return result;
4002
4095
  for (const side of sides) {
4003
4096
  const sideAngleRange = side.end - side.start;
4004
- if (sideAngleRange < EPSILON || side.length < EPSILON) continue;
4097
+ if (sideAngleRange < EPSILON2 || side.length < EPSILON2) continue;
4005
4098
  const overlapStart = Math.max(startAngle, side.start);
4006
4099
  const overlapEnd = Math.min(endAngle, side.end);
4007
- if (overlapStart < overlapEnd - EPSILON) {
4100
+ if (overlapStart < overlapEnd - EPSILON2) {
4008
4101
  const traversedAngleOnSide = overlapEnd - overlapStart;
4009
4102
  const percentage = traversedAngleOnSide / sideAngleRange;
4010
4103
  result[side.name] += Math.max(0, percentage);
@@ -5389,6 +5482,7 @@ var convertSrjToGraphicsObject = (srj) => {
5389
5482
  const circles = [];
5390
5483
  const points = [];
5391
5484
  const colorMap = getColorMap(srj);
5485
+ const layerCount = 2;
5392
5486
  if (srj.connections) {
5393
5487
  for (const connection of srj.connections) {
5394
5488
  for (const point of connection.pointsToConnect) {
@@ -5396,6 +5490,7 @@ var convertSrjToGraphicsObject = (srj) => {
5396
5490
  x: point.x,
5397
5491
  y: point.y,
5398
5492
  color: colorMap[connection.name],
5493
+ layer: point.layer ?? ("z" in point ? mapZToLayerName(point.z, layerCount) : "top"),
5399
5494
  label: `${connection.name} (${point.layer})`
5400
5495
  });
5401
5496
  }
@@ -5412,7 +5507,8 @@ var convertSrjToGraphicsObject = (srj) => {
5412
5507
  radius: 0.3,
5413
5508
  // 0.6 via diameter
5414
5509
  fill: "blue",
5415
- stroke: "none"
5510
+ stroke: "none",
5511
+ layer: "z0,1"
5416
5512
  });
5417
5513
  } else if (routePoint.route_type === "wire" && nextRoutePoint.route_type === "wire" && nextRoutePoint.layer === routePoint.layer) {
5418
5514
  lines.push({
@@ -5420,6 +5516,7 @@ var convertSrjToGraphicsObject = (srj) => {
5420
5516
  { x: routePoint.x, y: routePoint.y },
5421
5517
  { x: nextRoutePoint.x, y: nextRoutePoint.y }
5422
5518
  ],
5519
+ layer: `z${mapLayerNameToZ(routePoint.layer, layerCount)}`,
5423
5520
  strokeWidth: 0.15,
5424
5521
  strokeColor: safeTransparentize(
5425
5522
  {
@@ -5443,7 +5540,8 @@ var convertSrjToGraphicsObject = (srj) => {
5443
5540
  center: o.center,
5444
5541
  width: o.width,
5445
5542
  height: o.height,
5446
- fill: "rgba(255,0,0,0.5)"
5543
+ fill: "rgba(255,0,0,0.5)",
5544
+ layer: `z${o.layers.map(mapLayerNameToZ).join(",")}`
5447
5545
  })
5448
5546
  ),
5449
5547
  circles,
@@ -5456,23 +5554,26 @@ var convertSrjToGraphicsObject = (srj) => {
5456
5554
  function getNodesNearNode(params) {
5457
5555
  const { nodeId, nodeIdToSegmentIds, segmentIdToNodeIds, hops } = params;
5458
5556
  if (hops === 0) return [nodeId];
5459
- const segments = nodeIdToSegmentIds.get(nodeId);
5460
- const nodes = /* @__PURE__ */ new Set();
5461
- for (const segmentId of segments) {
5462
- const adjacentNodeIds = segmentIdToNodeIds.get(segmentId);
5463
- for (const adjacentNodeId of adjacentNodeIds) {
5464
- const ancestors = getNodesNearNode({
5465
- nodeId: adjacentNodeId,
5466
- nodeIdToSegmentIds,
5467
- segmentIdToNodeIds,
5468
- hops: hops - 1
5469
- });
5470
- for (const ancestor of ancestors) {
5471
- nodes.add(ancestor);
5557
+ const visitedNodes = /* @__PURE__ */ new Set([nodeId]);
5558
+ const exploreQueue = [{ nodeId, remainingHops: hops }];
5559
+ while (exploreQueue.length > 0) {
5560
+ const { nodeId: node, remainingHops } = exploreQueue.shift();
5561
+ if (remainingHops === 0) continue;
5562
+ const segments = nodeIdToSegmentIds.get(node) || [];
5563
+ for (const segmentId of segments) {
5564
+ const adjacentNodeIds = segmentIdToNodeIds.get(segmentId) || [];
5565
+ for (const adjacentNodeId of adjacentNodeIds) {
5566
+ if (!visitedNodes.has(adjacentNodeId)) {
5567
+ visitedNodes.add(adjacentNodeId);
5568
+ exploreQueue.push({
5569
+ nodeId: adjacentNodeId,
5570
+ remainingHops: remainingHops - 1
5571
+ });
5572
+ }
5472
5573
  }
5473
5574
  }
5474
5575
  }
5475
- return Array.from(nodes);
5576
+ return Array.from(visitedNodes);
5476
5577
  }
5477
5578
 
5478
5579
  // lib/solvers/UnravelSolver/createPointModificationsHash.ts
@@ -6257,7 +6358,7 @@ var getDedupedSegments = (assignedSegments) => {
6257
6358
  const dedupedSegPointMap = /* @__PURE__ */ new Map();
6258
6359
  let highestSegmentId = -1;
6259
6360
  for (const seg of assignedSegments) {
6260
- const segKey = `${seg.start.x}-${seg.start.y}-${seg.end.x}-${seg.end.y}`;
6361
+ const segKey = `${seg.start.x}-${seg.start.y}-${seg.end.x}-${seg.end.y}-${seg.availableZ.join(",")}`;
6261
6362
  const existingSeg = dedupedSegPointMap.get(segKey);
6262
6363
  if (!existingSeg) {
6263
6364
  highestSegmentId++;
@@ -6271,36 +6372,48 @@ var getDedupedSegments = (assignedSegments) => {
6271
6372
  return dedupedSegments;
6272
6373
  };
6273
6374
 
6274
- // lib/utils/getIntraNodeCrossingsFromSegments.ts
6275
- var getIntraNodeCrossingsFromSegments = (segments) => {
6375
+ // lib/solvers/UnravelSolver/calculateCrossingProbabilityOfFailure.ts
6376
+ var calculateNodeProbabilityOfFailure = (node, numSameLayerCrossings, numEntryExitLayerChanges, numTransitionCrossings) => {
6377
+ if (node?._containsTarget) return 0;
6378
+ const totalCapacity = getTunedTotalCapacity1(node);
6379
+ const estNumVias = numSameLayerCrossings * 0.82 + numEntryExitLayerChanges * 0.41 + numTransitionCrossings * 0.2;
6380
+ const estUsedCapacity = (estNumVias / 2) ** 1.1;
6381
+ const approxProb = estUsedCapacity / totalCapacity;
6382
+ return approxProb;
6383
+ };
6384
+
6385
+ // lib/utils/getIntraNodeCrossingsFromSegmentPoints.ts
6386
+ var getIntraNodeCrossingsFromSegmentPoints = (segmentPoints) => {
6276
6387
  let numSameLayerCrossings = 0;
6277
- const pointPairs = [];
6278
- const transitionPairPoints = [];
6279
6388
  let numEntryExitLayerChanges = 0;
6280
- const portPoints = segments.flatMap((seg) => seg.assignedPoints);
6281
- for (const { connectionName: aConnName, point: A } of portPoints) {
6282
- if (pointPairs.some((p) => p.connectionName === aConnName)) {
6283
- continue;
6284
- }
6285
- if (transitionPairPoints.some((p) => p.connectionName === aConnName)) {
6286
- continue;
6389
+ let numTransitionCrossings = 0;
6390
+ const connectionGroups = /* @__PURE__ */ new Map();
6391
+ for (const point of segmentPoints) {
6392
+ if (!connectionGroups.has(point.connectionName)) {
6393
+ connectionGroups.set(point.connectionName, []);
6287
6394
  }
6288
- const pointPair = {
6289
- connectionName: aConnName,
6290
- z: A.z,
6291
- points: [A]
6292
- };
6293
- for (const { connectionName: bConnName, point: B } of portPoints) {
6294
- if (aConnName !== bConnName) continue;
6295
- if (A === B) continue;
6296
- pointPair.points.push(B);
6297
- if (pointPair.points.some((p) => p.z !== pointPair.z)) {
6395
+ connectionGroups.get(point.connectionName).push(point);
6396
+ }
6397
+ const pointPairs = [];
6398
+ const transitionPairPoints = [];
6399
+ for (const [connectionName, points] of connectionGroups.entries()) {
6400
+ if (points.length < 2) continue;
6401
+ const firstPoint = points[0];
6402
+ for (let i = 1; i < points.length; i++) {
6403
+ const secondPoint = points[i];
6404
+ const pointPair = {
6405
+ connectionName,
6406
+ z: firstPoint.z,
6407
+ points: [firstPoint, secondPoint]
6408
+ };
6409
+ if (firstPoint.z !== secondPoint.z) {
6298
6410
  numEntryExitLayerChanges++;
6299
- transitionPairPoints.push(pointPair);
6300
- break;
6411
+ transitionPairPoints.push({
6412
+ connectionName,
6413
+ points: [firstPoint, secondPoint]
6414
+ });
6301
6415
  } else {
6302
6416
  pointPairs.push(pointPair);
6303
- break;
6304
6417
  }
6305
6418
  }
6306
6419
  }
@@ -6318,7 +6431,6 @@ var getIntraNodeCrossingsFromSegments = (segments) => {
6318
6431
  }
6319
6432
  }
6320
6433
  }
6321
- let numTransitionCrossings = 0;
6322
6434
  for (let i = 0; i < transitionPairPoints.length; i++) {
6323
6435
  for (let j = i + 1; j < transitionPairPoints.length; j++) {
6324
6436
  const pair1 = transitionPairPoints[i];
@@ -6354,16 +6466,6 @@ var getIntraNodeCrossingsFromSegments = (segments) => {
6354
6466
  };
6355
6467
  };
6356
6468
 
6357
- // lib/solvers/UnravelSolver/calculateCrossingProbabilityOfFailure.ts
6358
- var calculateNodeProbabilityOfFailure = (node, numSameLayerCrossings, numEntryExitLayerChanges, numTransitionCrossings) => {
6359
- if (node?._containsTarget) return 0;
6360
- const totalCapacity = getTunedTotalCapacity1(node);
6361
- const estNumVias = numSameLayerCrossings * 0.82 + numEntryExitLayerChanges * 0.41 + numTransitionCrossings * 0.2;
6362
- const estUsedCapacity = (estNumVias / 2) ** 1.1;
6363
- const approxProb = estUsedCapacity / totalCapacity;
6364
- return approxProb;
6365
- };
6366
-
6367
6469
  // lib/solvers/UnravelSolver/UnravelMultiSectionSolver.ts
6368
6470
  var UnravelMultiSectionSolver = class extends BaseSolver {
6369
6471
  nodeMap;
@@ -6437,8 +6539,10 @@ var UnravelMultiSectionSolver = class extends BaseSolver {
6437
6539
  numSameLayerCrossings,
6438
6540
  numEntryExitLayerChanges,
6439
6541
  numTransitionCrossings
6440
- } = getIntraNodeCrossingsFromSegments(
6441
- this.nodeIdToSegmentIds.get(node.capacityMeshNodeId)?.map((segId) => this.dedupedSegmentMap.get(segId)) || []
6542
+ } = getIntraNodeCrossingsFromSegmentPoints(
6543
+ (this.nodeToSegmentPointMap.get(node.capacityMeshNodeId) ?? []).map(
6544
+ (segPointId) => this.segmentPointMap.get(segPointId)
6545
+ )
6442
6546
  );
6443
6547
  const probabilityOfFailure = calculateNodeProbabilityOfFailure(
6444
6548
  node,
@@ -6501,12 +6605,18 @@ var UnravelMultiSectionSolver = class extends BaseSolver {
6501
6605
  segmentPoint.y = pointModification.y ?? segmentPoint.y;
6502
6606
  segmentPoint.z = pointModification.z ?? segmentPoint.z;
6503
6607
  }
6504
- }
6505
- for (const nodeId of this.activeSolver.unravelSection.allNodeIds) {
6506
- this.nodePfMap.set(
6507
- nodeId,
6508
- this.computeNodePf(this.nodeMap.get(nodeId))
6509
- );
6608
+ const possiblyImpactedNodeIds = getNodesNearNode({
6609
+ hops: this.activeSolver.MUTABLE_HOPS + 2,
6610
+ nodeId: this.activeSolver.rootNodeId,
6611
+ nodeIdToSegmentIds: this.nodeIdToSegmentIds,
6612
+ segmentIdToNodeIds: this.segmentIdToNodeIds
6613
+ });
6614
+ for (const nodeId of possiblyImpactedNodeIds) {
6615
+ this.nodePfMap.set(
6616
+ nodeId,
6617
+ this.computeNodePf(this.nodeMap.get(nodeId))
6618
+ );
6619
+ }
6510
6620
  }
6511
6621
  this.activeSolver = null;
6512
6622
  }
@@ -7337,7 +7447,7 @@ var CapacityNodeTree = class {
7337
7447
  };
7338
7448
 
7339
7449
  // lib/solvers/SingleLayerNodeMerger/SingleLayerNodeMergerSolver.ts
7340
- var EPSILON2 = 5e-3;
7450
+ var EPSILON3 = 5e-3;
7341
7451
  var SingleLayerNodeMergerSolver = class extends BaseSolver {
7342
7452
  nodeMap;
7343
7453
  currentBatchNodeIds;
@@ -7496,7 +7606,7 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
7496
7606
  adjacentNodesToLeft.reduce((acc, adjNode) => {
7497
7607
  return acc + adjNode.height;
7498
7608
  }, 0) - rootNode.height
7499
- ) < EPSILON2;
7609
+ ) < EPSILON3;
7500
7610
  if (leftAdjNodesTakeUpEntireHeight && leftAdjNodesAreAllSameSize) {
7501
7611
  rootNode.width += leftAdjNodeWidth;
7502
7612
  rootNode.center.x = rootNode.center.x - leftAdjNodeWidth / 2;
@@ -7516,7 +7626,7 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
7516
7626
  adjacentNodesToRight.reduce((acc, adjNode) => {
7517
7627
  return acc + adjNode.height;
7518
7628
  }, 0) - rootNode.height
7519
- ) < EPSILON2;
7629
+ ) < EPSILON3;
7520
7630
  if (rightAdjNodesTakeUpEntireHeight && rightAdjNodesAreAllSameSize) {
7521
7631
  rootNode.width += rightAdjNodeWidth;
7522
7632
  rootNode.center.x = rootNode.center.x + rightAdjNodeWidth / 2;
@@ -7536,7 +7646,7 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
7536
7646
  adjacentNodesToTop.reduce((acc, adjNode) => {
7537
7647
  return acc + adjNode.width;
7538
7648
  }, 0) - rootNode.width
7539
- ) < EPSILON2;
7649
+ ) < EPSILON3;
7540
7650
  if (topAdjNodesTakeUpEntireWidth && topAdjNodesAreAllSameSize) {
7541
7651
  rootNode.height += topAdjNodeHeight;
7542
7652
  rootNode.center.y = rootNode.center.y + topAdjNodeHeight / 2;
@@ -7556,7 +7666,7 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
7556
7666
  adjacentNodesToBottom.reduce((acc, adjNode) => {
7557
7667
  return acc + adjNode.width;
7558
7668
  }, 0) - rootNode.width
7559
- ) < EPSILON2;
7669
+ ) < EPSILON3;
7560
7670
  if (bottomAdjNodesTakeUpEntireWidth && bottomAdjNodesAreAllSameSize) {
7561
7671
  rootNode.height += bottomAdjNodeHeight;
7562
7672
  rootNode.center.y = rootNode.center.y - bottomAdjNodeHeight / 2;
@@ -7750,6 +7860,14 @@ var SingleSimplifiedPathSolver = class extends BaseSolver {
7750
7860
  });
7751
7861
  }
7752
7862
  }
7863
+ if ("filteredObstaclePathSegments" in this) {
7864
+ const filteredObstaclePathSegments = this.filteredObstaclePathSegments;
7865
+ for (const [start, end] of filteredObstaclePathSegments) {
7866
+ graphics.lines.push({
7867
+ points: [start, end]
7868
+ });
7869
+ }
7870
+ }
7753
7871
  return graphics;
7754
7872
  }
7755
7873
  };
@@ -7854,23 +7972,33 @@ var getSegmentBounds = (segment) => {
7854
7972
  };
7855
7973
  };
7856
7974
  var SegmentTree = class {
7975
+ // traceThickness + obstacleMargin
7857
7976
  constructor(segments) {
7858
7977
  this.segments = segments;
7859
7978
  this.buckets = /* @__PURE__ */ new Map();
7979
+ const segmentsById = /* @__PURE__ */ new Map();
7860
7980
  for (const segment of segments) {
7981
+ const segmentKey = this.getSegmentKey(segment);
7982
+ if (segmentsById.has(segmentKey)) continue;
7983
+ segmentsById.set(segmentKey, segment);
7861
7984
  const bounds = getSegmentBounds(segment);
7862
- const bucketMinX = Math.floor(bounds.minX / this.CELL_SIZE) * this.CELL_SIZE;
7863
- const bucketMinY = Math.floor(bounds.minY / this.CELL_SIZE) * this.CELL_SIZE;
7864
- for (let x = bucketMinX; x <= bounds.maxX; x += this.CELL_SIZE) {
7865
- for (let y = bucketMinY; y <= bounds.maxY; y += this.CELL_SIZE) {
7866
- const bucketKey = this.getBucketKey(x, y);
7985
+ const minIndexX = Math.floor(bounds.minX / this.CELL_SIZE);
7986
+ const maxIndexX = Math.floor(bounds.maxX / this.CELL_SIZE);
7987
+ const minIndexY = Math.floor(bounds.minY / this.CELL_SIZE);
7988
+ const maxIndexY = Math.floor(bounds.maxY / this.CELL_SIZE);
7989
+ for (let ix = minIndexX; ix <= maxIndexX; ix++) {
7990
+ for (let iy = minIndexY; iy <= maxIndexY; iy++) {
7991
+ const bucketKey = `${ix}x${iy}`;
7867
7992
  const bucket = this.buckets.get(bucketKey);
7993
+ const segmentWithId = [
7994
+ segment[0],
7995
+ segment[1],
7996
+ segmentKey
7997
+ ];
7868
7998
  if (!bucket) {
7869
- this.buckets.set(bucketKey, [
7870
- [segment[0], segment[1], this.getSegmentKey(segment)]
7871
- ]);
7999
+ this.buckets.set(bucketKey, [segmentWithId]);
7872
8000
  } else {
7873
- bucket.push([segment[0], segment[1], this.getSegmentKey(segment)]);
8001
+ bucket.push(segmentWithId);
7874
8002
  }
7875
8003
  }
7876
8004
  }
@@ -7878,7 +8006,7 @@ var SegmentTree = class {
7878
8006
  }
7879
8007
  buckets;
7880
8008
  CELL_SIZE = 0.4;
7881
- SEGMENT_MARGIN = 0.2;
8009
+ SEGMENT_MARGIN = 0.4;
7882
8010
  getBucketKey(x, y) {
7883
8011
  return `${Math.floor(x / this.CELL_SIZE)}x${Math.floor(y / this.CELL_SIZE)}`;
7884
8012
  }
@@ -7892,17 +8020,22 @@ var SegmentTree = class {
7892
8020
  const minY = Math.min(A.y, B.y) - this.SEGMENT_MARGIN;
7893
8021
  const maxX = Math.max(A.x, B.x) + this.SEGMENT_MARGIN;
7894
8022
  const maxY = Math.max(A.y, B.y) + this.SEGMENT_MARGIN;
7895
- const bucketMinX = Math.floor(minX / this.CELL_SIZE) * this.CELL_SIZE;
7896
- const bucketMinY = Math.floor(minY / this.CELL_SIZE) * this.CELL_SIZE;
7897
- for (let x = bucketMinX; x <= maxX; x += this.CELL_SIZE) {
7898
- for (let y = bucketMinY; y <= maxY; y += this.CELL_SIZE) {
7899
- const bucketKey = this.getBucketKey(x, y);
7900
- const bucket = this.buckets.get(bucketKey) || [];
7901
- for (const segment of bucket) {
7902
- const key = segment[2];
7903
- if (alreadyAddedSegments.has(key)) continue;
7904
- alreadyAddedSegments.add(key);
7905
- segments.push(segment);
8023
+ const minIndexX = Math.floor(minX / this.CELL_SIZE);
8024
+ const maxIndexX = Math.floor(maxX / this.CELL_SIZE);
8025
+ const minIndexY = Math.floor(minY / this.CELL_SIZE);
8026
+ const maxIndexY = Math.floor(maxY / this.CELL_SIZE);
8027
+ for (let ix = minIndexX; ix <= maxIndexX; ix++) {
8028
+ for (let iy = minIndexY; iy <= maxIndexY; iy++) {
8029
+ const bucketKey = `${ix}x${iy}`;
8030
+ const bucket = this.buckets.get(bucketKey);
8031
+ if (bucket) {
8032
+ for (const segment of bucket) {
8033
+ const key = segment[2];
8034
+ if (!alreadyAddedSegments.has(key)) {
8035
+ alreadyAddedSegments.add(key);
8036
+ segments.push(segment);
8037
+ }
8038
+ }
7906
8039
  }
7907
8040
  }
7908
8041
  }