@tscircuit/capacity-autorouter 0.0.40 → 0.0.42

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
@@ -3567,6 +3567,540 @@ var TwoCrossingRoutesHighDensitySolver = class extends BaseSolver {
3567
3567
  }
3568
3568
  };
3569
3569
 
3570
+ // lib/utils/findClosestPointToABCWithinBounds.ts
3571
+ function findClosestPointToABCWithinBounds(A, B, C, radius, bounds) {
3572
+ const avgPoint = {
3573
+ x: (A.x + B.x + C.x) / 3,
3574
+ y: (A.y + B.y + C.y) / 3
3575
+ };
3576
+ const distance5 = (p1, p2) => {
3577
+ return Math.sqrt((p2.x - p1.x) ** 2 + (p2.y - p1.y) ** 2);
3578
+ };
3579
+ const isValidPoint = (point) => {
3580
+ const distToA = distance5(point, A);
3581
+ const distToB = distance5(point, B);
3582
+ const distToC = distance5(point, C);
3583
+ const withinBounds = point.x >= bounds.minX && point.x <= bounds.maxX && point.y >= bounds.minY && point.y <= bounds.maxY;
3584
+ return distToA >= radius && distToB >= radius && distToC >= radius && withinBounds;
3585
+ };
3586
+ const isOnBoundary = (point) => {
3587
+ const epsilon = 1e-6;
3588
+ 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;
3589
+ };
3590
+ if (isValidPoint(avgPoint)) {
3591
+ return avgPoint;
3592
+ }
3593
+ const pointOnCircle = (center, constraint, r) => {
3594
+ const vx = center.x - constraint.x;
3595
+ const vy = center.y - constraint.y;
3596
+ const dist = Math.sqrt(vx * vx + vy * vy);
3597
+ if (dist < 1e-10) {
3598
+ return { x: constraint.x + r, y: constraint.y };
3599
+ }
3600
+ return {
3601
+ x: constraint.x + vx / dist * r,
3602
+ y: constraint.y + vy / dist * r
3603
+ };
3604
+ };
3605
+ const findCircleIntersections = (c1, c2, r) => {
3606
+ const dx = c2.x - c1.x;
3607
+ const dy = c2.y - c1.y;
3608
+ const dist = Math.sqrt(dx * dx + dy * dy);
3609
+ if (dist > 2 * r - 1e-10 || dist < 1e-10) {
3610
+ return [];
3611
+ }
3612
+ const a = dist * dist / (2 * dist);
3613
+ const h = Math.sqrt(Math.max(0, r * r - a * a));
3614
+ const midX = c1.x + dx * a / dist;
3615
+ const midY = c1.y + dy * a / dist;
3616
+ const intersection1 = {
3617
+ x: midX + h * dy / dist,
3618
+ y: midY - h * dx / dist
3619
+ };
3620
+ const intersection2 = {
3621
+ x: midX - h * dy / dist,
3622
+ y: midY + h * dx / dist
3623
+ };
3624
+ const result = [];
3625
+ const epsilon = 1e-6;
3626
+ if (Math.abs(distance5(intersection1, c1) - r) < epsilon && Math.abs(distance5(intersection1, c2) - r) < epsilon) {
3627
+ result.push(intersection1);
3628
+ }
3629
+ if (Math.abs(distance5(intersection2, c1) - r) < epsilon && Math.abs(distance5(intersection2, c2) - r) < epsilon) {
3630
+ result.push(intersection2);
3631
+ }
3632
+ return result;
3633
+ };
3634
+ const candidateA = pointOnCircle(avgPoint, A, radius);
3635
+ const candidateB = pointOnCircle(avgPoint, B, radius);
3636
+ const candidateC = pointOnCircle(avgPoint, C, radius);
3637
+ const intersectionsAB = findCircleIntersections(A, B, radius);
3638
+ const intersectionsBC = findCircleIntersections(B, C, radius);
3639
+ const intersectionsCA = findCircleIntersections(C, A, radius);
3640
+ const allCandidates = [
3641
+ candidateA,
3642
+ candidateB,
3643
+ candidateC,
3644
+ ...intersectionsAB,
3645
+ ...intersectionsBC,
3646
+ ...intersectionsCA
3647
+ ];
3648
+ const validCandidates = allCandidates.filter(isValidPoint);
3649
+ if (validCandidates.length > 0) {
3650
+ const interiorCandidates = validCandidates.filter((p) => !isOnBoundary(p));
3651
+ if (interiorCandidates.length > 0) {
3652
+ interiorCandidates.sort(
3653
+ (a, b) => distance5(a, avgPoint) - distance5(b, avgPoint)
3654
+ );
3655
+ return interiorCandidates[0];
3656
+ }
3657
+ }
3658
+ const gridStep = 5;
3659
+ let bestPoint = null;
3660
+ let bestDistance = Infinity;
3661
+ for (let x = bounds.minX + 1; x < bounds.maxX; x += gridStep) {
3662
+ for (let y = bounds.minY + 1; y < bounds.maxY; y += gridStep) {
3663
+ const point = { x, y };
3664
+ if (isValidPoint(point)) {
3665
+ const dist = distance5(point, avgPoint);
3666
+ if (dist < bestDistance) {
3667
+ bestDistance = dist;
3668
+ bestPoint = point;
3669
+ }
3670
+ }
3671
+ }
3672
+ }
3673
+ if (bestPoint !== null) {
3674
+ return bestPoint;
3675
+ }
3676
+ const numSamples = 100;
3677
+ const boundaryPoints = [];
3678
+ for (let i = 0; i <= numSamples; i++) {
3679
+ const t = i / numSamples;
3680
+ boundaryPoints.push({
3681
+ x: bounds.minX + t * (bounds.maxX - bounds.minX),
3682
+ y: bounds.minY
3683
+ });
3684
+ boundaryPoints.push({
3685
+ x: bounds.maxX,
3686
+ y: bounds.minY + t * (bounds.maxY - bounds.minY)
3687
+ });
3688
+ boundaryPoints.push({
3689
+ x: bounds.maxX - t * (bounds.maxX - bounds.minX),
3690
+ y: bounds.maxY
3691
+ });
3692
+ boundaryPoints.push({
3693
+ x: bounds.minX,
3694
+ y: bounds.maxY - t * (bounds.maxY - bounds.minY)
3695
+ });
3696
+ }
3697
+ const validBoundaryPoints = boundaryPoints.filter(isValidPoint);
3698
+ if (validBoundaryPoints.length > 0) {
3699
+ validBoundaryPoints.sort(
3700
+ (a, b) => distance5(a, avgPoint) - distance5(b, avgPoint)
3701
+ );
3702
+ return validBoundaryPoints[0];
3703
+ }
3704
+ let minViolation = Infinity;
3705
+ let leastBadPoint = { x: bounds.minX, y: bounds.minY };
3706
+ for (const point of [...allCandidates, ...boundaryPoints]) {
3707
+ if (point.x >= bounds.minX && point.x <= bounds.maxX && point.y >= bounds.minY && point.y <= bounds.maxY) {
3708
+ const violationA = Math.max(0, radius - distance5(point, A));
3709
+ const violationB = Math.max(0, radius - distance5(point, B));
3710
+ const violationC = Math.max(0, radius - distance5(point, C));
3711
+ const totalViolation = violationA + violationB + violationC;
3712
+ if (totalViolation < minViolation) {
3713
+ minViolation = totalViolation;
3714
+ leastBadPoint = point;
3715
+ }
3716
+ }
3717
+ }
3718
+ return leastBadPoint;
3719
+ }
3720
+
3721
+ // lib/utils/findPointToGetAroundCircle.ts
3722
+ function findPointToGetAroundCircle(A, C, Q) {
3723
+ const B = computeTangentPoint(C, A, Q.center, Q.radius);
3724
+ const D = computeTangentPoint(A, C, Q.center, Q.radius);
3725
+ const E = computeIntersection(
3726
+ { x: C.x, y: C.y },
3727
+ { x: B.x, y: B.y },
3728
+ { x: A.x, y: A.y },
3729
+ { x: D.x, y: D.y }
3730
+ );
3731
+ return { B, D, E };
3732
+ }
3733
+ function computeTangentPoint(observationPoint, referencePoint, circleCenter, radius) {
3734
+ const CQ = [
3735
+ circleCenter.x - observationPoint.x,
3736
+ circleCenter.y - observationPoint.y
3737
+ ];
3738
+ const CQLength = Math.sqrt(CQ[0] * CQ[0] + CQ[1] * CQ[1]);
3739
+ if (CQLength < radius) {
3740
+ return observationPoint;
3741
+ }
3742
+ const CR = [
3743
+ referencePoint.x - observationPoint.x,
3744
+ referencePoint.y - observationPoint.y
3745
+ ];
3746
+ const d = Math.sqrt(CQLength * CQLength - radius * radius);
3747
+ const CQUnit = [CQ[0] / CQLength, CQ[1] / CQLength];
3748
+ const perp1 = [-CQUnit[1], CQUnit[0]];
3749
+ const perp2 = [CQUnit[1], -CQUnit[0]];
3750
+ const dot1 = CR[0] * perp1[0] + CR[1] * perp1[1];
3751
+ const dot2 = CR[0] * perp2[0] + CR[1] * perp2[1];
3752
+ const perp = dot1 > dot2 ? perp1 : perp2;
3753
+ const sinTheta = radius / CQLength;
3754
+ const cosTheta = d / CQLength;
3755
+ const unitToTangent = [
3756
+ CQUnit[0] * cosTheta + perp[0] * sinTheta,
3757
+ CQUnit[1] * cosTheta + perp[1] * sinTheta
3758
+ ];
3759
+ return {
3760
+ x: observationPoint.x + d * unitToTangent[0],
3761
+ y: observationPoint.y + d * unitToTangent[1]
3762
+ };
3763
+ }
3764
+ function computeIntersection(p1, p2, p3, p4) {
3765
+ const a1 = p2.y - p1.y;
3766
+ const b1 = p1.x - p2.x;
3767
+ const c1 = a1 * p1.x + b1 * p1.y;
3768
+ const a2 = p4.y - p3.y;
3769
+ const b2 = p3.x - p4.x;
3770
+ const c2 = a2 * p3.x + b2 * p3.y;
3771
+ const det = a1 * b2 - a2 * b1;
3772
+ if (Math.abs(det) < 1e-8) {
3773
+ return {
3774
+ x: (p1.x + p3.x) / 2,
3775
+ y: (p1.y + p3.y) / 2
3776
+ };
3777
+ }
3778
+ const x = (b2 * c1 - b1 * c2) / det;
3779
+ const y = (a1 * c2 - a2 * c1) / det;
3780
+ return { x, y };
3781
+ }
3782
+
3783
+ // lib/solvers/HighDensitySolver/TwoRouteHighDensitySolver/SingleTransitionCrossingRouteSolver.ts
3784
+ var SingleTransitionCrossingRouteSolver = class extends BaseSolver {
3785
+ // Input parameters
3786
+ nodeWithPortPoints;
3787
+ routes;
3788
+ // Configuration parameters
3789
+ viaDiameter;
3790
+ traceThickness;
3791
+ obstacleMargin;
3792
+ layerCount = 2;
3793
+ debugViaPositions;
3794
+ // Solution state
3795
+ solvedRoutes = [];
3796
+ // Bounds
3797
+ bounds;
3798
+ constructor(params) {
3799
+ super();
3800
+ this.nodeWithPortPoints = params.nodeWithPortPoints;
3801
+ this.viaDiameter = params?.viaDiameter ?? 0.6;
3802
+ this.traceThickness = params?.traceThickness ?? 0.15;
3803
+ this.obstacleMargin = params?.obstacleMargin ?? 0.1;
3804
+ this.layerCount = params?.layerCount ?? 2;
3805
+ this.debugViaPositions = [];
3806
+ this.routes = this.extractRoutesFromNode();
3807
+ this.bounds = this.calculateBounds();
3808
+ if (this.routes.length !== 2) {
3809
+ this.failed = true;
3810
+ return;
3811
+ }
3812
+ const [routeA, routeB] = this.routes;
3813
+ const routeAHasTransition = routeA.A.z !== routeA.B.z;
3814
+ const routeBHasTransition = routeB.A.z !== routeB.B.z;
3815
+ if (routeAHasTransition && routeBHasTransition || !routeAHasTransition && !routeBHasTransition) {
3816
+ this.failed = true;
3817
+ return;
3818
+ }
3819
+ }
3820
+ /**
3821
+ * Extract routes that need to be connected from the node data
3822
+ */
3823
+ extractRoutesFromNode() {
3824
+ const routes = [];
3825
+ const connectedPorts = this.nodeWithPortPoints.portPoints;
3826
+ const connectionGroups = /* @__PURE__ */ new Map();
3827
+ for (const connectedPort of connectedPorts) {
3828
+ const { connectionName } = connectedPort;
3829
+ if (!connectionGroups.has(connectionName)) {
3830
+ connectionGroups.set(connectionName, []);
3831
+ }
3832
+ connectionGroups.get(connectionName)?.push(connectedPort);
3833
+ }
3834
+ for (const [connectionName, points] of connectionGroups.entries()) {
3835
+ if (points.length === 2) {
3836
+ routes.push({
3837
+ A: { ...points[0], z: points[0].z ?? 0 },
3838
+ B: { ...points[1], z: points[1].z ?? 0 },
3839
+ connectionName
3840
+ });
3841
+ }
3842
+ }
3843
+ return routes;
3844
+ }
3845
+ /**
3846
+ * Calculate the bounding box of the node
3847
+ */
3848
+ calculateBounds() {
3849
+ return {
3850
+ minX: this.nodeWithPortPoints.center.x - this.nodeWithPortPoints.width / 2,
3851
+ maxX: this.nodeWithPortPoints.center.x + this.nodeWithPortPoints.width / 2,
3852
+ minY: this.nodeWithPortPoints.center.y - this.nodeWithPortPoints.height / 2,
3853
+ maxY: this.nodeWithPortPoints.center.y + this.nodeWithPortPoints.height / 2
3854
+ };
3855
+ }
3856
+ /**
3857
+ * Check if two routes are crossing
3858
+ */
3859
+ doRoutesCross(routeA, routeB) {
3860
+ return doSegmentsIntersect(routeA.A, routeA.B, routeB.A, routeB.B);
3861
+ }
3862
+ calculateViaPosition(transitionRoute, flatRoute) {
3863
+ const flatRouteZ = flatRoute.A.z;
3864
+ const ntrP1 = transitionRoute.A.z !== flatRouteZ ? transitionRoute.A : transitionRoute.B;
3865
+ const marginFromBorderWithTrace = this.obstacleMargin * 2 + this.viaDiameter / 2 + this.traceThickness;
3866
+ const marginFromBorderWithoutTrace = this.obstacleMargin + this.viaDiameter / 2;
3867
+ return findClosestPointToABCWithinBounds(
3868
+ flatRoute.A,
3869
+ flatRoute.B,
3870
+ ntrP1,
3871
+ marginFromBorderWithTrace,
3872
+ {
3873
+ minX: this.bounds.minX + marginFromBorderWithoutTrace,
3874
+ minY: this.bounds.minY + marginFromBorderWithoutTrace,
3875
+ maxX: this.bounds.maxX - marginFromBorderWithTrace,
3876
+ maxY: this.bounds.maxY - marginFromBorderWithTrace
3877
+ }
3878
+ );
3879
+ }
3880
+ /**
3881
+ * Create a single transition route with properly placed via
3882
+ */
3883
+ createTransitionRoute(start, end, via, connectionName) {
3884
+ const route = [
3885
+ { x: start.x, y: start.y, z: start.z ?? 0 },
3886
+ { x: via.x, y: via.y, z: start.z ?? 0 },
3887
+ { x: via.x, y: via.y, z: end.z ?? 0 },
3888
+ { x: end.x, y: end.y, z: end.z ?? 0 }
3889
+ ];
3890
+ return {
3891
+ connectionName,
3892
+ route,
3893
+ traceThickness: this.traceThickness,
3894
+ viaDiameter: this.viaDiameter,
3895
+ vias: [via]
3896
+ };
3897
+ }
3898
+ /**
3899
+ * Create the non-transition route
3900
+ */
3901
+ createFlatRoute(flatStart, flatEnd, via, otherRouteStart, otherRouteEnd, flatRouteConnectionName) {
3902
+ const ntrP1 = otherRouteStart.z !== flatStart.z ? otherRouteStart : otherRouteEnd;
3903
+ const middle = (a, b) => {
3904
+ return {
3905
+ x: (a.x + b.x) / 2,
3906
+ y: (a.y + b.y) / 2
3907
+ };
3908
+ };
3909
+ const middleWithMargin = (a, aMargin, b, bMargin) => {
3910
+ const dx = b.x - a.x;
3911
+ const dy = b.y - a.y;
3912
+ const effectiveA = {
3913
+ x: a.x + dx * aMargin,
3914
+ y: a.y + dy * aMargin
3915
+ };
3916
+ const effectiveB = {
3917
+ x: b.x - dx * bMargin,
3918
+ y: b.y - dy * bMargin
3919
+ };
3920
+ return middle(effectiveA, effectiveB);
3921
+ };
3922
+ const traceBounds = {
3923
+ maxX: this.bounds.maxX - this.obstacleMargin - this.traceThickness / 2,
3924
+ maxY: this.bounds.maxY - this.obstacleMargin - this.traceThickness / 2,
3925
+ minX: this.bounds.minX + this.obstacleMargin + this.traceThickness / 2,
3926
+ minY: this.bounds.minY + this.obstacleMargin + this.traceThickness / 2
3927
+ };
3928
+ const minDistFromViaToTrace = this.viaDiameter / 2 + this.traceThickness / 2 + this.obstacleMargin;
3929
+ const p2 = middleWithMargin(
3930
+ via,
3931
+ this.viaDiameter,
3932
+ otherRouteStart.z !== flatStart.z ? otherRouteStart : otherRouteEnd,
3933
+ this.traceThickness
3934
+ );
3935
+ const p1 = findPointToGetAroundCircle(flatStart, p2, {
3936
+ center: { x: via.x, y: via.y },
3937
+ radius: minDistFromViaToTrace
3938
+ }).E;
3939
+ const p3 = findPointToGetAroundCircle(p2, flatEnd, {
3940
+ center: { x: via.x, y: via.y },
3941
+ radius: minDistFromViaToTrace
3942
+ }).E;
3943
+ const p1IsNeeded = pointToSegmentDistance(via, flatStart, p2) < minDistFromViaToTrace;
3944
+ const p3IsNeeded = pointToSegmentDistance(via, p2, flatEnd) < minDistFromViaToTrace;
3945
+ return {
3946
+ connectionName: flatRouteConnectionName,
3947
+ route: [
3948
+ { x: flatStart.x, y: flatStart.y, z: flatStart.z ?? 0 },
3949
+ ...p1IsNeeded ? [{ x: p1.x, y: p1.y, z: flatStart.z ?? 0 }] : [],
3950
+ { x: p2.x, y: p2.y, z: flatStart.z ?? 0 },
3951
+ ...p3IsNeeded ? [{ x: p3.x, y: p3.y, z: flatStart.z ?? 0 }] : [],
3952
+ { x: flatEnd.x, y: flatEnd.y, z: flatEnd.z ?? 0 }
3953
+ ],
3954
+ traceThickness: this.traceThickness,
3955
+ viaDiameter: this.viaDiameter,
3956
+ vias: []
3957
+ };
3958
+ }
3959
+ /**
3960
+ * Try to solve with one route having a transition and the other staying flat
3961
+ */
3962
+ trySolve() {
3963
+ const [routeA, routeB] = this.routes;
3964
+ const routeAHasTransition = routeA.A.z !== routeA.B.z;
3965
+ const transitionRoute = routeAHasTransition ? routeA : routeB;
3966
+ const flatRoute = routeAHasTransition ? routeB : routeA;
3967
+ const viaPosition = this.calculateViaPosition(transitionRoute, flatRoute);
3968
+ if (viaPosition) {
3969
+ this.debugViaPositions.push({ via: viaPosition });
3970
+ } else {
3971
+ return false;
3972
+ }
3973
+ const transitionRouteSolution = this.createTransitionRoute(
3974
+ transitionRoute.A,
3975
+ transitionRoute.B,
3976
+ viaPosition,
3977
+ transitionRoute.connectionName
3978
+ );
3979
+ const flatRouteSolution = this.createFlatRoute(
3980
+ flatRoute.A,
3981
+ flatRoute.B,
3982
+ viaPosition,
3983
+ transitionRoute.A,
3984
+ transitionRoute.B,
3985
+ flatRoute.connectionName
3986
+ );
3987
+ this.solvedRoutes.push(transitionRouteSolution, flatRouteSolution);
3988
+ return true;
3989
+ }
3990
+ /**
3991
+ * Main step method that attempts to solve the routes
3992
+ */
3993
+ _step() {
3994
+ if (!this.doRoutesCross(this.routes[0], this.routes[1])) {
3995
+ this.failed = true;
3996
+ this.error = "Can only solve routes that have a single transition crossing";
3997
+ return;
3998
+ }
3999
+ if (this.trySolve()) {
4000
+ this.solved = true;
4001
+ return;
4002
+ }
4003
+ this.failed = true;
4004
+ }
4005
+ /**
4006
+ * Visualization for debugging
4007
+ */
4008
+ visualize() {
4009
+ const graphics = {
4010
+ lines: [],
4011
+ points: [],
4012
+ rects: [],
4013
+ circles: []
4014
+ };
4015
+ graphics.rects.push({
4016
+ center: {
4017
+ x: (this.bounds.minX + this.bounds.maxX) / 2,
4018
+ y: (this.bounds.minY + this.bounds.maxY) / 2
4019
+ },
4020
+ width: this.bounds.maxX - this.bounds.minX,
4021
+ height: this.bounds.maxY - this.bounds.minY,
4022
+ stroke: "rgba(0, 0, 0, 0.5)",
4023
+ fill: "rgba(240, 240, 240, 0.1)",
4024
+ label: "PCB Bounds"
4025
+ });
4026
+ for (const route of this.routes) {
4027
+ graphics.points.push({
4028
+ x: route.A.x,
4029
+ y: route.A.y,
4030
+ label: `${route.connectionName} start (z=${route.A.z})`,
4031
+ color: "orange"
4032
+ });
4033
+ graphics.points.push({
4034
+ x: route.B.x,
4035
+ y: route.B.y,
4036
+ label: `${route.connectionName} end (z=${route.B.z})`,
4037
+ color: "orange"
4038
+ });
4039
+ graphics.lines.push({
4040
+ points: [route.A, route.B],
4041
+ strokeColor: "rgba(255, 0, 0, 0.5)",
4042
+ label: `${route.connectionName} direct`
4043
+ });
4044
+ }
4045
+ for (let i = 0; i < this.debugViaPositions.length; i++) {
4046
+ const { via } = this.debugViaPositions[i];
4047
+ graphics.circles.push({
4048
+ center: via,
4049
+ radius: this.viaDiameter / 2,
4050
+ fill: "rgba(255, 165, 0, 0.7)",
4051
+ stroke: "rgba(0, 0, 0, 0.5)",
4052
+ label: `Computed Via (attempt ${i + 1})`
4053
+ });
4054
+ const safetyMargin = this.viaDiameter / 2 + this.obstacleMargin;
4055
+ graphics.circles.push({
4056
+ center: via,
4057
+ radius: safetyMargin,
4058
+ stroke: "rgba(255, 165, 0, 0.7)",
4059
+ fill: "rgba(0, 0, 0, 0)",
4060
+ label: "Safety Margin"
4061
+ });
4062
+ }
4063
+ for (let si = 0; si < this.solvedRoutes.length; si++) {
4064
+ const route = this.solvedRoutes[si];
4065
+ const routeColor = si % 2 === 0 ? "rgba(0, 255, 0, 0.75)" : "rgba(255, 0, 255, 0.75)";
4066
+ for (let i = 0; i < route.route.length - 1; i++) {
4067
+ const pointA = route.route[i];
4068
+ const pointB = route.route[i + 1];
4069
+ graphics.lines.push({
4070
+ points: [pointA, pointB],
4071
+ strokeColor: routeColor,
4072
+ strokeDash: pointA.z !== route.route[0].z ? [0.2, 0.2] : void 0,
4073
+ strokeWidth: route.traceThickness,
4074
+ label: `${route.connectionName} z=${pointA.z}`
4075
+ });
4076
+ }
4077
+ for (const via of route.vias) {
4078
+ graphics.circles.push({
4079
+ center: via,
4080
+ radius: this.viaDiameter / 2,
4081
+ fill: "rgba(0, 0, 255, 0.8)",
4082
+ stroke: "black",
4083
+ label: "Solved Via"
4084
+ });
4085
+ graphics.circles.push({
4086
+ center: via,
4087
+ radius: this.viaDiameter / 2 + this.obstacleMargin,
4088
+ fill: "rgba(0, 0, 255, 0.3)",
4089
+ stroke: "black",
4090
+ label: "Via Margin"
4091
+ });
4092
+ }
4093
+ }
4094
+ return graphics;
4095
+ }
4096
+ /**
4097
+ * Get the solved routes
4098
+ */
4099
+ getSolvedRoutes() {
4100
+ return this.solvedRoutes;
4101
+ }
4102
+ };
4103
+
3570
4104
  // lib/solvers/HyperHighDensitySolver/HyperSingleIntraNodeSolver.ts
