@tscircuit/capacity-autorouter 0.0.42 → 0.0.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +9 -0
- package/dist/index.js +624 -242
- 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) {
|
|
@@ -1639,30 +1642,40 @@ var CapacityEdgeToPortSegmentSolver = class extends BaseSolver {
|
|
|
1639
1642
|
const node = this.nodeMap.get(nodeId);
|
|
1640
1643
|
segments.forEach((segment) => {
|
|
1641
1644
|
const isVertical = segment.start.x === segment.end.x;
|
|
1642
|
-
const THICKNESS = 0.
|
|
1645
|
+
const THICKNESS = 0.05;
|
|
1643
1646
|
for (let i = 0; i < segment.connectionNames.length; i++) {
|
|
1644
|
-
const offsetAmount = (i / (segment.connectionNames.length - 1 + 1e-6) - 0.5) * THICKNESS;
|
|
1645
1647
|
const offset = {
|
|
1646
|
-
x:
|
|
1647
|
-
y:
|
|
1648
|
+
x: 0.05 * Math.max(...segment.availableZ),
|
|
1649
|
+
y: 0.05 * Math.max(...segment.availableZ)
|
|
1648
1650
|
};
|
|
1649
1651
|
const trueSegmentCenter = {
|
|
1650
|
-
x: (segment.start.x + segment.end.x) / 2
|
|
1651
|
-
y: (segment.start.y + segment.end.y) / 2
|
|
1652
|
+
x: (segment.start.x + segment.end.x) / 2,
|
|
1653
|
+
y: (segment.start.y + segment.end.y) / 2
|
|
1652
1654
|
};
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1655
|
+
const segmentCenter = {
|
|
1656
|
+
x: trueSegmentCenter.x + offset.x,
|
|
1657
|
+
y: trueSegmentCenter.y + offset.y
|
|
1658
|
+
};
|
|
1659
|
+
if (offset.x > 0) {
|
|
1660
|
+
graphics.lines.push({
|
|
1661
|
+
points: [trueSegmentCenter, segmentCenter],
|
|
1662
|
+
strokeColor: "rgba(0, 0, 0, 0.25)",
|
|
1663
|
+
strokeDash: "5 5"
|
|
1664
|
+
});
|
|
1665
|
+
}
|
|
1666
|
+
graphics.points.push({
|
|
1667
|
+
x: segmentCenter.x,
|
|
1668
|
+
y: segmentCenter.y,
|
|
1669
|
+
label: `${nodeId}: ${segment.connectionNames.join(", ")}
|
|
1670
|
+
availableZ: ${segment.availableZ.join(",")}
|
|
1671
|
+
nodePortSegmentId: ${segment.nodePortSegmentId}`
|
|
1672
|
+
});
|
|
1673
|
+
graphics.lines.push({
|
|
1674
|
+
points: [segment.start, segment.end],
|
|
1675
|
+
strokeColor: safeTransparentize(
|
|
1661
1676
|
this.colorMap[segment.connectionNames[i]],
|
|
1662
1677
|
0.6
|
|
1663
|
-
)
|
|
1664
|
-
label: `${nodeId}: ${segment.connectionNames.join(", ")}
|
|
1665
|
-
availableZ: ${segment.availableZ.join(",")}`
|
|
1678
|
+
)
|
|
1666
1679
|
});
|
|
1667
1680
|
}
|
|
1668
1681
|
});
|
|
@@ -1707,19 +1720,50 @@ function findOverlappingSegment(node, adjNode) {
|
|
|
1707
1720
|
};
|
|
1708
1721
|
}
|
|
1709
1722
|
}
|
|
1723
|
+
var EPSILON = 1e-9;
|
|
1724
|
+
function coordsAreEqual(p1, p2) {
|
|
1725
|
+
return Math.abs(p1.x - p2.x) < EPSILON && Math.abs(p1.y - p2.y) < EPSILON;
|
|
1726
|
+
}
|
|
1727
|
+
function availableZAreEqual(zA1, zA2) {
|
|
1728
|
+
if (zA1.length !== zA2.length) {
|
|
1729
|
+
return false;
|
|
1730
|
+
}
|
|
1731
|
+
for (let i = 0; i < zA1.length; i++) {
|
|
1732
|
+
if (zA1[i] !== zA2[i]) {
|
|
1733
|
+
return false;
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
return true;
|
|
1737
|
+
}
|
|
1710
1738
|
function combineSegments(segments) {
|
|
1711
1739
|
const mergedSegments = [];
|
|
1712
|
-
const remainingSegments =
|
|
1740
|
+
const remainingSegments = segments.map((s) => ({
|
|
1741
|
+
...s,
|
|
1742
|
+
connectionNames: [...s.connectionNames],
|
|
1743
|
+
availableZ: [...s.availableZ].sort((a, b) => a - b)
|
|
1744
|
+
// Ensure Z is sorted for comparison
|
|
1745
|
+
}));
|
|
1713
1746
|
while (remainingSegments.length > 0) {
|
|
1714
1747
|
const segmentUnderTest = remainingSegments.pop();
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1748
|
+
let foundMatch = false;
|
|
1749
|
+
for (let i = 0; i < mergedSegments.length; i++) {
|
|
1750
|
+
const mergedSegment = mergedSegments[i];
|
|
1751
|
+
const geometryMatch = coordsAreEqual(mergedSegment.start, segmentUnderTest.start) && coordsAreEqual(mergedSegment.end, segmentUnderTest.end) || coordsAreEqual(mergedSegment.start, segmentUnderTest.end) && coordsAreEqual(mergedSegment.end, segmentUnderTest.start);
|
|
1752
|
+
const zMatch = availableZAreEqual(
|
|
1753
|
+
mergedSegment.availableZ,
|
|
1754
|
+
segmentUnderTest.availableZ
|
|
1721
1755
|
);
|
|
1722
|
-
|
|
1756
|
+
if (geometryMatch && zMatch) {
|
|
1757
|
+
const currentConnections = new Set(mergedSegment.connectionNames);
|
|
1758
|
+
segmentUnderTest.connectionNames.forEach(
|
|
1759
|
+
(cn) => currentConnections.add(cn)
|
|
1760
|
+
);
|
|
1761
|
+
mergedSegment.connectionNames = Array.from(currentConnections);
|
|
1762
|
+
foundMatch = true;
|
|
1763
|
+
break;
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
if (!foundMatch) {
|
|
1723
1767
|
mergedSegments.push(segmentUnderTest);
|
|
1724
1768
|
}
|
|
1725
1769
|
}
|
|
@@ -1844,25 +1888,49 @@ var CapacitySegmentToPointSolver = class extends BaseSolver {
|
|
|
1844
1888
|
*/
|
|
1845
1889
|
visualize() {
|
|
1846
1890
|
const graphics = {
|
|
1847
|
-
points:
|
|
1848
|
-
(seg) => seg.assignedPoints.map((ap) => ({
|
|
1849
|
-
x: ap.point.x,
|
|
1850
|
-
y: ap.point.y,
|
|
1851
|
-
label: [
|
|
1852
|
-
`${seg.capacityMeshNodeId}-${ap.connectionName}`,
|
|
1853
|
-
`z: ${seg.availableZ.join(",")}`
|
|
1854
|
-
].join("\n"),
|
|
1855
|
-
color: this.colorMap[ap.connectionName],
|
|
1856
|
-
step: 4
|
|
1857
|
-
}))
|
|
1858
|
-
),
|
|
1891
|
+
points: [],
|
|
1859
1892
|
lines: this.solvedSegments.map((seg) => ({
|
|
1860
1893
|
points: [seg.start, seg.end],
|
|
1861
1894
|
step: 4
|
|
1862
1895
|
})),
|
|
1863
1896
|
rects: [],
|
|
1864
|
-
circles: []
|
|
1897
|
+
circles: [],
|
|
1898
|
+
coordinateSystem: "cartesian",
|
|
1899
|
+
title: "Capacity Segment to Point Solver"
|
|
1865
1900
|
};
|
|
1901
|
+
for (let i = 0; i < this.solvedSegments.length; i++) {
|
|
1902
|
+
const seg = this.solvedSegments[i];
|
|
1903
|
+
for (let j = 0; j < seg.assignedPoints.length; j++) {
|
|
1904
|
+
const ap = seg.assignedPoints[j];
|
|
1905
|
+
const truePoint = {
|
|
1906
|
+
x: ap.point.x,
|
|
1907
|
+
y: ap.point.y
|
|
1908
|
+
};
|
|
1909
|
+
const offsetPoint = {
|
|
1910
|
+
x: ap.point.x + ap.point.z * 0.05,
|
|
1911
|
+
y: ap.point.y + ap.point.z * 0.05
|
|
1912
|
+
};
|
|
1913
|
+
if (ap.point.z !== 0) {
|
|
1914
|
+
graphics.lines.push({
|
|
1915
|
+
points: [truePoint, offsetPoint],
|
|
1916
|
+
strokeColor: "rgba(0, 0, 0, 0.25)",
|
|
1917
|
+
strokeDash: "5 5",
|
|
1918
|
+
step: 4
|
|
1919
|
+
});
|
|
1920
|
+
}
|
|
1921
|
+
graphics.points.push({
|
|
1922
|
+
x: offsetPoint.x,
|
|
1923
|
+
y: offsetPoint.y,
|
|
1924
|
+
label: [
|
|
1925
|
+
`${seg.capacityMeshNodeId}-${ap.connectionName}`,
|
|
1926
|
+
`z: ${seg.availableZ.join(",")}`,
|
|
1927
|
+
`nodePortSegmentId: ${seg.nodePortSegmentId}`
|
|
1928
|
+
].join("\n"),
|
|
1929
|
+
color: this.colorMap[ap.connectionName],
|
|
1930
|
+
step: 4
|
|
1931
|
+
});
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1866
1934
|
const dashedLines = [];
|
|
1867
1935
|
const nodeConnections = {};
|
|
1868
1936
|
for (const seg of this.solvedSegments) {
|
|
@@ -1938,6 +2006,86 @@ function distance(p1, p2) {
|
|
|
1938
2006
|
return Math.sqrt(dx * dx + dy * dy);
|
|
1939
2007
|
}
|
|
1940
2008
|
|
|
2009
|
+
// node_modules/@tscircuit/math-utils/dist/chunk-MHHTZHOJ.js
|
|
2010
|
+
function getBoundingBox(box) {
|
|
2011
|
+
const halfWidth = box.width / 2;
|
|
2012
|
+
const halfHeight = box.height / 2;
|
|
2013
|
+
return {
|
|
2014
|
+
minX: box.center.x - halfWidth,
|
|
2015
|
+
maxX: box.center.x + halfWidth,
|
|
2016
|
+
minY: box.center.y - halfHeight,
|
|
2017
|
+
maxY: box.center.y + halfHeight
|
|
2018
|
+
};
|
|
2019
|
+
}
|
|
2020
|
+
function computeDistanceBetweenBoxes(boxA, boxB) {
|
|
2021
|
+
const a = getBoundingBox(boxA);
|
|
2022
|
+
const b = getBoundingBox(boxB);
|
|
2023
|
+
const dx = Math.max(a.minX - b.maxX, b.minX - a.maxX, 0);
|
|
2024
|
+
const dy = Math.max(a.minY - b.maxY, b.minY - a.maxY, 0);
|
|
2025
|
+
const pointA = { x: 0, y: 0 };
|
|
2026
|
+
const pointB = { x: 0, y: 0 };
|
|
2027
|
+
if (dx === 0 && dy === 0) {
|
|
2028
|
+
return { distance: 0, pointA: boxA.center, pointB: boxB.center };
|
|
2029
|
+
}
|
|
2030
|
+
pointA.x = clamp(boxA.center.x, b.minX, b.maxX);
|
|
2031
|
+
pointA.y = clamp(boxA.center.y, b.minY, b.maxY);
|
|
2032
|
+
pointB.x = clamp(boxB.center.x, a.minX, a.maxX);
|
|
2033
|
+
pointB.y = clamp(boxB.center.y, a.minY, a.maxY);
|
|
2034
|
+
const distance6 = Math.hypot(pointA.x - pointB.x, pointA.y - pointB.y);
|
|
2035
|
+
return { distance: distance6, pointA, pointB };
|
|
2036
|
+
}
|
|
2037
|
+
function clamp(value, min, max) {
|
|
2038
|
+
return Math.max(min, Math.min(max, value));
|
|
2039
|
+
}
|
|
2040
|
+
|
|
2041
|
+
// node_modules/@tscircuit/math-utils/dist/chunk-OMVVSGKD.js
|
|
2042
|
+
function segmentToBoundsMinDistance(a, b, bounds) {
|
|
2043
|
+
const topLeft = { x: bounds.minX, y: bounds.minY };
|
|
2044
|
+
const topRight = { x: bounds.maxX, y: bounds.minY };
|
|
2045
|
+
const bottomLeft = { x: bounds.minX, y: bounds.maxY };
|
|
2046
|
+
const bottomRight = { x: bounds.maxX, y: bounds.maxY };
|
|
2047
|
+
if (doSegmentsIntersect(a, b, topLeft, topRight) || doSegmentsIntersect(a, b, topRight, bottomRight) || doSegmentsIntersect(a, b, bottomRight, bottomLeft) || doSegmentsIntersect(a, b, bottomLeft, topLeft)) {
|
|
2048
|
+
return 0;
|
|
2049
|
+
}
|
|
2050
|
+
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) {
|
|
2051
|
+
return 0;
|
|
2052
|
+
}
|
|
2053
|
+
const distances = [
|
|
2054
|
+
pointToSegmentDistance(topLeft, a, b),
|
|
2055
|
+
pointToSegmentDistance(topRight, a, b),
|
|
2056
|
+
pointToSegmentDistance(bottomLeft, a, b),
|
|
2057
|
+
pointToSegmentDistance(bottomRight, a, b)
|
|
2058
|
+
];
|
|
2059
|
+
if (a.x >= bounds.minX && a.x <= bounds.maxX && a.y >= bounds.minY && a.y <= bounds.maxY) {
|
|
2060
|
+
return 0;
|
|
2061
|
+
}
|
|
2062
|
+
if (b.x >= bounds.minX && b.x <= bounds.maxX && b.y >= bounds.minY && b.y <= bounds.maxY) {
|
|
2063
|
+
return 0;
|
|
2064
|
+
}
|
|
2065
|
+
if (a.x < bounds.minX || a.x > bounds.maxX || a.y < bounds.minY || a.y > bounds.maxY) {
|
|
2066
|
+
const closestX = clamp(a.x, bounds.minX, bounds.maxX);
|
|
2067
|
+
const closestY = clamp(a.y, bounds.minY, bounds.maxY);
|
|
2068
|
+
distances.push(distance(a, { x: closestX, y: closestY }));
|
|
2069
|
+
}
|
|
2070
|
+
if (b.x < bounds.minX || b.x > bounds.maxX || b.y < bounds.minY || b.y > bounds.maxY) {
|
|
2071
|
+
const closestX = clamp(b.x, bounds.minX, bounds.maxX);
|
|
2072
|
+
const closestY = clamp(b.y, bounds.minY, bounds.maxY);
|
|
2073
|
+
distances.push(distance(b, { x: closestX, y: closestY }));
|
|
2074
|
+
}
|
|
2075
|
+
return Math.min(...distances);
|
|
2076
|
+
}
|
|
2077
|
+
function segmentToBoxMinDistance(a, b, box) {
|
|
2078
|
+
const halfWidth = box.width / 2;
|
|
2079
|
+
const halfHeight = box.height / 2;
|
|
2080
|
+
const bounds = {
|
|
2081
|
+
minX: box.center.x - halfWidth,
|
|
2082
|
+
maxX: box.center.x + halfWidth,
|
|
2083
|
+
minY: box.center.y - halfHeight,
|
|
2084
|
+
maxY: box.center.y + halfHeight
|
|
2085
|
+
};
|
|
2086
|
+
return segmentToBoundsMinDistance(a, b, bounds);
|
|
2087
|
+
}
|
|
2088
|
+
|
|
1941
2089
|
// lib/data-structures/SingleRouteCandidatePriorityQueue.ts
|
|
1942
2090
|
var SingleRouteCandidatePriorityQueue = class {
|
|
1943
2091
|
heap = [];
|
|
@@ -1992,6 +2140,12 @@ var SingleRouteCandidatePriorityQueue = class {
|
|
|
1992
2140
|
this.heapifyDown();
|
|
1993
2141
|
return item;
|
|
1994
2142
|
}
|
|
2143
|
+
peek() {
|
|
2144
|
+
if (this.heap.length === 0) {
|
|
2145
|
+
return null;
|
|
2146
|
+
}
|
|
2147
|
+
return this.heap[0];
|
|
2148
|
+
}
|
|
1995
2149
|
enqueue(item) {
|
|
1996
2150
|
this.heap.push(item);
|
|
1997
2151
|
this.heapifyUp();
|
|
@@ -2074,16 +2228,6 @@ var SingleHighDensityRouteSolver = class extends BaseSolver {
|
|
|
2074
2228
|
this.obstacleMargin = opts.obstacleMargin ?? 0.2;
|
|
2075
2229
|
this.layerCount = opts.layerCount ?? 2;
|
|
2076
2230
|
this.exploredNodes = /* @__PURE__ */ new Set();
|
|
2077
|
-
this.candidates = new SingleRouteCandidatePriorityQueue([
|
|
2078
|
-
{
|
|
2079
|
-
...opts.A,
|
|
2080
|
-
z: opts.A.z ?? 0,
|
|
2081
|
-
g: 0,
|
|
2082
|
-
h: 0,
|
|
2083
|
-
f: 0,
|
|
2084
|
-
parent: null
|
|
2085
|
-
}
|
|
2086
|
-
]);
|
|
2087
2231
|
this.straightLineDistance = distance(this.A, this.B);
|
|
2088
2232
|
this.futureConnections = opts.futureConnections ?? [];
|
|
2089
2233
|
this.MAX_ITERATIONS = 5e3;
|
|
@@ -2106,6 +2250,25 @@ var SingleHighDensityRouteSolver = class extends BaseSolver {
|
|
|
2106
2250
|
if (this.futureConnections && this.futureConnections.length === 0 && this.obstacleRoutes.length === 0) {
|
|
2107
2251
|
this.handleSimpleCases();
|
|
2108
2252
|
}
|
|
2253
|
+
this.candidates = new SingleRouteCandidatePriorityQueue([
|
|
2254
|
+
{
|
|
2255
|
+
...opts.A,
|
|
2256
|
+
x: Math.floor(opts.A.x / this.cellStep) * this.cellStep,
|
|
2257
|
+
y: Math.floor(opts.A.y / this.cellStep) * this.cellStep,
|
|
2258
|
+
z: opts.A.z ?? 0,
|
|
2259
|
+
g: 0,
|
|
2260
|
+
h: 0,
|
|
2261
|
+
f: 0,
|
|
2262
|
+
parent: {
|
|
2263
|
+
...opts.A,
|
|
2264
|
+
z: opts.A.z ?? 0,
|
|
2265
|
+
g: 0,
|
|
2266
|
+
h: 0,
|
|
2267
|
+
f: 0,
|
|
2268
|
+
parent: null
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
]);
|
|
2109
2272
|
}
|
|
2110
2273
|
handleSimpleCases() {
|
|
2111
2274
|
this.solved = true;
|
|
@@ -2385,6 +2548,19 @@ z: ${this.B.z}`,
|
|
|
2385
2548
|
label: `Explored (z=${z})`
|
|
2386
2549
|
});
|
|
2387
2550
|
}
|
|
2551
|
+
if (this.candidates.peek()) {
|
|
2552
|
+
const nextNode = this.candidates.peek();
|
|
2553
|
+
graphics.rects.push({
|
|
2554
|
+
center: {
|
|
2555
|
+
x: nextNode.x + nextNode.z * this.cellStep / 20,
|
|
2556
|
+
y: nextNode.y + nextNode.z * this.cellStep / 20
|
|
2557
|
+
},
|
|
2558
|
+
fill: "rgba(0, 255, 0, 0.8)",
|
|
2559
|
+
width: this.cellStep * 0.9,
|
|
2560
|
+
height: this.cellStep * 0.9,
|
|
2561
|
+
label: `Next (z=${nextNode.z})`
|
|
2562
|
+
});
|
|
2563
|
+
}
|
|
2388
2564
|
for (const route of this.obstacleRoutes) {
|
|
2389
2565
|
for (const via of route.vias) {
|
|
2390
2566
|
graphics.circles.push({
|
|
@@ -3573,13 +3749,13 @@ function findClosestPointToABCWithinBounds(A, B, C, radius, bounds) {
|
|
|
3573
3749
|
x: (A.x + B.x + C.x) / 3,
|
|
3574
3750
|
y: (A.y + B.y + C.y) / 3
|
|
3575
3751
|
};
|
|
3576
|
-
const
|
|
3752
|
+
const distance6 = (p1, p2) => {
|
|
3577
3753
|
return Math.sqrt((p2.x - p1.x) ** 2 + (p2.y - p1.y) ** 2);
|
|
3578
3754
|
};
|
|
3579
3755
|
const isValidPoint = (point) => {
|
|
3580
|
-
const distToA =
|
|
3581
|
-
const distToB =
|
|
3582
|
-
const distToC =
|
|
3756
|
+
const distToA = distance6(point, A);
|
|
3757
|
+
const distToB = distance6(point, B);
|
|
3758
|
+
const distToC = distance6(point, C);
|
|
3583
3759
|
const withinBounds = point.x >= bounds.minX && point.x <= bounds.maxX && point.y >= bounds.minY && point.y <= bounds.maxY;
|
|
3584
3760
|
return distToA >= radius && distToB >= radius && distToC >= radius && withinBounds;
|
|
3585
3761
|
};
|
|
@@ -3623,10 +3799,10 @@ function findClosestPointToABCWithinBounds(A, B, C, radius, bounds) {
|
|
|
3623
3799
|
};
|
|
3624
3800
|
const result = [];
|
|
3625
3801
|
const epsilon = 1e-6;
|
|
3626
|
-
if (Math.abs(
|
|
3802
|
+
if (Math.abs(distance6(intersection1, c1) - r) < epsilon && Math.abs(distance6(intersection1, c2) - r) < epsilon) {
|
|
3627
3803
|
result.push(intersection1);
|
|
3628
3804
|
}
|
|
3629
|
-
if (Math.abs(
|
|
3805
|
+
if (Math.abs(distance6(intersection2, c1) - r) < epsilon && Math.abs(distance6(intersection2, c2) - r) < epsilon) {
|
|
3630
3806
|
result.push(intersection2);
|
|
3631
3807
|
}
|
|
3632
3808
|
return result;
|
|
@@ -3650,7 +3826,7 @@ function findClosestPointToABCWithinBounds(A, B, C, radius, bounds) {
|
|
|
3650
3826
|
const interiorCandidates = validCandidates.filter((p) => !isOnBoundary(p));
|
|
3651
3827
|
if (interiorCandidates.length > 0) {
|
|
3652
3828
|
interiorCandidates.sort(
|
|
3653
|
-
(a, b) =>
|
|
3829
|
+
(a, b) => distance6(a, avgPoint) - distance6(b, avgPoint)
|
|
3654
3830
|
);
|
|
3655
3831
|
return interiorCandidates[0];
|
|
3656
3832
|
}
|
|
@@ -3662,7 +3838,7 @@ function findClosestPointToABCWithinBounds(A, B, C, radius, bounds) {
|
|
|
3662
3838
|
for (let y = bounds.minY + 1; y < bounds.maxY; y += gridStep) {
|
|
3663
3839
|
const point = { x, y };
|
|
3664
3840
|
if (isValidPoint(point)) {
|
|
3665
|
-
const dist =
|
|
3841
|
+
const dist = distance6(point, avgPoint);
|
|
3666
3842
|
if (dist < bestDistance) {
|
|
3667
3843
|
bestDistance = dist;
|
|
3668
3844
|
bestPoint = point;
|
|
@@ -3697,7 +3873,7 @@ function findClosestPointToABCWithinBounds(A, B, C, radius, bounds) {
|
|
|
3697
3873
|
const validBoundaryPoints = boundaryPoints.filter(isValidPoint);
|
|
3698
3874
|
if (validBoundaryPoints.length > 0) {
|
|
3699
3875
|
validBoundaryPoints.sort(
|
|
3700
|
-
(a, b) =>
|
|
3876
|
+
(a, b) => distance6(a, avgPoint) - distance6(b, avgPoint)
|
|
3701
3877
|
);
|
|
3702
3878
|
return validBoundaryPoints[0];
|
|
3703
3879
|
}
|
|
@@ -3705,9 +3881,9 @@ function findClosestPointToABCWithinBounds(A, B, C, radius, bounds) {
|
|
|
3705
3881
|
let leastBadPoint = { x: bounds.minX, y: bounds.minY };
|
|
3706
3882
|
for (const point of [...allCandidates, ...boundaryPoints]) {
|
|
3707
3883
|
if (point.x >= bounds.minX && point.x <= bounds.maxX && point.y >= bounds.minY && point.y <= bounds.maxY) {
|
|
3708
|
-
const violationA = Math.max(0, radius -
|
|
3709
|
-
const violationB = Math.max(0, radius -
|
|
3710
|
-
const violationC = Math.max(0, radius -
|
|
3884
|
+
const violationA = Math.max(0, radius - distance6(point, A));
|
|
3885
|
+
const violationB = Math.max(0, radius - distance6(point, B));
|
|
3886
|
+
const violationC = Math.max(0, radius - distance6(point, C));
|
|
3711
3887
|
const totalViolation = violationA + violationB + violationC;
|
|
3712
3888
|
if (totalViolation < minViolation) {
|
|
3713
3889
|
minViolation = totalViolation;
|
|
@@ -3722,12 +3898,62 @@ function findClosestPointToABCWithinBounds(A, B, C, radius, bounds) {
|
|
|
3722
3898
|
function findPointToGetAroundCircle(A, C, Q) {
|
|
3723
3899
|
const B = computeTangentPoint(C, A, Q.center, Q.radius);
|
|
3724
3900
|
const D = computeTangentPoint(A, C, Q.center, Q.radius);
|
|
3725
|
-
const
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3901
|
+
const distBC = distance2(B, C);
|
|
3902
|
+
const distAD = distance2(A, D);
|
|
3903
|
+
const minDistThreshold = 1e-6;
|
|
3904
|
+
const BIsValid = distBC > minDistThreshold;
|
|
3905
|
+
const DIsValid = distAD > minDistThreshold;
|
|
3906
|
+
let E;
|
|
3907
|
+
if (!BIsValid || !DIsValid) {
|
|
3908
|
+
const midAC = {
|
|
3909
|
+
x: (A.x + C.x) / 2,
|
|
3910
|
+
y: (A.y + C.y) / 2
|
|
3911
|
+
};
|
|
3912
|
+
const distFromCenter = distance2(midAC, Q.center);
|
|
3913
|
+
if (distFromCenter < Q.radius * 1.1) {
|
|
3914
|
+
const dirFromCenter = {
|
|
3915
|
+
x: (midAC.x - Q.center.x) / distFromCenter,
|
|
3916
|
+
y: (midAC.y - Q.center.y) / distFromCenter
|
|
3917
|
+
};
|
|
3918
|
+
E = {
|
|
3919
|
+
x: Q.center.x + dirFromCenter.x * Q.radius * 1.2,
|
|
3920
|
+
y: Q.center.y + dirFromCenter.y * Q.radius * 1.2
|
|
3921
|
+
};
|
|
3922
|
+
} else {
|
|
3923
|
+
E = midAC;
|
|
3924
|
+
}
|
|
3925
|
+
} else {
|
|
3926
|
+
E = {
|
|
3927
|
+
x: (B.x + D.x) / 2,
|
|
3928
|
+
y: (B.y + D.y) / 2
|
|
3929
|
+
};
|
|
3930
|
+
const distBE = distance2(B, E);
|
|
3931
|
+
const distDE = distance2(D, E);
|
|
3932
|
+
if (Math.abs(distBE - distDE) > Math.min(distBE, distDE) * 0.5) {
|
|
3933
|
+
const distAB = distance2(A, B);
|
|
3934
|
+
const distCD = distance2(C, D);
|
|
3935
|
+
const totalDist = distAB + distCD;
|
|
3936
|
+
if (totalDist > minDistThreshold) {
|
|
3937
|
+
const weightB = distCD / totalDist;
|
|
3938
|
+
const weightD = distAB / totalDist;
|
|
3939
|
+
E = {
|
|
3940
|
+
x: B.x * weightB + D.x * weightD,
|
|
3941
|
+
y: B.y * weightB + D.y * weightD
|
|
3942
|
+
};
|
|
3943
|
+
}
|
|
3944
|
+
}
|
|
3945
|
+
const distEToCenter = distance2(E, Q.center);
|
|
3946
|
+
if (distEToCenter < Q.radius * 1.05) {
|
|
3947
|
+
const dirFromCenter = {
|
|
3948
|
+
x: (E.x - Q.center.x) / distEToCenter,
|
|
3949
|
+
y: (E.y - Q.center.y) / distEToCenter
|
|
3950
|
+
};
|
|
3951
|
+
E = {
|
|
3952
|
+
x: Q.center.x + dirFromCenter.x * Q.radius * 1.2,
|
|
3953
|
+
y: Q.center.y + dirFromCenter.y * Q.radius * 1.2
|
|
3954
|
+
};
|
|
3955
|
+
}
|
|
3956
|
+
}
|
|
3731
3957
|
return { B, D, E };
|
|
3732
3958
|
}
|
|
3733
3959
|
function computeTangentPoint(observationPoint, referencePoint, circleCenter, radius) {
|
|
@@ -3736,8 +3962,29 @@ function computeTangentPoint(observationPoint, referencePoint, circleCenter, rad
|
|
|
3736
3962
|
circleCenter.y - observationPoint.y
|
|
3737
3963
|
];
|
|
3738
3964
|
const CQLength = Math.sqrt(CQ[0] * CQ[0] + CQ[1] * CQ[1]);
|
|
3739
|
-
if (CQLength
|
|
3740
|
-
|
|
3965
|
+
if (CQLength <= radius) {
|
|
3966
|
+
if (CQLength < 1e-8) {
|
|
3967
|
+
const refVec = [
|
|
3968
|
+
referencePoint.x - observationPoint.x,
|
|
3969
|
+
referencePoint.y - observationPoint.y
|
|
3970
|
+
];
|
|
3971
|
+
const refLength = Math.sqrt(refVec[0] * refVec[0] + refVec[1] * refVec[1]);
|
|
3972
|
+
if (refLength < 1e-8) {
|
|
3973
|
+
return {
|
|
3974
|
+
x: circleCenter.x + radius,
|
|
3975
|
+
y: circleCenter.y
|
|
3976
|
+
};
|
|
3977
|
+
}
|
|
3978
|
+
return {
|
|
3979
|
+
x: circleCenter.x + refVec[0] / refLength * radius,
|
|
3980
|
+
y: circleCenter.y + refVec[1] / refLength * radius
|
|
3981
|
+
};
|
|
3982
|
+
}
|
|
3983
|
+
const CQUnit2 = [CQ[0] / CQLength, CQ[1] / CQLength];
|
|
3984
|
+
return {
|
|
3985
|
+
x: circleCenter.x - CQUnit2[0] * radius,
|
|
3986
|
+
y: circleCenter.y - CQUnit2[1] * radius
|
|
3987
|
+
};
|
|
3741
3988
|
}
|
|
3742
3989
|
const CR = [
|
|
3743
3990
|
referencePoint.x - observationPoint.x,
|
|
@@ -3761,23 +4008,108 @@ function computeTangentPoint(observationPoint, referencePoint, circleCenter, rad
|
|
|
3761
4008
|
y: observationPoint.y + d * unitToTangent[1]
|
|
3762
4009
|
};
|
|
3763
4010
|
}
|
|
3764
|
-
function
|
|
3765
|
-
const
|
|
3766
|
-
const
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
4011
|
+
function distance2(p1, p2) {
|
|
4012
|
+
const dx = p2.x - p1.x;
|
|
4013
|
+
const dy = p2.y - p1.y;
|
|
4014
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
4015
|
+
}
|
|
4016
|
+
|
|
4017
|
+
// lib/solvers/HighDensitySolver/TwoRouteHighDensitySolver/calculateSideTraversal.ts
|
|
4018
|
+
var EPSILON2 = 1e-9;
|
|
4019
|
+
function calculateSegmentTraversal(startPoint, endPoint, bounds) {
|
|
4020
|
+
const startAngle = pointToAngle(startPoint, bounds);
|
|
4021
|
+
let endAngle = pointToAngle(endPoint, bounds);
|
|
4022
|
+
if (endAngle < startAngle) {
|
|
4023
|
+
endAngle += 2 * Math.PI;
|
|
4024
|
+
}
|
|
4025
|
+
if (Math.abs(endAngle - startAngle) < EPSILON2) {
|
|
4026
|
+
return { left: 0, top: 0, right: 0, bottom: 0 };
|
|
4027
|
+
}
|
|
4028
|
+
return calculateSidePercentages(startAngle, endAngle, bounds);
|
|
4029
|
+
}
|
|
4030
|
+
function calculateTraversalPercentages(A, B, C, bounds) {
|
|
4031
|
+
const percentagesAB = calculateSegmentTraversal(A, B, bounds);
|
|
4032
|
+
const percentagesBC = calculateSegmentTraversal(B, C, bounds);
|
|
4033
|
+
const totalPercentages = {
|
|
4034
|
+
left: Math.min(1, percentagesAB.left + percentagesBC.left),
|
|
4035
|
+
top: Math.min(1, percentagesAB.top + percentagesBC.top),
|
|
4036
|
+
right: Math.min(1, percentagesAB.right + percentagesBC.right),
|
|
4037
|
+
bottom: Math.min(1, percentagesAB.bottom + percentagesBC.bottom)
|
|
4038
|
+
};
|
|
4039
|
+
for (const key in totalPercentages) {
|
|
4040
|
+
if (Math.abs(totalPercentages[key]) < EPSILON2) {
|
|
4041
|
+
totalPercentages[key] = 0;
|
|
4042
|
+
}
|
|
4043
|
+
}
|
|
4044
|
+
return totalPercentages;
|
|
4045
|
+
}
|
|
4046
|
+
function pointToAngle(point, bounds) {
|
|
4047
|
+
const width = bounds.maxX - bounds.minX;
|
|
4048
|
+
const height = bounds.maxY - bounds.minY;
|
|
4049
|
+
if (width < EPSILON2 && height < EPSILON2) return 0;
|
|
4050
|
+
const perimeter = 2 * (width + height);
|
|
4051
|
+
if (perimeter < EPSILON2) return 0;
|
|
4052
|
+
let distance6 = 0;
|
|
4053
|
+
if (Math.abs(point.y - bounds.maxY) < EPSILON2 && point.x >= bounds.minX - EPSILON2 && point.x <= bounds.maxX + EPSILON2) {
|
|
4054
|
+
distance6 = Math.max(0, Math.min(width, point.x - bounds.minX));
|
|
4055
|
+
} else if (Math.abs(point.x - bounds.maxX) < EPSILON2 && point.y >= bounds.minY - EPSILON2 && point.y <= bounds.maxY + EPSILON2) {
|
|
4056
|
+
distance6 = width + Math.max(0, Math.min(height, bounds.maxY - point.y));
|
|
4057
|
+
} else if (Math.abs(point.y - bounds.minY) < EPSILON2 && point.x >= bounds.minX - EPSILON2 && point.x <= bounds.maxX + EPSILON2) {
|
|
4058
|
+
distance6 = width + height + Math.max(0, Math.min(width, bounds.maxX - point.x));
|
|
4059
|
+
} else if (Math.abs(point.x - bounds.minX) < EPSILON2 && point.y >= bounds.minY - EPSILON2 && point.y <= bounds.maxY + EPSILON2) {
|
|
4060
|
+
distance6 = width + height + width + Math.max(0, Math.min(height, point.y - bounds.minY));
|
|
4061
|
+
} else {
|
|
4062
|
+
throw new Error(
|
|
4063
|
+
`Point (${point.x}, ${point.y}) does not lie on the boundary defined by ${JSON.stringify(bounds)}`
|
|
4064
|
+
);
|
|
4065
|
+
}
|
|
4066
|
+
distance6 = Math.max(0, Math.min(perimeter, distance6));
|
|
4067
|
+
return perimeter > EPSILON2 ? distance6 / perimeter * (2 * Math.PI) : 0;
|
|
4068
|
+
}
|
|
4069
|
+
function calculateSidePercentages(startAngle, endAngle, bounds) {
|
|
4070
|
+
const width = bounds.maxX - bounds.minX;
|
|
4071
|
+
const height = bounds.maxY - bounds.minY;
|
|
4072
|
+
if (width < EPSILON2 && height < EPSILON2)
|
|
4073
|
+
return { left: 0, top: 0, right: 0, bottom: 0 };
|
|
4074
|
+
const perimeter = 2 * (width + height);
|
|
4075
|
+
if (perimeter < EPSILON2) return { left: 0, top: 0, right: 0, bottom: 0 };
|
|
4076
|
+
const angleTopEnd = width / perimeter * (2 * Math.PI);
|
|
4077
|
+
const angleRightEnd = (width + height) / perimeter * (2 * Math.PI);
|
|
4078
|
+
const angleBottomEnd = (width + width + height) / perimeter * (2 * Math.PI);
|
|
4079
|
+
const angleLeftEnd = 2 * Math.PI;
|
|
4080
|
+
const sides = [
|
|
4081
|
+
{ name: "top", start: 0, end: angleTopEnd, length: width },
|
|
4082
|
+
{ name: "right", start: angleTopEnd, end: angleRightEnd, length: height },
|
|
4083
|
+
{
|
|
4084
|
+
name: "bottom",
|
|
4085
|
+
start: angleRightEnd,
|
|
4086
|
+
end: angleBottomEnd,
|
|
4087
|
+
length: width
|
|
4088
|
+
},
|
|
4089
|
+
{ name: "left", start: angleBottomEnd, end: angleLeftEnd, length: height }
|
|
4090
|
+
// Ends at 2PI
|
|
4091
|
+
];
|
|
4092
|
+
const result = { left: 0, top: 0, right: 0, bottom: 0 };
|
|
4093
|
+
const totalAngleTraversal = endAngle - startAngle;
|
|
4094
|
+
if (totalAngleTraversal < EPSILON2) return result;
|
|
4095
|
+
for (const side of sides) {
|
|
4096
|
+
const sideAngleRange = side.end - side.start;
|
|
4097
|
+
if (sideAngleRange < EPSILON2 || side.length < EPSILON2) continue;
|
|
4098
|
+
const overlapStart = Math.max(startAngle, side.start);
|
|
4099
|
+
const overlapEnd = Math.min(endAngle, side.end);
|
|
4100
|
+
if (overlapStart < overlapEnd - EPSILON2) {
|
|
4101
|
+
const traversedAngleOnSide = overlapEnd - overlapStart;
|
|
4102
|
+
const percentage = traversedAngleOnSide / sideAngleRange;
|
|
4103
|
+
result[side.name] += Math.max(0, percentage);
|
|
4104
|
+
}
|
|
4105
|
+
}
|
|
4106
|
+
for (const key in result) {
|
|
4107
|
+
result[key] = Math.max(
|
|
4108
|
+
0,
|
|
4109
|
+
Math.min(1, result[key])
|
|
4110
|
+
);
|
|
3777
4111
|
}
|
|
3778
|
-
|
|
3779
|
-
const y = (a1 * c2 - a2 * c1) / det;
|
|
3780
|
-
return { x, y };
|
|
4112
|
+
return result;
|
|
3781
4113
|
}
|
|
3782
4114
|
|
|
3783
4115
|
// lib/solvers/HighDensitySolver/TwoRouteHighDensitySolver/SingleTransitionCrossingRouteSolver.ts
|
|
@@ -3864,17 +4196,30 @@ var SingleTransitionCrossingRouteSolver = class extends BaseSolver {
|
|
|
3864
4196
|
const ntrP1 = transitionRoute.A.z !== flatRouteZ ? transitionRoute.A : transitionRoute.B;
|
|
3865
4197
|
const marginFromBorderWithTrace = this.obstacleMargin * 2 + this.viaDiameter / 2 + this.traceThickness;
|
|
3866
4198
|
const marginFromBorderWithoutTrace = this.obstacleMargin + this.viaDiameter / 2;
|
|
4199
|
+
const A = flatRoute.A;
|
|
4200
|
+
const B = ntrP1;
|
|
4201
|
+
const C = flatRoute.B;
|
|
4202
|
+
const sideTraversal = calculateTraversalPercentages(A, B, C, this.bounds);
|
|
4203
|
+
const viaBounds = {
|
|
4204
|
+
minX: this.bounds.minX + (sideTraversal.left > 0.5 ? marginFromBorderWithTrace : marginFromBorderWithoutTrace),
|
|
4205
|
+
minY: this.bounds.minY + (sideTraversal.bottom > 0.5 ? marginFromBorderWithTrace : marginFromBorderWithoutTrace),
|
|
4206
|
+
maxX: this.bounds.maxX - (sideTraversal.right > 0.5 ? marginFromBorderWithTrace : marginFromBorderWithoutTrace),
|
|
4207
|
+
maxY: this.bounds.maxY - (sideTraversal.top > 0.5 ? marginFromBorderWithTrace : marginFromBorderWithoutTrace)
|
|
4208
|
+
};
|
|
4209
|
+
if (viaBounds.maxY < viaBounds.minY) {
|
|
4210
|
+
viaBounds.minY = (viaBounds.minY + viaBounds.maxY) / 2;
|
|
4211
|
+
viaBounds.maxY = viaBounds.minY;
|
|
4212
|
+
}
|
|
4213
|
+
if (viaBounds.maxX < viaBounds.minX) {
|
|
4214
|
+
viaBounds.minX = (viaBounds.minX + viaBounds.maxX) / 2;
|
|
4215
|
+
viaBounds.maxX = viaBounds.minX;
|
|
4216
|
+
}
|
|
3867
4217
|
return findClosestPointToABCWithinBounds(
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
4218
|
+
A,
|
|
4219
|
+
B,
|
|
4220
|
+
C,
|
|
3871
4221
|
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
|
-
}
|
|
4222
|
+
viaBounds
|
|
3878
4223
|
);
|
|
3879
4224
|
}
|
|
3880
4225
|
/**
|
|
@@ -4662,13 +5007,13 @@ function buildMinimumSpanningTree(points) {
|
|
|
4662
5007
|
if (point.x === neighbor.x && point.y === neighbor.y) {
|
|
4663
5008
|
continue;
|
|
4664
5009
|
}
|
|
4665
|
-
const
|
|
5010
|
+
const distance6 = Math.sqrt(
|
|
4666
5011
|
(point.x - neighbor.x) ** 2 + (point.y - neighbor.y) ** 2
|
|
4667
5012
|
);
|
|
4668
5013
|
edges.push({
|
|
4669
5014
|
from: point,
|
|
4670
5015
|
to: neighbor,
|
|
4671
|
-
weight:
|
|
5016
|
+
weight: distance6
|
|
4672
5017
|
});
|
|
4673
5018
|
}
|
|
4674
5019
|
}
|
|
@@ -5137,6 +5482,7 @@ var convertSrjToGraphicsObject = (srj) => {
|
|
|
5137
5482
|
const circles = [];
|
|
5138
5483
|
const points = [];
|
|
5139
5484
|
const colorMap = getColorMap(srj);
|
|
5485
|
+
const layerCount = 2;
|
|
5140
5486
|
if (srj.connections) {
|
|
5141
5487
|
for (const connection of srj.connections) {
|
|
5142
5488
|
for (const point of connection.pointsToConnect) {
|
|
@@ -5144,6 +5490,7 @@ var convertSrjToGraphicsObject = (srj) => {
|
|
|
5144
5490
|
x: point.x,
|
|
5145
5491
|
y: point.y,
|
|
5146
5492
|
color: colorMap[connection.name],
|
|
5493
|
+
layer: point.layer ?? ("z" in point ? mapZToLayerName(point.z, layerCount) : "top"),
|
|
5147
5494
|
label: `${connection.name} (${point.layer})`
|
|
5148
5495
|
});
|
|
5149
5496
|
}
|
|
@@ -5160,7 +5507,8 @@ var convertSrjToGraphicsObject = (srj) => {
|
|
|
5160
5507
|
radius: 0.3,
|
|
5161
5508
|
// 0.6 via diameter
|
|
5162
5509
|
fill: "blue",
|
|
5163
|
-
stroke: "none"
|
|
5510
|
+
stroke: "none",
|
|
5511
|
+
layer: "z0,1"
|
|
5164
5512
|
});
|
|
5165
5513
|
} else if (routePoint.route_type === "wire" && nextRoutePoint.route_type === "wire" && nextRoutePoint.layer === routePoint.layer) {
|
|
5166
5514
|
lines.push({
|
|
@@ -5168,6 +5516,7 @@ var convertSrjToGraphicsObject = (srj) => {
|
|
|
5168
5516
|
{ x: routePoint.x, y: routePoint.y },
|
|
5169
5517
|
{ x: nextRoutePoint.x, y: nextRoutePoint.y }
|
|
5170
5518
|
],
|
|
5519
|
+
layer: `z${mapLayerNameToZ(routePoint.layer, layerCount)}`,
|
|
5171
5520
|
strokeWidth: 0.15,
|
|
5172
5521
|
strokeColor: safeTransparentize(
|
|
5173
5522
|
{
|
|
@@ -5191,7 +5540,8 @@ var convertSrjToGraphicsObject = (srj) => {
|
|
|
5191
5540
|
center: o.center,
|
|
5192
5541
|
width: o.width,
|
|
5193
5542
|
height: o.height,
|
|
5194
|
-
fill: "rgba(255,0,0,0.5)"
|
|
5543
|
+
fill: "rgba(255,0,0,0.5)",
|
|
5544
|
+
layer: `z${o.layers.map(mapLayerNameToZ).join(",")}`
|
|
5195
5545
|
})
|
|
5196
5546
|
),
|
|
5197
5547
|
circles,
|
|
@@ -5204,23 +5554,26 @@ var convertSrjToGraphicsObject = (srj) => {
|
|
|
5204
5554
|
function getNodesNearNode(params) {
|
|
5205
5555
|
const { nodeId, nodeIdToSegmentIds, segmentIdToNodeIds, hops } = params;
|
|
5206
5556
|
if (hops === 0) return [nodeId];
|
|
5207
|
-
const
|
|
5208
|
-
const
|
|
5209
|
-
|
|
5210
|
-
const
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5557
|
+
const visitedNodes = /* @__PURE__ */ new Set([nodeId]);
|
|
5558
|
+
const exploreQueue = [{ nodeId, remainingHops: hops }];
|
|
5559
|
+
while (exploreQueue.length > 0) {
|
|
5560
|
+
const { nodeId: node, remainingHops } = exploreQueue.shift();
|
|
5561
|
+
if (remainingHops === 0) continue;
|
|
5562
|
+
const segments = nodeIdToSegmentIds.get(node) || [];
|
|
5563
|
+
for (const segmentId of segments) {
|
|
5564
|
+
const adjacentNodeIds = segmentIdToNodeIds.get(segmentId) || [];
|
|
5565
|
+
for (const adjacentNodeId of adjacentNodeIds) {
|
|
5566
|
+
if (!visitedNodes.has(adjacentNodeId)) {
|
|
5567
|
+
visitedNodes.add(adjacentNodeId);
|
|
5568
|
+
exploreQueue.push({
|
|
5569
|
+
nodeId: adjacentNodeId,
|
|
5570
|
+
remainingHops: remainingHops - 1
|
|
5571
|
+
});
|
|
5572
|
+
}
|
|
5220
5573
|
}
|
|
5221
5574
|
}
|
|
5222
5575
|
}
|
|
5223
|
-
return Array.from(
|
|
5576
|
+
return Array.from(visitedNodes);
|
|
5224
5577
|
}
|
|
5225
5578
|
|
|
5226
5579
|
// lib/solvers/UnravelSolver/createPointModificationsHash.ts
|
|
@@ -6005,7 +6358,7 @@ var getDedupedSegments = (assignedSegments) => {
|
|
|
6005
6358
|
const dedupedSegPointMap = /* @__PURE__ */ new Map();
|
|
6006
6359
|
let highestSegmentId = -1;
|
|
6007
6360
|
for (const seg of assignedSegments) {
|
|
6008
|
-
const segKey = `${seg.start.x}-${seg.start.y}-${seg.end.x}-${seg.end.y}`;
|
|
6361
|
+
const segKey = `${seg.start.x}-${seg.start.y}-${seg.end.x}-${seg.end.y}-${seg.availableZ.join(",")}`;
|
|
6009
6362
|
const existingSeg = dedupedSegPointMap.get(segKey);
|
|
6010
6363
|
if (!existingSeg) {
|
|
6011
6364
|
highestSegmentId++;
|
|
@@ -6019,36 +6372,48 @@ var getDedupedSegments = (assignedSegments) => {
|
|
|
6019
6372
|
return dedupedSegments;
|
|
6020
6373
|
};
|
|
6021
6374
|
|
|
6022
|
-
// lib/
|
|
6023
|
-
var
|
|
6375
|
+
// lib/solvers/UnravelSolver/calculateCrossingProbabilityOfFailure.ts
|
|
6376
|
+
var calculateNodeProbabilityOfFailure = (node, numSameLayerCrossings, numEntryExitLayerChanges, numTransitionCrossings) => {
|
|
6377
|
+
if (node?._containsTarget) return 0;
|
|
6378
|
+
const totalCapacity = getTunedTotalCapacity1(node);
|
|
6379
|
+
const estNumVias = numSameLayerCrossings * 0.82 + numEntryExitLayerChanges * 0.41 + numTransitionCrossings * 0.2;
|
|
6380
|
+
const estUsedCapacity = (estNumVias / 2) ** 1.1;
|
|
6381
|
+
const approxProb = estUsedCapacity / totalCapacity;
|
|
6382
|
+
return approxProb;
|
|
6383
|
+
};
|
|
6384
|
+
|
|
6385
|
+
// lib/utils/getIntraNodeCrossingsFromSegmentPoints.ts
|
|
6386
|
+
var getIntraNodeCrossingsFromSegmentPoints = (segmentPoints) => {
|
|
6024
6387
|
let numSameLayerCrossings = 0;
|
|
6025
|
-
const pointPairs = [];
|
|
6026
|
-
const transitionPairPoints = [];
|
|
6027
6388
|
let numEntryExitLayerChanges = 0;
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
if (transitionPairPoints.some((p) => p.connectionName === aConnName)) {
|
|
6034
|
-
continue;
|
|
6389
|
+
let numTransitionCrossings = 0;
|
|
6390
|
+
const connectionGroups = /* @__PURE__ */ new Map();
|
|
6391
|
+
for (const point of segmentPoints) {
|
|
6392
|
+
if (!connectionGroups.has(point.connectionName)) {
|
|
6393
|
+
connectionGroups.set(point.connectionName, []);
|
|
6035
6394
|
}
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
|
|
6395
|
+
connectionGroups.get(point.connectionName).push(point);
|
|
6396
|
+
}
|
|
6397
|
+
const pointPairs = [];
|
|
6398
|
+
const transitionPairPoints = [];
|
|
6399
|
+
for (const [connectionName, points] of connectionGroups.entries()) {
|
|
6400
|
+
if (points.length < 2) continue;
|
|
6401
|
+
const firstPoint = points[0];
|
|
6402
|
+
for (let i = 1; i < points.length; i++) {
|
|
6403
|
+
const secondPoint = points[i];
|
|
6404
|
+
const pointPair = {
|
|
6405
|
+
connectionName,
|
|
6406
|
+
z: firstPoint.z,
|
|
6407
|
+
points: [firstPoint, secondPoint]
|
|
6408
|
+
};
|
|
6409
|
+
if (firstPoint.z !== secondPoint.z) {
|
|
6046
6410
|
numEntryExitLayerChanges++;
|
|
6047
|
-
transitionPairPoints.push(
|
|
6048
|
-
|
|
6411
|
+
transitionPairPoints.push({
|
|
6412
|
+
connectionName,
|
|
6413
|
+
points: [firstPoint, secondPoint]
|
|
6414
|
+
});
|
|
6049
6415
|
} else {
|
|
6050
6416
|
pointPairs.push(pointPair);
|
|
6051
|
-
break;
|
|
6052
6417
|
}
|
|
6053
6418
|
}
|
|
6054
6419
|
}
|
|
@@ -6066,7 +6431,6 @@ var getIntraNodeCrossingsFromSegments = (segments) => {
|
|
|
6066
6431
|
}
|
|
6067
6432
|
}
|
|
6068
6433
|
}
|
|
6069
|
-
let numTransitionCrossings = 0;
|
|
6070
6434
|
for (let i = 0; i < transitionPairPoints.length; i++) {
|
|
6071
6435
|
for (let j = i + 1; j < transitionPairPoints.length; j++) {
|
|
6072
6436
|
const pair1 = transitionPairPoints[i];
|
|
@@ -6102,16 +6466,6 @@ var getIntraNodeCrossingsFromSegments = (segments) => {
|
|
|
6102
6466
|
};
|
|
6103
6467
|
};
|
|
6104
6468
|
|
|
6105
|
-
// lib/solvers/UnravelSolver/calculateCrossingProbabilityOfFailure.ts
|
|
6106
|
-
var calculateNodeProbabilityOfFailure = (node, numSameLayerCrossings, numEntryExitLayerChanges, numTransitionCrossings) => {
|
|
6107
|
-
if (node?._containsTarget) return 0;
|
|
6108
|
-
const totalCapacity = getTunedTotalCapacity1(node);
|
|
6109
|
-
const estNumVias = numSameLayerCrossings * 0.82 + numEntryExitLayerChanges * 0.41 + numTransitionCrossings * 0.2;
|
|
6110
|
-
const estUsedCapacity = (estNumVias / 2) ** 1.1;
|
|
6111
|
-
const approxProb = estUsedCapacity / totalCapacity;
|
|
6112
|
-
return approxProb;
|
|
6113
|
-
};
|
|
6114
|
-
|
|
6115
6469
|
// lib/solvers/UnravelSolver/UnravelMultiSectionSolver.ts
|
|
6116
6470
|
var UnravelMultiSectionSolver = class extends BaseSolver {
|
|
6117
6471
|
nodeMap;
|
|
@@ -6185,8 +6539,10 @@ var UnravelMultiSectionSolver = class extends BaseSolver {
|
|
|
6185
6539
|
numSameLayerCrossings,
|
|
6186
6540
|
numEntryExitLayerChanges,
|
|
6187
6541
|
numTransitionCrossings
|
|
6188
|
-
} =
|
|
6189
|
-
this.
|
|
6542
|
+
} = getIntraNodeCrossingsFromSegmentPoints(
|
|
6543
|
+
(this.nodeToSegmentPointMap.get(node.capacityMeshNodeId) ?? []).map(
|
|
6544
|
+
(segPointId) => this.segmentPointMap.get(segPointId)
|
|
6545
|
+
)
|
|
6190
6546
|
);
|
|
6191
6547
|
const probabilityOfFailure = calculateNodeProbabilityOfFailure(
|
|
6192
6548
|
node,
|
|
@@ -6249,12 +6605,18 @@ var UnravelMultiSectionSolver = class extends BaseSolver {
|
|
|
6249
6605
|
segmentPoint.y = pointModification.y ?? segmentPoint.y;
|
|
6250
6606
|
segmentPoint.z = pointModification.z ?? segmentPoint.z;
|
|
6251
6607
|
}
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
this.
|
|
6257
|
-
);
|
|
6608
|
+
const possiblyImpactedNodeIds = getNodesNearNode({
|
|
6609
|
+
hops: this.activeSolver.MUTABLE_HOPS + 2,
|
|
6610
|
+
nodeId: this.activeSolver.rootNodeId,
|
|
6611
|
+
nodeIdToSegmentIds: this.nodeIdToSegmentIds,
|
|
6612
|
+
segmentIdToNodeIds: this.segmentIdToNodeIds
|
|
6613
|
+
});
|
|
6614
|
+
for (const nodeId of possiblyImpactedNodeIds) {
|
|
6615
|
+
this.nodePfMap.set(
|
|
6616
|
+
nodeId,
|
|
6617
|
+
this.computeNodePf(this.nodeMap.get(nodeId))
|
|
6618
|
+
);
|
|
6619
|
+
}
|
|
6258
6620
|
}
|
|
6259
6621
|
this.activeSolver = null;
|
|
6260
6622
|
}
|
|
@@ -6489,11 +6851,11 @@ var CapacityPathingSolver = class extends BaseSolver {
|
|
|
6489
6851
|
let closestNode = this.nodes[0];
|
|
6490
6852
|
let minDistance = Number.MAX_VALUE;
|
|
6491
6853
|
for (const node of nodesWithTargets) {
|
|
6492
|
-
const
|
|
6854
|
+
const distance6 = Math.sqrt(
|
|
6493
6855
|
(node.center.x - point.x) ** 2 + (node.center.y - point.y) ** 2
|
|
6494
6856
|
);
|
|
6495
|
-
if (
|
|
6496
|
-
minDistance =
|
|
6857
|
+
if (distance6 < minDistance) {
|
|
6858
|
+
minDistance = distance6;
|
|
6497
6859
|
closestNode = node;
|
|
6498
6860
|
}
|
|
6499
6861
|
}
|
|
@@ -7085,7 +7447,7 @@ var CapacityNodeTree = class {
|
|
|
7085
7447
|
};
|
|
7086
7448
|
|
|
7087
7449
|
// lib/solvers/SingleLayerNodeMerger/SingleLayerNodeMergerSolver.ts
|
|
7088
|
-
var
|
|
7450
|
+
var EPSILON3 = 5e-3;
|
|
7089
7451
|
var SingleLayerNodeMergerSolver = class extends BaseSolver {
|
|
7090
7452
|
nodeMap;
|
|
7091
7453
|
currentBatchNodeIds;
|
|
@@ -7244,7 +7606,7 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
|
|
|
7244
7606
|
adjacentNodesToLeft.reduce((acc, adjNode) => {
|
|
7245
7607
|
return acc + adjNode.height;
|
|
7246
7608
|
}, 0) - rootNode.height
|
|
7247
|
-
) <
|
|
7609
|
+
) < EPSILON3;
|
|
7248
7610
|
if (leftAdjNodesTakeUpEntireHeight && leftAdjNodesAreAllSameSize) {
|
|
7249
7611
|
rootNode.width += leftAdjNodeWidth;
|
|
7250
7612
|
rootNode.center.x = rootNode.center.x - leftAdjNodeWidth / 2;
|
|
@@ -7264,7 +7626,7 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
|
|
|
7264
7626
|
adjacentNodesToRight.reduce((acc, adjNode) => {
|
|
7265
7627
|
return acc + adjNode.height;
|
|
7266
7628
|
}, 0) - rootNode.height
|
|
7267
|
-
) <
|
|
7629
|
+
) < EPSILON3;
|
|
7268
7630
|
if (rightAdjNodesTakeUpEntireHeight && rightAdjNodesAreAllSameSize) {
|
|
7269
7631
|
rootNode.width += rightAdjNodeWidth;
|
|
7270
7632
|
rootNode.center.x = rootNode.center.x + rightAdjNodeWidth / 2;
|
|
@@ -7284,7 +7646,7 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
|
|
|
7284
7646
|
adjacentNodesToTop.reduce((acc, adjNode) => {
|
|
7285
7647
|
return acc + adjNode.width;
|
|
7286
7648
|
}, 0) - rootNode.width
|
|
7287
|
-
) <
|
|
7649
|
+
) < EPSILON3;
|
|
7288
7650
|
if (topAdjNodesTakeUpEntireWidth && topAdjNodesAreAllSameSize) {
|
|
7289
7651
|
rootNode.height += topAdjNodeHeight;
|
|
7290
7652
|
rootNode.center.y = rootNode.center.y + topAdjNodeHeight / 2;
|
|
@@ -7304,7 +7666,7 @@ var SingleLayerNodeMergerSolver = class extends BaseSolver {
|
|
|
7304
7666
|
adjacentNodesToBottom.reduce((acc, adjNode) => {
|
|
7305
7667
|
return acc + adjNode.width;
|
|
7306
7668
|
}, 0) - rootNode.width
|
|
7307
|
-
) <
|
|
7669
|
+
) < EPSILON3;
|
|
7308
7670
|
if (bottomAdjNodesTakeUpEntireWidth && bottomAdjNodesAreAllSameSize) {
|
|
7309
7671
|
rootNode.height += bottomAdjNodeHeight;
|
|
7310
7672
|
rootNode.center.y = rootNode.center.y - bottomAdjNodeHeight / 2;
|
|
@@ -7401,6 +7763,15 @@ var SingleSimplifiedPathSolver = class extends BaseSolver {
|
|
|
7401
7763
|
this.newRoute = [this.inputRoute.route[0]];
|
|
7402
7764
|
this.newVias = [];
|
|
7403
7765
|
}
|
|
7766
|
+
getConstructorParams() {
|
|
7767
|
+
return {
|
|
7768
|
+
inputRoute: this.inputRoute,
|
|
7769
|
+
otherHdRoutes: this.otherHdRoutes,
|
|
7770
|
+
obstacles: this.obstacles,
|
|
7771
|
+
connMap: this.connMap.netMap,
|
|
7772
|
+
colorMap: this.colorMap
|
|
7773
|
+
};
|
|
7774
|
+
}
|
|
7404
7775
|
get simplifiedRoute() {
|
|
7405
7776
|
return {
|
|
7406
7777
|
connectionName: this.inputRoute.connectionName,
|
|
@@ -7489,6 +7860,14 @@ var SingleSimplifiedPathSolver = class extends BaseSolver {
|
|
|
7489
7860
|
});
|
|
7490
7861
|
}
|
|
7491
7862
|
}
|
|
7863
|
+
if ("filteredObstaclePathSegments" in this) {
|
|
7864
|
+
const filteredObstaclePathSegments = this.filteredObstaclePathSegments;
|
|
7865
|
+
for (const [start, end] of filteredObstaclePathSegments) {
|
|
7866
|
+
graphics.lines.push({
|
|
7867
|
+
points: [start, end]
|
|
7868
|
+
});
|
|
7869
|
+
}
|
|
7870
|
+
}
|
|
7492
7871
|
return graphics;
|
|
7493
7872
|
}
|
|
7494
7873
|
};
|
|
@@ -7541,23 +7920,23 @@ function pointToSegmentDistance4(P, Q1, Q2) {
|
|
|
7541
7920
|
const w = { x: P.x - Q1.x, y: P.y - Q1.y };
|
|
7542
7921
|
const c1 = dotProduct(w, v);
|
|
7543
7922
|
if (c1 <= 0) {
|
|
7544
|
-
return
|
|
7923
|
+
return distance5(P, Q1);
|
|
7545
7924
|
}
|
|
7546
7925
|
const c2 = dotProduct(v, v);
|
|
7547
7926
|
if (c2 <= c1) {
|
|
7548
|
-
return
|
|
7927
|
+
return distance5(P, Q2);
|
|
7549
7928
|
}
|
|
7550
7929
|
const b = c1 / c2;
|
|
7551
7930
|
const Pb = {
|
|
7552
7931
|
x: Q1.x + b * v.x,
|
|
7553
7932
|
y: Q1.y + b * v.y
|
|
7554
7933
|
};
|
|
7555
|
-
return
|
|
7934
|
+
return distance5(P, Pb);
|
|
7556
7935
|
}
|
|
7557
7936
|
function dotProduct(v1, v2) {
|
|
7558
7937
|
return v1.x * v2.x + v1.y * v2.y;
|
|
7559
7938
|
}
|
|
7560
|
-
function
|
|
7939
|
+
function distance5(p1, p2) {
|
|
7561
7940
|
const dx = p2.x - p1.x;
|
|
7562
7941
|
const dy = p2.y - p1.y;
|
|
7563
7942
|
return Math.sqrt(dx * dx + dy * dy);
|
|
@@ -7593,23 +7972,33 @@ var getSegmentBounds = (segment) => {
|
|
|
7593
7972
|
};
|
|
7594
7973
|
};
|
|
7595
7974
|
var SegmentTree = class {
|
|
7975
|
+
// traceThickness + obstacleMargin
|
|
7596
7976
|
constructor(segments) {
|
|
7597
7977
|
this.segments = segments;
|
|
7598
7978
|
this.buckets = /* @__PURE__ */ new Map();
|
|
7979
|
+
const segmentsById = /* @__PURE__ */ new Map();
|
|
7599
7980
|
for (const segment of segments) {
|
|
7981
|
+
const segmentKey = this.getSegmentKey(segment);
|
|
7982
|
+
if (segmentsById.has(segmentKey)) continue;
|
|
7983
|
+
segmentsById.set(segmentKey, segment);
|
|
7600
7984
|
const bounds = getSegmentBounds(segment);
|
|
7601
|
-
const
|
|
7602
|
-
const
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
7985
|
+
const minIndexX = Math.floor(bounds.minX / this.CELL_SIZE);
|
|
7986
|
+
const maxIndexX = Math.floor(bounds.maxX / this.CELL_SIZE);
|
|
7987
|
+
const minIndexY = Math.floor(bounds.minY / this.CELL_SIZE);
|
|
7988
|
+
const maxIndexY = Math.floor(bounds.maxY / this.CELL_SIZE);
|
|
7989
|
+
for (let ix = minIndexX; ix <= maxIndexX; ix++) {
|
|
7990
|
+
for (let iy = minIndexY; iy <= maxIndexY; iy++) {
|
|
7991
|
+
const bucketKey = `${ix}x${iy}`;
|
|
7606
7992
|
const bucket = this.buckets.get(bucketKey);
|
|
7993
|
+
const segmentWithId = [
|
|
7994
|
+
segment[0],
|
|
7995
|
+
segment[1],
|
|
7996
|
+
segmentKey
|
|
7997
|
+
];
|
|
7607
7998
|
if (!bucket) {
|
|
7608
|
-
this.buckets.set(bucketKey, [
|
|
7609
|
-
[segment[0], segment[1], this.getSegmentKey(segment)]
|
|
7610
|
-
]);
|
|
7999
|
+
this.buckets.set(bucketKey, [segmentWithId]);
|
|
7611
8000
|
} else {
|
|
7612
|
-
bucket.push(
|
|
8001
|
+
bucket.push(segmentWithId);
|
|
7613
8002
|
}
|
|
7614
8003
|
}
|
|
7615
8004
|
}
|
|
@@ -7617,6 +8006,7 @@ var SegmentTree = class {
|
|
|
7617
8006
|
}
|
|
7618
8007
|
buckets;
|
|
7619
8008
|
CELL_SIZE = 0.4;
|
|
8009
|
+
SEGMENT_MARGIN = 0.4;
|
|
7620
8010
|
getBucketKey(x, y) {
|
|
7621
8011
|
return `${Math.floor(x / this.CELL_SIZE)}x${Math.floor(y / this.CELL_SIZE)}`;
|
|
7622
8012
|
}
|
|
@@ -7626,21 +8016,26 @@ var SegmentTree = class {
|
|
|
7626
8016
|
getSegmentsThatCouldIntersect(A, B) {
|
|
7627
8017
|
const segments = [];
|
|
7628
8018
|
const alreadyAddedSegments = /* @__PURE__ */ new Set();
|
|
7629
|
-
const minX = Math.min(A.x, B.x);
|
|
7630
|
-
const minY = Math.min(A.y, B.y);
|
|
7631
|
-
const maxX = Math.max(A.x, B.x);
|
|
7632
|
-
const maxY = Math.max(A.y, B.y);
|
|
7633
|
-
const
|
|
7634
|
-
const
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
7641
|
-
|
|
7642
|
-
|
|
7643
|
-
|
|
8019
|
+
const minX = Math.min(A.x, B.x) - this.SEGMENT_MARGIN;
|
|
8020
|
+
const minY = Math.min(A.y, B.y) - this.SEGMENT_MARGIN;
|
|
8021
|
+
const maxX = Math.max(A.x, B.x) + this.SEGMENT_MARGIN;
|
|
8022
|
+
const maxY = Math.max(A.y, B.y) + this.SEGMENT_MARGIN;
|
|
8023
|
+
const minIndexX = Math.floor(minX / this.CELL_SIZE);
|
|
8024
|
+
const maxIndexX = Math.floor(maxX / this.CELL_SIZE);
|
|
8025
|
+
const minIndexY = Math.floor(minY / this.CELL_SIZE);
|
|
8026
|
+
const maxIndexY = Math.floor(maxY / this.CELL_SIZE);
|
|
8027
|
+
for (let ix = minIndexX; ix <= maxIndexX; ix++) {
|
|
8028
|
+
for (let iy = minIndexY; iy <= maxIndexY; iy++) {
|
|
8029
|
+
const bucketKey = `${ix}x${iy}`;
|
|
8030
|
+
const bucket = this.buckets.get(bucketKey);
|
|
8031
|
+
if (bucket) {
|
|
8032
|
+
for (const segment of bucket) {
|
|
8033
|
+
const key = segment[2];
|
|
8034
|
+
if (!alreadyAddedSegments.has(key)) {
|
|
8035
|
+
alreadyAddedSegments.add(key);
|
|
8036
|
+
segments.push(segment);
|
|
8037
|
+
}
|
|
8038
|
+
}
|
|
7644
8039
|
}
|
|
7645
8040
|
}
|
|
7646
8041
|
}
|
|
@@ -7669,7 +8064,8 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
|
|
|
7669
8064
|
filteredObstaclePathSegments = [];
|
|
7670
8065
|
filteredVias = [];
|
|
7671
8066
|
segmentTree;
|
|
7672
|
-
OBSTACLE_MARGIN = 0.
|
|
8067
|
+
OBSTACLE_MARGIN = 0.1;
|
|
8068
|
+
TRACE_THICKNESS = 0.15;
|
|
7673
8069
|
TAIL_JUMP_RATIO = 0.8;
|
|
7674
8070
|
constructor(params) {
|
|
7675
8071
|
super(params);
|
|
@@ -7689,6 +8085,14 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
|
|
|
7689
8085
|
},
|
|
7690
8086
|
{ minX: Infinity, maxX: -Infinity, minY: Infinity, maxY: -Infinity }
|
|
7691
8087
|
);
|
|
8088
|
+
const boundsBox = {
|
|
8089
|
+
center: {
|
|
8090
|
+
x: (bounds.minX + bounds.maxX) / 2,
|
|
8091
|
+
y: (bounds.minY + bounds.maxY) / 2
|
|
8092
|
+
},
|
|
8093
|
+
width: bounds.maxX - bounds.minX,
|
|
8094
|
+
height: bounds.maxY - bounds.minY
|
|
8095
|
+
};
|
|
7692
8096
|
this.filteredObstacles = this.obstacles.filter(
|
|
7693
8097
|
(obstacle) => !obstacle.connectedTo.some(
|
|
7694
8098
|
(id) => this.connMap.areIdsConnected(this.inputRoute.connectionName, id)
|
|
@@ -7699,11 +8103,11 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
|
|
|
7699
8103
|
)) {
|
|
7700
8104
|
return false;
|
|
7701
8105
|
}
|
|
7702
|
-
const
|
|
7703
|
-
|
|
7704
|
-
|
|
7705
|
-
|
|
7706
|
-
return
|
|
8106
|
+
const { distance: distance6 } = computeDistanceBetweenBoxes(boundsBox, obstacle);
|
|
8107
|
+
if (distance6 < this.OBSTACLE_MARGIN + 0.5) {
|
|
8108
|
+
return true;
|
|
8109
|
+
}
|
|
8110
|
+
return false;
|
|
7707
8111
|
});
|
|
7708
8112
|
this.filteredObstaclePathSegments = this.otherHdRoutes.flatMap(
|
|
7709
8113
|
(hdRoute) => {
|
|
@@ -7775,15 +8179,15 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
|
|
|
7775
8179
|
return p1.x === p2.x && p1.y === p2.y && p1.z === p2.z;
|
|
7776
8180
|
}
|
|
7777
8181
|
// Get point at a specific distance along the path
|
|
7778
|
-
getPointAtDistance(
|
|
7779
|
-
|
|
8182
|
+
getPointAtDistance(distance6) {
|
|
8183
|
+
distance6 = Math.max(0, Math.min(distance6, this.totalPathLength));
|
|
7780
8184
|
const segment = this.pathSegments.find(
|
|
7781
|
-
(seg) =>
|
|
8185
|
+
(seg) => distance6 >= seg.startDistance && distance6 <= seg.endDistance
|
|
7782
8186
|
);
|
|
7783
8187
|
if (!segment) {
|
|
7784
8188
|
return this.inputRoute.route[this.inputRoute.route.length - 1];
|
|
7785
8189
|
}
|
|
7786
|
-
const factor = (
|
|
8190
|
+
const factor = (distance6 - segment.startDistance) / segment.length;
|
|
7787
8191
|
return {
|
|
7788
8192
|
x: segment.start.x + factor * (segment.end.x - segment.start.x),
|
|
7789
8193
|
y: segment.start.y + factor * (segment.end.y - segment.start.y),
|
|
@@ -7792,17 +8196,17 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
|
|
|
7792
8196
|
};
|
|
7793
8197
|
}
|
|
7794
8198
|
// Find nearest index in the original route for a given distance
|
|
7795
|
-
getNearestIndexForDistance(
|
|
7796
|
-
if (
|
|
7797
|
-
if (
|
|
8199
|
+
getNearestIndexForDistance(distance6) {
|
|
8200
|
+
if (distance6 <= 0) return 0;
|
|
8201
|
+
if (distance6 >= this.totalPathLength)
|
|
7798
8202
|
return this.inputRoute.route.length - 1;
|
|
7799
8203
|
const segmentIndex = this.pathSegments.findIndex(
|
|
7800
|
-
(seg) =>
|
|
8204
|
+
(seg) => distance6 >= seg.startDistance && distance6 <= seg.endDistance
|
|
7801
8205
|
);
|
|
7802
8206
|
if (segmentIndex === -1) return 0;
|
|
7803
8207
|
const segment = this.pathSegments[segmentIndex];
|
|
7804
8208
|
const midDistance = (segment.startDistance + segment.endDistance) / 2;
|
|
7805
|
-
return
|
|
8209
|
+
return distance6 > midDistance ? segmentIndex + 1 : segmentIndex;
|
|
7806
8210
|
}
|
|
7807
8211
|
// Check if a path segment is valid
|
|
7808
8212
|
isValidPathSegment(start, end) {
|
|
@@ -7810,49 +8214,27 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
|
|
|
7810
8214
|
if (!obstacle.zLayers?.includes(start.z)) {
|
|
7811
8215
|
continue;
|
|
7812
8216
|
}
|
|
7813
|
-
const
|
|
7814
|
-
|
|
7815
|
-
const obstacleTop = obstacle.center.y - obstacle.height / 2 - this.OBSTACLE_MARGIN;
|
|
7816
|
-
const obstacleBottom = obstacle.center.y + obstacle.height / 2 + this.OBSTACLE_MARGIN;
|
|
7817
|
-
if (doSegmentsIntersect(
|
|
7818
|
-
{ x: start.x, y: start.y },
|
|
7819
|
-
{ x: end.x, y: end.y },
|
|
7820
|
-
{ x: obstacleLeft, y: obstacleTop },
|
|
7821
|
-
{ x: obstacleRight, y: obstacleTop }
|
|
7822
|
-
) || doSegmentsIntersect(
|
|
7823
|
-
{ x: start.x, y: start.y },
|
|
7824
|
-
{ x: end.x, y: end.y },
|
|
7825
|
-
{ x: obstacleRight, y: obstacleTop },
|
|
7826
|
-
{ x: obstacleRight, y: obstacleBottom }
|
|
7827
|
-
) || doSegmentsIntersect(
|
|
7828
|
-
{ x: start.x, y: start.y },
|
|
7829
|
-
{ x: end.x, y: end.y },
|
|
7830
|
-
{ x: obstacleRight, y: obstacleBottom },
|
|
7831
|
-
{ x: obstacleLeft, y: obstacleBottom }
|
|
7832
|
-
) || doSegmentsIntersect(
|
|
7833
|
-
{ x: start.x, y: start.y },
|
|
7834
|
-
{ x: end.x, y: end.y },
|
|
7835
|
-
{ x: obstacleLeft, y: obstacleBottom },
|
|
7836
|
-
{ x: obstacleLeft, y: obstacleTop }
|
|
7837
|
-
)) {
|
|
8217
|
+
const distToObstacle = segmentToBoxMinDistance(start, end, obstacle);
|
|
8218
|
+
if (distToObstacle < this.OBSTACLE_MARGIN + this.TRACE_THICKNESS / 2) {
|
|
7838
8219
|
return false;
|
|
7839
8220
|
}
|
|
7840
8221
|
}
|
|
7841
8222
|
const segmentsThatCouldIntersect = this.segmentTree.getSegmentsThatCouldIntersect(start, end);
|
|
7842
|
-
for (const [otherSegA, otherSegB] of segmentsThatCouldIntersect) {
|
|
8223
|
+
for (const [otherSegA, otherSegB, segId] of segmentsThatCouldIntersect) {
|
|
7843
8224
|
if (otherSegA.z === start.z && otherSegB.z === start.z) {
|
|
7844
|
-
|
|
8225
|
+
const distBetweenSegments = minimumDistanceBetweenSegments(
|
|
7845
8226
|
{ x: start.x, y: start.y },
|
|
7846
8227
|
{ x: end.x, y: end.y },
|
|
7847
8228
|
{ x: otherSegA.x, y: otherSegA.y },
|
|
7848
8229
|
{ x: otherSegB.x, y: otherSegB.y }
|
|
7849
|
-
)
|
|
8230
|
+
);
|
|
8231
|
+
if (distBetweenSegments < this.OBSTACLE_MARGIN + this.TRACE_THICKNESS) {
|
|
7850
8232
|
return false;
|
|
7851
8233
|
}
|
|
7852
8234
|
}
|
|
7853
8235
|
}
|
|
7854
8236
|
for (const via of this.filteredVias) {
|
|
7855
|
-
if (pointToSegmentDistance(via, start, end) < this.OBSTACLE_MARGIN + via.diameter / 2) {
|
|
8237
|
+
if (pointToSegmentDistance(via, start, end) < this.OBSTACLE_MARGIN + via.diameter / 2 + this.TRACE_THICKNESS / 2) {
|
|
7856
8238
|
return false;
|
|
7857
8239
|
}
|
|
7858
8240
|
}
|
|
@@ -7904,10 +8286,10 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
|
|
|
7904
8286
|
}
|
|
7905
8287
|
this.currentStepSize = this.maxStepSize;
|
|
7906
8288
|
}
|
|
7907
|
-
moveHead(
|
|
7908
|
-
this.lastHeadMoveDistance =
|
|
8289
|
+
moveHead(distance6) {
|
|
8290
|
+
this.lastHeadMoveDistance = distance6;
|
|
7909
8291
|
this.headDistanceAlongPath = Math.min(
|
|
7910
|
-
this.headDistanceAlongPath +
|
|
8292
|
+
this.headDistanceAlongPath + distance6,
|
|
7911
8293
|
this.totalPathLength
|
|
7912
8294
|
);
|
|
7913
8295
|
}
|
|
@@ -8045,9 +8427,9 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
|
|
|
8045
8427
|
color: "red",
|
|
8046
8428
|
label: ["Tentative Head", `z: ${tentativeHead.z}`].join("\n")
|
|
8047
8429
|
});
|
|
8048
|
-
let
|
|
8049
|
-
while (
|
|
8050
|
-
const point = this.getPointAtDistance(
|
|
8430
|
+
let distance6 = 0;
|
|
8431
|
+
while (distance6 < this.totalPathLength) {
|
|
8432
|
+
const point = this.getPointAtDistance(distance6);
|
|
8051
8433
|
graphics.circles.push({
|
|
8052
8434
|
center: {
|
|
8053
8435
|
x: point.x,
|
|
@@ -8056,7 +8438,7 @@ var SingleSimplifiedPathSolver5 = class extends SingleSimplifiedPathSolver {
|
|
|
8056
8438
|
radius: 0.05,
|
|
8057
8439
|
fill: "rgba(100, 100, 100, 0.5)"
|
|
8058
8440
|
});
|
|
8059
|
-
|
|
8441
|
+
distance6 += this.totalPathLength / 20;
|
|
8060
8442
|
}
|
|
8061
8443
|
if (this.lastValidPath && this.lastValidPath.length > 1) {
|
|
8062
8444
|
for (let i = 0; i < this.lastValidPath.length - 1; i++) {
|