@tscircuit/hypergraph 0.0.59 → 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 -7
- package/dist/index.js +1292 -502
- 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,27 +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
|
-
|
|
1714
|
-
region.assignments = [];
|
|
1715
|
-
}
|
|
1792
|
+
clearAssignmentsFromGraph(this.graph);
|
|
1716
1793
|
this.connections = convertSerializedConnectionsToConnections(
|
|
1717
1794
|
input.inputConnections,
|
|
1718
1795
|
this.graph
|
|
1719
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
|
+
}
|
|
1720
1807
|
if (input.greedyMultiplier !== void 0)
|
|
1721
1808
|
this.greedyMultiplier = input.greedyMultiplier;
|
|
1722
1809
|
if (input.rippingEnabled !== void 0)
|
|
1723
1810
|
this.rippingEnabled = input.rippingEnabled;
|
|
1724
1811
|
if (input.ripCost !== void 0) this.ripCost = input.ripCost;
|
|
1725
|
-
|
|
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
|
+
);
|
|
1726
1820
|
this.candidateQueue = new PriorityQueue();
|
|
1727
|
-
this.
|
|
1821
|
+
if (this.unprocessedConnections.length === 0) {
|
|
1822
|
+
this.solved = true;
|
|
1823
|
+
} else {
|
|
1824
|
+
this.beginNewConnection();
|
|
1825
|
+
}
|
|
1728
1826
|
}
|
|
1729
1827
|
getSolverName() {
|
|
1730
1828
|
return "HyperGraphSolver";
|
|
@@ -1749,9 +1847,13 @@ var HyperGraphSolver = class extends BaseSolver {
|
|
|
1749
1847
|
),
|
|
1750
1848
|
greedyMultiplier: this.greedyMultiplier,
|
|
1751
1849
|
rippingEnabled: this.rippingEnabled,
|
|
1752
|
-
ripCost: this.ripCost
|
|
1850
|
+
ripCost: this.ripCost,
|
|
1851
|
+
inputSolvedRoutes: this.solvedRoutes
|
|
1753
1852
|
};
|
|
1754
1853
|
}
|
|
1854
|
+
getOutput() {
|
|
1855
|
+
return this.solvedRoutes;
|
|
1856
|
+
}
|
|
1755
1857
|
computeH(candidate) {
|
|
1756
1858
|
return this.estimateCostToEnd(candidate.port);
|
|
1757
1859
|
}
|
|
@@ -2063,197 +2165,8 @@ var HyperGraphSolver = class extends BaseSolver {
|
|
|
2063
2165
|
}
|
|
2064
2166
|
};
|
|
2065
2167
|
|
|
2066
|
-
//
|
|
2067
|
-
|
|
2068
|
-
return Array.isArray(point2) ? [
|
|
2069
|
-
matrix2.a * point2[0] + matrix2.c * point2[1] + matrix2.e,
|
|
2070
|
-
matrix2.b * point2[0] + matrix2.d * point2[1] + matrix2.f
|
|
2071
|
-
] : {
|
|
2072
|
-
x: matrix2.a * point2.x + matrix2.c * point2.y + matrix2.e,
|
|
2073
|
-
y: matrix2.b * point2.x + matrix2.d * point2.y + matrix2.f
|
|
2074
|
-
};
|
|
2075
|
-
}
|
|
2076
|
-
|
|
2077
|
-
// node_modules/transformation-matrix/src/utils.js
|
|
2078
|
-
function isUndefined(val) {
|
|
2079
|
-
return typeof val === "undefined";
|
|
2080
|
-
}
|
|
2081
|
-
|
|
2082
|
-
// node_modules/transformation-matrix/src/translate.js
|
|
2083
|
-
function translate(tx, ty = 0) {
|
|
2084
|
-
return {
|
|
2085
|
-
a: 1,
|
|
2086
|
-
c: 0,
|
|
2087
|
-
e: tx,
|
|
2088
|
-
b: 0,
|
|
2089
|
-
d: 1,
|
|
2090
|
-
f: ty
|
|
2091
|
-
};
|
|
2092
|
-
}
|
|
2093
|
-
|
|
2094
|
-
// node_modules/transformation-matrix/src/transform.js
|
|
2095
|
-
function transform(...matrices) {
|
|
2096
|
-
matrices = Array.isArray(matrices[0]) ? matrices[0] : matrices;
|
|
2097
|
-
const multiply = (m1, m2) => {
|
|
2098
|
-
return {
|
|
2099
|
-
a: m1.a * m2.a + m1.c * m2.b,
|
|
2100
|
-
c: m1.a * m2.c + m1.c * m2.d,
|
|
2101
|
-
e: m1.a * m2.e + m1.c * m2.f + m1.e,
|
|
2102
|
-
b: m1.b * m2.a + m1.d * m2.b,
|
|
2103
|
-
d: m1.b * m2.c + m1.d * m2.d,
|
|
2104
|
-
f: m1.b * m2.e + m1.d * m2.f + m1.f
|
|
2105
|
-
};
|
|
2106
|
-
};
|
|
2107
|
-
switch (matrices.length) {
|
|
2108
|
-
case 0:
|
|
2109
|
-
throw new Error("no matrices provided");
|
|
2110
|
-
case 1:
|
|
2111
|
-
return matrices[0];
|
|
2112
|
-
case 2:
|
|
2113
|
-
return multiply(matrices[0], matrices[1]);
|
|
2114
|
-
default: {
|
|
2115
|
-
const [m1, m2, ...rest] = matrices;
|
|
2116
|
-
const m = multiply(m1, m2);
|
|
2117
|
-
return transform(m, ...rest);
|
|
2118
|
-
}
|
|
2119
|
-
}
|
|
2120
|
-
}
|
|
2121
|
-
function compose(...matrices) {
|
|
2122
|
-
return transform(...matrices);
|
|
2123
|
-
}
|
|
2124
|
-
|
|
2125
|
-
// node_modules/transformation-matrix/src/rotate.js
|
|
2126
|
-
var { cos, sin, PI } = Math;
|
|
2127
|
-
function rotate(angle, cx, cy) {
|
|
2128
|
-
const cosAngle = cos(angle);
|
|
2129
|
-
const sinAngle = sin(angle);
|
|
2130
|
-
const rotationMatrix = {
|
|
2131
|
-
a: cosAngle,
|
|
2132
|
-
c: -sinAngle,
|
|
2133
|
-
e: 0,
|
|
2134
|
-
b: sinAngle,
|
|
2135
|
-
d: cosAngle,
|
|
2136
|
-
f: 0
|
|
2137
|
-
};
|
|
2138
|
-
if (isUndefined(cx) || isUndefined(cy)) {
|
|
2139
|
-
return rotationMatrix;
|
|
2140
|
-
}
|
|
2141
|
-
return transform([
|
|
2142
|
-
translate(cx, cy),
|
|
2143
|
-
rotationMatrix,
|
|
2144
|
-
translate(-cx, -cy)
|
|
2145
|
-
]);
|
|
2146
|
-
}
|
|
2147
|
-
|
|
2148
|
-
// node_modules/transformation-matrix/src/skew.js
|
|
2149
|
-
var { tan } = Math;
|
|
2150
|
-
|
|
2151
|
-
// lib/JumperGraphSolver/jumper-graph-generator/calculateGraphBounds.ts
|
|
2152
|
-
var calculateGraphBounds = (regions) => {
|
|
2153
|
-
let minX = Number.POSITIVE_INFINITY;
|
|
2154
|
-
let maxX = Number.NEGATIVE_INFINITY;
|
|
2155
|
-
let minY = Number.POSITIVE_INFINITY;
|
|
2156
|
-
let maxY = Number.NEGATIVE_INFINITY;
|
|
2157
|
-
for (const region of regions) {
|
|
2158
|
-
const { bounds } = region.d;
|
|
2159
|
-
minX = Math.min(minX, bounds.minX);
|
|
2160
|
-
maxX = Math.max(maxX, bounds.maxX);
|
|
2161
|
-
minY = Math.min(minY, bounds.minY);
|
|
2162
|
-
maxY = Math.max(maxY, bounds.maxY);
|
|
2163
|
-
}
|
|
2164
|
-
return { minX, maxX, minY, maxY };
|
|
2165
|
-
};
|
|
2166
|
-
|
|
2167
|
-
// lib/JumperGraphSolver/geometry/getBoundsCenter.ts
|
|
2168
|
-
var computeBoundsCenter = (bounds) => {
|
|
2169
|
-
return {
|
|
2170
|
-
x: (bounds.minX + bounds.maxX) / 2,
|
|
2171
|
-
y: (bounds.minY + bounds.maxY) / 2
|
|
2172
|
-
};
|
|
2173
|
-
};
|
|
2174
|
-
|
|
2175
|
-
// lib/JumperGraphSolver/geometry/applyTransformToGraph.ts
|
|
2176
|
-
var applyTransformToGraph = (graph, matrix2) => {
|
|
2177
|
-
const transformedRegions = graph.regions.map((region) => {
|
|
2178
|
-
const { bounds, center, ...rest } = region.d;
|
|
2179
|
-
const corners = [
|
|
2180
|
-
{ x: bounds.minX, y: bounds.minY },
|
|
2181
|
-
{ x: bounds.maxX, y: bounds.minY },
|
|
2182
|
-
{ x: bounds.minX, y: bounds.maxY },
|
|
2183
|
-
{ x: bounds.maxX, y: bounds.maxY }
|
|
2184
|
-
].map((corner) => applyToPoint(matrix2, corner));
|
|
2185
|
-
const newBounds = {
|
|
2186
|
-
minX: Math.min(...corners.map((c) => c.x)),
|
|
2187
|
-
maxX: Math.max(...corners.map((c) => c.x)),
|
|
2188
|
-
minY: Math.min(...corners.map((c) => c.y)),
|
|
2189
|
-
maxY: Math.max(...corners.map((c) => c.y))
|
|
2190
|
-
};
|
|
2191
|
-
const newCenter = applyToPoint(matrix2, center);
|
|
2192
|
-
return {
|
|
2193
|
-
...region,
|
|
2194
|
-
// Clear ports array - will be rebuilt with new port objects
|
|
2195
|
-
ports: [],
|
|
2196
|
-
d: {
|
|
2197
|
-
...rest,
|
|
2198
|
-
bounds: newBounds,
|
|
2199
|
-
center: newCenter
|
|
2200
|
-
}
|
|
2201
|
-
};
|
|
2202
|
-
});
|
|
2203
|
-
const regionMap = /* @__PURE__ */ new Map();
|
|
2204
|
-
for (let i = 0; i < graph.regions.length; i++) {
|
|
2205
|
-
regionMap.set(graph.regions[i], transformedRegions[i]);
|
|
2206
|
-
}
|
|
2207
|
-
const transformedPorts = graph.ports.map((port) => {
|
|
2208
|
-
const newPosition = applyToPoint(matrix2, port.d);
|
|
2209
|
-
const newRegion1 = regionMap.get(port.region1);
|
|
2210
|
-
const newRegion2 = regionMap.get(port.region2);
|
|
2211
|
-
const newPort = {
|
|
2212
|
-
...port,
|
|
2213
|
-
region1: newRegion1,
|
|
2214
|
-
region2: newRegion2,
|
|
2215
|
-
d: newPosition
|
|
2216
|
-
};
|
|
2217
|
-
newRegion1.ports.push(newPort);
|
|
2218
|
-
newRegion2.ports.push(newPort);
|
|
2219
|
-
return newPort;
|
|
2220
|
-
});
|
|
2221
|
-
const transformedJumperLocations = graph.jumperLocations?.map((loc) => {
|
|
2222
|
-
const newCenter = applyToPoint(matrix2, loc.center);
|
|
2223
|
-
const newPadRegions = loc.padRegions.map(
|
|
2224
|
-
(region) => regionMap.get(region)
|
|
2225
|
-
);
|
|
2226
|
-
const unitX = applyToPoint(matrix2, { x: 1, y: 0 });
|
|
2227
|
-
const origin = applyToPoint(matrix2, { x: 0, y: 0 });
|
|
2228
|
-
const dx = unitX.x - origin.x;
|
|
2229
|
-
const dy = unitX.y - origin.y;
|
|
2230
|
-
const isRotated90 = Math.abs(dy) > Math.abs(dx);
|
|
2231
|
-
const newOrientation = isRotated90 ? loc.orientation === "horizontal" ? "vertical" : "horizontal" : loc.orientation;
|
|
2232
|
-
return {
|
|
2233
|
-
center: newCenter,
|
|
2234
|
-
orientation: newOrientation,
|
|
2235
|
-
padRegions: newPadRegions
|
|
2236
|
-
};
|
|
2237
|
-
});
|
|
2238
|
-
return {
|
|
2239
|
-
regions: transformedRegions,
|
|
2240
|
-
ports: transformedPorts,
|
|
2241
|
-
...transformedJumperLocations && {
|
|
2242
|
-
jumperLocations: transformedJumperLocations
|
|
2243
|
-
}
|
|
2244
|
-
};
|
|
2245
|
-
};
|
|
2246
|
-
var rotateGraph90Degrees = (graph) => {
|
|
2247
|
-
const bounds = calculateGraphBounds(graph.regions);
|
|
2248
|
-
const center = computeBoundsCenter(bounds);
|
|
2249
|
-
const matrix2 = compose(
|
|
2250
|
-
translate(center.x, center.y),
|
|
2251
|
-
rotate(-Math.PI / 2),
|
|
2252
|
-
// -90 degrees (clockwise)
|
|
2253
|
-
translate(-center.x, -center.y)
|
|
2254
|
-
);
|
|
2255
|
-
return applyTransformToGraph(graph, matrix2);
|
|
2256
|
-
};
|
|
2168
|
+
// lib/HyperGraphSectionOptimizer/HyperGraphSectionOptimizer.ts
|
|
2169
|
+
import { BaseSolver as BaseSolver2 } from "@tscircuit/solver-utils";
|
|
2257
2170
|
|
|
2258
2171
|
// lib/JumperGraphSolver/perimeterChordUtils.ts
|
|
2259
2172
|
function clamp2(value, min, max) {
|
|
@@ -2414,24 +2327,1119 @@ function chordsCross(chord1, chord2, perimeter) {
|
|
|
2414
2327
|
return a < c && c < b && b < d || c < a && a < d && d < b;
|
|
2415
2328
|
}
|
|
2416
2329
|
|
|
2417
|
-
// lib/JumperGraphSolver/
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
const
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
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];
|
|
2435
3443
|
if (chordsCross(newChord, existingChord, perimeter)) {
|
|
2436
3444
|
crossingAssignments.push(assignment);
|
|
2437
3445
|
}
|
|
@@ -2637,31 +3645,74 @@ var getConnectionColor = (connectionId, alpha = 0.8) => {
|
|
|
2637
3645
|
const hue = Math.abs(hash) % 360;
|
|
2638
3646
|
return `hsla(${hue}, 70%, 50%, ${alpha})`;
|
|
2639
3647
|
};
|
|
2640
|
-
var
|
|
2641
|
-
const
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
};
|
|
2645
|
-
const graphics = visualizeJumperGraph(jumperGraph, {
|
|
2646
|
-
connections: solver.connections,
|
|
2647
|
-
...solver.iterations > 0 ? {
|
|
3648
|
+
var visualizeJumperGraphWithSolvedRoutes = (input) => {
|
|
3649
|
+
const graphics = visualizeJumperGraph(input.graph, {
|
|
3650
|
+
connections: input.connections,
|
|
3651
|
+
...input.hideInitialGeometry ? {
|
|
2648
3652
|
hideRegionPortLines: true,
|
|
2649
3653
|
hideConnectionLines: true,
|
|
2650
3654
|
hidePortPoints: true
|
|
2651
3655
|
} : {}
|
|
2652
3656
|
});
|
|
2653
|
-
if (
|
|
3657
|
+
if (!input.hideInitialGeometry) {
|
|
2654
3658
|
for (const polygon2 of graphics.polygons) {
|
|
2655
3659
|
polygon2.stroke = "rgba(128, 128, 128, 0.5)";
|
|
2656
3660
|
polygon2.strokeWidth = 0.03;
|
|
2657
3661
|
}
|
|
2658
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
|
+
});
|
|
2659
3707
|
if (solver.currentConnection && !solver.solved) {
|
|
2660
3708
|
const connectionColor = getConnectionColor(
|
|
2661
3709
|
solver.currentConnection.connectionId
|
|
2662
3710
|
);
|
|
2663
3711
|
const startRegion = solver.currentConnection.startRegion;
|
|
2664
3712
|
const endRegion = solver.currentConnection.endRegion;
|
|
3713
|
+
if (!startRegion?.d || !endRegion?.d) {
|
|
3714
|
+
return graphics;
|
|
3715
|
+
}
|
|
2665
3716
|
const startCenter = {
|
|
2666
3717
|
x: (startRegion.d.bounds.minX + startRegion.d.bounds.maxX) / 2,
|
|
2667
3718
|
y: (startRegion.d.bounds.minY + startRegion.d.bounds.maxY) / 2
|
|
@@ -2677,32 +3728,16 @@ var visualizeJumperGraphSolver = (solver) => {
|
|
|
2677
3728
|
});
|
|
2678
3729
|
graphics.points.push({
|
|
2679
3730
|
x: startCenter.x - 0.1,
|
|
2680
|
-
y: startCenter.y + 0.1,
|
|
2681
|
-
color: connectionColor,
|
|
2682
|
-
label: [solver.currentConnection.connectionId, "start"].join("\n")
|
|
2683
|
-
});
|
|
2684
|
-
graphics.points.push({
|
|
2685
|
-
x: endCenter.x - 0.1,
|
|
2686
|
-
y: endCenter.y + 0.1,
|
|
2687
|
-
color: connectionColor,
|
|
2688
|
-
label: [solver.currentConnection.connectionId, "end"].join("\n")
|
|
2689
|
-
});
|
|
2690
|
-
}
|
|
2691
|
-
for (const solvedRoute of solver.solvedRoutes) {
|
|
2692
|
-
const connectionColor = getConnectionColor(
|
|
2693
|
-
solvedRoute.connection.connectionId
|
|
2694
|
-
);
|
|
2695
|
-
const pathPoints = [];
|
|
2696
|
-
for (const candidate of solvedRoute.path) {
|
|
2697
|
-
const port = candidate.port;
|
|
2698
|
-
pathPoints.push({ x: port.d.x, y: port.d.y });
|
|
2699
|
-
}
|
|
2700
|
-
if (pathPoints.length > 0) {
|
|
2701
|
-
graphics.lines.push({
|
|
2702
|
-
points: pathPoints,
|
|
2703
|
-
strokeColor: connectionColor
|
|
2704
|
-
});
|
|
2705
|
-
}
|
|
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
|
+
});
|
|
2706
3741
|
}
|
|
2707
3742
|
const candidates = solver.candidateQueue.peekMany(10);
|
|
2708
3743
|
for (let candidateIndex = 0; candidateIndex < candidates.length; candidateIndex++) {
|
|
@@ -2881,252 +3916,6 @@ var JumperGraphSolver = class extends HyperGraphSolver {
|
|
|
2881
3916
|
}
|
|
2882
3917
|
};
|
|
2883
3918
|
|
|
2884
|
-
// lib/JumperGraphSolver/jumper-graph-generator/createConnectionPort.ts
|
|
2885
|
-
var createConnectionPort = (portId, connectionRegion, boundaryRegion, portPosition) => {
|
|
2886
|
-
const port = {
|
|
2887
|
-
portId,
|
|
2888
|
-
region1: connectionRegion,
|
|
2889
|
-
region2: boundaryRegion,
|
|
2890
|
-
d: { x: portPosition.x, y: portPosition.y }
|
|
2891
|
-
};
|
|
2892
|
-
connectionRegion.ports.push(port);
|
|
2893
|
-
boundaryRegion.ports.push(port);
|
|
2894
|
-
return port;
|
|
2895
|
-
};
|
|
2896
|
-
|
|
2897
|
-
// lib/JumperGraphSolver/jumper-graph-generator/createConnectionRegion.ts
|
|
2898
|
-
var CONNECTION_REGION_SIZE = 0.4;
|
|
2899
|
-
var createConnectionRegion = (regionId, x, y) => {
|
|
2900
|
-
const halfSize = CONNECTION_REGION_SIZE / 2;
|
|
2901
|
-
return {
|
|
2902
|
-
regionId,
|
|
2903
|
-
ports: [],
|
|
2904
|
-
d: {
|
|
2905
|
-
bounds: {
|
|
2906
|
-
minX: x - halfSize,
|
|
2907
|
-
maxX: x + halfSize,
|
|
2908
|
-
minY: y - halfSize,
|
|
2909
|
-
maxY: y + halfSize
|
|
2910
|
-
},
|
|
2911
|
-
center: { x, y },
|
|
2912
|
-
isPad: false,
|
|
2913
|
-
isConnectionRegion: true
|
|
2914
|
-
}
|
|
2915
|
-
};
|
|
2916
|
-
};
|
|
2917
|
-
|
|
2918
|
-
// lib/JumperGraphSolver/jumper-graph-generator/findBoundaryRegion.ts
|
|
2919
|
-
var EPS = 0.01;
|
|
2920
|
-
var clamp3 = (value, min, max) => {
|
|
2921
|
-
return Math.max(min, Math.min(max, value));
|
|
2922
|
-
};
|
|
2923
|
-
var getBoundarySidesForPoint = (x, y, graphBounds) => {
|
|
2924
|
-
const sides = [];
|
|
2925
|
-
if (Math.abs(x - graphBounds.minX) < EPS) sides.push("left");
|
|
2926
|
-
if (Math.abs(x - graphBounds.maxX) < EPS) sides.push("right");
|
|
2927
|
-
if (Math.abs(y - graphBounds.maxY) < EPS) sides.push("top");
|
|
2928
|
-
if (Math.abs(y - graphBounds.minY) < EPS) sides.push("bottom");
|
|
2929
|
-
return sides;
|
|
2930
|
-
};
|
|
2931
|
-
var isPointOnSide = (p, side, b) => {
|
|
2932
|
-
if (side === "left") return Math.abs(p.x - b.minX) < EPS;
|
|
2933
|
-
if (side === "right") return Math.abs(p.x - b.maxX) < EPS;
|
|
2934
|
-
if (side === "top") return Math.abs(p.y - b.maxY) < EPS;
|
|
2935
|
-
return Math.abs(p.y - b.minY) < EPS;
|
|
2936
|
-
};
|
|
2937
|
-
var projectToSegment2 = (x, y, a, b) => {
|
|
2938
|
-
const abx = b.x - a.x;
|
|
2939
|
-
const aby = b.y - a.y;
|
|
2940
|
-
const apx = x - a.x;
|
|
2941
|
-
const apy = y - a.y;
|
|
2942
|
-
const ab2 = abx * abx + aby * aby;
|
|
2943
|
-
const t = ab2 > 0 ? clamp3((apx * abx + apy * aby) / ab2, 0, 1) : 0;
|
|
2944
|
-
const px = a.x + t * abx;
|
|
2945
|
-
const py = a.y + t * aby;
|
|
2946
|
-
const dx = x - px;
|
|
2947
|
-
const dy = y - py;
|
|
2948
|
-
return {
|
|
2949
|
-
x: px,
|
|
2950
|
-
y: py,
|
|
2951
|
-
d2: dx * dx + dy * dy
|
|
2952
|
-
};
|
|
2953
|
-
};
|
|
2954
|
-
var getRegionBoundaryProjection = (x, y, region, graphBounds, preferredSides) => {
|
|
2955
|
-
const polygon2 = region.d.polygon;
|
|
2956
|
-
if (polygon2 && polygon2.length >= 3) {
|
|
2957
|
-
const sideSet = new Set(preferredSides);
|
|
2958
|
-
let best2 = null;
|
|
2959
|
-
for (let i = 0; i < polygon2.length; i++) {
|
|
2960
|
-
const a = polygon2[i];
|
|
2961
|
-
const b = polygon2[(i + 1) % polygon2.length];
|
|
2962
|
-
if (preferredSides.length > 0) {
|
|
2963
|
-
const edgeOnPreferredSide = preferredSides.some(
|
|
2964
|
-
(side) => isPointOnSide(a, side, graphBounds) && isPointOnSide(b, side, graphBounds) && sideSet.has(side)
|
|
2965
|
-
);
|
|
2966
|
-
if (!edgeOnPreferredSide) continue;
|
|
2967
|
-
}
|
|
2968
|
-
const p = projectToSegment2(x, y, a, b);
|
|
2969
|
-
if (!best2 || p.d2 < best2.d2) {
|
|
2970
|
-
best2 = p;
|
|
2971
|
-
}
|
|
2972
|
-
}
|
|
2973
|
-
if (best2) return best2;
|
|
2974
|
-
}
|
|
2975
|
-
const bounds = region.d.bounds;
|
|
2976
|
-
const sideCandidates = [];
|
|
2977
|
-
if (preferredSides.length > 0) {
|
|
2978
|
-
for (const side of preferredSides) {
|
|
2979
|
-
if (side === "left") {
|
|
2980
|
-
sideCandidates.push({
|
|
2981
|
-
side,
|
|
2982
|
-
x: bounds.minX,
|
|
2983
|
-
y: clamp3(y, bounds.minY, bounds.maxY)
|
|
2984
|
-
});
|
|
2985
|
-
} else if (side === "right") {
|
|
2986
|
-
sideCandidates.push({
|
|
2987
|
-
side,
|
|
2988
|
-
x: bounds.maxX,
|
|
2989
|
-
y: clamp3(y, bounds.minY, bounds.maxY)
|
|
2990
|
-
});
|
|
2991
|
-
} else if (side === "top") {
|
|
2992
|
-
sideCandidates.push({
|
|
2993
|
-
side,
|
|
2994
|
-
x: clamp3(x, bounds.minX, bounds.maxX),
|
|
2995
|
-
y: bounds.maxY
|
|
2996
|
-
});
|
|
2997
|
-
} else {
|
|
2998
|
-
sideCandidates.push({
|
|
2999
|
-
side,
|
|
3000
|
-
x: clamp3(x, bounds.minX, bounds.maxX),
|
|
3001
|
-
y: bounds.minY
|
|
3002
|
-
});
|
|
3003
|
-
}
|
|
3004
|
-
}
|
|
3005
|
-
}
|
|
3006
|
-
if (sideCandidates.length === 0) {
|
|
3007
|
-
sideCandidates.push(
|
|
3008
|
-
{ side: "left", x: bounds.minX, y: clamp3(y, bounds.minY, bounds.maxY) },
|
|
3009
|
-
{
|
|
3010
|
-
side: "right",
|
|
3011
|
-
x: bounds.maxX,
|
|
3012
|
-
y: clamp3(y, bounds.minY, bounds.maxY)
|
|
3013
|
-
},
|
|
3014
|
-
{ side: "top", x: clamp3(x, bounds.minX, bounds.maxX), y: bounds.maxY },
|
|
3015
|
-
{
|
|
3016
|
-
side: "bottom",
|
|
3017
|
-
x: clamp3(x, bounds.minX, bounds.maxX),
|
|
3018
|
-
y: bounds.minY
|
|
3019
|
-
}
|
|
3020
|
-
);
|
|
3021
|
-
}
|
|
3022
|
-
let best = null;
|
|
3023
|
-
for (const c of sideCandidates) {
|
|
3024
|
-
if (preferredSides.length > 0 && !preferredSides.includes(c.side)) continue;
|
|
3025
|
-
const dx = x - c.x;
|
|
3026
|
-
const dy = y - c.y;
|
|
3027
|
-
const d2 = dx * dx + dy * dy;
|
|
3028
|
-
if (!best || d2 < best.d2) {
|
|
3029
|
-
best = { x: c.x, y: c.y, d2 };
|
|
3030
|
-
}
|
|
3031
|
-
}
|
|
3032
|
-
return best;
|
|
3033
|
-
};
|
|
3034
|
-
var findBoundaryRegion = (x, y, regions, graphBounds) => {
|
|
3035
|
-
const preferredSides = getBoundarySidesForPoint(x, y, graphBounds);
|
|
3036
|
-
let closestRegion = null;
|
|
3037
|
-
let closestDistance = Number.POSITIVE_INFINITY;
|
|
3038
|
-
let closestPortPosition = { x, y };
|
|
3039
|
-
for (const region of regions) {
|
|
3040
|
-
if (region.d.isPad || region.d.isThroughJumper) continue;
|
|
3041
|
-
const bounds = region.d.bounds;
|
|
3042
|
-
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;
|
|
3043
|
-
if (!isOuterRegion) continue;
|
|
3044
|
-
const projection = getRegionBoundaryProjection(
|
|
3045
|
-
x,
|
|
3046
|
-
y,
|
|
3047
|
-
region,
|
|
3048
|
-
graphBounds,
|
|
3049
|
-
preferredSides
|
|
3050
|
-
);
|
|
3051
|
-
if (!projection) continue;
|
|
3052
|
-
const dist = Math.sqrt(projection.d2);
|
|
3053
|
-
if (dist < closestDistance) {
|
|
3054
|
-
closestDistance = dist;
|
|
3055
|
-
closestRegion = region;
|
|
3056
|
-
closestPortPosition = { x: projection.x, y: projection.y };
|
|
3057
|
-
}
|
|
3058
|
-
}
|
|
3059
|
-
if (closestRegion) {
|
|
3060
|
-
return { region: closestRegion, portPosition: closestPortPosition };
|
|
3061
|
-
}
|
|
3062
|
-
return null;
|
|
3063
|
-
};
|
|
3064
|
-
|
|
3065
|
-
// lib/JumperGraphSolver/jumper-graph-generator/createGraphWithConnectionsFromBaseGraph.ts
|
|
3066
|
-
var createGraphWithConnectionsFromBaseGraph = (baseGraph, xyConnections) => {
|
|
3067
|
-
const regions = [...baseGraph.regions];
|
|
3068
|
-
const ports = [...baseGraph.ports];
|
|
3069
|
-
const connections = [];
|
|
3070
|
-
const graphBounds = calculateGraphBounds(baseGraph.regions);
|
|
3071
|
-
for (const xyConn of xyConnections) {
|
|
3072
|
-
const { start, end, connectionId } = xyConn;
|
|
3073
|
-
const startRegion = createConnectionRegion(
|
|
3074
|
-
`conn:${connectionId}:start`,
|
|
3075
|
-
start.x,
|
|
3076
|
-
start.y
|
|
3077
|
-
);
|
|
3078
|
-
regions.push(startRegion);
|
|
3079
|
-
const endRegion = createConnectionRegion(
|
|
3080
|
-
`conn:${connectionId}:end`,
|
|
3081
|
-
end.x,
|
|
3082
|
-
end.y
|
|
3083
|
-
);
|
|
3084
|
-
regions.push(endRegion);
|
|
3085
|
-
const startBoundary = findBoundaryRegion(
|
|
3086
|
-
start.x,
|
|
3087
|
-
start.y,
|
|
3088
|
-
baseGraph.regions,
|
|
3089
|
-
graphBounds
|
|
3090
|
-
);
|
|
3091
|
-
if (startBoundary) {
|
|
3092
|
-
const startPort = createConnectionPort(
|
|
3093
|
-
`conn:${connectionId}:start-port`,
|
|
3094
|
-
startRegion,
|
|
3095
|
-
startBoundary.region,
|
|
3096
|
-
startBoundary.portPosition
|
|
3097
|
-
);
|
|
3098
|
-
ports.push(startPort);
|
|
3099
|
-
}
|
|
3100
|
-
const endBoundary = findBoundaryRegion(
|
|
3101
|
-
end.x,
|
|
3102
|
-
end.y,
|
|
3103
|
-
baseGraph.regions,
|
|
3104
|
-
graphBounds
|
|
3105
|
-
);
|
|
3106
|
-
if (endBoundary) {
|
|
3107
|
-
const endPort = createConnectionPort(
|
|
3108
|
-
`conn:${connectionId}:end-port`,
|
|
3109
|
-
endRegion,
|
|
3110
|
-
endBoundary.region,
|
|
3111
|
-
endBoundary.portPosition
|
|
3112
|
-
);
|
|
3113
|
-
ports.push(endPort);
|
|
3114
|
-
}
|
|
3115
|
-
const connection = {
|
|
3116
|
-
connectionId,
|
|
3117
|
-
mutuallyConnectedNetworkId: connectionId,
|
|
3118
|
-
startRegion,
|
|
3119
|
-
endRegion
|
|
3120
|
-
};
|
|
3121
|
-
connections.push(connection);
|
|
3122
|
-
}
|
|
3123
|
-
return {
|
|
3124
|
-
regions,
|
|
3125
|
-
ports,
|
|
3126
|
-
connections
|
|
3127
|
-
};
|
|
3128
|
-
};
|
|
3129
|
-
|
|
3130
3919
|
// lib/JumperGraphSolver/jumper-graph-generator/generateSingleJumperRegions.ts
|
|
3131
3920
|
var dims0603 = {
|
|
3132
3921
|
padToPad: 1.65,
|
|
@@ -14382,7 +15171,7 @@ function setStepOfAllObjects(graphics, step) {
|
|
|
14382
15171
|
}
|
|
14383
15172
|
return graphics;
|
|
14384
15173
|
}
|
|
14385
|
-
var
|
|
15174
|
+
var BaseSolver3 = class {
|
|
14386
15175
|
MAX_ITERATIONS = 1e5;
|
|
14387
15176
|
solved = false;
|
|
14388
15177
|
failed = false;
|
|
@@ -14489,7 +15278,7 @@ function definePipelineStep(solverName, solverClass, getConstructorParams, opts
|
|
|
14489
15278
|
onSolved: opts.onSolved
|
|
14490
15279
|
};
|
|
14491
15280
|
}
|
|
14492
|
-
var BasePipelineSolver = class extends
|
|
15281
|
+
var BasePipelineSolver = class extends BaseSolver3 {
|
|
14493
15282
|
startTimeOfStage = {};
|
|
14494
15283
|
endTimeOfStage = {};
|
|
14495
15284
|
timeSpentOnStage = {};
|
|
@@ -14704,7 +15493,7 @@ var clampPointToBounds = (point4, bounds) => ({
|
|
|
14704
15493
|
x: Math.min(bounds.maxX, Math.max(bounds.minX, point4.x)),
|
|
14705
15494
|
y: Math.min(bounds.maxY, Math.max(bounds.minY, point4.y))
|
|
14706
15495
|
});
|
|
14707
|
-
var BuildRegionsSolver = class extends
|
|
15496
|
+
var BuildRegionsSolver = class extends BaseSolver3 {
|
|
14708
15497
|
input;
|
|
14709
15498
|
output = null;
|
|
14710
15499
|
constructor(input) {
|
|
@@ -15125,7 +15914,7 @@ var generateBoundaryPointsWithEdges = (params) => {
|
|
|
15125
15914
|
hadCrossings: resolved.hadCrossings
|
|
15126
15915
|
};
|
|
15127
15916
|
};
|
|
15128
|
-
var GeneratePointsSolver = class extends
|
|
15917
|
+
var GeneratePointsSolver = class extends BaseSolver3 {
|
|
15129
15918
|
input;
|
|
15130
15919
|
output = null;
|
|
15131
15920
|
constructor(input) {
|
|
@@ -15580,7 +16369,7 @@ var mergeCellsPolyanya = (params) => {
|
|
|
15580
16369
|
const depths = liveCells.map(() => 0);
|
|
15581
16370
|
return { cells: liveCells, depths };
|
|
15582
16371
|
};
|
|
15583
|
-
var MergeCellsSolver = class extends
|
|
16372
|
+
var MergeCellsSolver = class extends BaseSolver3 {
|
|
15584
16373
|
input;
|
|
15585
16374
|
output = null;
|
|
15586
16375
|
constructor(input) {
|
|
@@ -15838,7 +16627,7 @@ var filterTris = (params) => {
|
|
|
15838
16627
|
return true;
|
|
15839
16628
|
});
|
|
15840
16629
|
};
|
|
15841
|
-
var TriangulateSolver = class extends
|
|
16630
|
+
var TriangulateSolver = class extends BaseSolver3 {
|
|
15842
16631
|
input;
|
|
15843
16632
|
output = null;
|
|
15844
16633
|
constructor(input) {
|
|
@@ -23795,6 +24584,7 @@ function createConvexViaGraphFromXYConnections(xyConnections, viaTileOrProblem,
|
|
|
23795
24584
|
}
|
|
23796
24585
|
export {
|
|
23797
24586
|
ConnectBuilder,
|
|
24587
|
+
HyperGraphSectionOptimizer,
|
|
23798
24588
|
HyperGraphSolver,
|
|
23799
24589
|
JUMPER_GRAPH_SOLVER_DEFAULTS,
|
|
23800
24590
|
JumperGraphSolver,
|