@tscircuit/capacity-autorouter 0.0.41 → 0.0.43

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
@@ -68,6 +68,9 @@ var BaseSolver = class {
68
68
  }
69
69
  _step() {
70
70
  }
71
+ getConstructorParams() {
72
+ throw new Error("getConstructorParams not implemented");
73
+ }
71
74
  solve() {
72
75
  const startTime = Date.now();
73
76
  while (!this.solved && !this.failed) {
@@ -1938,6 +1941,86 @@ function distance(p1, p2) {
1938
1941
  return Math.sqrt(dx * dx + dy * dy);
1939
1942
  }
1940
1943
 
1944
+ // node_modules/@tscircuit/math-utils/dist/chunk-MHHTZHOJ.js
1945
+ function getBoundingBox(box) {
1946
+ const halfWidth = box.width / 2;
1947
+ const halfHeight = box.height / 2;
1948
+ return {
1949
+ minX: box.center.x - halfWidth,
1950
+ maxX: box.center.x + halfWidth,
1951
+ minY: box.center.y - halfHeight,
1952
+ maxY: box.center.y + halfHeight
1953
+ };
1954
+ }
1955
+ function computeDistanceBetweenBoxes(boxA, boxB) {
1956
+ const a = getBoundingBox(boxA);
1957
+ const b = getBoundingBox(boxB);
1958
+ const dx = Math.max(a.minX - b.maxX, b.minX - a.maxX, 0);
1959
+ const dy = Math.max(a.minY - b.maxY, b.minY - a.maxY, 0);
1960
+ const pointA = { x: 0, y: 0 };
1961
+ const pointB = { x: 0, y: 0 };
1962
+ if (dx === 0 && dy === 0) {
1963
+ return { distance: 0, pointA: boxA.center, pointB: boxB.center };
1964
+ }
1965
+ pointA.x = clamp(boxA.center.x, b.minX, b.maxX);
1966
+ pointA.y = clamp(boxA.center.y, b.minY, b.maxY);
1967
+ pointB.x = clamp(boxB.center.x, a.minX, a.maxX);
1968
+ pointB.y = clamp(boxB.center.y, a.minY, a.maxY);
1969
+ const distance6 = Math.hypot(pointA.x - pointB.x, pointA.y - pointB.y);
1970
+ return { distance: distance6, pointA, pointB };
1971
+ }
1972
+ function clamp(value, min, max) {
1973
+ return Math.max(min, Math.min(max, value));
1974
+ }
1975
+
1976
+ // node_modules/@tscircuit/math-utils/dist/chunk-OMVVSGKD.js
1977
+ function segmentToBoundsMinDistance(a, b, bounds) {
1978
+ const topLeft = { x: bounds.minX, y: bounds.minY };
1979
+ const topRight = { x: bounds.maxX, y: bounds.minY };
1980
+ const bottomLeft = { x: bounds.minX, y: bounds.maxY };
1981
+ const bottomRight = { x: bounds.maxX, y: bounds.maxY };
1982
+ if (doSegmentsIntersect(a, b, topLeft, topRight) || doSegmentsIntersect(a, b, topRight, bottomRight) || doSegmentsIntersect(a, b, bottomRight, bottomLeft) || doSegmentsIntersect(a, b, bottomLeft, topLeft)) {
1983
+ return 0;
1984
+ }
1985
+ if (a.x >= bounds.minX && a.x <= bounds.maxX && a.y >= bounds.minY && a.y <= bounds.maxY && b.x >= bounds.minX && b.x <= bounds.maxX && b.y >= bounds.minY && b.y <= bounds.maxY) {
1986
+ return 0;
1987
+ }
1988
+ const distances = [
1989
+ pointToSegmentDistance(topLeft, a, b),
1990
+ pointToSegmentDistance(topRight, a, b),
1991
+ pointToSegmentDistance(bottomLeft, a, b),
1992
+ pointToSegmentDistance(bottomRight, a, b)
1993
+ ];
1994
+ if (a.x >= bounds.minX && a.x <= bounds.maxX && a.y >= bounds.minY && a.y <= bounds.maxY) {
1995
+ return 0;
1996
+ }
1997
+ if (b.x >= bounds.minX && b.x <= bounds.maxX && b.y >= bounds.minY && b.y <= bounds.maxY) {
1998
+ return 0;
1999
+ }
2000
+ if (a.x < bounds.minX || a.x > bounds.maxX || a.y < bounds.minY || a.y > bounds.maxY) {
2001
+ const closestX = clamp(a.x, bounds.minX, bounds.maxX);
2002
+ const closestY = clamp(a.y, bounds.minY, bounds.maxY);
2003
+ distances.push(distance(a, { x: closestX, y: closestY }));
2004
+ }
2005
+ if (b.x < bounds.minX || b.x > bounds.maxX || b.y < bounds.minY || b.y > bounds.maxY) {
2006
+ const closestX = clamp(b.x, bounds.minX, bounds.maxX);
2007
+ const closestY = clamp(b.y, bounds.minY, bounds.maxY);
2008
+ distances.push(distance(b, { x: closestX, y: closestY }));
2009
+ }
2010
+ return Math.min(...distances);
2011
+ }
2012
+ function segmentToBoxMinDistance(a, b, box) {
2013
+ const halfWidth = box.width / 2;
2014
+ const halfHeight = box.height / 2;
2015
+ const bounds = {
2016
+ minX: box.center.x - halfWidth,
2017
+ maxX: box.center.x + halfWidth,
2018
+ minY: box.center.y - halfHeight,
2019
+ maxY: box.center.y + halfHeight
2020
+ };
2021
+ return segmentToBoundsMinDistance(a, b, bounds);
2022
+ }
2023
+
1941
2024
  // lib/data-structures/SingleRouteCandidatePriorityQueue.ts
1942
2025
  var SingleRouteCandidatePriorityQueue = class {
1943
2026
  heap = [];
@@ -3567,6 +3650,709 @@ var TwoCrossingRoutesHighDensitySolver = class extends BaseSolver {
3567
3650
  }
3568
3651
  };
3569
3652
 
3653
+ // lib/utils/findClosestPointToABCWithinBounds.ts
3654
+ function findClosestPointToABCWithinBounds(A, B, C, radius, bounds) {
3655
+ const avgPoint = {
3656
+ x: (A.x + B.x + C.x) / 3,
3657
+ y: (A.y + B.y + C.y) / 3
3658
+ };
3659
+ const distance6 = (p1, p2) => {
3660
+ return Math.sqrt((p2.x - p1.x) ** 2 + (p2.y - p1.y) ** 2);
3661
+ };
3662
+ const isValidPoint = (point) => {
3663
+ const distToA = distance6(point, A);
3664
+ const distToB = distance6(point, B);
3665
+ const distToC = distance6(point, C);
3666
+ const withinBounds = point.x >= bounds.minX && point.x <= bounds.maxX && point.y >= bounds.minY && point.y <= bounds.maxY;
3667
+ return distToA >= radius && distToB >= radius && distToC >= radius && withinBounds;
3668
+ };
3669
+ const isOnBoundary = (point) => {
3670
+ const epsilon = 1e-6;
3671
+ return Math.abs(point.x - bounds.minX) < epsilon || Math.abs(point.x - bounds.maxX) < epsilon || Math.abs(point.y - bounds.minY) < epsilon || Math.abs(point.y - bounds.maxY) < epsilon;
3672
+ };
3673
+ if (isValidPoint(avgPoint)) {
3674
+ return avgPoint;
3675
+ }
3676
+ const pointOnCircle = (center, constraint, r) => {
3677
+ const vx = center.x - constraint.x;
3678
+ const vy = center.y - constraint.y;
3679
+ const dist = Math.sqrt(vx * vx + vy * vy);
3680
+ if (dist < 1e-10) {
3681
+ return { x: constraint.x + r, y: constraint.y };
3682
+ }
3683
+ return {
3684
+ x: constraint.x + vx / dist * r,
3685
+ y: constraint.y + vy / dist * r
3686
+ };
3687
+ };
3688
+ const findCircleIntersections = (c1, c2, r) => {
3689
+ const dx = c2.x - c1.x;
3690
+ const dy = c2.y - c1.y;
3691
+ const dist = Math.sqrt(dx * dx + dy * dy);
3692
+ if (dist > 2 * r - 1e-10 || dist < 1e-10) {
3693
+ return [];
3694
+ }
3695
+ const a = dist * dist / (2 * dist);
3696
+ const h = Math.sqrt(Math.max(0, r * r - a * a));
3697
+ const midX = c1.x + dx * a / dist;
3698
+ const midY = c1.y + dy * a / dist;
3699
+ const intersection1 = {
3700
+ x: midX + h * dy / dist,
3701
+ y: midY - h * dx / dist
3702
+ };
3703
+ const intersection2 = {
3704
+ x: midX - h * dy / dist,
3705
+ y: midY + h * dx / dist
3706
+ };
3707
+ const result = [];
3708
+ const epsilon = 1e-6;
3709
+ if (Math.abs(distance6(intersection1, c1) - r) < epsilon && Math.abs(distance6(intersection1, c2) - r) < epsilon) {
3710
+ result.push(intersection1);
3711
+ }
3712
+ if (Math.abs(distance6(intersection2, c1) - r) < epsilon && Math.abs(distance6(intersection2, c2) - r) < epsilon) {
3713
+ result.push(intersection2);
3714
+ }
3715
+ return result;
3716
+ };
3717
+ const candidateA = pointOnCircle(avgPoint, A, radius);
3718
+ const candidateB = pointOnCircle(avgPoint, B, radius);
3719
+ const candidateC = pointOnCircle(avgPoint, C, radius);
3720
+ const intersectionsAB = findCircleIntersections(A, B, radius);
3721
+ const intersectionsBC = findCircleIntersections(B, C, radius);
3722
+ const intersectionsCA = findCircleIntersections(C, A, radius);
3723
+ const allCandidates = [
3724
+ candidateA,
3725
+ candidateB,
3726
+ candidateC,
3727
+ ...intersectionsAB,
3728
+ ...intersectionsBC,
3729
+ ...intersectionsCA
3730
+ ];
3731
+ const validCandidates = allCandidates.filter(isValidPoint);
3732
+ if (validCandidates.length > 0) {
3733
+ const interiorCandidates = validCandidates.filter((p) => !isOnBoundary(p));
3734
+ if (interiorCandidates.length > 0) {
3735
+ interiorCandidates.sort(
3736
+ (a, b) => distance6(a, avgPoint) - distance6(b, avgPoint)
3737
+ );
3738
+ return interiorCandidates[0];
3739
+ }
3740
+ }
3741
+ const gridStep = 5;
3742
+ let bestPoint = null;
3743
+ let bestDistance = Infinity;
3744
+ for (let x = bounds.minX + 1; x < bounds.maxX; x += gridStep) {
3745
+ for (let y = bounds.minY + 1; y < bounds.maxY; y += gridStep) {
3746
+ const point = { x, y };
3747
+ if (isValidPoint(point)) {
3748
+ const dist = distance6(point, avgPoint);
3749
+ if (dist < bestDistance) {
3750
+ bestDistance = dist;
3751
+ bestPoint = point;
3752
+ }
3753
+ }
3754
+ }
3755
+ }
3756
+ if (bestPoint !== null) {
3757
+ return bestPoint;
3758
+ }
3759
+ const numSamples = 100;
3760
+ const boundaryPoints = [];
3761
+ for (let i = 0; i <= numSamples; i++) {
3762
+ const t = i / numSamples;
3763
+ boundaryPoints.push({
3764
+ x: bounds.minX + t * (bounds.maxX - bounds.minX),
3765
+ y: bounds.minY
3766
+ });
3767
+ boundaryPoints.push({
3768
+ x: bounds.maxX,
3769
+ y: bounds.minY + t * (bounds.maxY - bounds.minY)
3770
+ });
3771
+ boundaryPoints.push({
3772
+ x: bounds.maxX - t * (bounds.maxX - bounds.minX),
3773
+ y: bounds.maxY
3774
+ });
3775
+ boundaryPoints.push({
3776
+ x: bounds.minX,
3777
+ y: bounds.maxY - t * (bounds.maxY - bounds.minY)
3778
+ });
3779
+ }
3780
+ const validBoundaryPoints = boundaryPoints.filter(isValidPoint);
3781
+ if (validBoundaryPoints.length > 0) {
3782
+ validBoundaryPoints.sort(
3783
+ (a, b) => distance6(a, avgPoint) - distance6(b, avgPoint)
3784
+ );
3785
+ return validBoundaryPoints[0];
3786
+ }
3787
+ let minViolation = Infinity;
3788
+ let leastBadPoint = { x: bounds.minX, y: bounds.minY };
3789
+ for (const point of [...allCandidates, ...boundaryPoints]) {
3790
+ if (point.x >= bounds.minX && point.x <= bounds.maxX && point.y >= bounds.minY && point.y <= bounds.maxY) {
3791
+ const violationA = Math.max(0, radius - distance6(point, A));
3792
+ const violationB = Math.max(0, radius - distance6(point, B));
3793
+ const violationC = Math.max(0, radius - distance6(point, C));
3794
+ const totalViolation = violationA + violationB + violationC;
3795
+ if (totalViolation < minViolation) {
3796
+ minViolation = totalViolation;
3797
+ leastBadPoint = point;
3798
+ }
3799
+ }
3800
+ }
3801
+ return leastBadPoint;
3802
+ }
3803
+
3804
+ // lib/utils/findPointToGetAroundCircle.ts
3805
+ function findPointToGetAroundCircle(A, C, Q) {
3806
+ const B = computeTangentPoint(C, A, Q.center, Q.radius);
3807
+ const D = computeTangentPoint(A, C, Q.center, Q.radius);
3808
+ const distBC = distance2(B, C);
3809
+ const distAD = distance2(A, D);
3810
+ const minDistThreshold = 1e-6;
3811
+ const BIsValid = distBC > minDistThreshold;
3812
+ const DIsValid = distAD > minDistThreshold;
3813
+ let E;
3814
+ if (!BIsValid || !DIsValid) {
3815
+ const midAC = {
3816
+ x: (A.x + C.x) / 2,
3817
+ y: (A.y + C.y) / 2
3818
+ };
3819
+ const distFromCenter = distance2(midAC, Q.center);
3820
+ if (distFromCenter < Q.radius * 1.1) {
3821
+ const dirFromCenter = {
3822
+ x: (midAC.x - Q.center.x) / distFromCenter,
3823
+ y: (midAC.y - Q.center.y) / distFromCenter
3824
+ };
3825
+ E = {
3826
+ x: Q.center.x + dirFromCenter.x * Q.radius * 1.2,
3827
+ y: Q.center.y + dirFromCenter.y * Q.radius * 1.2
3828
+ };
3829
+ } else {
3830
+ E = midAC;
3831
+ }
3832
+ } else {
3833
+ E = {
3834
+ x: (B.x + D.x) / 2,
3835
+ y: (B.y + D.y) / 2
3836
+ };
3837
+ const distBE = distance2(B, E);
3838
+ const distDE = distance2(D, E);
3839
+ if (Math.abs(distBE - distDE) > Math.min(distBE, distDE) * 0.5) {
3840
+ const distAB = distance2(A, B);
3841
+ const distCD = distance2(C, D);
3842
+ const totalDist = distAB + distCD;
3843
+ if (totalDist > minDistThreshold) {
3844
+ const weightB = distCD / totalDist;
3845
+ const weightD = distAB / totalDist;
3846
+ E = {
3847
+ x: B.x * weightB + D.x * weightD,
3848
+ y: B.y * weightB + D.y * weightD
3849
+ };
3850
+ }
3851
+ }
3852
+ const distEToCenter = distance2(E, Q.center);
3853
+ if (distEToCenter < Q.radius * 1.05) {
3854
+ const dirFromCenter = {
3855
+ x: (E.x - Q.center.x) / distEToCenter,
3856
+ y: (E.y - Q.center.y) / distEToCenter
3857
+ };
3858
+ E = {
3859
+ x: Q.center.x + dirFromCenter.x * Q.radius * 1.2,
3860
+ y: Q.center.y + dirFromCenter.y * Q.radius * 1.2
3861
+ };
3862
+ }
3863
+ }
3864
+ return { B, D, E };
3865
+ }
3866
+ function computeTangentPoint(observationPoint, referencePoint, circleCenter, radius) {
3867
+ const CQ = [
3868
+ circleCenter.x - observationPoint.x,
3869
+ circleCenter.y - observationPoint.y
3870
+ ];
3871
+ const CQLength = Math.sqrt(CQ[0] * CQ[0] + CQ[1] * CQ[1]);
3872
+ if (CQLength <= radius) {
3873
+ if (CQLength < 1e-8) {
3874
+ const refVec = [
3875
+ referencePoint.x - observationPoint.x,
3876
+ referencePoint.y - observationPoint.y
3877
+ ];
3878
+ const refLength = Math.sqrt(refVec[0] * refVec[0] + refVec[1] * refVec[1]);
3879
+ if (refLength < 1e-8) {
3880
+ return {
3881
+ x: circleCenter.x + radius,
3882
+ y: circleCenter.y
3883
+ };
3884
+ }
3885
+ return {
3886
+ x: circleCenter.x + refVec[0] / refLength * radius,
3887
+ y: circleCenter.y + refVec[1] / refLength * radius
3888
+ };
3889
+ }
3890
+ const CQUnit2 = [CQ[0] / CQLength, CQ[1] / CQLength];
3891
+ return {
3892
+ x: circleCenter.x - CQUnit2[0] * radius,
3893
+ y: circleCenter.y - CQUnit2[1] * radius
3894
+ };
3895
+ }
3896
+ const CR = [
3897
+ referencePoint.x - observationPoint.x,
3898
+ referencePoint.y - observationPoint.y
3899
+ ];
3900
+ const d = Math.sqrt(CQLength * CQLength - radius * radius);
3901
+ const CQUnit = [CQ[0] / CQLength, CQ[1] / CQLength];
3902
+ const perp1 = [-CQUnit[1], CQUnit[0]];
3903
+ const perp2 = [CQUnit[1], -CQUnit[0]];
3904
+ const dot1 = CR[0] * perp1[0] + CR[1] * perp1[1];
3905
+ const dot2 = CR[0] * perp2[0] + CR[1] * perp2[1];
3906
+ const perp = dot1 > dot2 ? perp1 : perp2;
3907
+ const sinTheta = radius / CQLength;
3908
+ const cosTheta = d / CQLength;
3909
+ const unitToTangent = [
3910
+ CQUnit[0] * cosTheta + perp[0] * sinTheta,
3911
+ CQUnit[1] * cosTheta + perp[1] * sinTheta
3912
+ ];
3913
+ return {
3914
+ x: observationPoint.x + d * unitToTangent[0],
3915
+ y: observationPoint.y + d * unitToTangent[1]
3916
+ };
3917
+ }
3918
+ function distance2(p1, p2) {
3919
+ const dx = p2.x - p1.x;
3920
+ const dy = p2.y - p1.y;
3921
+ return Math.sqrt(dx * dx + dy * dy);
3922
+ }
3923
+
3924
+ // lib/solvers/HighDensitySolver/TwoRouteHighDensitySolver/calculateSideTraversal.ts
3925
+ var EPSILON = 1e-9;
3926
+ function calculateSegmentTraversal(startPoint, endPoint, bounds) {
3927
+ const startAngle = pointToAngle(startPoint, bounds);
3928
+ let endAngle = pointToAngle(endPoint, bounds);
3929
+ if (endAngle < startAngle) {
3930
+ endAngle += 2 * Math.PI;
3931
+ }
3932
+ if (Math.abs(endAngle - startAngle) < EPSILON) {
3933
+ return { left: 0, top: 0, right: 0, bottom: 0 };
3934
+ }
3935
+ return calculateSidePercentages(startAngle, endAngle, bounds);
3936
+ }
3937
+ function calculateTraversalPercentages(A, B, C, bounds) {
3938
+ const percentagesAB = calculateSegmentTraversal(A, B, bounds);
3939
+ const percentagesBC = calculateSegmentTraversal(B, C, bounds);
3940
+ const totalPercentages = {
3941
+ left: Math.min(1, percentagesAB.left + percentagesBC.left),
3942
+ top: Math.min(1, percentagesAB.top + percentagesBC.top),
3943
+ right: Math.min(1, percentagesAB.right + percentagesBC.right),
3944
+ bottom: Math.min(1, percentagesAB.bottom + percentagesBC.bottom)
3945
+ };
3946
+ for (const key in totalPercentages) {
3947
+ if (Math.abs(totalPercentages[key]) < EPSILON) {
3948
+ totalPercentages[key] = 0;
3949
+ }
3950
+ }
3951
+ return totalPercentages;
3952
+ }
3953
+ function pointToAngle(point, bounds) {
3954
+ const width = bounds.maxX - bounds.minX;
3955
+ const height = bounds.maxY - bounds.minY;
3956
+ if (width < EPSILON && height < EPSILON) return 0;
3957
+ const perimeter = 2 * (width + height);
3958
+ if (perimeter < EPSILON) return 0;
3959
+ let distance6 = 0;
3960
+ if (Math.abs(point.y - bounds.maxY) < EPSILON && point.x >= bounds.minX - EPSILON && point.x <= bounds.maxX + EPSILON) {
3961
+ 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) {
3963
+ 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) {
3965
+ 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) {
3967
+ distance6 = width + height + width + Math.max(0, Math.min(height, point.y - bounds.minY));
3968
+ } else {
3969
+ throw new Error(
3970
+ `Point (${point.x}, ${point.y}) does not lie on the boundary defined by ${JSON.stringify(bounds)}`
3971
+ );
3972
+ }
3973
+ distance6 = Math.max(0, Math.min(perimeter, distance6));
3974
+ return perimeter > EPSILON ? distance6 / perimeter * (2 * Math.PI) : 0;
3975
+ }
3976
+ function calculateSidePercentages(startAngle, endAngle, bounds) {
3977
+ const width = bounds.maxX - bounds.minX;
3978
+ const height = bounds.maxY - bounds.minY;
3979
+ if (width < EPSILON && height < EPSILON)
3980
+ return { left: 0, top: 0, right: 0, bottom: 0 };
3981
+ const perimeter = 2 * (width + height);
3982
+ if (perimeter < EPSILON) return { left: 0, top: 0, right: 0, bottom: 0 };
3983
+ const angleTopEnd = width / perimeter * (2 * Math.PI);
3984
+ const angleRightEnd = (width + height) / perimeter * (2 * Math.PI);
3985
+ const angleBottomEnd = (width + width + height) / perimeter * (2 * Math.PI);
3986
+ const angleLeftEnd = 2 * Math.PI;
3987
+ const sides = [
3988
+ { name: "top", start: 0, end: angleTopEnd, length: width },
3989
+ { name: "right", start: angleTopEnd, end: angleRightEnd, length: height },
3990
+ {
3991
+ name: "bottom",
3992
+ start: angleRightEnd,
3993
+ end: angleBottomEnd,
3994
+ length: width
3995
+ },
3996
+ { name: "left", start: angleBottomEnd, end: angleLeftEnd, length: height }
3997
+ // Ends at 2PI
3998
+ ];
3999
+ const result = { left: 0, top: 0, right: 0, bottom: 0 };
4000
+ const totalAngleTraversal = endAngle - startAngle;
4001
+ if (totalAngleTraversal < EPSILON) return result;
4002
+ for (const side of sides) {
4003
+ const sideAngleRange = side.end - side.start;
4004
+ if (sideAngleRange < EPSILON || side.length < EPSILON) continue;
4005
+ const overlapStart = Math.max(startAngle, side.start);
4006
+ const overlapEnd = Math.min(endAngle, side.end);
4007
+ if (overlapStart < overlapEnd - EPSILON) {
4008
+ const traversedAngleOnSide = overlapEnd - overlapStart;
4009
+ const percentage = traversedAngleOnSide / sideAngleRange;
4010
+ result[side.name] += Math.max(0, percentage);
4011
+ }
4012
+ }
4013
+ for (const key in result) {
4014
+ result[key] = Math.max(
4015
+ 0,
4016
+ Math.min(1, result[key])
4017
+ );
4018
+ }
4019
+ return result;
4020
+ }
4021
+
4022
+ // lib/solvers/HighDensitySolver/TwoRouteHighDensitySolver/SingleTransitionCrossingRouteSolver.ts
4023
+ var SingleTransitionCrossingRouteSolver = class extends BaseSolver {
4024
+ // Input parameters
4025
+ nodeWithPortPoints;
4026
+ routes;
4027
+ // Configuration parameters
4028
+ viaDiameter;
4029
+ traceThickness;
4030
+ obstacleMargin;
4031
+ layerCount = 2;
4032
+ debugViaPositions;
4033
+ // Solution state
4034
+ solvedRoutes = [];
4035
+ // Bounds
4036
+ bounds;
4037
+ constructor(params) {
4038
+ super();
4039
+ this.nodeWithPortPoints = params.nodeWithPortPoints;
4040
+ this.viaDiameter = params?.viaDiameter ?? 0.6;
4041
+ this.traceThickness = params?.traceThickness ?? 0.15;
4042
+ this.obstacleMargin = params?.obstacleMargin ?? 0.1;
4043
+ this.layerCount = params?.layerCount ?? 2;
4044
+ this.debugViaPositions = [];
4045
+ this.routes = this.extractRoutesFromNode();
4046
+ this.bounds = this.calculateBounds();
4047
+ if (this.routes.length !== 2) {
4048
+ this.failed = true;
4049
+ return;
4050
+ }
4051
+ const [routeA, routeB] = this.routes;
4052
+ const routeAHasTransition = routeA.A.z !== routeA.B.z;
4053
+ const routeBHasTransition = routeB.A.z !== routeB.B.z;
4054
+ if (routeAHasTransition && routeBHasTransition || !routeAHasTransition && !routeBHasTransition) {
4055
+ this.failed = true;
4056
+ return;
4057
+ }
4058
+ }
4059
+ /**
4060
+ * Extract routes that need to be connected from the node data
4061
+ */
4062
+ extractRoutesFromNode() {
4063
+ const routes = [];
4064
+ const connectedPorts = this.nodeWithPortPoints.portPoints;
4065
+ const connectionGroups = /* @__PURE__ */ new Map();
4066
+ for (const connectedPort of connectedPorts) {
4067
+ const { connectionName } = connectedPort;
4068
+ if (!connectionGroups.has(connectionName)) {
4069
+ connectionGroups.set(connectionName, []);
4070
+ }
4071
+ connectionGroups.get(connectionName)?.push(connectedPort);
4072
+ }
4073
+ for (const [connectionName, points] of connectionGroups.entries()) {
4074
+ if (points.length === 2) {
4075
+ routes.push({
4076
+ A: { ...points[0], z: points[0].z ?? 0 },
4077
+ B: { ...points[1], z: points[1].z ?? 0 },
4078
+ connectionName
4079
+ });
4080
+ }
4081
+ }
4082
+ return routes;
4083
+ }
4084
+ /**
4085
+ * Calculate the bounding box of the node
4086
+ */
4087
+ calculateBounds() {
4088
+ return {
4089
+ minX: this.nodeWithPortPoints.center.x - this.nodeWithPortPoints.width / 2,
4090
+ maxX: this.nodeWithPortPoints.center.x + this.nodeWithPortPoints.width / 2,
4091
+ minY: this.nodeWithPortPoints.center.y - this.nodeWithPortPoints.height / 2,
4092
+ maxY: this.nodeWithPortPoints.center.y + this.nodeWithPortPoints.height / 2
4093
+ };
4094
+ }
4095
+ /**
4096
+ * Check if two routes are crossing
4097
+ */
4098
+ doRoutesCross(routeA, routeB) {
4099
+ return doSegmentsIntersect(routeA.A, routeA.B, routeB.A, routeB.B);
4100
+ }
4101
+ calculateViaPosition(transitionRoute, flatRoute) {
4102
+ const flatRouteZ = flatRoute.A.z;
4103
+ const ntrP1 = transitionRoute.A.z !== flatRouteZ ? transitionRoute.A : transitionRoute.B;
4104
+ const marginFromBorderWithTrace = this.obstacleMargin * 2 + this.viaDiameter / 2 + this.traceThickness;
4105
+ const marginFromBorderWithoutTrace = this.obstacleMargin + this.viaDiameter / 2;
4106
+ const A = flatRoute.A;
4107
+ const B = ntrP1;
4108
+ const C = flatRoute.B;
4109
+ const sideTraversal = calculateTraversalPercentages(A, B, C, this.bounds);
4110
+ const viaBounds = {
4111
+ minX: this.bounds.minX + (sideTraversal.left > 0.5 ? marginFromBorderWithTrace : marginFromBorderWithoutTrace),
4112
+ minY: this.bounds.minY + (sideTraversal.bottom > 0.5 ? marginFromBorderWithTrace : marginFromBorderWithoutTrace),
4113
+ maxX: this.bounds.maxX - (sideTraversal.right > 0.5 ? marginFromBorderWithTrace : marginFromBorderWithoutTrace),
4114
+ maxY: this.bounds.maxY - (sideTraversal.top > 0.5 ? marginFromBorderWithTrace : marginFromBorderWithoutTrace)
4115
+ };
4116
+ if (viaBounds.maxY < viaBounds.minY) {
4117
+ viaBounds.minY = (viaBounds.minY + viaBounds.maxY) / 2;
4118
+ viaBounds.maxY = viaBounds.minY;
4119
+ }
4120
+ if (viaBounds.maxX < viaBounds.minX) {
4121
+ viaBounds.minX = (viaBounds.minX + viaBounds.maxX) / 2;
4122
+ viaBounds.maxX = viaBounds.minX;
4123
+ }
4124
+ return findClosestPointToABCWithinBounds(
4125
+ A,
4126
+ B,
4127
+ C,
4128
+ marginFromBorderWithTrace,
4129
+ viaBounds
4130
+ );
4131
+ }
4132
+ /**
4133
+ * Create a single transition route with properly placed via
4134
+ */
4135
+ createTransitionRoute(start, end, via, connectionName) {
4136
+ const route = [
4137
+ { x: start.x, y: start.y, z: start.z ?? 0 },
4138
+ { x: via.x, y: via.y, z: start.z ?? 0 },
4139
+ { x: via.x, y: via.y, z: end.z ?? 0 },
4140
+ { x: end.x, y: end.y, z: end.z ?? 0 }
4141
+ ];
4142
+ return {
4143
+ connectionName,
4144
+ route,
4145
+ traceThickness: this.traceThickness,
4146
+ viaDiameter: this.viaDiameter,
4147
+ vias: [via]
4148
+ };
4149
+ }
4150
+ /**
4151
+ * Create the non-transition route
4152
+ */
4153
+ createFlatRoute(flatStart, flatEnd, via, otherRouteStart, otherRouteEnd, flatRouteConnectionName) {
4154
+ const ntrP1 = otherRouteStart.z !== flatStart.z ? otherRouteStart : otherRouteEnd;
4155
+ const middle = (a, b) => {
4156
+ return {
4157
+ x: (a.x + b.x) / 2,
4158
+ y: (a.y + b.y) / 2
4159
+ };
4160
+ };
4161
+ const middleWithMargin = (a, aMargin, b, bMargin) => {
4162
+ const dx = b.x - a.x;
4163
+ const dy = b.y - a.y;
4164
+ const effectiveA = {
4165
+ x: a.x + dx * aMargin,
4166
+ y: a.y + dy * aMargin
4167
+ };
4168
+ const effectiveB = {
4169
+ x: b.x - dx * bMargin,
4170
+ y: b.y - dy * bMargin
4171
+ };
4172
+ return middle(effectiveA, effectiveB);
4173
+ };
4174
+ const traceBounds = {
4175
+ maxX: this.bounds.maxX - this.obstacleMargin - this.traceThickness / 2,
4176
+ maxY: this.bounds.maxY - this.obstacleMargin - this.traceThickness / 2,
4177
+ minX: this.bounds.minX + this.obstacleMargin + this.traceThickness / 2,
4178
+ minY: this.bounds.minY + this.obstacleMargin + this.traceThickness / 2
4179
+ };
4180
+ const minDistFromViaToTrace = this.viaDiameter / 2 + this.traceThickness / 2 + this.obstacleMargin;
4181
+ const p2 = middleWithMargin(
4182
+ via,
4183
+ this.viaDiameter,
4184
+ otherRouteStart.z !== flatStart.z ? otherRouteStart : otherRouteEnd,
4185
+ this.traceThickness
4186
+ );
4187
+ const p1 = findPointToGetAroundCircle(flatStart, p2, {
4188
+ center: { x: via.x, y: via.y },
4189
+ radius: minDistFromViaToTrace
4190
+ }).E;
4191
+ const p3 = findPointToGetAroundCircle(p2, flatEnd, {
4192
+ center: { x: via.x, y: via.y },
4193
+ radius: minDistFromViaToTrace
4194
+ }).E;
4195
+ const p1IsNeeded = pointToSegmentDistance(via, flatStart, p2) < minDistFromViaToTrace;
4196
+ const p3IsNeeded = pointToSegmentDistance(via, p2, flatEnd) < minDistFromViaToTrace;
4197
+ return {
4198
+ connectionName: flatRouteConnectionName,
4199
+ route: [
4200
+ { x: flatStart.x, y: flatStart.y, z: flatStart.z ?? 0 },
4201
+ ...p1IsNeeded ? [{ x: p1.x, y: p1.y, z: flatStart.z ?? 0 }] : [],
4202
+ { x: p2.x, y: p2.y, z: flatStart.z ?? 0 },
4203
+ ...p3IsNeeded ? [{ x: p3.x, y: p3.y, z: flatStart.z ?? 0 }] : [],
4204
+ { x: flatEnd.x, y: flatEnd.y, z: flatEnd.z ?? 0 }
4205
+ ],
4206
+ traceThickness: this.traceThickness,
4207
+ viaDiameter: this.viaDiameter,
4208
+ vias: []
4209
+ };
4210
+ }
4211
+ /**
4212
+ * Try to solve with one route having a transition and the other staying flat
4213
+ */
4214
+ trySolve() {
4215
+ const [routeA, routeB] = this.routes;
4216
+ const routeAHasTransition = routeA.A.z !== routeA.B.z;
4217
+ const transitionRoute = routeAHasTransition ? routeA : routeB;
4218
+ const flatRoute = routeAHasTransition ? routeB : routeA;
4219
+ const viaPosition = this.calculateViaPosition(transitionRoute, flatRoute);
4220
+ if (viaPosition) {
4221
+ this.debugViaPositions.push({ via: viaPosition });
4222
+ } else {
4223
+ return false;
4224
+ }
4225
+ const transitionRouteSolution = this.createTransitionRoute(
4226
+ transitionRoute.A,
4227
+ transitionRoute.B,
4228
+ viaPosition,
4229
+ transitionRoute.connectionName
4230
+ );
4231
+ const flatRouteSolution = this.createFlatRoute(
4232
+ flatRoute.A,
4233
+ flatRoute.B,
4234
+ viaPosition,
4235
+ transitionRoute.A,
4236
+ transitionRoute.B,
4237
+ flatRoute.connectionName
4238
+ );
4239
+ this.solvedRoutes.push(transitionRouteSolution, flatRouteSolution);
4240
+ return true;
4241
+ }
4242
+ /**
4243
+ * Main step method that attempts to solve the routes
4244
+ */
4245
+ _step() {
4246
+ if (!this.doRoutesCross(this.routes[0], this.routes[1])) {
4247
+ this.failed = true;
4248
+ this.error = "Can only solve routes that have a single transition crossing";
4249
+ return;
4250
+ }
4251
+ if (this.trySolve()) {
4252
+ this.solved = true;
4253
+ return;
4254
+ }
4255
+ this.failed = true;
4256
+ }
4257
+ /**
4258
+ * Visualization for debugging
4259
+ */
4260
+ visualize() {
4261
+ const graphics = {
4262
+ lines: [],
4263
+ points: [],
4264
+ rects: [],
4265
+ circles: []
4266
+ };
4267
+ graphics.rects.push({
4268
+ center: {
4269
+ x: (this.bounds.minX + this.bounds.maxX) / 2,
4270
+ y: (this.bounds.minY + this.bounds.maxY) / 2
4271
+ },
4272
+ width: this.bounds.maxX - this.bounds.minX,
4273
+ height: this.bounds.maxY - this.bounds.minY,
4274
+ stroke: "rgba(0, 0, 0, 0.5)",
4275
+ fill: "rgba(240, 240, 240, 0.1)",
4276
+ label: "PCB Bounds"
4277
+ });
4278
+ for (const route of this.routes) {
4279
+ graphics.points.push({
4280
+ x: route.A.x,
4281
+ y: route.A.y,
4282
+ label: `${route.connectionName} start (z=${route.A.z})`,
4283
+ color: "orange"
4284
+ });
4285
+ graphics.points.push({
4286
+ x: route.B.x,
4287
+ y: route.B.y,
4288
+ label: `${route.connectionName} end (z=${route.B.z})`,
4289
+ color: "orange"
4290
+ });
4291
+ graphics.lines.push({
4292
+ points: [route.A, route.B],
4293
+ strokeColor: "rgba(255, 0, 0, 0.5)",
4294
+ label: `${route.connectionName} direct`
4295
+ });
4296
+ }
4297
+ for (let i = 0; i < this.debugViaPositions.length; i++) {
4298
+ const { via } = this.debugViaPositions[i];
4299
+ graphics.circles.push({
4300
+ center: via,
4301
+ radius: this.viaDiameter / 2,
4302
+ fill: "rgba(255, 165, 0, 0.7)",
4303
+ stroke: "rgba(0, 0, 0, 0.5)",
4304
+ label: `Computed Via (attempt ${i + 1})`
4305
+ });
4306
+ const safetyMargin = this.viaDiameter / 2 + this.obstacleMargin;
4307
+ graphics.circles.push({
4308
+ center: via,
4309
+ radius: safetyMargin,
4310
+ stroke: "rgba(255, 165, 0, 0.7)",
4311
+ fill: "rgba(0, 0, 0, 0)",
4312
+ label: "Safety Margin"
4313
+ });
4314
+ }
4315
+ for (let si = 0; si < this.solvedRoutes.length; si++) {
4316
+ const route = this.solvedRoutes[si];
4317
+ const routeColor = si % 2 === 0 ? "rgba(0, 255, 0, 0.75)" : "rgba(255, 0, 255, 0.75)";
4318
+ for (let i = 0; i < route.route.length - 1; i++) {
4319
+ const pointA = route.route[i];
4320
+ const pointB = route.route[i + 1];
4321
+ graphics.lines.push({
4322
+ points: [pointA, pointB],
4323
+ strokeColor: routeColor,
4324
+ strokeDash: pointA.z !== route.route[0].z ? [0.2, 0.2] : void 0,
4325
+ strokeWidth: route.traceThickness,
4326
+ label: `${route.connectionName} z=${pointA.z}`
4327
+ });
4328
+ }
4329
+ for (const via of route.vias) {
4330
+ graphics.circles.push({
4331
+ center: via,
4332
+ radius: this.viaDiameter / 2,
4333
+ fill: "rgba(0, 0, 255, 0.8)",
4334
+ stroke: "black",
4335
+ label: "Solved Via"
4336
+ });
4337
+ graphics.circles.push({
4338
+ center: via,
4339
+ radius: this.viaDiameter / 2 + this.obstacleMargin,
4340
+ fill: "rgba(0, 0, 255, 0.3)",
4341
+ stroke: "black",
4342
+ label: "Via Margin"
4343
+ });
4344
+ }
4345
+ }
4346
+ return graphics;
4347
+ }
4348
+ /**
4349
+ * Get the solved routes
4350
+ */
4351
+ getSolvedRoutes() {
4352
+ return this.solvedRoutes;
4353
+ }
4354
+ };
4355
+
3570
4356
  // lib/solvers/HyperHighDensitySolver/HyperSingleIntraNodeSolver.ts
3571
4357
  var HyperSingleIntraNodeSolver = class extends HyperParameterSupervisorSolver {
3572
4358
  constructorParams;
@@ -3582,7 +4368,7 @@ var HyperSingleIntraNodeSolver = class extends HyperParameterSupervisorSolver {
3582
4368
  }
3583
4369
  getCombinationDefs() {
3584
4370
  return [
3585
- ["closedFormTwoTraceSameLayer"],
4371
+ ["closedFormTwoTrace"],
3586
4372
  ["majorCombinations", "orderings6", "cellSizeFactor"],
3587
4373
  ["noVias"],
3588
4374
  ["orderings50"],
@@ -3673,10 +4459,13 @@ var HyperSingleIntraNodeSolver = class extends HyperParameterSupervisorSolver {
3673
4459
  }))
3674
4460
  },
3675
4461
  {
3676
- name: "closedFormTwoTraceSameLayer",
4462
+ name: "closedFormTwoTrace",
3677
4463
  possibleValues: [
3678
4464
  {
3679
4465
  CLOSED_FORM_TWO_TRACE_SAME_LAYER: true
4466
+ },
4467
+ {
4468
+ CLOSED_FORM_TWO_TRACE_TRANSITION_CROSSING: true
3680
4469
  }
3681
4470
  ]
3682
4471
  }
@@ -3694,6 +4483,11 @@ var HyperSingleIntraNodeSolver = class extends HyperParameterSupervisorSolver {
3694
4483
  nodeWithPortPoints: this.nodeWithPortPoints
3695
4484
  });
3696
4485
  }