3571
4105
  var HyperSingleIntraNodeSolver = class extends HyperParameterSupervisorSolver {
3572
4106
  constructorParams;
@@ -3582,7 +4116,7 @@ var HyperSingleIntraNodeSolver = class extends HyperParameterSupervisorSolver {
3582
4116
  }
3583
4117
  getCombinationDefs() {
3584
4118
  return [
3585
- ["closedFormTwoTraceSameLayer"],
4119
+ ["closedFormTwoTrace"],
3586
4120
  ["majorCombinations", "orderings6", "cellSizeFactor"],
3587
4121
  ["noVias"],
3588
4122
  ["orderings50"],
@@ -3673,10 +4207,13 @@ var HyperSingleIntraNodeSolver = class extends HyperParameterSupervisorSolver {
3673
4207
  }))
3674
4208
  },
3675
4209
  {
3676
- name: "closedFormTwoTraceSameLayer",
4210
+ name: "closedFormTwoTrace",
3677
4211
  possibleValues: [
3678
4212
  {
3679
4213
  CLOSED_FORM_TWO_TRACE_SAME_LAYER: true
4214
+ },
4215
+ {
4216
+ CLOSED_FORM_TWO_TRACE_TRANSITION_CROSSING: true
3680
4217
  }
3681
4218
  ]
3682
4219
  }
