@tscircuit/hypergraph 0.0.60 → 0.0.61
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 +106 -8
- package/dist/index.js +1292 -503
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1532,6 +1532,7 @@ var convertSerializedHyperGraphToHyperGraph = (inputGraph) => {
|
|
|
1532
1532
|
const { assignments: _, ...regionWithoutAssignments } = region;
|
|
1533
1533
|
regionMap.set(region.regionId, {
|
|
1534
1534
|
...regionWithoutAssignments,
|
|
1535
|
+
d: regionWithoutAssignments.d ? structuredClone(regionWithoutAssignments.d) : regionWithoutAssignments.d,
|
|
1535
1536
|
ports: [],
|
|
1536
1537
|
assignments: void 0
|
|
1537
1538
|
});
|
|
@@ -1704,28 +1705,124 @@ var PriorityQueue = class {
|
|
|
1704
1705
|
}
|
|
1705
1706
|
};
|
|
1706
1707
|
|
|
1708
|
+
// lib/solvedRoutes.ts
|
|
1709
|
+
var clearAssignmentsFromGraph = (graph) => {
|
|
1710
|
+
for (const region of graph.regions) {
|
|
1711
|
+
region.assignments = [];
|
|
1712
|
+
}
|
|
1713
|
+
for (const port of graph.ports) {
|
|
1714
|
+
port.assignment = void 0;
|
|
1715
|
+
}
|
|
1716
|
+
};
|
|
1717
|
+
var commitSolvedRoutes = ({
|
|
1718
|
+
graph,
|
|
1719
|
+
connections,
|
|
1720
|
+
solvedRoutes
|
|
1721
|
+
}) => {
|
|
1722
|
+
const portMap = new Map(graph.ports.map((port) => [port.portId, port]));
|
|
1723
|
+
const regionMap = new Map(
|
|
1724
|
+
graph.regions.map((region) => [region.regionId, region])
|
|
1725
|
+
);
|
|
1726
|
+
const connectionMap = new Map(
|
|
1727
|
+
connections.map((connection) => [connection.connectionId, connection])
|
|
1728
|
+
);
|
|
1729
|
+
const committedSolvedRoutes = solvedRoutes.map((solvedRoute) => {
|
|
1730
|
+
const path = [];
|
|
1731
|
+
for (const originalCandidate of solvedRoute.path) {
|
|
1732
|
+
const candidate = {
|
|
1733
|
+
port: portMap.get(originalCandidate.port.portId),
|
|
1734
|
+
g: originalCandidate.g,
|
|
1735
|
+
h: originalCandidate.h,
|
|
1736
|
+
f: originalCandidate.f,
|
|
1737
|
+
hops: originalCandidate.hops,
|
|
1738
|
+
ripRequired: originalCandidate.ripRequired
|
|
1739
|
+
};
|
|
1740
|
+
if (originalCandidate.lastPort) {
|
|
1741
|
+
candidate.lastPort = portMap.get(originalCandidate.lastPort.portId);
|
|
1742
|
+
}
|
|
1743
|
+
if (originalCandidate.lastRegion) {
|
|
1744
|
+
candidate.lastRegion = regionMap.get(
|
|
1745
|
+
originalCandidate.lastRegion.regionId
|
|
1746
|
+
);
|
|
1747
|
+
}
|
|
1748
|
+
if (originalCandidate.nextRegion) {
|
|
1749
|
+
candidate.nextRegion = regionMap.get(
|
|
1750
|
+
originalCandidate.nextRegion.regionId
|
|
1751
|
+
);
|
|
1752
|
+
}
|
|
1753
|
+
const parent = path[path.length - 1];
|
|
1754
|
+
if (parent) candidate.parent = parent;
|
|
1755
|
+
path.push(candidate);
|
|
1756
|
+
}
|
|
1757
|
+
return {
|
|
1758
|
+
path,
|
|
1759
|
+
connection: connectionMap.get(solvedRoute.connection.connectionId),
|
|
1760
|
+
requiredRip: solvedRoute.requiredRip
|
|
1761
|
+
};
|
|
1762
|
+
});
|
|
1763
|
+
clearAssignmentsFromGraph(graph);
|
|
1764
|
+
graph.solvedRoutes = committedSolvedRoutes;
|
|
1765
|
+
for (const solvedRoute of committedSolvedRoutes) {
|
|
1766
|
+
for (const candidate of solvedRoute.path) {
|
|
1767
|
+
candidate.port.assignment = {
|
|
1768
|
+
solvedRoute,
|
|
1769
|
+
connection: solvedRoute.connection
|
|
1770
|
+
};
|
|
1771
|
+
if (!candidate.lastPort || !candidate.lastRegion) continue;
|
|
1772
|
+
const regionPortAssignment = {
|
|
1773
|
+
regionPort1: candidate.lastPort,
|
|
1774
|
+
regionPort2: candidate.port,
|
|
1775
|
+
region: candidate.lastRegion,
|
|
1776
|
+
connection: solvedRoute.connection,
|
|
1777
|
+
solvedRoute
|
|
1778
|
+
};
|
|
1779
|
+
candidate.lastRegion.assignments ??= [];
|
|
1780
|
+
candidate.lastRegion.assignments.push(regionPortAssignment);
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
return committedSolvedRoutes;
|
|
1784
|
+
};
|
|
1785
|
+
|
|
1707
1786
|
// lib/HyperGraphSolver.ts
|
|
1708
1787
|
var HyperGraphSolver = class extends BaseSolver {
|
|
1709
1788
|
constructor(input) {
|
|
1710
1789
|
super();
|
|
1711
1790
|
this.input = input;
|
|
1712
1791
|
this.graph = convertSerializedHyperGraphToHyperGraph(input.inputGraph);
|
|
1713
|
-
this.graph
|
|
1714
|
-
for (const region of this.graph.regions) {
|
|
1715
|
-
region.assignments = [];
|
|
1716
|
-
}
|
|
1792
|
+
clearAssignmentsFromGraph(this.graph);
|
|
1717
1793
|
this.connections = convertSerializedConnectionsToConnections(
|
|
1718
1794
|
input.inputConnections,
|
|
1719
1795
|
this.graph
|
|
1720
1796
|
);
|
|
1797
|
+
if (input.inputSolvedRoutes) {
|
|
1798
|
+
this.solvedRoutes = commitSolvedRoutes({
|
|
1799
|
+
graph: this.graph,
|
|
1800
|
+
connections: this.connections,
|
|
1801
|
+
solvedRoutes: input.inputSolvedRoutes
|
|
1802
|
+
});
|
|
1803
|
+
} else {
|
|
1804
|
+
;
|
|
1805
|
+
this.graph.solvedRoutes = this.solvedRoutes;
|
|
1806
|
+
}
|
|
1721
1807
|
if (input.greedyMultiplier !== void 0)
|
|
1722
1808
|
this.greedyMultiplier = input.greedyMultiplier;
|
|
1723
1809
|
if (input.rippingEnabled !== void 0)
|
|
1724
1810
|
this.rippingEnabled = input.rippingEnabled;
|
|
1725
1811
|
if (input.ripCost !== void 0) this.ripCost = input.ripCost;
|
|
1726
|
-
|
|
1812
|
+
const preSolvedConnectionIds = new Set(
|
|
1813
|
+
this.solvedRoutes.map(
|
|
1814
|
+
(solvedRoute) => solvedRoute.connection.connectionId
|
|
1815
|
+
)
|
|
1816
|
+
);
|
|
1817
|
+
this.unprocessedConnections = this.connections.filter(
|
|
1818
|
+
(connection) => !preSolvedConnectionIds.has(connection.connectionId)
|
|
1819
|
+
);
|
|
1727
1820
|
this.candidateQueue = new PriorityQueue();
|
|
1728
|
-
this.
|
|
1821
|
+
if (this.unprocessedConnections.length === 0) {
|
|
1822
|
+
this.solved = true;
|
|
1823
|
+
} else {
|
|
1824
|
+
this.beginNewConnection();
|
|
1825
|
+
}
|
|
1729
1826
|
}
|
|
1730
1827
|
getSolverName() {
|
|
1731
1828
|
return "HyperGraphSolver";
|
|
@@ -1750,9 +1847,13 @@ var HyperGraphSolver = class extends BaseSolver {
|
|
|
1750
1847
|
),
|
|
1751
1848
|
greedyMultiplier: this.greedyMultiplier,
|
|
1752
1849
|
rippingEnabled: this.rippingEnabled,
|
|
1753
|
-
ripCost: this.ripCost
|
|
1850
|
+
ripCost: this.ripCost,
|
|
1851
|
+
inputSolvedRoutes: this.solvedRoutes
|
|
1754
1852
|
};
|
|
1755
1853
|
}
|
|
1854
|
+
getOutput() {
|
|
1855
|
+
return this.solvedRoutes;
|
|
1856
|
+
}
|
|
1756
1857
|
computeH(candidate) {
|
|
1757
1858
|
return this.estimateCostToEnd(candidate.port);
|
|
1758
1859
|
}
|
|
@@ -2064,197 +2165,8 @@ var HyperGraphSolver = class extends BaseSolver {
|
|
|
2064
2165
|
}
|
|
2065
2166
|
};
|
|
2066
2167
|
|
|
2067
|
-
//
|
|
2068
|
-
|
|
2069
|
-
return Array.isArray(point2) ? [
|
|
2070
|
-
matrix2.a * point2[0] + matrix2.c * point2[1] + matrix2.e,
|
|
2071
|
-
matrix2.b * point2[0] + matrix2.d * point2[1] + matrix2.f
|
|
2072
|
-
] : {
|
|
2073
|
-
x: matrix2.a * point2.x + matrix2.c * point2.y + matrix2.e,
|
|
2074
|
-
y: matrix2.b * point2.x + matrix2.d * point2.y + matrix2.f
|
|
2075
|
-
};
|
|
2076
|
-
}
|
|
2077
|
-
|
|
2078
|
-
// node_modules/transformation-matrix/src/utils.js
|
|
2079
|
-
function isUndefined(val) {
|
|
2080
|
-
return typeof val === "undefined";
|
|
2081
|
-
}
|
|
2082
|
-
|
|
2083
|
-
// node_modules/transformation-matrix/src/translate.js
|
|
2084
|
-
function translate(tx, ty = 0) {
|
|
2085
|
-
return {
|
|
2086
|
-
a: 1,
|
|
2087
|
-
c: 0,
|
|
2088
|
-
e: tx,
|
|
2089
|
-
b: 0,
|
|
2090
|
-
d: 1,
|
|
2091
|
-
f: ty
|
|
2092
|
-
};
|
|
2093
|
-
}
|
|
2094
|
-
|
|
2095
|
-
// node_modules/transformation-matrix/src/transform.js
|
|
2096
|
-
function transform(...matrices) {
|
|
2097
|
-
matrices = Array.isArray(matrices[0]) ? matrices[0] : matrices;
|
|
2098
|
-
const multiply = (m1, m2) => {
|
|
2099
|
-
return {
|
|
2100
|
-
a: m1.a * m2.a + m1.c * m2.b,
|
|
2101
|
-
c: m1.a * m2.c + m1.c * m2.d,
|
|
2102
|
-
e: m1.a * m2.e + m1.c * m2.f + m1.e,
|
|
2103
|
-
b: m1.b * m2.a + m1.d * m2.b,
|
|
2104
|
-
d: m1.b * m2.c + m1.d * m2.d,
|
|
2105
|
-
f: m1.b * m2.e + m1.d * m2.f + m1.f
|
|
2106
|
-
};
|
|
2107
|
-
};
|
|
2108
|
-
switch (matrices.length) {
|
|
2109
|
-
case 0:
|
|
2110
|
-
throw new Error("no matrices provided");
|
|
2111
|
-
case 1:
|
|
2112
|
-
return matrices[0];
|
|
2113
|
-
case 2:
|
|
2114
|
-
return multiply(matrices[0], matrices[1]);
|
|
2115
|
-
default: {
|
|
2116
|
-
const [m1, m2, ...rest] = matrices;
|
|
2117
|
-
const m = multiply(m1, m2);
|
|
2118
|
-
return transform(m, ...rest);
|
|
2119
|
-
}
|
|
2120
|
-
}
|
|
2121
|
-
}
|
|
2122
|
-
function compose(...matrices) {
|
|
2123
|
-
return transform(...matrices);
|
|
2124
|
-
}
|
|
2125
|
-
|
|
2126
|
-
// node_modules/transformation-matrix/src/rotate.js
|
|
2127
|
-
var { cos, sin, PI } = Math;
|
|
2128
|
-
function rotate(angle, cx, cy) {
|
|
2129
|
-
const cosAngle = cos(angle);
|
|
2130
|
-
const sinAngle = sin(angle);
|
|
2131
|
-
const rotationMatrix = {
|
|
2132
|
-
a: cosAngle,
|
|
2133
|
-
c: -sinAngle,
|
|
2134
|
-
e: 0,
|
|
2135
|
-
b: sinAngle,
|
|
2136
|
-
d: cosAngle,
|
|
2137
|
-
f: 0
|
|
2138
|
-
};
|
|
2139
|
-
if (isUndefined(cx) || isUndefined(cy)) {
|
|
2140
|
-
return rotationMatrix;
|
|
2141
|
-
}
|
|
2142
|
-
return transform([
|
|
2143
|
-
translate(cx, cy),
|
|
2144
|
-
rotationMatrix,
|
|
2145
|
-
translate(-cx, -cy)
|
|
2146
|
-
]);
|
|
2147
|
-
}
|
|
2148
|
-
|
|
2149
|
-
// node_modules/transformation-matrix/src/skew.js
|
|
2150
|
-
var { tan } = Math;
|
|
2151
|
-
|
|
2152
|
-
// lib/JumperGraphSolver/jumper-graph-generator/calculateGraphBounds.ts
|
|
2153
|
-
var calculateGraphBounds = (regions) => {
|
|
2154
|
-
let minX = Number.POSITIVE_INFINITY;
|
|
2155
|
-
let maxX = Number.NEGATIVE_INFINITY;
|
|
2156
|
-
let minY = Number.POSITIVE_INFINITY;
|
|
2157
|
-
let maxY = Number.NEGATIVE_INFINITY;
|
|
2158
|
-
for (const region of regions) {
|
|
2159
|
-
const { bounds } = region.d;
|
|
2160
|
-
minX = Math.min(minX, bounds.minX);
|
|
2161
|
-
maxX = Math.max(maxX, bounds.maxX);
|
|
2162
|
-
minY = Math.min(minY, bounds.minY);
|
|
2163
|
-
maxY = Math.max(maxY, bounds.maxY);
|
|
2164
|
-
}
|
|
2165
|
-
return { minX, maxX, minY, maxY };
|
|
2166
|
-
};
|
|
2167
|
-
|
|
2168
|
-
// lib/JumperGraphSolver/geometry/getBoundsCenter.ts
|
|
2169
|
-
var computeBoundsCenter = (bounds) => {
|
|
2170
|
-
return {
|
|
2171
|
-
x: (bounds.minX + bounds.maxX) / 2,
|
|
2172
|
-
y: (bounds.minY + bounds.maxY) / 2
|
|
2173
|
-
};
|
|
2174
|
-
};
|
|
2175
|
-
|
|
2176
|
-
// lib/JumperGraphSolver/geometry/applyTransformToGraph.ts
|
|
2177
|
-
var applyTransformToGraph = (graph, matrix2) => {
|
|
2178
|
-
const transformedRegions = graph.regions.map((region) => {
|
|
2179
|
-
const { bounds, center, ...rest } = region.d;
|
|
2180
|
-
const corners = [
|
|
2181
|
-
{ x: bounds.minX, y: bounds.minY },
|
|
2182
|
-
{ x: bounds.maxX, y: bounds.minY },
|
|
2183
|
-
{ x: bounds.minX, y: bounds.maxY },
|
|
2184
|
-
{ x: bounds.maxX, y: bounds.maxY }
|
|
2185
|
-
].map((corner) => applyToPoint(matrix2, corner));
|
|
2186
|
-
const newBounds = {
|
|
2187
|
-
minX: Math.min(...corners.map((c) => c.x)),
|
|
2188
|
-
maxX: Math.max(...corners.map((c) => c.x)),
|
|
2189
|
-
minY: Math.min(...corners.map((c) => c.y)),
|
|
2190
|
-
maxY: Math.max(...corners.map((c) => c.y))
|
|
2191
|
-
};
|
|
2192
|
-
const newCenter = applyToPoint(matrix2, center);
|
|
2193
|
-
return {
|
|
2194
|
-
...region,
|
|
2195
|
-
// Clear ports array - will be rebuilt with new port objects
|
|
2196
|
-
ports: [],
|
|
2197
|
-
d: {
|
|
2198
|
-
...rest,
|
|
2199
|
-
bounds: newBounds,
|
|
2200
|
-
center: newCenter
|
|
2201
|
-
}
|
|
2202
|
-
};
|
|
2203
|
-
});
|
|
2204
|
-
const regionMap = /* @__PURE__ */ new Map();
|
|
2205
|
-
for (let i = 0; i < graph.regions.length; i++) {
|
|
2206
|
-
regionMap.set(graph.regions[i], transformedRegions[i]);
|
|
2207
|
-
}
|
|
2208
|
-
const transformedPorts = graph.ports.map((port) => {
|
|
2209
|
-
const newPosition = applyToPoint(matrix2, port.d);
|
|
2210
|
-
const newRegion1 = regionMap.get(port.region1);
|
|
2211
|
-
const newRegion2 = regionMap.get(port.region2);
|
|
2212
|
-
const newPort = {
|
|
2213
|
-
...port,
|
|
2214
|
-
region1: newRegion1,
|
|
2215
|
-
region2: newRegion2,
|
|
2216
|
-
d: newPosition
|
|
2217
|
-
};
|
|
2218
|
-
newRegion1.ports.push(newPort);
|
|
2219
|
-
newRegion2.ports.push(newPort);
|
|
2220
|
-
return newPort;
|
|
2221
|
-
});
|
|
2222
|
-
const transformedJumperLocations = graph.jumperLocations?.map((loc) => {
|
|
2223
|
-
const newCenter = applyToPoint(matrix2, loc.center);
|
|
2224
|
-
const newPadRegions = loc.padRegions.map(
|
|
2225
|
-
(region) => regionMap.get(region)
|
|
2226
|
-
);
|
|
2227
|
-
const unitX = applyToPoint(matrix2, { x: 1, y: 0 });
|
|
2228
|
-
const origin = applyToPoint(matrix2, { x: 0, y: 0 });
|
|
2229
|
-
const dx = unitX.x - origin.x;
|
|
2230
|
-
const dy = unitX.y - origin.y;
|
|
2231
|
-
const isRotated90 = Math.abs(dy) > Math.abs(dx);
|
|
2232
|
-
const newOrientation = isRotated90 ? loc.orientation === "horizontal" ? "vertical" : "horizontal" : loc.orientation;
|
|
2233
|
-
return {
|
|
2234
|
-
center: newCenter,
|
|
2235
|
-
orientation: newOrientation,
|
|
2236
|
-
padRegions: newPadRegions
|
|
2237
|
-
};
|
|
2238
|
-
});
|
|
2239
|
-
return {
|
|
2240
|
-
regions: transformedRegions,
|
|
2241
|
-
ports: transformedPorts,
|
|
2242
|
-
...transformedJumperLocations && {
|
|
2243
|
-
jumperLocations: transformedJumperLocations
|
|
2244
|
-
}
|
|
2245
|
-
};
|
|
2246
|
-
};
|
|
2247
|
-
var rotateGraph90Degrees = (graph) => {
|
|
2248
|
-
const bounds = calculateGraphBounds(graph.regions);
|
|
2249
|
-
const center = computeBoundsCenter(bounds);
|
|
2250
|
-
const matrix2 = compose(
|
|
2251
|
-
translate(center.x, center.y),
|
|
2252
|
-
rotate(-Math.PI / 2),
|
|
2253
|
-
// -90 degrees (clockwise)
|
|
2254
|
-
translate(-center.x, -center.y)
|
|
2255
|
-
);
|
|
2256
|
-
return applyTransformToGraph(graph, matrix2);
|
|
2257
|
-
};
|
|
2168
|
+
// lib/HyperGraphSectionOptimizer/HyperGraphSectionOptimizer.ts
|
|
2169
|
+
import { BaseSolver as BaseSolver2 } from "@tscircuit/solver-utils";
|
|
2258
2170
|
|
|
2259
2171
|
// lib/JumperGraphSolver/perimeterChordUtils.ts
|
|
2260
2172
|
function clamp2(value, min, max) {
|
|
@@ -2415,24 +2327,1119 @@ function chordsCross(chord1, chord2, perimeter) {
|
|
|
2415
2327
|
return a < c && c < b && b < d || c < a && a < d && d < b;
|
|
2416
2328
|
}
|
|
2417
2329
|
|
|
2418
|
-
// lib/JumperGraphSolver/
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
const
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2330
|
+
// lib/JumperGraphSolver/jumper-graph-generator/calculateGraphBounds.ts
|
|
2331
|
+
var calculateGraphBounds = (regions) => {
|
|
2332
|
+
let minX = Number.POSITIVE_INFINITY;
|
|
2333
|
+
let maxX = Number.NEGATIVE_INFINITY;
|
|
2334
|
+
let minY = Number.POSITIVE_INFINITY;
|
|
2335
|
+
let maxY = Number.NEGATIVE_INFINITY;
|
|
2336
|
+
for (const region of regions) {
|
|
2337
|
+
const { bounds } = region.d;
|
|
2338
|
+
minX = Math.min(minX, bounds.minX);
|
|
2339
|
+
maxX = Math.max(maxX, bounds.maxX);
|
|
2340
|
+
minY = Math.min(minY, bounds.minY);
|
|
2341
|
+
maxY = Math.max(maxY, bounds.maxY);
|
|
2342
|
+
}
|
|
2343
|
+
return { minX, maxX, minY, maxY };
|
|
2344
|
+
};
|
|
2345
|
+
|
|
2346
|
+
// lib/JumperGraphSolver/jumper-graph-generator/createConnectionPort.ts
|
|
2347
|
+
var createConnectionPort = (portId, connectionRegion, boundaryRegion, portPosition) => {
|
|
2348
|
+
const port = {
|
|
2349
|
+
portId,
|
|
2350
|
+
region1: connectionRegion,
|
|
2351
|
+
region2: boundaryRegion,
|
|
2352
|
+
d: { x: portPosition.x, y: portPosition.y }
|
|
2353
|
+
};
|
|
2354
|
+
connectionRegion.ports.push(port);
|
|
2355
|
+
boundaryRegion.ports.push(port);
|
|
2356
|
+
return port;
|
|
2357
|
+
};
|
|
2358
|
+
|
|
2359
|
+
// lib/JumperGraphSolver/jumper-graph-generator/createConnectionRegion.ts
|
|
2360
|
+
var CONNECTION_REGION_SIZE = 0.4;
|
|
2361
|
+
var createConnectionRegion = (regionId, x, y) => {
|
|
2362
|
+
const halfSize = CONNECTION_REGION_SIZE / 2;
|
|
2363
|
+
return {
|
|
2364
|
+
regionId,
|
|
2365
|
+
ports: [],
|
|
2366
|
+
d: {
|
|
2367
|
+
bounds: {
|
|
2368
|
+
minX: x - halfSize,
|
|
2369
|
+
maxX: x + halfSize,
|
|
2370
|
+
minY: y - halfSize,
|
|
2371
|
+
maxY: y + halfSize
|
|
2372
|
+
},
|
|
2373
|
+
center: { x, y },
|
|
2374
|
+
isPad: false,
|
|
2375
|
+
isConnectionRegion: true
|
|
2376
|
+
}
|
|
2377
|
+
};
|
|
2378
|
+
};
|
|
2379
|
+
|
|
2380
|
+
// lib/JumperGraphSolver/jumper-graph-generator/findBoundaryRegion.ts
|
|
2381
|
+
var EPS = 0.01;
|
|
2382
|
+
var clamp3 = (value, min, max) => {
|
|
2383
|
+
return Math.max(min, Math.min(max, value));
|
|
2384
|
+
};
|
|
2385
|
+
var getBoundarySidesForPoint = (x, y, graphBounds) => {
|
|
2386
|
+
const sides = [];
|
|
2387
|
+
if (Math.abs(x - graphBounds.minX) < EPS) sides.push("left");
|
|
2388
|
+
if (Math.abs(x - graphBounds.maxX) < EPS) sides.push("right");
|
|
2389
|
+
if (Math.abs(y - graphBounds.maxY) < EPS) sides.push("top");
|
|
2390
|
+
if (Math.abs(y - graphBounds.minY) < EPS) sides.push("bottom");
|
|
2391
|
+
return sides;
|
|
2392
|
+
};
|
|
2393
|
+
var isPointOnSide = (p, side, b) => {
|
|
2394
|
+
if (side === "left") return Math.abs(p.x - b.minX) < EPS;
|
|
2395
|
+
if (side === "right") return Math.abs(p.x - b.maxX) < EPS;
|
|
2396
|
+
if (side === "top") return Math.abs(p.y - b.maxY) < EPS;
|
|
2397
|
+
return Math.abs(p.y - b.minY) < EPS;
|
|
2398
|
+
};
|
|
2399
|
+
var projectToSegment2 = (x, y, a, b) => {
|
|
2400
|
+
const abx = b.x - a.x;
|
|
2401
|
+
const aby = b.y - a.y;
|
|
2402
|
+
const apx = x - a.x;
|
|
2403
|
+
const apy = y - a.y;
|
|
2404
|
+
const ab2 = abx * abx + aby * aby;
|
|
2405
|
+
const t = ab2 > 0 ? clamp3((apx * abx + apy * aby) / ab2, 0, 1) : 0;
|
|
2406
|
+
const px = a.x + t * abx;
|
|
2407
|
+
const py = a.y + t * aby;
|
|
2408
|
+
const dx = x - px;
|
|
2409
|
+
const dy = y - py;
|
|
2410
|
+
return {
|
|
2411
|
+
x: px,
|
|
2412
|
+
y: py,
|
|
2413
|
+
d2: dx * dx + dy * dy
|
|
2414
|
+
};
|
|
2415
|
+
};
|
|
2416
|
+
var getRegionBoundaryProjection = (x, y, region, graphBounds, preferredSides) => {
|
|
2417
|
+
const polygon2 = region.d.polygon;
|
|
2418
|
+
if (polygon2 && polygon2.length >= 3) {
|
|
2419
|
+
const sideSet = new Set(preferredSides);
|
|
2420
|
+
let best2 = null;
|
|
2421
|
+
for (let i = 0; i < polygon2.length; i++) {
|
|
2422
|
+
const a = polygon2[i];
|
|
2423
|
+
const b = polygon2[(i + 1) % polygon2.length];
|
|
2424
|
+
if (preferredSides.length > 0) {
|
|
2425
|
+
const edgeOnPreferredSide = preferredSides.some(
|
|
2426
|
+
(side) => isPointOnSide(a, side, graphBounds) && isPointOnSide(b, side, graphBounds) && sideSet.has(side)
|
|
2427
|
+
);
|
|
2428
|
+
if (!edgeOnPreferredSide) continue;
|
|
2429
|
+
}
|
|
2430
|
+
const p = projectToSegment2(x, y, a, b);
|
|
2431
|
+
if (!best2 || p.d2 < best2.d2) {
|
|
2432
|
+
best2 = p;
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
if (best2) return best2;
|
|
2436
|
+
}
|
|
2437
|
+
const bounds = region.d.bounds;
|
|
2438
|
+
const sideCandidates = [];
|
|
2439
|
+
if (preferredSides.length > 0) {
|
|
2440
|
+
for (const side of preferredSides) {
|
|
2441
|
+
if (side === "left") {
|
|
2442
|
+
sideCandidates.push({
|
|
2443
|
+
side,
|
|
2444
|
+
x: bounds.minX,
|
|
2445
|
+
y: clamp3(y, bounds.minY, bounds.maxY)
|
|
2446
|
+
});
|
|
2447
|
+
} else if (side === "right") {
|
|
2448
|
+
sideCandidates.push({
|
|
2449
|
+
side,
|
|
2450
|
+
x: bounds.maxX,
|
|
2451
|
+
y: clamp3(y, bounds.minY, bounds.maxY)
|
|
2452
|
+
});
|
|
2453
|
+
} else if (side === "top") {
|
|
2454
|
+
sideCandidates.push({
|
|
2455
|
+
side,
|
|
2456
|
+
x: clamp3(x, bounds.minX, bounds.maxX),
|
|
2457
|
+
y: bounds.maxY
|
|
2458
|
+
});
|
|
2459
|
+
} else {
|
|
2460
|
+
sideCandidates.push({
|
|
2461
|
+
side,
|
|
2462
|
+
x: clamp3(x, bounds.minX, bounds.maxX),
|
|
2463
|
+
y: bounds.minY
|
|
2464
|
+
});
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
if (sideCandidates.length === 0) {
|
|
2469
|
+
sideCandidates.push(
|
|
2470
|
+
{ side: "left", x: bounds.minX, y: clamp3(y, bounds.minY, bounds.maxY) },
|
|
2471
|
+
{
|
|
2472
|
+
side: "right",
|
|
2473
|
+
x: bounds.maxX,
|
|
2474
|
+
y: clamp3(y, bounds.minY, bounds.maxY)
|
|
2475
|
+
},
|
|
2476
|
+
{ side: "top", x: clamp3(x, bounds.minX, bounds.maxX), y: bounds.maxY },
|
|
2477
|
+
{
|
|
2478
|
+
side: "bottom",
|
|
2479
|
+
x: clamp3(x, bounds.minX, bounds.maxX),
|
|
2480
|
+
y: bounds.minY
|
|
2481
|
+
}
|
|
2482
|
+
);
|
|
2483
|
+
}
|
|
2484
|
+
let best = null;
|
|
2485
|
+
for (const c of sideCandidates) {
|
|
2486
|
+
if (preferredSides.length > 0 && !preferredSides.includes(c.side)) continue;
|
|
2487
|
+
const dx = x - c.x;
|
|
2488
|
+
const dy = y - c.y;
|
|
2489
|
+
const d2 = dx * dx + dy * dy;
|
|
2490
|
+
if (!best || d2 < best.d2) {
|
|
2491
|
+
best = { x: c.x, y: c.y, d2 };
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
return best;
|
|
2495
|
+
};
|
|
2496
|
+
var findBoundaryRegion = (x, y, regions, graphBounds) => {
|
|
2497
|
+
const preferredSides = getBoundarySidesForPoint(x, y, graphBounds);
|
|
2498
|
+
let closestRegion = null;
|
|
2499
|
+
let closestDistance = Number.POSITIVE_INFINITY;
|
|
2500
|
+
let closestPortPosition = { x, y };
|
|
2501
|
+
for (const region of regions) {
|
|
2502
|
+
if (region.d.isPad || region.d.isThroughJumper) continue;
|
|
2503
|
+
const bounds = region.d.bounds;
|
|
2504
|
+
const isOuterRegion = Math.abs(bounds.minX - graphBounds.minX) < 0.01 || Math.abs(bounds.maxX - graphBounds.maxX) < 0.01 || Math.abs(bounds.minY - graphBounds.minY) < 0.01 || Math.abs(bounds.maxY - graphBounds.maxY) < 0.01;
|
|
2505
|
+
if (!isOuterRegion) continue;
|
|
2506
|
+
const projection = getRegionBoundaryProjection(
|
|
2507
|
+
x,
|
|
2508
|
+
y,
|
|
2509
|
+
region,
|
|
2510
|
+
graphBounds,
|
|
2511
|
+
preferredSides
|
|
2512
|
+
);
|
|
2513
|
+
if (!projection) continue;
|
|
2514
|
+
const dist = Math.sqrt(projection.d2);
|
|
2515
|
+
if (dist < closestDistance) {
|
|
2516
|
+
closestDistance = dist;
|
|
2517
|
+
closestRegion = region;
|
|
2518
|
+
closestPortPosition = { x: projection.x, y: projection.y };
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
if (closestRegion) {
|
|
2522
|
+
return { region: closestRegion, portPosition: closestPortPosition };
|
|
2523
|
+
}
|
|
2524
|
+
return null;
|
|
2525
|
+
};
|
|
2526
|
+
|
|
2527
|
+
// lib/JumperGraphSolver/jumper-graph-generator/createGraphWithConnectionsFromBaseGraph.ts
|
|
2528
|
+
var createGraphWithConnectionsFromBaseGraph = (baseGraph, xyConnections) => {
|
|
2529
|
+
const regions = [...baseGraph.regions];
|
|
2530
|
+
const ports = [...baseGraph.ports];
|
|
2531
|
+
const connections = [];
|
|
2532
|
+
const graphBounds = calculateGraphBounds(baseGraph.regions);
|
|
2533
|
+
for (const xyConn of xyConnections) {
|
|
2534
|
+
const { start, end, connectionId } = xyConn;
|
|
2535
|
+
const startRegion = createConnectionRegion(
|
|
2536
|
+
`conn:${connectionId}:start`,
|
|
2537
|
+
start.x,
|
|
2538
|
+
start.y
|
|
2539
|
+
);
|
|
2540
|
+
regions.push(startRegion);
|
|
2541
|
+
const endRegion = createConnectionRegion(
|
|
2542
|
+
`conn:${connectionId}:end`,
|
|
2543
|
+
end.x,
|
|
2544
|
+
end.y
|
|
2545
|
+
);
|
|
2546
|
+
regions.push(endRegion);
|
|
2547
|
+
const startBoundary = findBoundaryRegion(
|
|
2548
|
+
start.x,
|
|
2549
|
+
start.y,
|
|
2550
|
+
baseGraph.regions,
|
|
2551
|
+
graphBounds
|
|
2552
|
+
);
|
|
2553
|
+
if (startBoundary) {
|
|
2554
|
+
const startPort = createConnectionPort(
|
|
2555
|
+
`conn:${connectionId}:start-port`,
|
|
2556
|
+
startRegion,
|
|
2557
|
+
startBoundary.region,
|
|
2558
|
+
startBoundary.portPosition
|
|
2559
|
+
);
|
|
2560
|
+
ports.push(startPort);
|
|
2561
|
+
}
|
|
2562
|
+
const endBoundary = findBoundaryRegion(
|
|
2563
|
+
end.x,
|
|
2564
|
+
end.y,
|
|
2565
|
+
baseGraph.regions,
|
|
2566
|
+
graphBounds
|
|
2567
|
+
);
|
|
2568
|
+
if (endBoundary) {
|
|
2569
|
+
const endPort = createConnectionPort(
|
|
2570
|
+
`conn:${connectionId}:end-port`,
|
|
2571
|
+
endRegion,
|
|
2572
|
+
endBoundary.region,
|
|
2573
|
+
endBoundary.portPosition
|
|
2574
|
+
);
|
|
2575
|
+
ports.push(endPort);
|
|
2576
|
+
}
|
|
2577
|
+
const connection = {
|
|
2578
|
+
connectionId,
|
|
2579
|
+
mutuallyConnectedNetworkId: connectionId,
|
|
2580
|
+
startRegion,
|
|
2581
|
+
endRegion
|
|
2582
|
+
};
|
|
2583
|
+
connections.push(connection);
|
|
2584
|
+
}
|
|
2585
|
+
return {
|
|
2586
|
+
regions,
|
|
2587
|
+
ports,
|
|
2588
|
+
connections
|
|
2589
|
+
};
|
|
2590
|
+
};
|
|
2591
|
+
|
|
2592
|
+
// lib/JumperGraphSolver/jumper-graph-generator/createProblemFromBaseGraph.ts
|
|
2593
|
+
var createSeededRandom = (seed) => {
|
|
2594
|
+
let state = seed;
|
|
2595
|
+
return () => {
|
|
2596
|
+
state = state * 1664525 + 1013904223 >>> 0;
|
|
2597
|
+
return state / 4294967295;
|
|
2598
|
+
};
|
|
2599
|
+
};
|
|
2600
|
+
|
|
2601
|
+
// lib/HyperGraphSectionOptimizer/routes/previewSectionReplacement.ts
|
|
2602
|
+
var previewSectionReplacement = (input) => {
|
|
2603
|
+
const { solvedRoutes, section, replacementSolvedRoutes } = input;
|
|
2604
|
+
const replacementByConnectionId = new Map(
|
|
2605
|
+
replacementSolvedRoutes.map((route) => [
|
|
2606
|
+
route.connection.connectionId,
|
|
2607
|
+
route
|
|
2608
|
+
])
|
|
2609
|
+
);
|
|
2610
|
+
return solvedRoutes.map((solvedRoute) => {
|
|
2611
|
+
const sectionRoute = section.sectionRoutes.find(
|
|
2612
|
+
(route) => route.globalConnection.connectionId === solvedRoute.connection.connectionId
|
|
2613
|
+
);
|
|
2614
|
+
if (!sectionRoute) return solvedRoute;
|
|
2615
|
+
const replacementSolvedRoute = replacementByConnectionId.get(
|
|
2616
|
+
solvedRoute.connection.connectionId
|
|
2617
|
+
);
|
|
2618
|
+
if (!replacementSolvedRoute) return solvedRoute;
|
|
2619
|
+
const path = [
|
|
2620
|
+
...solvedRoute.path.slice(0, sectionRoute.sectionStartIndex),
|
|
2621
|
+
...replacementSolvedRoute.path,
|
|
2622
|
+
...solvedRoute.path.slice(sectionRoute.sectionEndIndex + 1)
|
|
2623
|
+
];
|
|
2624
|
+
const copiedPath = path.map((candidate) => ({
|
|
2625
|
+
port: candidate.port,
|
|
2626
|
+
g: candidate.g,
|
|
2627
|
+
h: candidate.h,
|
|
2628
|
+
f: candidate.f,
|
|
2629
|
+
hops: candidate.hops,
|
|
2630
|
+
ripRequired: candidate.ripRequired,
|
|
2631
|
+
lastPort: candidate.lastPort,
|
|
2632
|
+
lastRegion: candidate.lastRegion,
|
|
2633
|
+
nextRegion: candidate.nextRegion
|
|
2634
|
+
}));
|
|
2635
|
+
for (let i = 0; i < copiedPath.length; i++) {
|
|
2636
|
+
copiedPath[i].parent = i > 0 ? copiedPath[i - 1] : void 0;
|
|
2637
|
+
}
|
|
2638
|
+
return {
|
|
2639
|
+
connection: solvedRoute.connection,
|
|
2640
|
+
path: copiedPath,
|
|
2641
|
+
requiredRip: solvedRoute.requiredRip || replacementSolvedRoute.requiredRip
|
|
2642
|
+
};
|
|
2643
|
+
});
|
|
2644
|
+
};
|
|
2645
|
+
|
|
2646
|
+
// lib/HyperGraphSectionOptimizer/getOrCreateBoundaryRegion.ts
|
|
2647
|
+
var getOrCreateBoundaryRegion = ({
|
|
2648
|
+
port,
|
|
2649
|
+
boundaryRegionMap
|
|
2650
|
+
}) => {
|
|
2651
|
+
let boundaryRegion = boundaryRegionMap.get(port.portId);
|
|
2652
|
+
if (!boundaryRegion) {
|
|
2653
|
+
const x = typeof port.d?.x === "number" ? port.d.x : 0;
|
|
2654
|
+
const y = typeof port.d?.y === "number" ? port.d.y : 0;
|
|
2655
|
+
boundaryRegion = {
|
|
2656
|
+
regionId: `__section_boundary__${port.portId}`,
|
|
2657
|
+
ports: [],
|
|
2658
|
+
d: {
|
|
2659
|
+
isBoundaryRegion: true,
|
|
2660
|
+
boundaryPortId: port.portId,
|
|
2661
|
+
isPad: false,
|
|
2662
|
+
isThroughJumper: false,
|
|
2663
|
+
isConnectionRegion: true,
|
|
2664
|
+
center: { x, y },
|
|
2665
|
+
bounds: {
|
|
2666
|
+
minX: x - 0.05,
|
|
2667
|
+
maxX: x + 0.05,
|
|
2668
|
+
minY: y - 0.05,
|
|
2669
|
+
maxY: y + 0.05
|
|
2670
|
+
}
|
|
2671
|
+
},
|
|
2672
|
+
assignments: []
|
|
2673
|
+
};
|
|
2674
|
+
boundaryRegionMap.set(port.portId, boundaryRegion);
|
|
2675
|
+
}
|
|
2676
|
+
return boundaryRegion;
|
|
2677
|
+
};
|
|
2678
|
+
|
|
2679
|
+
// lib/HyperGraphSectionOptimizer/routes/sliceSolvedRouteIntoLocalSection.ts
|
|
2680
|
+
var sliceSolvedRouteIntoLocalSection = (input) => {
|
|
2681
|
+
const { sectionRoute, graph } = input;
|
|
2682
|
+
const localPortIds = new Set(graph.ports.map((port) => port.portId));
|
|
2683
|
+
const originalLocalPath = sectionRoute.globalRoute.path.slice(sectionRoute.sectionStartIndex, sectionRoute.sectionEndIndex + 1).filter((candidate) => localPortIds.has(candidate.port.portId));
|
|
2684
|
+
const path = [];
|
|
2685
|
+
let currentRegion = sectionRoute.sectionConnection.startRegion;
|
|
2686
|
+
for (let index = 0; index < originalLocalPath.length; index++) {
|
|
2687
|
+
const originalCandidate = originalLocalPath[index];
|
|
2688
|
+
const port = graph.ports.find(
|
|
2689
|
+
(candidatePort) => candidatePort.portId === originalCandidate.port.portId
|
|
2690
|
+
);
|
|
2691
|
+
const nextRegion = port.region1 === currentRegion ? port.region2 : port.region1;
|
|
2692
|
+
path.push({
|
|
2693
|
+
port,
|
|
2694
|
+
g: originalCandidate.g,
|
|
2695
|
+
h: originalCandidate.h,
|
|
2696
|
+
f: originalCandidate.f,
|
|
2697
|
+
hops: index,
|
|
2698
|
+
ripRequired: originalCandidate.ripRequired,
|
|
2699
|
+
parent: index > 0 ? path[index - 1] : void 0,
|
|
2700
|
+
lastPort: index > 0 ? path[index - 1].port : void 0,
|
|
2701
|
+
lastRegion: index > 0 ? currentRegion : void 0,
|
|
2702
|
+
nextRegion
|
|
2703
|
+
});
|
|
2704
|
+
currentRegion = nextRegion;
|
|
2705
|
+
}
|
|
2706
|
+
return {
|
|
2707
|
+
connection: sectionRoute.sectionConnection,
|
|
2708
|
+
path,
|
|
2709
|
+
requiredRip: sectionRoute.globalRoute.requiredRip
|
|
2710
|
+
};
|
|
2711
|
+
};
|
|
2712
|
+
|
|
2713
|
+
// lib/HyperGraphSectionOptimizer/sections/getRouteSectionSpan.ts
|
|
2714
|
+
var getRouteSectionSpan = (route, sectionRegionIds) => {
|
|
2715
|
+
let startIndex = -1;
|
|
2716
|
+
let endIndex = -1;
|
|
2717
|
+
for (let i = 0; i < route.path.length; i++) {
|
|
2718
|
+
const candidate = route.path[i];
|
|
2719
|
+
const touchesSection = candidate.lastRegion && sectionRegionIds.has(candidate.lastRegion.regionId) || candidate.nextRegion && sectionRegionIds.has(candidate.nextRegion.regionId);
|
|
2720
|
+
if (!touchesSection) continue;
|
|
2721
|
+
if (startIndex === -1) startIndex = i;
|
|
2722
|
+
endIndex = i;
|
|
2723
|
+
}
|
|
2724
|
+
if (startIndex === -1) return null;
|
|
2725
|
+
return { startIndex, endIndex };
|
|
2726
|
+
};
|
|
2727
|
+
|
|
2728
|
+
// lib/HyperGraphSectionOptimizer/sections/getSectionRegionIds.ts
|
|
2729
|
+
var getSectionRegionIds = ({
|
|
2730
|
+
centralRegion,
|
|
2731
|
+
expansionHopsFromCentralRegion
|
|
2732
|
+
}) => {
|
|
2733
|
+
const sectionRegionIds = /* @__PURE__ */ new Set([centralRegion.regionId]);
|
|
2734
|
+
const queue = [
|
|
2735
|
+
{ region: centralRegion, hops: 0 }
|
|
2736
|
+
];
|
|
2737
|
+
while (queue.length > 0) {
|
|
2738
|
+
const { region, hops } = queue.shift();
|
|
2739
|
+
if (hops >= expansionHopsFromCentralRegion + 1) continue;
|
|
2740
|
+
for (const port of region.ports) {
|
|
2741
|
+
const nextRegion = port.region1 === region ? port.region2 : port.region1;
|
|
2742
|
+
if (sectionRegionIds.has(nextRegion.regionId)) continue;
|
|
2743
|
+
sectionRegionIds.add(nextRegion.regionId);
|
|
2744
|
+
queue.push({ region: nextRegion, hops: hops + 1 });
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
return sectionRegionIds;
|
|
2748
|
+
};
|
|
2749
|
+
|
|
2750
|
+
// lib/HyperGraphSectionOptimizer/sections/getSectionOfHyperGraphAsHyperGraph.ts
|
|
2751
|
+
var getSectionOfHyperGraphAsHyperGraph = (input) => {
|
|
2752
|
+
const { graph, solvedRoutes, centralRegion, expansionHopsFromCentralRegion } = input;
|
|
2753
|
+
const sectionRegionIds = getSectionRegionIds({
|
|
2754
|
+
graph,
|
|
2755
|
+
centralRegion,
|
|
2756
|
+
expansionHopsFromCentralRegion
|
|
2757
|
+
});
|
|
2758
|
+
const clonedRegionMap = /* @__PURE__ */ new Map();
|
|
2759
|
+
const boundaryRegionMap = /* @__PURE__ */ new Map();
|
|
2760
|
+
const clonedPorts = [];
|
|
2761
|
+
for (const region of graph.regions) {
|
|
2762
|
+
if (!sectionRegionIds.has(region.regionId)) continue;
|
|
2763
|
+
clonedRegionMap.set(region.regionId, {
|
|
2764
|
+
regionId: region.regionId,
|
|
2765
|
+
ports: [],
|
|
2766
|
+
d: region.d ? structuredClone(region.d) : region.d,
|
|
2767
|
+
assignments: []
|
|
2768
|
+
});
|
|
2769
|
+
}
|
|
2770
|
+
for (const port of graph.ports) {
|
|
2771
|
+
const region1InSection = sectionRegionIds.has(port.region1.regionId);
|
|
2772
|
+
const region2InSection = sectionRegionIds.has(port.region2.regionId);
|
|
2773
|
+
if (!region1InSection && !region2InSection) continue;
|
|
2774
|
+
if (region1InSection && region2InSection) {
|
|
2775
|
+
const clonedPort2 = {
|
|
2776
|
+
portId: port.portId,
|
|
2777
|
+
region1: clonedRegionMap.get(port.region1.regionId),
|
|
2778
|
+
region2: clonedRegionMap.get(port.region2.regionId),
|
|
2779
|
+
d: port.d
|
|
2780
|
+
};
|
|
2781
|
+
clonedPort2.region1.ports.push(clonedPort2);
|
|
2782
|
+
clonedPort2.region2.ports.push(clonedPort2);
|
|
2783
|
+
clonedPorts.push(clonedPort2);
|
|
2784
|
+
continue;
|
|
2785
|
+
}
|
|
2786
|
+
const insideRegion = region1InSection ? port.region1 : port.region2;
|
|
2787
|
+
const boundaryRegion = getOrCreateBoundaryRegion({
|
|
2788
|
+
port,
|
|
2789
|
+
boundaryRegionMap
|
|
2790
|
+
});
|
|
2791
|
+
const clonedPort = {
|
|
2792
|
+
portId: port.portId,
|
|
2793
|
+
region1: clonedRegionMap.get(insideRegion.regionId),
|
|
2794
|
+
region2: boundaryRegion,
|
|
2795
|
+
d: port.d
|
|
2796
|
+
};
|
|
2797
|
+
clonedPort.region1.ports.push(clonedPort);
|
|
2798
|
+
clonedPort.region2.ports.push(clonedPort);
|
|
2799
|
+
clonedPorts.push(clonedPort);
|
|
2800
|
+
}
|
|
2801
|
+
const sectionGraph = {
|
|
2802
|
+
regions: [...clonedRegionMap.values(), ...boundaryRegionMap.values()],
|
|
2803
|
+
ports: clonedPorts
|
|
2804
|
+
};
|
|
2805
|
+
const sectionRoutes = [];
|
|
2806
|
+
const sectionConnections = [];
|
|
2807
|
+
const sectionRegionMap = new Map(
|
|
2808
|
+
sectionGraph.regions.map((region) => [region.regionId, region])
|
|
2809
|
+
);
|
|
2810
|
+
for (const solvedRoute of solvedRoutes) {
|
|
2811
|
+
const span = getRouteSectionSpan(solvedRoute, sectionRegionIds);
|
|
2812
|
+
if (!span) continue;
|
|
2813
|
+
const startCandidate = solvedRoute.path[span.startIndex];
|
|
2814
|
+
const endCandidate = solvedRoute.path[span.endIndex];
|
|
2815
|
+
let startRegionId;
|
|
2816
|
+
let startRegion;
|
|
2817
|
+
if (sectionRegionIds.has(solvedRoute.connection.startRegion.regionId)) {
|
|
2818
|
+
startRegionId = solvedRoute.connection.startRegion.regionId;
|
|
2819
|
+
startRegion = sectionRegionMap.get(startRegionId);
|
|
2820
|
+
} else {
|
|
2821
|
+
const boundaryRegion = getOrCreateBoundaryRegion({
|
|
2822
|
+
port: startCandidate.port,
|
|
2823
|
+
boundaryRegionMap
|
|
2824
|
+
});
|
|
2825
|
+
startRegionId = boundaryRegion.regionId;
|
|
2826
|
+
startRegion = boundaryRegion;
|
|
2827
|
+
if (!sectionRegionMap.has(startRegionId)) {
|
|
2828
|
+
sectionRegionMap.set(startRegionId, boundaryRegion);
|
|
2829
|
+
}
|
|
2830
|
+
}
|
|
2831
|
+
let endRegionId;
|
|
2832
|
+
let endRegion;
|
|
2833
|
+
if (sectionRegionIds.has(solvedRoute.connection.endRegion.regionId)) {
|
|
2834
|
+
endRegionId = solvedRoute.connection.endRegion.regionId;
|
|
2835
|
+
endRegion = sectionRegionMap.get(endRegionId);
|
|
2836
|
+
} else {
|
|
2837
|
+
const boundaryRegion = getOrCreateBoundaryRegion({
|
|
2838
|
+
port: endCandidate.port,
|
|
2839
|
+
boundaryRegionMap
|
|
2840
|
+
});
|
|
2841
|
+
endRegionId = boundaryRegion.regionId;
|
|
2842
|
+
endRegion = boundaryRegion;
|
|
2843
|
+
if (!sectionRegionMap.has(endRegionId)) {
|
|
2844
|
+
sectionRegionMap.set(endRegionId, boundaryRegion);
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
if (!startRegion) {
|
|
2848
|
+
console.error(
|
|
2849
|
+
`[getSectionOfHyperGraphAsHyperGraph] CRITICAL ERROR: startRegion not found!`
|
|
2850
|
+
);
|
|
2851
|
+
console.error(` Looking for: ${startRegionId}`);
|
|
2852
|
+
console.error(
|
|
2853
|
+
` Route connection: ${solvedRoute.connection.connectionId}`
|
|
2854
|
+
);
|
|
2855
|
+
console.error(
|
|
2856
|
+
` Original startRegion: ${solvedRoute.connection.startRegion.regionId}`
|
|
2857
|
+
);
|
|
2858
|
+
console.error(` startCandidate port: ${startCandidate.port.portId}`);
|
|
2859
|
+
console.error(
|
|
2860
|
+
` Available regions in sectionRegionMap:`,
|
|
2861
|
+
Array.from(sectionRegionMap.keys())
|
|
2862
|
+
);
|
|
2863
|
+
throw new Error(
|
|
2864
|
+
`startRegion ${startRegionId} not found in sectionRegionMap`
|
|
2865
|
+
);
|
|
2866
|
+
}
|
|
2867
|
+
if (!endRegion) {
|
|
2868
|
+
console.error(
|
|
2869
|
+
`[getSectionOfHyperGraphAsHyperGraph] CRITICAL ERROR: endRegion not found!`
|
|
2870
|
+
);
|
|
2871
|
+
console.error(` Looking for: ${endRegionId}`);
|
|
2872
|
+
console.error(
|
|
2873
|
+
` Route connection: ${solvedRoute.connection.connectionId}`
|
|
2874
|
+
);
|
|
2875
|
+
console.error(
|
|
2876
|
+
` Original endRegion: ${solvedRoute.connection.endRegion.regionId}`
|
|
2877
|
+
);
|
|
2878
|
+
console.error(` endCandidate port: ${endCandidate.port.portId}`);
|
|
2879
|
+
console.error(
|
|
2880
|
+
` Available regions in sectionRegionMap:`,
|
|
2881
|
+
Array.from(sectionRegionMap.keys())
|
|
2882
|
+
);
|
|
2883
|
+
throw new Error(`endRegion ${endRegionId} not found in sectionRegionMap`);
|
|
2884
|
+
}
|
|
2885
|
+
const sectionConnection = {
|
|
2886
|
+
connectionId: solvedRoute.connection.connectionId,
|
|
2887
|
+
mutuallyConnectedNetworkId: solvedRoute.connection.mutuallyConnectedNetworkId,
|
|
2888
|
+
startRegion,
|
|
2889
|
+
endRegion
|
|
2890
|
+
};
|
|
2891
|
+
const rawPath = solvedRoute.path.slice(span.startIndex, span.endIndex + 1);
|
|
2892
|
+
const sectionRouteBase = {
|
|
2893
|
+
globalRoute: solvedRoute,
|
|
2894
|
+
globalConnection: solvedRoute.connection,
|
|
2895
|
+
sectionConnection,
|
|
2896
|
+
sectionStartIndex: span.startIndex,
|
|
2897
|
+
sectionEndIndex: span.endIndex
|
|
2898
|
+
};
|
|
2899
|
+
sectionRoutes.push({
|
|
2900
|
+
...sectionRouteBase,
|
|
2901
|
+
canRemainFixedInSectionSolve: rawPath.every(
|
|
2902
|
+
(candidate) => sectionGraph.ports.some(
|
|
2903
|
+
(port) => port.portId === candidate.port.portId
|
|
2904
|
+
)
|
|
2905
|
+
),
|
|
2906
|
+
sectionRoute: sliceSolvedRouteIntoLocalSection({
|
|
2907
|
+
sectionRoute: sectionRouteBase,
|
|
2908
|
+
graph: sectionGraph
|
|
2909
|
+
})
|
|
2910
|
+
});
|
|
2911
|
+
sectionConnections.push(sectionConnection);
|
|
2912
|
+
}
|
|
2913
|
+
return {
|
|
2914
|
+
centralRegionId: centralRegion.regionId,
|
|
2915
|
+
sectionRegionIds,
|
|
2916
|
+
graph: sectionGraph,
|
|
2917
|
+
connections: sectionConnections,
|
|
2918
|
+
sectionRoutes
|
|
2919
|
+
};
|
|
2920
|
+
};
|
|
2921
|
+
|
|
2922
|
+
// lib/HyperGraphSectionOptimizer/HyperGraphSectionOptimizer.ts
|
|
2923
|
+
var HyperGraphSectionOptimizer = class extends BaseSolver2 {
|
|
2924
|
+
constructor(input) {
|
|
2925
|
+
super();
|
|
2926
|
+
this.input = input;
|
|
2927
|
+
this.graph = input.hyperGraphSolver.graph;
|
|
2928
|
+
const initialSolvedRoutes = input.inputSolvedRoutes;
|
|
2929
|
+
const inputConnections = input.hyperGraphSolver.connections;
|
|
2930
|
+
this.connections = inputConnections;
|
|
2931
|
+
this.solvedRoutes = commitSolvedRoutes({
|
|
2932
|
+
graph: this.graph,
|
|
2933
|
+
connections: this.connections,
|
|
2934
|
+
solvedRoutes: initialSolvedRoutes
|
|
2935
|
+
});
|
|
2936
|
+
this.maxAttemptsPerRegion = input.MAX_ATTEMPTS_PER_REGION;
|
|
2937
|
+
this.maxSectionAttempts = input.MAX_ATTEMPTS_PER_SECTION ?? 500;
|
|
2938
|
+
this.iterations += this.iterations * (input.effort ?? 1);
|
|
2939
|
+
this.fractionToReplace = input.FRACTION_TO_REPLACE ?? 0.2;
|
|
2940
|
+
this.alwaysRipConflicts = input.alwaysRipConflicts ?? true;
|
|
2941
|
+
}
|
|
2942
|
+
graph;
|
|
2943
|
+
connections;
|
|
2944
|
+
solvedRoutes;
|
|
2945
|
+
activeSection = null;
|
|
2946
|
+
baselineSectionCost = Infinity;
|
|
2947
|
+
baselineGlobalCost = Infinity;
|
|
2948
|
+
regionAttemptCounts = /* @__PURE__ */ new Map();
|
|
2949
|
+
sectionAttempts = 0;
|
|
2950
|
+
maxAttemptsPerRegion;
|
|
2951
|
+
maxSectionAttempts;
|
|
2952
|
+
fractionToReplace;
|
|
2953
|
+
alwaysRipConflicts;
|
|
2954
|
+
random = createSeededRandom(31337);
|
|
2955
|
+
getSolverName() {
|
|
2956
|
+
return "HyperGraphSectionOptimizer";
|
|
2957
|
+
}
|
|
2958
|
+
getConstructorParams() {
|
|
2959
|
+
return {
|
|
2960
|
+
inputGraph: convertHyperGraphToSerializedHyperGraph(this.graph),
|
|
2961
|
+
inputConnections: convertConnectionsToSerializedConnections(
|
|
2962
|
+
this.connections
|
|
2963
|
+
),
|
|
2964
|
+
inputSolvedRoutes: this.solvedRoutes,
|
|
2965
|
+
expansionHopsFromCentralRegion: this.input.expansionHopsFromCentralRegion,
|
|
2966
|
+
maxAttemptsPerRegion: this.maxAttemptsPerRegion,
|
|
2967
|
+
maxSectionAttempts: this.maxSectionAttempts,
|
|
2968
|
+
effort: this.input.effort,
|
|
2969
|
+
fractionToReplace: this.fractionToReplace,
|
|
2970
|
+
alwaysRipConflicts: this.alwaysRipConflicts,
|
|
2971
|
+
ACCEPTABLE_COST: this.input.ACCEPTABLE_REGION_COST
|
|
2972
|
+
};
|
|
2973
|
+
}
|
|
2974
|
+
getOutput() {
|
|
2975
|
+
return this.solvedRoutes;
|
|
2976
|
+
}
|
|
2977
|
+
_setup() {
|
|
2978
|
+
this.beginSectionSolve();
|
|
2979
|
+
}
|
|
2980
|
+
visualize() {
|
|
2981
|
+
if (this.activeSubSolver) {
|
|
2982
|
+
return this.activeSubSolver.visualize();
|
|
2983
|
+
}
|
|
2984
|
+
return {
|
|
2985
|
+
title: "HyperGraphSectionOptimizer",
|
|
2986
|
+
points: [],
|
|
2987
|
+
lines: [],
|
|
2988
|
+
rects: [],
|
|
2989
|
+
circles: [],
|
|
2990
|
+
texts: [],
|
|
2991
|
+
polygons: [],
|
|
2992
|
+
arrows: []
|
|
2993
|
+
};
|
|
2994
|
+
}
|
|
2995
|
+
getCostOfRegionWithAttempts(region) {
|
|
2996
|
+
const attempts = this.regionAttemptCounts.get(region.regionId) ?? 0;
|
|
2997
|
+
return this.input.regionCost(region) + attempts * 1e4;
|
|
2998
|
+
}
|
|
2999
|
+
computeCostOfSection({
|
|
3000
|
+
section,
|
|
3001
|
+
solvedRoutes
|
|
3002
|
+
}) {
|
|
3003
|
+
if (this.input.computeSolvedGraphCost) {
|
|
3004
|
+
return this.input.computeSolvedGraphCost(solvedRoutes);
|
|
3005
|
+
}
|
|
3006
|
+
const solver = this.input.createHyperGraphSolver({
|
|
3007
|
+
inputGraph: section.graph,
|
|
3008
|
+
inputConnections: section.connections,
|
|
3009
|
+
inputSolvedRoutes: solvedRoutes
|
|
3010
|
+
});
|
|
3011
|
+
let totalCost = 0;
|
|
3012
|
+
for (const region of solver.graph.regions) {
|
|
3013
|
+
totalCost += this.input.regionCost(region);
|
|
3014
|
+
}
|
|
3015
|
+
return totalCost;
|
|
3016
|
+
}
|
|
3017
|
+
computeSolvedGraphCost(solvedRoutes) {
|
|
3018
|
+
if (this.input.computeSolvedGraphCost) {
|
|
3019
|
+
return this.input.computeSolvedGraphCost(solvedRoutes);
|
|
3020
|
+
}
|
|
3021
|
+
const solver = this.input.createHyperGraphSolver({
|
|
3022
|
+
inputGraph: this.graph,
|
|
3023
|
+
inputConnections: this.connections,
|
|
3024
|
+
inputSolvedRoutes: solvedRoutes
|
|
3025
|
+
});
|
|
3026
|
+
let totalCost = 0;
|
|
3027
|
+
for (const region of solver.graph.regions) {
|
|
3028
|
+
totalCost += this.input.regionCost(region);
|
|
3029
|
+
}
|
|
3030
|
+
return totalCost;
|
|
3031
|
+
}
|
|
3032
|
+
/**
|
|
3033
|
+
* TODO default behavior should be to rip entire section
|
|
3034
|
+
*/
|
|
3035
|
+
determineConnectionsToRip(section, evaluationSolver) {
|
|
3036
|
+
const allConnectionIds = section.sectionRoutes.map(
|
|
3037
|
+
(route) => route.globalConnection.connectionId
|
|
3038
|
+
);
|
|
3039
|
+
if (this.fractionToReplace >= 1) {
|
|
3040
|
+
return new Set(allConnectionIds);
|
|
3041
|
+
}
|
|
3042
|
+
const shuffledConnectionIds = [...allConnectionIds];
|
|
3043
|
+
for (let index = shuffledConnectionIds.length - 1; index > 0; index--) {
|
|
3044
|
+
const swapIndex = Math.floor(this.random() * (index + 1));
|
|
3045
|
+
[shuffledConnectionIds[index], shuffledConnectionIds[swapIndex]] = [
|
|
3046
|
+
shuffledConnectionIds[swapIndex],
|
|
3047
|
+
shuffledConnectionIds[index]
|
|
3048
|
+
];
|
|
3049
|
+
}
|
|
3050
|
+
const ripCount = Math.max(
|
|
3051
|
+
1,
|
|
3052
|
+
Math.ceil(shuffledConnectionIds.length * this.fractionToReplace)
|
|
3053
|
+
);
|
|
3054
|
+
const connectionsToReroute = new Set(
|
|
3055
|
+
shuffledConnectionIds.slice(0, ripCount)
|
|
3056
|
+
);
|
|
3057
|
+
if (!this.alwaysRipConflicts) {
|
|
3058
|
+
return connectionsToReroute;
|
|
3059
|
+
}
|
|
3060
|
+
const localRegionMap = new Map(
|
|
3061
|
+
section.graph.regions.map((region) => [region.regionId, region])
|
|
3062
|
+
);
|
|
3063
|
+
for (const route of section.sectionRoutes) {
|
|
3064
|
+
for (const candidate of route.sectionRoute.path) {
|
|
3065
|
+
if (!candidate.lastPort || !candidate.lastRegion) continue;
|
|
3066
|
+
const sectionRegion = localRegionMap.get(candidate.lastRegion.regionId);
|
|
3067
|
+
if (!sectionRegion) continue;
|
|
3068
|
+
evaluationSolver.currentConnection = route.globalConnection;
|
|
3069
|
+
const conflictingAssignments = evaluationSolver.getRipsRequiredForPortUsage(
|
|
3070
|
+
sectionRegion,
|
|
3071
|
+
candidate.lastPort,
|
|
3072
|
+
candidate.port
|
|
3073
|
+
);
|
|
3074
|
+
for (const conflict of conflictingAssignments) {
|
|
3075
|
+
const firstId = route.globalConnection.connectionId;
|
|
3076
|
+
const secondId = conflict.connection.connectionId;
|
|
3077
|
+
if (connectionsToReroute.has(firstId) || connectionsToReroute.has(secondId)) {
|
|
3078
|
+
continue;
|
|
3079
|
+
}
|
|
3080
|
+
connectionsToReroute.add(this.random() < 0.5 ? firstId : secondId);
|
|
3081
|
+
}
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
return connectionsToReroute;
|
|
3085
|
+
}
|
|
3086
|
+
getNextCentralRegion() {
|
|
3087
|
+
let bestRegion = null;
|
|
3088
|
+
let bestCost = Infinity;
|
|
3089
|
+
for (const region of this.graph.regions) {
|
|
3090
|
+
if ((region.assignments?.length ?? 0) === 0) continue;
|
|
3091
|
+
if ((this.regionAttemptCounts.get(region.regionId) ?? 0) >= this.maxAttemptsPerRegion) {
|
|
3092
|
+
continue;
|
|
3093
|
+
}
|
|
3094
|
+
const regionCost = this.input.regionCost(region);
|
|
3095
|
+
if (regionCost < this.input.ACCEPTABLE_REGION_COST) continue;
|
|
3096
|
+
const cost = this.getCostOfRegionWithAttempts(region);
|
|
3097
|
+
if (cost >= bestCost) continue;
|
|
3098
|
+
bestCost = cost;
|
|
3099
|
+
bestRegion = region;
|
|
3100
|
+
}
|
|
3101
|
+
return bestRegion;
|
|
3102
|
+
}
|
|
3103
|
+
beginSectionSolve() {
|
|
3104
|
+
if (this.sectionAttempts >= this.maxSectionAttempts) {
|
|
3105
|
+
console.log(
|
|
3106
|
+
`Reached max section attempts (${this.maxSectionAttempts}), stopping optimization`
|
|
3107
|
+
);
|
|
3108
|
+
this.solved = true;
|
|
3109
|
+
return;
|
|
3110
|
+
}
|
|
3111
|
+
const centralRegion = this.getNextCentralRegion();
|
|
3112
|
+
if (!centralRegion) {
|
|
3113
|
+
this.solved = true;
|
|
3114
|
+
return;
|
|
3115
|
+
}
|
|
3116
|
+
this.sectionAttempts++;
|
|
3117
|
+
this.activeSection = getSectionOfHyperGraphAsHyperGraph({
|
|
3118
|
+
graph: this.graph,
|
|
3119
|
+
solvedRoutes: this.solvedRoutes,
|
|
3120
|
+
centralRegion,
|
|
3121
|
+
expansionHopsFromCentralRegion: this.input.expansionHopsFromCentralRegion
|
|
3122
|
+
});
|
|
3123
|
+
if (!this.activeSection) {
|
|
3124
|
+
return;
|
|
3125
|
+
}
|
|
3126
|
+
if (this.activeSection.connections.length === 0) {
|
|
3127
|
+
this.regionAttemptCounts.set(
|
|
3128
|
+
centralRegion.regionId,
|
|
3129
|
+
(this.regionAttemptCounts.get(centralRegion.regionId) ?? 0) + 1
|
|
3130
|
+
);
|
|
3131
|
+
this.activeSection = null;
|
|
3132
|
+
return;
|
|
3133
|
+
}
|
|
3134
|
+
const fixedSectionRoutes = this.activeSection.sectionRoutes.filter(
|
|
3135
|
+
(route) => route.canRemainFixedInSectionSolve
|
|
3136
|
+
);
|
|
3137
|
+
const baselineSolver = this.input.createHyperGraphSolver({
|
|
3138
|
+
inputGraph: this.activeSection.graph,
|
|
3139
|
+
inputConnections: this.activeSection.connections,
|
|
3140
|
+
inputSolvedRoutes: fixedSectionRoutes.map((route) => route.sectionRoute)
|
|
3141
|
+
});
|
|
3142
|
+
const connectionsToReroute = this.determineConnectionsToRip(
|
|
3143
|
+
this.activeSection,
|
|
3144
|
+
baselineSolver
|
|
3145
|
+
);
|
|
3146
|
+
for (const route of this.activeSection.sectionRoutes) {
|
|
3147
|
+
if (!route.canRemainFixedInSectionSolve) {
|
|
3148
|
+
connectionsToReroute.add(route.globalConnection.connectionId);
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
const remainingSectionRoutes = this.activeSection.sectionRoutes.filter(
|
|
3152
|
+
(route) => route.canRemainFixedInSectionSolve && !connectionsToReroute.has(route.globalConnection.connectionId)
|
|
3153
|
+
);
|
|
3154
|
+
const fixedSectionSolvedRoutes = remainingSectionRoutes.map(
|
|
3155
|
+
(route) => route.sectionRoute
|
|
3156
|
+
);
|
|
3157
|
+
const baselineSectionSolvedRoutes = [
|
|
3158
|
+
...fixedSectionSolvedRoutes,
|
|
3159
|
+
...this.activeSection.sectionRoutes.filter(
|
|
3160
|
+
(route) => connectionsToReroute.has(route.globalConnection.connectionId)
|
|
3161
|
+
).map((route) => route.sectionRoute)
|
|
3162
|
+
];
|
|
3163
|
+
this.baselineSectionCost = this.computeCostOfSection({
|
|
3164
|
+
section: this.activeSection,
|
|
3165
|
+
solvedRoutes: baselineSectionSolvedRoutes
|
|
3166
|
+
});
|
|
3167
|
+
this.baselineGlobalCost = this.computeSolvedGraphCost(this.solvedRoutes);
|
|
3168
|
+
this.activeSubSolver = this.input.createHyperGraphSolver({
|
|
3169
|
+
inputGraph: this.activeSection.graph,
|
|
3170
|
+
inputConnections: this.activeSection.connections,
|
|
3171
|
+
inputSolvedRoutes: fixedSectionSolvedRoutes
|
|
3172
|
+
});
|
|
3173
|
+
for (const conn of this.activeSubSolver.connections) {
|
|
3174
|
+
if (!conn.startRegion || !conn.endRegion) {
|
|
3175
|
+
console.error({
|
|
3176
|
+
startRegion: conn.startRegion?.regionId,
|
|
3177
|
+
endRegion: conn.endRegion?.regionId
|
|
3178
|
+
});
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
}
|
|
3182
|
+
_step() {
|
|
3183
|
+
if (!this.activeSubSolver) {
|
|
3184
|
+
this.beginSectionSolve();
|
|
3185
|
+
return;
|
|
3186
|
+
}
|
|
3187
|
+
this.activeSubSolver.step();
|
|
3188
|
+
if (!this.activeSection) {
|
|
3189
|
+
return;
|
|
3190
|
+
}
|
|
3191
|
+
if (this.activeSubSolver.failed) {
|
|
3192
|
+
this.failedSubSolvers ??= [];
|
|
3193
|
+
this.failedSubSolvers.push(this.activeSubSolver);
|
|
3194
|
+
const attempts = this.regionAttemptCounts.get(this.activeSection.centralRegionId) ?? 0;
|
|
3195
|
+
this.regionAttemptCounts.set(
|
|
3196
|
+
this.activeSection.centralRegionId,
|
|
3197
|
+
attempts + 1
|
|
3198
|
+
);
|
|
3199
|
+
this.activeSubSolver = null;
|
|
3200
|
+
this.activeSection = null;
|
|
3201
|
+
this.baselineSectionCost = Infinity;
|
|
3202
|
+
this.baselineGlobalCost = Infinity;
|
|
3203
|
+
return;
|
|
3204
|
+
}
|
|
3205
|
+
if (!this.activeSubSolver.solved) return;
|
|
3206
|
+
const candidateSectionSolvedRoutes = this.activeSubSolver.solvedRoutes;
|
|
3207
|
+
const candidateCost = this.computeCostOfSection({
|
|
3208
|
+
section: this.activeSection,
|
|
3209
|
+
solvedRoutes: candidateSectionSolvedRoutes
|
|
3210
|
+
});
|
|
3211
|
+
const replacementAppliedSolvedRoutes = previewSectionReplacement({
|
|
3212
|
+
solvedRoutes: this.solvedRoutes,
|
|
3213
|
+
section: this.activeSection,
|
|
3214
|
+
replacementSolvedRoutes: candidateSectionSolvedRoutes
|
|
3215
|
+
});
|
|
3216
|
+
const candidateGlobalCost = this.computeSolvedGraphCost(
|
|
3217
|
+
replacementAppliedSolvedRoutes
|
|
3218
|
+
);
|
|
3219
|
+
const sectionNotWorse = candidateCost <= this.baselineSectionCost;
|
|
3220
|
+
const globalImproved = candidateGlobalCost < this.baselineGlobalCost;
|
|
3221
|
+
if (sectionNotWorse && globalImproved) {
|
|
3222
|
+
this.solvedRoutes = replacementAppliedSolvedRoutes;
|
|
3223
|
+
const sourceSolver = this.input.hyperGraphSolver;
|
|
3224
|
+
if (!sourceSolver) return;
|
|
3225
|
+
sourceSolver.solvedRoutes = commitSolvedRoutes({
|
|
3226
|
+
graph: sourceSolver.graph,
|
|
3227
|
+
connections: sourceSolver.connections,
|
|
3228
|
+
solvedRoutes: this.solvedRoutes
|
|
3229
|
+
});
|
|
3230
|
+
for (const regionId of this.activeSection.sectionRegionIds) {
|
|
3231
|
+
this.regionAttemptCounts.set(regionId, 0);
|
|
3232
|
+
}
|
|
3233
|
+
this.baselineSectionCost = candidateCost;
|
|
3234
|
+
this.baselineGlobalCost = candidateGlobalCost;
|
|
3235
|
+
} else {
|
|
3236
|
+
const attempts = this.regionAttemptCounts.get(this.activeSection.centralRegionId) ?? 0;
|
|
3237
|
+
this.regionAttemptCounts.set(
|
|
3238
|
+
this.activeSection.centralRegionId,
|
|
3239
|
+
attempts + 1
|
|
3240
|
+
);
|
|
3241
|
+
}
|
|
3242
|
+
this.activeSubSolver = null;
|
|
3243
|
+
this.activeSection = null;
|
|
3244
|
+
this.baselineSectionCost = Infinity;
|
|
3245
|
+
this.baselineGlobalCost = Infinity;
|
|
3246
|
+
}
|
|
3247
|
+
};
|
|
3248
|
+
|
|
3249
|
+
// node_modules/transformation-matrix/src/applyToPoint.js
|
|
3250
|
+
function applyToPoint(matrix2, point2) {
|
|
3251
|
+
return Array.isArray(point2) ? [
|
|
3252
|
+
matrix2.a * point2[0] + matrix2.c * point2[1] + matrix2.e,
|
|
3253
|
+
matrix2.b * point2[0] + matrix2.d * point2[1] + matrix2.f
|
|
3254
|
+
] : {
|
|
3255
|
+
x: matrix2.a * point2.x + matrix2.c * point2.y + matrix2.e,
|
|
3256
|
+
y: matrix2.b * point2.x + matrix2.d * point2.y + matrix2.f
|
|
3257
|
+
};
|
|
3258
|
+
}
|
|
3259
|
+
|
|
3260
|
+
// node_modules/transformation-matrix/src/utils.js
|
|
3261
|
+
function isUndefined(val) {
|
|
3262
|
+
return typeof val === "undefined";
|
|
3263
|
+
}
|
|
3264
|
+
|
|
3265
|
+
// node_modules/transformation-matrix/src/translate.js
|
|
3266
|
+
function translate(tx, ty = 0) {
|
|
3267
|
+
return {
|
|
3268
|
+
a: 1,
|
|
3269
|
+
c: 0,
|
|
3270
|
+
e: tx,
|
|
3271
|
+
b: 0,
|
|
3272
|
+
d: 1,
|
|
3273
|
+
f: ty
|
|
3274
|
+
};
|
|
3275
|
+
}
|
|
3276
|
+
|
|
3277
|
+
// node_modules/transformation-matrix/src/transform.js
|
|
3278
|
+
function transform(...matrices) {
|
|
3279
|
+
matrices = Array.isArray(matrices[0]) ? matrices[0] : matrices;
|
|
3280
|
+
const multiply = (m1, m2) => {
|
|
3281
|
+
return {
|
|
3282
|
+
a: m1.a * m2.a + m1.c * m2.b,
|
|
3283
|
+
c: m1.a * m2.c + m1.c * m2.d,
|
|
3284
|
+
e: m1.a * m2.e + m1.c * m2.f + m1.e,
|
|
3285
|
+
b: m1.b * m2.a + m1.d * m2.b,
|
|
3286
|
+
d: m1.b * m2.c + m1.d * m2.d,
|
|
3287
|
+
f: m1.b * m2.e + m1.d * m2.f + m1.f
|
|
3288
|
+
};
|
|
3289
|
+
};
|
|
3290
|
+
switch (matrices.length) {
|
|
3291
|
+
case 0:
|
|
3292
|
+
throw new Error("no matrices provided");
|
|
3293
|
+
case 1:
|
|
3294
|
+
return matrices[0];
|
|
3295
|
+
case 2:
|
|
3296
|
+
return multiply(matrices[0], matrices[1]);
|
|
3297
|
+
default: {
|
|
3298
|
+
const [m1, m2, ...rest] = matrices;
|
|
3299
|
+
const m = multiply(m1, m2);
|
|
3300
|
+
return transform(m, ...rest);
|
|
3301
|
+
}
|
|
3302
|
+
}
|
|
3303
|
+
}
|
|
3304
|
+
function compose(...matrices) {
|
|
3305
|
+
return transform(...matrices);
|
|
3306
|
+
}
|
|
3307
|
+
|
|
3308
|
+
// node_modules/transformation-matrix/src/rotate.js
|
|
3309
|
+
var { cos, sin, PI } = Math;
|
|
3310
|
+
function rotate(angle, cx, cy) {
|
|
3311
|
+
const cosAngle = cos(angle);
|
|
3312
|
+
const sinAngle = sin(angle);
|
|
3313
|
+
const rotationMatrix = {
|
|
3314
|
+
a: cosAngle,
|
|
3315
|
+
c: -sinAngle,
|
|
3316
|
+
e: 0,
|
|
3317
|
+
b: sinAngle,
|
|
3318
|
+
d: cosAngle,
|
|
3319
|
+
f: 0
|
|
3320
|
+
};
|
|
3321
|
+
if (isUndefined(cx) || isUndefined(cy)) {
|
|
3322
|
+
return rotationMatrix;
|
|
3323
|
+
}
|
|
3324
|
+
return transform([
|
|
3325
|
+
translate(cx, cy),
|
|
3326
|
+
rotationMatrix,
|
|
3327
|
+
translate(-cx, -cy)
|
|
3328
|
+
]);
|
|
3329
|
+
}
|
|
3330
|
+
|
|
3331
|
+
// node_modules/transformation-matrix/src/skew.js
|
|
3332
|
+
var { tan } = Math;
|
|
3333
|
+
|
|
3334
|
+
// lib/JumperGraphSolver/geometry/getBoundsCenter.ts
|
|
3335
|
+
var computeBoundsCenter = (bounds) => {
|
|
3336
|
+
return {
|
|
3337
|
+
x: (bounds.minX + bounds.maxX) / 2,
|
|
3338
|
+
y: (bounds.minY + bounds.maxY) / 2
|
|
3339
|
+
};
|
|
3340
|
+
};
|
|
3341
|
+
|
|
3342
|
+
// lib/JumperGraphSolver/geometry/applyTransformToGraph.ts
|
|
3343
|
+
var applyTransformToGraph = (graph, matrix2) => {
|
|
3344
|
+
const transformedRegions = graph.regions.map((region) => {
|
|
3345
|
+
const { bounds, center, ...rest } = region.d;
|
|
3346
|
+
const corners = [
|
|
3347
|
+
{ x: bounds.minX, y: bounds.minY },
|
|
3348
|
+
{ x: bounds.maxX, y: bounds.minY },
|
|
3349
|
+
{ x: bounds.minX, y: bounds.maxY },
|
|
3350
|
+
{ x: bounds.maxX, y: bounds.maxY }
|
|
3351
|
+
].map((corner) => applyToPoint(matrix2, corner));
|
|
3352
|
+
const newBounds = {
|
|
3353
|
+
minX: Math.min(...corners.map((c) => c.x)),
|
|
3354
|
+
maxX: Math.max(...corners.map((c) => c.x)),
|
|
3355
|
+
minY: Math.min(...corners.map((c) => c.y)),
|
|
3356
|
+
maxY: Math.max(...corners.map((c) => c.y))
|
|
3357
|
+
};
|
|
3358
|
+
const newCenter = applyToPoint(matrix2, center);
|
|
3359
|
+
return {
|
|
3360
|
+
...region,
|
|
3361
|
+
// Clear ports array - will be rebuilt with new port objects
|
|
3362
|
+
ports: [],
|
|
3363
|
+
d: {
|
|
3364
|
+
...rest,
|
|
3365
|
+
bounds: newBounds,
|
|
3366
|
+
center: newCenter
|
|
3367
|
+
}
|
|
3368
|
+
};
|
|
3369
|
+
});
|
|
3370
|
+
const regionMap = /* @__PURE__ */ new Map();
|
|
3371
|
+
for (let i = 0; i < graph.regions.length; i++) {
|
|
3372
|
+
regionMap.set(graph.regions[i], transformedRegions[i]);
|
|
3373
|
+
}
|
|
3374
|
+
const transformedPorts = graph.ports.map((port) => {
|
|
3375
|
+
const newPosition = applyToPoint(matrix2, port.d);
|
|
3376
|
+
const newRegion1 = regionMap.get(port.region1);
|
|
3377
|
+
const newRegion2 = regionMap.get(port.region2);
|
|
3378
|
+
const newPort = {
|
|
3379
|
+
...port,
|
|
3380
|
+
region1: newRegion1,
|
|
3381
|
+
region2: newRegion2,
|
|
3382
|
+
d: newPosition
|
|
3383
|
+
};
|
|
3384
|
+
newRegion1.ports.push(newPort);
|
|
3385
|
+
newRegion2.ports.push(newPort);
|
|
3386
|
+
return newPort;
|
|
3387
|
+
});
|
|
3388
|
+
const transformedJumperLocations = graph.jumperLocations?.map((loc) => {
|
|
3389
|
+
const newCenter = applyToPoint(matrix2, loc.center);
|
|
3390
|
+
const newPadRegions = loc.padRegions.map(
|
|
3391
|
+
(region) => regionMap.get(region)
|
|
3392
|
+
);
|
|
3393
|
+
const unitX = applyToPoint(matrix2, { x: 1, y: 0 });
|
|
3394
|
+
const origin = applyToPoint(matrix2, { x: 0, y: 0 });
|
|
3395
|
+
const dx = unitX.x - origin.x;
|
|
3396
|
+
const dy = unitX.y - origin.y;
|
|
3397
|
+
const isRotated90 = Math.abs(dy) > Math.abs(dx);
|
|
3398
|
+
const newOrientation = isRotated90 ? loc.orientation === "horizontal" ? "vertical" : "horizontal" : loc.orientation;
|
|
3399
|
+
return {
|
|
3400
|
+
center: newCenter,
|
|
3401
|
+
orientation: newOrientation,
|
|
3402
|
+
padRegions: newPadRegions
|
|
3403
|
+
};
|
|
3404
|
+
});
|
|
3405
|
+
return {
|
|
3406
|
+
regions: transformedRegions,
|
|
3407
|
+
ports: transformedPorts,
|
|
3408
|
+
...transformedJumperLocations && {
|
|
3409
|
+
jumperLocations: transformedJumperLocations
|
|
3410
|
+
}
|
|
3411
|
+
};
|
|
3412
|
+
};
|
|
3413
|
+
var rotateGraph90Degrees = (graph) => {
|
|
3414
|
+
const bounds = calculateGraphBounds(graph.regions);
|
|
3415
|
+
const center = computeBoundsCenter(bounds);
|
|
3416
|
+
const matrix2 = compose(
|
|
3417
|
+
translate(center.x, center.y),
|
|
3418
|
+
rotate(-Math.PI / 2),
|
|
3419
|
+
// -90 degrees (clockwise)
|
|
3420
|
+
translate(-center.x, -center.y)
|
|
3421
|
+
);
|
|
3422
|
+
return applyTransformToGraph(graph, matrix2);
|
|
3423
|
+
};
|
|
3424
|
+
|
|
3425
|
+
// lib/JumperGraphSolver/computeCrossingAssignments.ts
|
|
3426
|
+
function computeCrossingAssignments(region, port1, port2) {
|
|
3427
|
+
const perimeter = getRegionPerimeter(region);
|
|
3428
|
+
const t1 = getPortPerimeterTInRegion(port1, region);
|
|
3429
|
+
const t2 = getPortPerimeterTInRegion(port2, region);
|
|
3430
|
+
const newChord = [t1, t2];
|
|
3431
|
+
const crossingAssignments = [];
|
|
3432
|
+
const assignments = region.assignments ?? [];
|
|
3433
|
+
for (const assignment of assignments) {
|
|
3434
|
+
const existingT1 = getPortPerimeterTInRegion(
|
|
3435
|
+
assignment.regionPort1,
|
|
3436
|
+
region
|
|
3437
|
+
);
|
|
3438
|
+
const existingT2 = getPortPerimeterTInRegion(
|
|
3439
|
+
assignment.regionPort2,
|
|
3440
|
+
region
|
|
3441
|
+
);
|
|
3442
|
+
const existingChord = [existingT1, existingT2];
|
|
2436
3443
|
if (chordsCross(newChord, existingChord, perimeter)) {
|
|
2437
3444
|
crossingAssignments.push(assignment);
|
|
2438
3445
|
}
|
|
@@ -2638,31 +3645,74 @@ var getConnectionColor = (connectionId, alpha = 0.8) => {
|
|
|
2638
3645
|
const hue = Math.abs(hash) % 360;
|
|
2639
3646
|
return `hsla(${hue}, 70%, 50%, ${alpha})`;
|
|
2640
3647
|
};
|
|
2641
|
-
var
|
|
2642
|
-
const
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
};
|
|
2646
|
-
const graphics = visualizeJumperGraph(jumperGraph, {
|
|
2647
|
-
connections: solver.connections,
|
|
2648
|
-
...solver.iterations > 0 ? {
|
|
3648
|
+
var visualizeJumperGraphWithSolvedRoutes = (input) => {
|
|
3649
|
+
const graphics = visualizeJumperGraph(input.graph, {
|
|
3650
|
+
connections: input.connections,
|
|
3651
|
+
...input.hideInitialGeometry ? {
|
|
2649
3652
|
hideRegionPortLines: true,
|
|
2650
3653
|
hideConnectionLines: true,
|
|
2651
3654
|
hidePortPoints: true
|
|
2652
3655
|
} : {}
|
|
2653
3656
|
});
|
|
2654
|
-
if (
|
|
3657
|
+
if (!input.hideInitialGeometry) {
|
|
2655
3658
|
for (const polygon2 of graphics.polygons) {
|
|
2656
3659
|
polygon2.stroke = "rgba(128, 128, 128, 0.5)";
|
|
2657
3660
|
polygon2.strokeWidth = 0.03;
|
|
2658
3661
|
}
|
|
2659
3662
|
}
|
|
3663
|
+
if (input.title) {
|
|
3664
|
+
graphics.title = input.title;
|
|
3665
|
+
}
|
|
3666
|
+
for (const solvedRoute of input.priorSolvedRoutes ?? []) {
|
|
3667
|
+
const pathPoints = solvedRoute.path.map((candidate) => {
|
|
3668
|
+
const port = candidate.port;
|
|
3669
|
+
return { x: port.d.x, y: port.d.y };
|
|
3670
|
+
});
|
|
3671
|
+
if (pathPoints.length === 0) continue;
|
|
3672
|
+
graphics.lines.push({
|
|
3673
|
+
points: pathPoints,
|
|
3674
|
+
strokeColor: "rgba(80, 80, 80, 0.45)",
|
|
3675
|
+
strokeDash: "4 4"
|
|
3676
|
+
});
|
|
3677
|
+
}
|
|
3678
|
+
for (const solvedRoute of input.solvedRoutes) {
|
|
3679
|
+
const connectionColor = getConnectionColor(
|
|
3680
|
+
solvedRoute.connection.connectionId
|
|
3681
|
+
);
|
|
3682
|
+
const pathPoints = [];
|
|
3683
|
+
for (const candidate of solvedRoute.path) {
|
|
3684
|
+
const port = candidate.port;
|
|
3685
|
+
pathPoints.push({ x: port.d.x, y: port.d.y });
|
|
3686
|
+
}
|
|
3687
|
+
if (pathPoints.length > 0) {
|
|
3688
|
+
graphics.lines.push({
|
|
3689
|
+
points: pathPoints,
|
|
3690
|
+
strokeColor: connectionColor
|
|
3691
|
+
});
|
|
3692
|
+
}
|
|
3693
|
+
}
|
|
3694
|
+
return graphics;
|
|
3695
|
+
};
|
|
3696
|
+
var visualizeJumperGraphSolver = (solver) => {
|
|
3697
|
+
const jumperGraph = {
|
|
3698
|
+
regions: solver.graph.regions,
|
|
3699
|
+
ports: solver.graph.ports
|
|
3700
|
+
};
|
|
3701
|
+
const graphics = visualizeJumperGraphWithSolvedRoutes({
|
|
3702
|
+
graph: jumperGraph,
|
|
3703
|
+
connections: solver.connections,
|
|
3704
|
+
solvedRoutes: solver.solvedRoutes,
|
|
3705
|
+
hideInitialGeometry: solver.iterations > 0
|
|
3706
|
+
});
|
|
2660
3707
|
if (solver.currentConnection && !solver.solved) {
|
|
2661
3708
|
const connectionColor = getConnectionColor(
|
|
2662
3709
|
solver.currentConnection.connectionId
|
|
2663
3710
|
);
|
|
2664
3711
|
const startRegion = solver.currentConnection.startRegion;
|
|
2665
3712
|
const endRegion = solver.currentConnection.endRegion;
|
|
3713
|
+
if (!startRegion?.d || !endRegion?.d) {
|
|
3714
|
+
return graphics;
|
|
3715
|
+
}
|
|
2666
3716
|
const startCenter = {
|
|
2667
3717
|
x: (startRegion.d.bounds.minX + startRegion.d.bounds.maxX) / 2,
|
|
2668
3718
|
y: (startRegion.d.bounds.minY + startRegion.d.bounds.maxY) / 2
|
|
@@ -2678,32 +3728,16 @@ var visualizeJumperGraphSolver = (solver) => {
|
|
|
2678
3728
|
});
|
|
2679
3729
|
graphics.points.push({
|
|
2680
3730
|
x: startCenter.x - 0.1,
|
|
2681
|
-
y: startCenter.y + 0.1,
|
|
2682
|
-
color: connectionColor,
|
|
2683
|
-
label: [solver.currentConnection.connectionId, "start"].join("\n")
|
|
2684
|
-
});
|
|
2685
|
-
graphics.points.push({
|
|
2686
|
-
x: endCenter.x - 0.1,
|
|
2687
|
-
y: endCenter.y + 0.1,
|
|
2688
|
-
color: connectionColor,
|
|
2689
|
-
label: [solver.currentConnection.connectionId, "end"].join("\n")
|
|
2690
|
-
});
|
|
2691
|
-
}
|
|
2692
|
-
for (const solvedRoute of solver.solvedRoutes) {
|
|
2693
|
-
const connectionColor = getConnectionColor(
|
|
2694
|
-
solvedRoute.connection.connectionId
|
|
2695
|
-
);
|
|
2696
|
-
const pathPoints = [];
|
|
2697
|
-
for (const candidate of solvedRoute.path) {
|
|
2698
|
-
const port = candidate.port;
|
|
2699
|
-
pathPoints.push({ x: port.d.x, y: port.d.y });
|
|
2700
|
-
}
|
|
2701
|
-
if (pathPoints.length > 0) {
|
|
2702
|
-
graphics.lines.push({
|
|
2703
|
-
points: pathPoints,
|
|
2704
|
-
strokeColor: connectionColor
|
|
2705
|
-
});
|
|
2706
|
-
}
|
|
3731
|
+
y: startCenter.y + 0.1,
|
|
3732
|
+
color: connectionColor,
|
|
3733
|
+
label: [solver.currentConnection.connectionId, "start"].join("\n")
|
|
3734
|
+
});
|
|
3735
|
+
graphics.points.push({
|
|
3736
|
+
x: endCenter.x - 0.1,
|
|
3737
|
+
y: endCenter.y + 0.1,
|
|
3738
|
+
color: connectionColor,
|
|
3739
|
+
label: [solver.currentConnection.connectionId, "end"].join("\n")
|
|
3740
|
+
});
|
|
2707
3741
|
}
|
|
2708
3742
|
const candidates = solver.candidateQueue.peekMany(10);
|
|
2709
3743
|
for (let candidateIndex = 0; candidateIndex < candidates.length; candidateIndex++) {
|
|
@@ -2882,252 +3916,6 @@ var JumperGraphSolver = class extends HyperGraphSolver {
|
|
|
2882
3916
|
}
|
|
2883
3917
|
};
|
|
2884
3918
|
|
|
2885
|
-
// lib/JumperGraphSolver/jumper-graph-generator/createConnectionPort.ts
|
|
2886
|
-
var createConnectionPort = (portId, connectionRegion, boundaryRegion, portPosition) => {
|
|
2887
|
-
const port = {
|
|
2888
|
-
portId,
|
|
2889
|
-
region1: connectionRegion,
|
|
2890
|
-
region2: boundaryRegion,
|
|
2891
|
-
d: { x: portPosition.x, y: portPosition.y }
|
|
2892
|
-
};
|
|
2893
|
-
connectionRegion.ports.push(port);
|
|
2894
|
-
boundaryRegion.ports.push(port);
|
|
2895
|
-
return port;
|
|
2896
|
-
};
|
|
2897
|
-
|
|
2898
|
-
// lib/JumperGraphSolver/jumper-graph-generator/createConnectionRegion.ts
|
|
2899
|
-
var CONNECTION_REGION_SIZE = 0.4;
|
|
2900
|
-
var createConnectionRegion = (regionId, x, y) => {
|
|
2901
|
-
const halfSize = CONNECTION_REGION_SIZE / 2;
|
|
2902
|
-
return {
|
|
2903
|
-
regionId,
|
|
2904
|
-
ports: [],
|
|
2905
|
-
d: {
|
|
2906
|
-
bounds: {
|
|
2907
|
-
minX: x - halfSize,
|
|
2908
|
-
maxX: x + halfSize,
|
|
2909
|
-
minY: y - halfSize,
|
|
2910
|
-
maxY: y + halfSize
|
|
2911
|
-
},
|
|
2912
|
-
center: { x, y },
|
|
2913
|
-
isPad: false,
|
|
2914
|
-
isConnectionRegion: true
|
|
2915
|
-
}
|
|
2916
|
-
};
|
|
2917
|
-
};
|
|
2918
|
-
|
|
2919
|
-
// lib/JumperGraphSolver/jumper-graph-generator/findBoundaryRegion.ts
|
|
2920
|
-
var EPS = 0.01;
|
|
2921
|
-
var clamp3 = (value, min, max) => {
|
|
2922
|
-
return Math.max(min, Math.min(max, value));
|
|
2923
|
-
};
|
|
2924
|
-
var getBoundarySidesForPoint = (x, y, graphBounds) => {
|
|
2925
|
-
const sides = [];
|
|
2926
|
-
if (Math.abs(x - graphBounds.minX) < EPS) sides.push("left");
|
|
2927
|
-
if (Math.abs(x - graphBounds.maxX) < EPS) sides.push("right");
|
|
2928
|
-
if (Math.abs(y - graphBounds.maxY) < EPS) sides.push("top");
|
|
2929
|
-
if (Math.abs(y - graphBounds.minY) < EPS) sides.push("bottom");
|
|
2930
|
-
return sides;
|
|
2931
|
-
};
|
|
2932
|
-
var isPointOnSide = (p, side, b) => {
|
|
2933
|
-
if (side === "left") return Math.abs(p.x - b.minX) < EPS;
|
|
2934
|
-
if (side === "right") return Math.abs(p.x - b.maxX) < EPS;
|
|
2935
|
-
if (side === "top") return Math.abs(p.y - b.maxY) < EPS;
|
|
2936
|
-
return Math.abs(p.y - b.minY) < EPS;
|
|
2937
|
-
};
|
|
2938
|
-
var projectToSegment2 = (x, y, a, b) => {
|
|
2939
|
-
const abx = b.x - a.x;
|
|
2940
|
-
const aby = b.y - a.y;
|
|
2941
|
-
const apx = x - a.x;
|
|
2942
|
-
const apy = y - a.y;
|
|
2943
|
-
const ab2 = abx * abx + aby * aby;
|
|
2944
|
-
const t = ab2 > 0 ? clamp3((apx * abx + apy * aby) / ab2, 0, 1) : 0;
|
|
2945
|
-
const px = a.x + t * abx;
|
|
2946
|
-
const py = a.y + t * aby;
|
|
2947
|
-
const dx = x - px;
|
|
2948
|
-
const dy = y - py;
|
|
2949
|
-
return {
|
|
2950
|
-
x: px,
|
|
2951
|
-
y: py,
|
|
2952
|
-
d2: dx * dx + dy * dy
|
|
2953
|
-
};
|
|
2954
|
-
};
|
|
2955
|
-
var getRegionBoundaryProjection = (x, y, region, graphBounds, preferredSides) => {
|
|
2956
|
-
const polygon2 = region.d.polygon;
|
|
2957
|
-
if (polygon2 && polygon2.length >= 3) {
|
|
2958
|
-
const sideSet = new Set(preferredSides);
|
|
2959
|
-
let best2 = null;
|
|
2960
|
-
for (let i = 0; i < polygon2.length; i++) {
|
|
2961
|
-
const a = polygon2[i];
|
|
2962
|
-
const b = polygon2[(i + 1) % polygon2.length];
|
|
2963
|
-
if (preferredSides.length > 0) {
|
|
2964
|
-
const edgeOnPreferredSide = preferredSides.some(
|
|
2965
|
-
(side) => isPointOnSide(a, side, graphBounds) && isPointOnSide(b, side, graphBounds) && sideSet.has(side)
|
|
2966
|
-
);
|
|
2967
|
-
if (!edgeOnPreferredSide) continue;
|
|
2968
|
-
}
|
|
2969
|
-
const p = projectToSegment2(x, y, a, b);
|
|
2970
|
-
if (!best2 || p.d2 < best2.d2) {
|
|
2971
|
-
best2 = p;
|
|
2972
|
-
}
|
|
2973
|
-
}
|
|
2974
|
-
if (best2) return best2;
|
|
2975
|
-
}
|
|
2976
|
-
const bounds = region.d.bounds;
|
|
2977
|
-
const sideCandidates = [];
|
|
2978
|
-
if (preferredSides.length > 0) {
|
|
2979
|
-
for (const side of preferredSides) {
|
|
2980
|
-
if (side === "left") {
|
|
2981
|
-
sideCandidates.push({
|
|
2982
|
-
side,
|
|
2983
|
-
x: bounds.minX,
|
|
2984
|
-
y: clamp3(y, bounds.minY, bounds.maxY)
|
|
2985
|
-
});
|
|
2986
|
-
} else if (side === "right") {
|
|
2987
|
-
sideCandidates.push({
|
|
2988
|
-
side,
|
|
2989
|
-
x: bounds.maxX,
|
|
2990
|
-
y: clamp3(y, bounds.minY, bounds.maxY)
|
|
2991
|
-
});
|
|
2992
|
-
} else if (side === "top") {
|
|
2993
|
-
sideCandidates.push({
|
|
2994
|
-
side,
|
|
2995
|
-
x: clamp3(x, bounds.minX, bounds.maxX),
|
|
2996
|
-
y: bounds.maxY
|
|
2997
|
-
});
|
|
2998
|
-
} else {
|
|
2999
|
-
sideCandidates.push({
|
|
3000
|
-
side,
|
|
3001
|
-
x: clamp3(x, bounds.minX, bounds.maxX),
|
|
3002
|
-
y: bounds.minY
|
|
3003
|
-
});
|
|
3004
|
-
}
|
|
3005
|
-
}
|
|
3006
|
-
}
|
|
3007
|
-
if (sideCandidates.length === 0) {
|
|
3008
|
-
sideCandidates.push(
|
|
3009
|
-
{ side: "left", x: bounds.minX, y: clamp3(y, bounds.minY, bounds.maxY) },
|
|
3010
|
-
{
|
|
3011
|
-
side: "right",
|
|
3012
|
-
x: bounds.maxX,
|
|
3013
|
-
y: clamp3(y, bounds.minY, bounds.maxY)
|
|
3014
|
-
},
|
|
3015
|
-
{ side: "top", x: clamp3(x, bounds.minX, bounds.maxX), y: bounds.maxY },
|
|
3016
|
-
{
|
|
3017
|
-
side: "bottom",
|
|
3018
|
-
x: clamp3(x, bounds.minX, bounds.maxX),
|
|
3019
|
-
y: bounds.minY
|
|
3020
|
-
}
|
|
3021
|
-
);
|
|
3022
|
-
}
|
|
3023
|
-
let best = null;
|
|
3024
|
-
for (const c of sideCandidates) {
|
|
3025
|
-
if (preferredSides.length > 0 && !preferredSides.includes(c.side)) continue;
|
|
3026
|
-
const dx = x - c.x;
|
|
3027
|
-
const dy = y - c.y;
|
|
3028
|
-
const d2 = dx * dx + dy * dy;
|
|
3029
|
-
if (!best || d2 < best.d2) {
|
|
3030
|
-
best = { x: c.x, y: c.y, d2 };
|
|
3031
|
-
}
|
|
3032
|
-
}
|
|
3033
|
-
return best;
|
|
3034
|
-
};
|
|
3035
|
-
var findBoundaryRegion = (x, y, regions, graphBounds) => {
|
|
3036
|
-
const preferredSides = getBoundarySidesForPoint(x, y, graphBounds);
|
|
3037
|
-
let closestRegion = null;
|
|
3038
|
-
let closestDistance = Number.POSITIVE_INFINITY;
|
|
3039
|
-
let closestPortPosition = { x, y };
|
|
3040
|
-
for (const region of regions) {
|
|
3041
|
-
if (region.d.isPad || region.d.isThroughJumper) continue;
|
|
3042
|
-
const bounds = region.d.bounds;
|
|
3043
|
-
const isOuterRegion = Math.abs(bounds.minX - graphBounds.minX) < 0.01 || Math.abs(bounds.maxX - graphBounds.maxX) < 0.01 || Math.abs(bounds.minY - graphBounds.minY) < 0.01 || Math.abs(bounds.maxY - graphBounds.maxY) < 0.01;
|
|
3044
|
-
if (!isOuterRegion) continue;
|
|
3045
|
-
const projection = getRegionBoundaryProjection(
|
|
3046
|
-
x,
|
|
3047
|
-
y,
|
|
3048
|
-
region,
|
|
3049
|
-
graphBounds,
|
|
3050
|
-
preferredSides
|
|
3051
|
-
);
|
|
3052
|
-
if (!projection) continue;
|
|
3053
|
-
const dist = Math.sqrt(projection.d2);
|
|
3054
|
-
if (dist < closestDistance) {
|
|
3055
|
-
closestDistance = dist;
|
|
3056
|
-
closestRegion = region;
|
|
3057
|
-
closestPortPosition = { x: projection.x, y: projection.y };
|
|
3058
|
-
}
|
|
3059
|
-
}
|
|
3060
|
-
if (closestRegion) {
|
|
3061
|
-
return { region: closestRegion, portPosition: closestPortPosition };
|
|
3062
|
-
}
|
|
3063
|
-
return null;
|
|
3064
|
-
};
|
|
3065
|
-
|
|
3066
|
-
// lib/JumperGraphSolver/jumper-graph-generator/createGraphWithConnectionsFromBaseGraph.ts
|
|
3067
|
-
var createGraphWithConnectionsFromBaseGraph = (baseGraph, xyConnections) => {
|
|
3068
|
-
const regions = [...baseGraph.regions];
|
|
3069
|
-
const ports = [...baseGraph.ports];
|
|
3070
|
-
const connections = [];
|
|
3071
|
-
const graphBounds = calculateGraphBounds(baseGraph.regions);
|
|
3072
|
-
for (const xyConn of xyConnections) {
|
|
3073
|
-
const { start, end, connectionId } = xyConn;
|
|
3074
|
-
const startRegion = createConnectionRegion(
|
|
3075
|
-
`conn:${connectionId}:start`,
|
|
3076
|
-
start.x,
|
|
3077
|
-
start.y
|
|
3078
|
-
);
|
|
3079
|
-
regions.push(startRegion);
|
|
3080
|
-
const endRegion = createConnectionRegion(
|
|
3081
|
-
`conn:${connectionId}:end`,
|
|
3082
|
-
end.x,
|
|
3083
|
-
end.y
|
|
3084
|
-
);
|
|
3085
|
-
regions.push(endRegion);
|
|
3086
|
-
const startBoundary = findBoundaryRegion(
|
|
3087
|
-
start.x,
|
|
3088
|
-
start.y,
|
|
3089
|
-
baseGraph.regions,
|
|
3090
|
-
graphBounds
|
|
3091
|
-
);
|
|
3092
|
-
if (startBoundary) {
|
|
3093
|
-
const startPort = createConnectionPort(
|
|
3094
|
-
`conn:${connectionId}:start-port`,
|
|
3095
|
-
startRegion,
|
|
3096
|
-
startBoundary.region,
|
|
3097
|
-
startBoundary.portPosition
|
|
3098
|
-
);
|
|
3099
|
-
ports.push(startPort);
|
|
3100
|
-
}
|
|
3101
|
-
const endBoundary = findBoundaryRegion(
|
|
3102
|
-
end.x,
|
|
3103
|
-
end.y,
|
|
3104
|
-
baseGraph.regions,
|
|
3105
|
-
graphBounds
|
|
3106
|
-
);
|
|
3107
|
-
if (endBoundary) {
|
|
3108
|
-
const endPort = createConnectionPort(
|
|
3109
|
-
`conn:${connectionId}:end-port`,
|
|
3110
|
-
endRegion,
|
|
3111
|
-
endBoundary.region,
|
|
3112
|
-
endBoundary.portPosition
|
|
3113
|
-
);
|
|
3114
|
-
ports.push(endPort);
|
|
3115
|
-
}
|
|
3116
|
-
const connection = {
|
|
3117
|
-
connectionId,
|
|
3118
|
-
mutuallyConnectedNetworkId: connectionId,
|
|
3119
|
-
startRegion,
|
|
3120
|
-
endRegion
|
|
3121
|
-
};
|
|
3122
|
-
connections.push(connection);
|
|
3123
|
-
}
|
|
3124
|
-
return {
|
|
3125
|
-
regions,
|
|
3126
|
-
ports,
|
|
3127
|
-
connections
|
|
3128
|
-
};
|
|
3129
|
-
};
|
|
3130
|
-
|
|
3131
3919
|
// lib/JumperGraphSolver/jumper-graph-generator/generateSingleJumperRegions.ts
|
|
3132
3920
|
var dims0603 = {
|
|
3133
3921
|
padToPad: 1.65,
|
|
@@ -14383,7 +15171,7 @@ function setStepOfAllObjects(graphics, step) {
|
|
|
14383
15171
|
}
|
|
14384
15172
|
return graphics;
|
|
14385
15173
|
}
|
|
14386
|
-
var
|
|
15174
|
+
var BaseSolver3 = class {
|
|
14387
15175
|
MAX_ITERATIONS = 1e5;
|
|
14388
15176
|
solved = false;
|
|
14389
15177
|
failed = false;
|
|
@@ -14490,7 +15278,7 @@ function definePipelineStep(solverName, solverClass, getConstructorParams, opts
|
|
|
14490
15278
|
onSolved: opts.onSolved
|
|
14491
15279
|
};
|
|
14492
15280
|
}
|
|
14493
|
-
var BasePipelineSolver = class extends
|
|
15281
|
+
var BasePipelineSolver = class extends BaseSolver3 {
|
|
14494
15282
|
startTimeOfStage = {};
|
|
14495
15283
|
endTimeOfStage = {};
|
|
14496
15284
|
timeSpentOnStage = {};
|
|
@@ -14705,7 +15493,7 @@ var clampPointToBounds = (point4, bounds) => ({
|
|
|
14705
15493
|
x: Math.min(bounds.maxX, Math.max(bounds.minX, point4.x)),
|
|
14706
15494
|
y: Math.min(bounds.maxY, Math.max(bounds.minY, point4.y))
|
|
14707
15495
|
});
|
|
14708
|
-
var BuildRegionsSolver = class extends
|
|
15496
|
+
var BuildRegionsSolver = class extends BaseSolver3 {
|
|
14709
15497
|
input;
|
|
14710
15498
|
output = null;
|
|
14711
15499
|
constructor(input) {
|
|
@@ -15126,7 +15914,7 @@ var generateBoundaryPointsWithEdges = (params) => {
|
|
|
15126
15914
|
hadCrossings: resolved.hadCrossings
|
|
15127
15915
|
};
|
|
15128
15916
|
};
|
|
15129
|
-
var GeneratePointsSolver = class extends
|
|
15917
|
+
var GeneratePointsSolver = class extends BaseSolver3 {
|
|
15130
15918
|
input;
|
|
15131
15919
|
output = null;
|
|
15132
15920
|
constructor(input) {
|
|
@@ -15581,7 +16369,7 @@ var mergeCellsPolyanya = (params) => {
|
|
|
15581
16369
|
const depths = liveCells.map(() => 0);
|
|
15582
16370
|
return { cells: liveCells, depths };
|
|
15583
16371
|
};
|
|
15584
|
-
var MergeCellsSolver = class extends
|
|
16372
|
+
var MergeCellsSolver = class extends BaseSolver3 {
|
|
15585
16373
|
input;
|
|
15586
16374
|
output = null;
|
|
15587
16375
|
constructor(input) {
|
|
@@ -15839,7 +16627,7 @@ var filterTris = (params) => {
|
|
|
15839
16627
|
return true;
|
|
15840
16628
|
});
|
|
15841
16629
|
};
|
|
15842
|
-
var TriangulateSolver = class extends
|
|
16630
|
+
var TriangulateSolver = class extends BaseSolver3 {
|
|
15843
16631
|
input;
|
|
15844
16632
|
output = null;
|
|
15845
16633
|
constructor(input) {
|
|
@@ -23796,6 +24584,7 @@ function createConvexViaGraphFromXYConnections(xyConnections, viaTileOrProblem,
|
|
|
23796
24584
|
}
|
|
23797
24585
|
export {
|
|
23798
24586
|
ConnectBuilder,
|
|
24587
|
+
HyperGraphSectionOptimizer,
|
|
23799
24588
|
HyperGraphSolver,
|
|
23800
24589
|
JUMPER_GRAPH_SOLVER_DEFAULTS,
|
|
23801
24590
|
JumperGraphSolver,
|