4486
+ if (hyperParameters.CLOSED_FORM_TWO_TRACE_TRANSITION_CROSSING) {
4487
+ return new SingleTransitionCrossingRouteSolver({
4488
+ nodeWithPortPoints: this.nodeWithPortPoints
4489
+ });
4490
+ }
3697
4491
  return new IntraNodeRouteSolver({
3698
4492
  ...this.constructorParams,
3699
4493
  hyperParameters
@@ -4120,13 +4914,13 @@ function buildMinimumSpanningTree(points) {
4120
4914
  if (point.x === neighbor.x && point.y === neighbor.y) {
4121
4915
  continue;
4122
4916
  }
4123
- const distance4 = Math.sqrt(
4917
+ const distance6 = Math.sqrt(
4124
4918
  (point.x - neighbor.x) ** 2 + (point.y - neighbor.y) ** 2
4125
4919
  );
4126
4920
  edges.push({
4127
4921
  from: point,
4128
4922
  to: neighbor,
4129
- weight: distance4
4923
+ weight: distance6
4130
4924
  });
4131
4925
  }
4132
4926
  }
@@ -5387,6 +6181,7 @@ ${isMutable ? "MUTABLE" : "IMMUTABLE"}`,
5387
6181
  for (const connectedPointId of segmentPoint.directlyConnectedSegmentPointIds) {
5388
6182
  if (segmentPointId < connectedPointId) {
5389
6183
  const connectedPoint = modifiedSegmentPoints.get(connectedPointId);
6184
+ if (!connectedPoint) continue;
5390
6185
  const sameLayer = segmentPoint.z === connectedPoint.z;
5391
6186
  const commonLayer = segmentPoint.z;
5392
6187
  let strokeDash;
@@ -5946,11 +6741,11 @@ var CapacityPathingSolver = class extends BaseSolver {
5946
6741
  let closestNode = this.nodes[0];
5947
6742
  let minDistance = Number.MAX_VALUE;
5948
6743
  for (const node of nodesWithTargets) {
5949
- const distance4 = Math.sqrt(
6744
+ const distance6 = Math.sqrt(
5950
6745
  (node.center.x - point.x) ** 2 + (node.center.y - point.y) ** 2
5951
6746
  );
5952
- if (distance4 < minDistance) {
5953
- minDistance = distance4;
6747
+ if (distance6 < minDistance) {
6748
+ minDistance = distance6;
5954
6749
  closestNode = node;
5955
6750
  }
5956
6751
  }
@@ -6542,7 +7337,7 @@ var CapacityNodeTree = class {
6542
7337
  };
6543
7338
 
6544
7339
  // lib/solvers/SingleLayerNodeMerger/SingleLayerNodeMergerSolver.ts
6545
- var EPSILON = 5e-3;
7340
+ var EPSILON2 = 5e-3;
6546
7341
  var SingleLayerNodeMergerSolver = class extends BaseSolver {
6547
7342
  nodeMap;
6548
7343
  currentBatchNodeIds;
@@ -6701,7 +7496,7 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
6701
7496
  adjacentNodesToLeft.reduce((acc, adjNode) => {
6702
7497
  return acc + adjNode.height;
6703
7498
  }, 0) - rootNode.height
6704
- ) < EPSILON;
7499
+ ) < EPSILON2;
6705
7500
  if (leftAdjNodesTakeUpEntireHeight && leftAdjNodesAreAllSameSize) {
6706
7501
  rootNode.width += leftAdjNodeWidth;
6707
7502
  rootNode.center.x = rootNode.center.x - leftAdjNodeWidth / 2;
@@ -6721,7 +7516,7 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
6721
7516
  adjacentNodesToRight.reduce((acc, adjNode) => {
6722
7517
  return acc + adjNode.height;
6723
7518
  }, 0) - rootNode.height
6724
- ) < EPSILON;
7519
+ ) < EPSILON2;
6725
7520
  if (rightAdjNodesTakeUpEntireHeight && rightAdjNodesAreAllSameSize) {
6726
7521
  rootNode.width += rightAdjNodeWidth;
6727
7522
  rootNode.center.x = rootNode.center.x + rightAdjNodeWidth / 2;
@@ -6741,7 +7536,7 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
6741
7536
  adjacentNodesToTop.reduce((acc, adjNode) => {
6742
7537
  return acc + adjNode.width;
6743
7538
  }, 0) - rootNode.width
6744
- ) < EPSILON;
7539
+ ) < EPSILON2;
6745
7540
  if (topAdjNodesTakeUpEntireWidth && topAdjNodesAreAllSameSize) {
6746
7541
  rootNode.height += topAdjNodeHeight;
6747
7542
  rootNode.center.y = rootNode.center.y + topAdjNodeHeight / 2;
@@ -6761,7 +7556,7 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
6761
7556
  adjacentNodesToBottom.reduce((acc, adjNode) => {
6762
7557
  return acc + adjNode.width;
6763
7558
  }, 0) - rootNode.width
6764
- ) < EPSILON;
7559
+ ) < EPSILON2;
6765
7560
  if (bottomAdjNodesTakeUpEntireWidth && bottomAdjNodesAreAllSameSize) {
6766
7561
  rootNode.height += bottomAdjNodeHeight;
6767
7562
  rootNode.center.y = rootNode.center.y - bottomAdjNodeHeight / 2;
@@ -6858,6 +7653,15 @@ var SingleSimplifiedPathSolver = class extends BaseSolver {
6858
7653
  this.newRoute = [this.inputRoute.route[0]];
6859
7654
  this.newVias = [];
6860
7655
  }
7656
+ getConstructorParams() {
7657
+ return {
7658
+ inputRoute: this.inputRoute,
7659
+ otherHdRoutes: this.otherHdRoutes,
7660
+ obstacles: this.obstacles,
7661
+ connMap: this.connMap.netMap,
7662
+ colorMap: this.colorMap
7663
+ };
7664
+ }
6861
7665
  get simplifiedRoute() {
6862
7666
  return {
6863
7667
  connectionName: this.inputRoute.connectionName,
@@ -6998,23 +7802,23 @@ function pointToSegmentDistance4(P, Q1, Q2) {
6998
7802
  const w = { x: P.x - Q1.x, y: P.y - Q1.y };
6999
7803
  const c1 = dotProduct(w, v);
7000
7804
  if (c1 <= 0) {
7001
- return distance3(P, Q1);
7805
+ return distance5(P, Q1);
7002
7806
  }
7003
7807
  const c2 = dotProduct(v, v);
7004
7808
  if (c2 <= c1) {
7005
- return distance3(P, Q2);
7809
+ return distance5(P, Q2);
7006
7810
  }
7007
7811
  const b = c1 / c2;
7008
7812
  const Pb = {
7009
7813
  x: Q1.x + b * v.x,
7010
7814
  y: Q1.y + b * v.y
7011
7815
  };
7012
- return distance3(P, Pb);
7816
+ return distance5(P, Pb);
7013
7817
  }
7014
7818
  function dotProduct(v1, v2) {
7015
7819
  return v1.x * v2.x + v1.y * v2.y;
7016
7820
  }
7017
- function distance3(p1, p2) {
7821
+ function distance5(p1, p2) {
7018
7822
  const dx = p2.x - p1.x;
7019
7823
  const dy = p2.y - p1.y;
7020
7824
  return Math.sqrt(dx * dx + dy * dy);
@@ -7074,6 +7878,7 @@ var SegmentTree = class {
7074
7878
  }
7075
7879
  buckets;
7076
7880
  CELL_SIZE = 0.4;
7881
+ SEGMENT_MARGIN = 0.2;
7077
7882
  getBucketKey(x, y) {
7078
7883
  return `${Math.floor(x / this.CELL_SIZE)}x${Math.floor(y / this.CELL_SIZE)}`;
7079
7884
  }
@@ -7083,10 +7888,10 @@ var SegmentTree = class {
7083
7888
  getSegmentsThatCouldIntersect(A, B) {
7084
7889
  const segments = [];
7085
7890
  const alreadyAddedSegments = /* @__PURE__ */ new Set();
7086
- const minX = Math.min(A.x, B.x);
7087
- const minY = Math.min(A.y, B.y);
7088
- const maxX = Math.max(A.x, B.x);
7089
- const maxY = Math.max(A.y, B.y);
7891
+ const minX = Math.min(A.x, B.x) - this.SEGMENT_MARGIN;
7892
+ const minY = Math.min(A.y, B.y) - this.SEGMENT_MARGIN;
7893
+ const maxX = Math.max(A.x, B.x) + this.SEGMENT_MARGIN;
7894
+ const maxY = Math.max(A.y, B.y) + this.SEGMENT_MARGIN;
7090
7895
  const bucketMinX = Math.floor(minX / this.CELL_SIZE) * this.CELL_SIZE;
7091
7896
  const bucketMinY = Math.floor(minY / this.CELL_SIZE) * this.CELL_SIZE;
7092
7897
  for (let x = bucketMinX; x <= maxX; x += this.CELL_SIZE) {
@@ -7126,7 +7931,8 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
7126
7931
  filteredObstaclePathSegments = [];
7127
7932
  filteredVias = [];
7128
7933
  segmentTree;
7129
- OBSTACLE_MARGIN = 0.15;
7934
+ OBSTACLE_MARGIN = 0.1;
7935
+ TRACE_THICKNESS = 0.15;
7130
7936
  TAIL_JUMP_RATIO = 0.8;
7131
7937
  constructor(params) {
7132
7938
  super(params);
@@ -7146,6 +7952,14 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
7146
7952
  },
7147
7953
  { minX: Infinity, maxX: -Infinity, minY: Infinity, maxY: -Infinity }
7148
7954
  );
7955
+ const boundsBox = {
7956
+ center: {
7957
+ x: (bounds.minX + bounds.maxX) / 2,
7958
+ y: (bounds.minY + bounds.maxY) / 2
7959
+ },
7960
+ width: bounds.maxX - bounds.minX,
7961
+ height: bounds.maxY - bounds.minY
7962
+ };
7149
7963
  this.filteredObstacles = this.obstacles.filter(
7150
7964
  (obstacle) => !obstacle.connectedTo.some(
7151
7965
  (id) => this.connMap.areIdsConnected(this.inputRoute.connectionName, id)
@@ -7156,11 +7970,11 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
7156
7970
  )) {
7157
7971
  return false;
7158
7972
  }
7159
- const obstacleMinX = obstacle.center.x - obstacle.width / 2 - this.OBSTACLE_MARGIN;
7160
- const obstacleMaxX = obstacle.center.x + obstacle.width / 2 + this.OBSTACLE_MARGIN;
7161
- const obstacleMinY = obstacle.center.y - obstacle.height / 2 - this.OBSTACLE_MARGIN;
7162
- const obstacleMaxY = obstacle.center.y + obstacle.height / 2 + this.OBSTACLE_MARGIN;
7163
- return obstacleMinX <= bounds.maxX && obstacleMaxX >= bounds.minX && obstacleMinY <= bounds.maxY && obstacleMaxY >= bounds.minY;
7973
+ const { distance: distance6 } = computeDistanceBetweenBoxes(boundsBox, obstacle);
7974
+ if (distance6 < this.OBSTACLE_MARGIN + 0.5) {
7975
+ return true;
7976
+ }
7977
+ return false;
7164
7978
  });
7165
7979
  this.filteredObstaclePathSegments = this.otherHdRoutes.flatMap(
7166
7980
  (hdRoute) => {
@@ -7232,15 +8046,15 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
7232
8046
  return p1.x === p2.x && p1.y === p2.y && p1.z === p2.z;
7233
8047
  }
7234
8048
  // Get point at a specific distance along the path
7235
- getPointAtDistance(distance4) {
7236
- distance4 = Math.max(0, Math.min(distance4, this.totalPathLength));
8049
+ getPointAtDistance(distance6) {
8050
+ distance6 = Math.max(0, Math.min(distance6, this.totalPathLength));
7237
8051
  const segment = this.pathSegments.find(
7238
- (seg) => distance4 >= seg.startDistance && distance4 <= seg.endDistance
8052
+ (seg) => distance6 >= seg.startDistance && distance6 <= seg.endDistance
7239
8053
  );
7240
8054
  if (!segment) {
7241
8055
  return this.inputRoute.route[this.inputRoute.route.length - 1];
7242
8056
  }
7243
- const factor = (distance4 - segment.startDistance) / segment.length;
8057
+ const factor = (distance6 - segment.startDistance) / segment.length;
7244
8058
  return {
7245
8059
  x: segment.start.x + factor * (segment.end.x - segment.start.x),
7246
8060
  y: segment.start.y + factor * (segment.end.y - segment.start.y),
@@ -7249,17 +8063,17 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
7249
8063
  };
7250
8064
  }
7251
8065
  // Find nearest index in the original route for a given distance
7252
- getNearestIndexForDistance(distance4) {
7253
- if (distance4 <= 0) return 0;
7254
- if (distance4 >= this.totalPathLength)
8066
+ getNearestIndexForDistance(distance6) {
8067
+ if (distance6 <= 0) return 0;
8068
+ if (distance6 >= this.totalPathLength)
7255
8069
  return this.inputRoute.route.length - 1;
7256
8070
  const segmentIndex = this.pathSegments.findIndex(
7257
- (seg) => distance4 >= seg.startDistance && distance4 <= seg.endDistance
8071
+ (seg) => distance6 >= seg.startDistance && distance6 <= seg.endDistance
7258
8072
  );
7259
8073
  if (segmentIndex === -1) return 0;
7260
8074
  const segment = this.pathSegments[segmentIndex];
7261
8075
  const midDistance = (segment.startDistance + segment.endDistance) / 2;
7262
- return distance4 > midDistance ? segmentIndex + 1 : segmentIndex;
8076
+ return distance6 > midDistance ? segmentIndex + 1 : segmentIndex;
7263
8077
  }
7264
8078
  // Check if a path segment is valid
7265
8079
  isValidPathSegment(start, end) {
@@ -7267,49 +8081,27 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
7267
8081
  if (!obstacle.zLayers?.includes(start.z)) {
7268
8082
  continue;
7269
8083
  }
7270
- const obstacleLeft = obstacle.center.x - obstacle.width / 2 - this.OBSTACLE_MARGIN;
7271
- const obstacleRight = obstacle.center.x + obstacle.width / 2 + this.OBSTACLE_MARGIN;
7272
- const obstacleTop = obstacle.center.y - obstacle.height / 2 - this.OBSTACLE_MARGIN;
7273
- const obstacleBottom = obstacle.center.y + obstacle.height / 2 + this.OBSTACLE_MARGIN;
7274
- if (doSegmentsIntersect(
7275
- { x: start.x, y: start.y },
7276
- { x: end.x, y: end.y },
7277
- { x: obstacleLeft, y: obstacleTop },
7278
- { x: obstacleRight, y: obstacleTop }
7279
- ) || doSegmentsIntersect(
7280
- { x: start.x, y: start.y },
7281
- { x: end.x, y: end.y },
7282
- { x: obstacleRight, y: obstacleTop },
7283
- { x: obstacleRight, y: obstacleBottom }
7284
- ) || doSegmentsIntersect(
7285
- { x: start.x, y: start.y },
7286
- { x: end.x, y: end.y },
7287
- { x: obstacleRight, y: obstacleBottom },
7288
- { x: obstacleLeft, y: obstacleBottom }
7289
- ) || doSegmentsIntersect(
7290
- { x: start.x, y: start.y },
7291
- { x: end.x, y: end.y },
7292
- { x: obstacleLeft, y: obstacleBottom },
7293
- { x: obstacleLeft, y: obstacleTop }
7294
- )) {
8084
+ const distToObstacle = segmentToBoxMinDistance(start, end, obstacle);
8085
+ if (distToObstacle < this.OBSTACLE_MARGIN + this.TRACE_THICKNESS / 2) {
7295
8086
  return false;
7296
8087
  }
7297
8088
  }
7298
8089
  const segmentsThatCouldIntersect = this.segmentTree.getSegmentsThatCouldIntersect(start, end);
7299
- for (const [otherSegA, otherSegB] of segmentsThatCouldIntersect) {
8090
+ for (const [otherSegA, otherSegB, segId] of segmentsThatCouldIntersect) {
7300
8091
  if (otherSegA.z === start.z && otherSegB.z === start.z) {
7301
- if (minimumDistanceBetweenSegments(
8092
+ const distBetweenSegments = minimumDistanceBetweenSegments(
7302
8093
  { x: start.x, y: start.y },
7303
8094
  { x: end.x, y: end.y },
7304
8095
  { x: otherSegA.x, y: otherSegA.y },
7305
8096
  { x: otherSegB.x, y: otherSegB.y }
7306
- ) < this.OBSTACLE_MARGIN) {
8097
+ );
8098
+ if (distBetweenSegments < this.OBSTACLE_MARGIN + this.TRACE_THICKNESS) {
7307
8099
  return false;
7308
8100
  }
7309
8101
  }
7310
8102
  }
7311
8103
  for (const via of this.filteredVias) {
7312
- if (pointToSegmentDistance(via, start, end) < this.OBSTACLE_MARGIN + via.diameter / 2) {
8104
+ if (pointToSegmentDistance(via, start, end) < this.OBSTACLE_MARGIN + via.diameter / 2 + this.TRACE_THICKNESS / 2) {
7313
8105
  return false;
7314
8106
  }
7315
8107
  }
@@ -7361,10 +8153,10 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
7361
8153
  }
7362
8154
  this.currentStepSize = this.maxStepSize;
7363
8155
  }
7364
- moveHead(distance4) {
7365
- this.lastHeadMoveDistance = distance4;
8156
+ moveHead(distance6) {
8157
+ this.lastHeadMoveDistance = distance6;
7366
8158
  this.headDistanceAlongPath = Math.min(
7367
- this.headDistanceAlongPath + distance4,
8159
+ this.headDistanceAlongPath + distance6,
7368
8160
  this.totalPathLength
7369
8161
  );
7370
8162
  }
@@ -7502,9 +8294,9 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
7502
8294
  color: "red",
7503
8295
  label: ["Tentative Head", `z: ${tentativeHead.z}`].join("\n")
7504
8296
  });
7505
- let distance4 = 0;
7506
- while (distance4 < this.totalPathLength) {
7507
- const point = this.getPointAtDistance(distance4);
8297
+ let distance6 = 0;
8298
+ while (distance6 < this.totalPathLength) {
8299
+ const point = this.getPointAtDistance(distance6);
7508
8300
  graphics.circles.push({
7509
8301
  center: {
7510
8302
  x: point.x,
@@ -7513,7 +8305,7 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
7513
8305
  radius: 0.05,
7514
8306
  fill: "rgba(100, 100, 100, 0.5)"
7515
8307
  });
7516
- distance4 += this.totalPathLength / 20;
8308
+ distance6 += this.totalPathLength / 20;
7517
8309
  }
7518
8310
  if (this.lastValidPath && this.lastValidPath.length > 1) {
7519
8311
  for (let i = 0; i < this.lastValidPath.length - 1; i++) {