@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.d.ts +14 -2
- package/dist/index.js +863 -71
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
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
|
-
["
|
|
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: "
|
|
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
|
|
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:
|
|
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
|
|
6744
|
+
const distance6 = Math.sqrt(
|
|
5950
6745
|
(node.center.x - point.x) ** 2 + (node.center.y - point.y) ** 2
|
|
5951
6746
|
);
|
|
5952
|
-
if (
|
|
5953
|
-
minDistance =
|
|
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
|
|
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
|
-
) <
|
|
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
|
-
) <
|
|
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
|
-
) <
|
|
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
|
-
) <
|
|
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
|
|
7805
|
+
return distance5(P, Q1);
|
|
7002
7806
|
}
|
|
7003
7807
|
const c2 = dotProduct(v, v);
|
|
7004
7808
|
if (c2 <= c1) {
|
|
7005
|
-
return
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
7160
|
-
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
return
|
|
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(
|
|
7236
|
-
|
|
8049
|
+
getPointAtDistance(distance6) {
|
|
8050
|
+
distance6 = Math.max(0, Math.min(distance6, this.totalPathLength));
|
|
7237
8051
|
const segment = this.pathSegments.find(
|
|
7238
|
-
(seg) =>
|
|
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 = (
|
|
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(
|
|
7253
|
-
if (
|
|
7254
|
-
if (
|
|
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) =>
|
|
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
|
|
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
|
|
7271
|
-
|
|
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
|
-
|
|
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
|
-
)
|
|
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(
|
|
7365
|
-
this.lastHeadMoveDistance =
|
|
8156
|
+
moveHead(distance6) {
|
|
8157
|
+
this.lastHeadMoveDistance = distance6;
|
|
7366
8158
|
this.headDistanceAlongPath = Math.min(
|
|
7367
|
-
this.headDistanceAlongPath +
|
|
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
|
|
7506
|
-
while (
|
|
7507
|
-
const point = this.getPointAtDistance(
|
|
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
|
-
|
|
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++) {
|