@@ -3694,6 +4231,11 @@ var HyperSingleIntraNodeSolver = class extends HyperParameterSupervisorSolver {
3694
4231
  nodeWithPortPoints: this.nodeWithPortPoints
3695
4232
  });
3696
4233
  }
4234
+ if (hyperParameters.CLOSED_FORM_TWO_TRACE_TRANSITION_CROSSING) {
4235
+ return new SingleTransitionCrossingRouteSolver({
4236
+ nodeWithPortPoints: this.nodeWithPortPoints
4237
+ });
4238
+ }
3697
4239
  return new IntraNodeRouteSolver({
3698
4240
  ...this.constructorParams,
3699
4241
  hyperParameters
@@ -4120,13 +4662,13 @@ function buildMinimumSpanningTree(points) {
4120
4662
  if (point.x === neighbor.x && point.y === neighbor.y) {
4121
4663
  continue;
4122
4664
  }
4123
- const distance4 = Math.sqrt(
4665
+ const distance5 = Math.sqrt(
4124
4666
  (point.x - neighbor.x) ** 2 + (point.y - neighbor.y) ** 2
4125
4667
  );
4126
4668
  edges.push({
4127
4669
  from: point,
4128
4670
  to: neighbor,
4129
- weight: distance4
4671
+ weight: distance5
4130
4672
  });
4131
4673
  }
4132
4674
  }
@@ -5387,6 +5929,7 @@ ${isMutable ? "MUTABLE" : "IMMUTABLE"}`,
5387
5929
  for (const connectedPointId of segmentPoint.directlyConnectedSegmentPointIds) {
5388
5930
  if (segmentPointId < connectedPointId) {
5389
5931
  const connectedPoint = modifiedSegmentPoints.get(connectedPointId);
5932
+ if (!connectedPoint) continue;
5390
5933
  const sameLayer = segmentPoint.z === connectedPoint.z;
5391
5934
  const commonLayer = segmentPoint.z;
5392
5935
  let strokeDash;
@@ -5946,11 +6489,11 @@ var CapacityPathingSolver = class extends BaseSolver {
5946
6489
  let closestNode = this.nodes[0];
5947
6490
  let minDistance = Number.MAX_VALUE;
5948
6491
  for (const node of nodesWithTargets) {
5949
- const distance4 = Math.sqrt(
6492
+ const distance5 = Math.sqrt(
5950
6493
  (node.center.x - point.x) ** 2 + (node.center.y - point.y) ** 2
5951
6494
  );
5952
- if (distance4 < minDistance) {
5953
- minDistance = distance4;
6495
+ if (distance5 < minDistance) {
6496
+ minDistance = distance5;
5954
6497
  closestNode = node;
5955
6498
  }
5956
6499
  }
@@ -5968,9 +6511,16 @@ var CapacityPathingSolver = class extends BaseSolver {
5968
6511
  connectionsWithNodes.push({
5969
6512
  connection,
5970
6513
  nodes: nodesForConnection,
5971
- pathFound: false
6514
+ pathFound: false,
6515
+ straightLineDistance: distance(
6516
+ nodesForConnection[0].center,
6517
+ nodesForConnection[nodesForConnection.length - 1].center
6518
+ )
5972
6519
  });
5973
6520
  }
6521
+ connectionsWithNodes.sort(
6522
+ (a, b) => a.straightLineDistance - b.straightLineDistance
6523
+ );
5974
6524
  return { connectionsWithNodes, connectionNameToGoalNodeIds };
5975
6525
  }
5976
6526
  currentConnectionIndex = 0;
@@ -6991,23 +7541,23 @@ function pointToSegmentDistance4(P, Q1, Q2) {
6991
7541
  const w = { x: P.x - Q1.x, y: P.y - Q1.y };
6992
7542
  const c1 = dotProduct(w, v);
6993
7543
  if (c1 <= 0) {
6994
- return distance3(P, Q1);
7544
+ return distance4(P, Q1);
6995
7545
  }
6996
7546
  const c2 = dotProduct(v, v);
6997
7547
  if (c2 <= c1) {
6998
- return distance3(P, Q2);
7548
+ return distance4(P, Q2);
6999
7549
  }
7000
7550
  const b = c1 / c2;
7001
7551
  const Pb = {
7002
7552
  x: Q1.x + b * v.x,
7003
7553
  y: Q1.y + b * v.y
7004
7554
  };
7005
- return distance3(P, Pb);
7555
+ return distance4(P, Pb);
7006
7556
  }
7007
7557
  function dotProduct(v1, v2) {
7008
7558
  return v1.x * v2.x + v1.y * v2.y;
7009
7559
  }
7010
- function distance3(p1, p2) {
7560
+ function distance4(p1, p2) {
7011
7561
  const dx = p2.x - p1.x;
7012
7562
  const dy = p2.y - p1.y;
7013
7563
  return Math.sqrt(dx * dx + dy * dy);
@@ -7225,15 +7775,15 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
7225
7775
  return p1.x === p2.x && p1.y === p2.y && p1.z === p2.z;
7226
7776
  }
7227
7777
  // Get point at a specific distance along the path
7228
- getPointAtDistance(distance4) {
7229
- distance4 = Math.max(0, Math.min(distance4, this.totalPathLength));
7778
+ getPointAtDistance(distance5) {
7779
+ distance5 = Math.max(0, Math.min(distance5, this.totalPathLength));
7230
7780
  const segment = this.pathSegments.find(
7231
- (seg) => distance4 >= seg.startDistance && distance4 <= seg.endDistance
7781
+ (seg) => distance5 >= seg.startDistance && distance5 <= seg.endDistance
7232
7782
  );
7233
7783
  if (!segment) {
7234
7784
  return this.inputRoute.route[this.inputRoute.route.length - 1];
7235
7785
  }
7236
- const factor = (distance4 - segment.startDistance) / segment.length;
7786
+ const factor = (distance5 - segment.startDistance) / segment.length;
7237
7787
  return {
7238
7788
  x: segment.start.x + factor * (segment.end.x - segment.start.x),
7239
7789
  y: segment.start.y + factor * (segment.end.y - segment.start.y),
@@ -7242,17 +7792,17 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
7242
7792
  };
7243
7793
  }
7244
7794
  // Find nearest index in the original route for a given distance
7245
- getNearestIndexForDistance(distance4) {
7246
- if (distance4 <= 0) return 0;
7247
- if (distance4 >= this.totalPathLength)
7795
+ getNearestIndexForDistance(distance5) {
7796
+ if (distance5 <= 0) return 0;
7797
+ if (distance5 >= this.totalPathLength)
7248
7798
  return this.inputRoute.route.length - 1;
7249
7799
  const segmentIndex = this.pathSegments.findIndex(
7250
- (seg) => distance4 >= seg.startDistance && distance4 <= seg.endDistance
7800
+ (seg) => distance5 >= seg.startDistance && distance5 <= seg.endDistance
7251
7801
  );
7252
7802
  if (segmentIndex === -1) return 0;
7253
7803
  const segment = this.pathSegments[segmentIndex];
7254
7804
  const midDistance = (segment.startDistance + segment.endDistance) / 2;
7255
- return distance4 > midDistance ? segmentIndex + 1 : segmentIndex;
7805
+ return distance5 > midDistance ? segmentIndex + 1 : segmentIndex;
7256
7806
  }
7257
7807
  // Check if a path segment is valid
7258
7808
  isValidPathSegment(start, end) {
@@ -7354,10 +7904,10 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
7354
7904
  }
7355
7905
  this.currentStepSize = this.maxStepSize;
7356
7906
  }
7357
- moveHead(distance4) {
7358
- this.lastHeadMoveDistance = distance4;
7907
+ moveHead(distance5) {
7908
+ this.lastHeadMoveDistance = distance5;
7359
7909
  this.headDistanceAlongPath = Math.min(
7360
- this.headDistanceAlongPath + distance4,
7910
+ this.headDistanceAlongPath + distance5,
7361
7911
  this.totalPathLength
7362
7912
  );
7363
7913
  }
@@ -7495,9 +8045,9 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
7495
8045
  color: "red",
7496
8046
  label: ["Tentative Head", `z: ${tentativeHead.z}`].join("\n")
7497
8047
  });
7498
- let distance4 = 0;
7499
- while (distance4 < this.totalPathLength) {
7500
- const point = this.getPointAtDistance(distance4);
8048
+ let distance5 = 0;
8049
+ while (distance5 < this.totalPathLength) {
8050
+ const point = this.getPointAtDistance(distance5);
7501
8051
  graphics.circles.push({
7502
8052
  center: {
7503
8053
  x: point.x,
@@ -7506,7 +8056,7 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
7506
8056
  radius: 0.05,
7507
8057
  fill: "rgba(100, 100, 100, 0.5)"
7508
8058
  });
7509
- distance4 += this.totalPathLength / 20;
8059
+ distance5 += this.totalPathLength / 20;
7510
8060
  }
7511
8061
  if (this.lastValidPath && this.lastValidPath.length > 1) {
7512
8062
  for (let i = 0; i < this.lastValidPath.length - 1; i++) {
@@ -7840,7 +8390,7 @@ function definePipelineStep(solverName, solverClass, getConstructorParams, opts
7840
8390
  onSolved: opts.onSolved
7841
8391
  };
7842
8392
  }
7843
- var CapacityMeshSolver = class extends BaseSolver {
8393
+ var AutoroutingPipelineSolver = class extends BaseSolver {
7844
8394
  constructor(srj, opts = {}) {
7845
8395
  super();
7846
8396
  this.srj = srj;
@@ -8072,6 +8622,11 @@ var CapacityMeshSolver = class extends BaseSolver {
8072
8622
  this.timeSpentOnPhase[pipelineStepDef.solverName] = 0;
8073
8623
  this.startTimeOfPhase[pipelineStepDef.solverName] = performance.now();
8074
8624
  }
8625
+ solveUntilPhase(phase) {
8626
+ while (this.getCurrentPhase() !== phase) {
8627
+ this.step();
8628
+ }
8629
+ }
8075
8630
  getCurrentPhase() {
8076
8631
  return this.pipelineDef[this.currentPipelineStepIndex]?.solverName ?? "none";
8077
8632
  }
@@ -8242,6 +8797,7 @@ var CapacityMeshSolver = class extends BaseSolver {
8242
8797
  };
8243
8798
  }
8244
8799
  };
8800
+ var CapacityMeshSolver = AutoroutingPipelineSolver;
8245
8801
  export {
8246
8802
  CapacityMeshSolver,
8247
8803
  calculateOptimalCapacityDepth,