@tscircuit/hypergraph 0.0.19 → 0.0.21
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 +29 -0
- package/dist/index.js +114 -26
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -110,6 +110,10 @@ interface JRegion extends Region {
|
|
|
110
110
|
x: number;
|
|
111
111
|
y: number;
|
|
112
112
|
};
|
|
113
|
+
polygon?: {
|
|
114
|
+
x: number;
|
|
115
|
+
y: number;
|
|
116
|
+
}[];
|
|
113
117
|
isPad: boolean;
|
|
114
118
|
isThroughJumper?: boolean;
|
|
115
119
|
isConnectionRegion?: boolean;
|
|
@@ -345,6 +349,23 @@ declare class HyperGraphSolver<RegionType extends Region = Region, RegionPortTyp
|
|
|
345
349
|
* redundant.
|
|
346
350
|
*/
|
|
347
351
|
selectCandidatesForEnteringRegion(candidates: Candidate[]): Candidate[];
|
|
352
|
+
/**
|
|
353
|
+
* OPTIONALLY OVERRIDE THIS
|
|
354
|
+
*
|
|
355
|
+
* Compute the full set of solved routes that must be ripped to accept
|
|
356
|
+
* `newlySolvedRoute`. By default this returns all conflicting routes
|
|
357
|
+
* (always-rip behavior)
|
|
358
|
+
*
|
|
359
|
+
* Override this to implement partial ripping, where only a subset of
|
|
360
|
+
* conflicting routes are removed.
|
|
361
|
+
*/
|
|
362
|
+
computeRoutesToRip(newlySolvedRoute: SolvedRoute): Set<SolvedRoute>;
|
|
363
|
+
/**
|
|
364
|
+
* Returns solved routes that overlap ports with the newly solved route.
|
|
365
|
+
* Use this in computeRoutesToRip overrides to include port reuse rips.
|
|
366
|
+
*/
|
|
367
|
+
computePortOverlapRoutes(newlySolvedRoute: SolvedRoute): Set<SolvedRoute>;
|
|
368
|
+
computeCrossingRoutes(newlySolvedRoute: SolvedRoute): Set<SolvedRoute>;
|
|
348
369
|
getNextCandidates(currentCandidate: CandidateType): CandidateType[];
|
|
349
370
|
processSolvedRoute(finalCandidate: CandidateType): void;
|
|
350
371
|
/**
|
|
@@ -458,6 +479,10 @@ type SharedBoundary = {
|
|
|
458
479
|
type RegionData = {
|
|
459
480
|
id: string;
|
|
460
481
|
bounds: Bounds | null;
|
|
482
|
+
polygon: {
|
|
483
|
+
x: number;
|
|
484
|
+
y: number;
|
|
485
|
+
}[] | null;
|
|
461
486
|
center: {
|
|
462
487
|
x: number;
|
|
463
488
|
y: number;
|
|
@@ -495,6 +520,10 @@ declare class RegionBuilder implements RegionRef {
|
|
|
495
520
|
constructor(id: string);
|
|
496
521
|
get id(): string;
|
|
497
522
|
rect(b: Bounds): this;
|
|
523
|
+
polygon(points: {
|
|
524
|
+
x: number;
|
|
525
|
+
y: number;
|
|
526
|
+
}[]): this;
|
|
498
527
|
center(x: number, y: number): this;
|
|
499
528
|
size(w: number, h: number, anchor?: "center" | "min"): this;
|
|
500
529
|
pad(isPad?: boolean): this;
|
package/dist/index.js
CHANGED
|
@@ -1915,6 +1915,52 @@ var HyperGraphSolver = class extends BaseSolver {
|
|
|
1915
1915
|
selectCandidatesForEnteringRegion(candidates) {
|
|
1916
1916
|
return candidates;
|
|
1917
1917
|
}
|
|
1918
|
+
/**
|
|
1919
|
+
* OPTIONALLY OVERRIDE THIS
|
|
1920
|
+
*
|
|
1921
|
+
* Compute the full set of solved routes that must be ripped to accept
|
|
1922
|
+
* `newlySolvedRoute`. By default this returns all conflicting routes
|
|
1923
|
+
* (always-rip behavior)
|
|
1924
|
+
*
|
|
1925
|
+
* Override this to implement partial ripping, where only a subset of
|
|
1926
|
+
* conflicting routes are removed.
|
|
1927
|
+
*/
|
|
1928
|
+
computeRoutesToRip(newlySolvedRoute) {
|
|
1929
|
+
const crossingRoutesToRip = this.computeCrossingRoutes(newlySolvedRoute);
|
|
1930
|
+
const portReuseRoutesToRip = this.computePortOverlapRoutes(newlySolvedRoute);
|
|
1931
|
+
return /* @__PURE__ */ new Set([
|
|
1932
|
+
...crossingRoutesToRip,
|
|
1933
|
+
...portReuseRoutesToRip
|
|
1934
|
+
]);
|
|
1935
|
+
}
|
|
1936
|
+
/**
|
|
1937
|
+
* Returns solved routes that overlap ports with the newly solved route.
|
|
1938
|
+
* Use this in computeRoutesToRip overrides to include port reuse rips.
|
|
1939
|
+
*/
|
|
1940
|
+
computePortOverlapRoutes(newlySolvedRoute) {
|
|
1941
|
+
const portReuseRoutesToRip = /* @__PURE__ */ new Set();
|
|
1942
|
+
for (const candidate of newlySolvedRoute.path) {
|
|
1943
|
+
if (candidate.port.assignment && candidate.port.assignment.connection.mutuallyConnectedNetworkId !== newlySolvedRoute.connection.mutuallyConnectedNetworkId) {
|
|
1944
|
+
portReuseRoutesToRip.add(candidate.port.assignment.solvedRoute);
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
return portReuseRoutesToRip;
|
|
1948
|
+
}
|
|
1949
|
+
computeCrossingRoutes(newlySolvedRoute) {
|
|
1950
|
+
const crossingRoutesToRip = /* @__PURE__ */ new Set();
|
|
1951
|
+
for (const candidate of newlySolvedRoute.path) {
|
|
1952
|
+
if (!candidate.lastPort || !candidate.lastRegion) continue;
|
|
1953
|
+
const ripsRequired = this.getRipsRequiredForPortUsage(
|
|
1954
|
+
candidate.lastRegion,
|
|
1955
|
+
candidate.lastPort,
|
|
1956
|
+
candidate.port
|
|
1957
|
+
);
|
|
1958
|
+
for (const assignment of ripsRequired) {
|
|
1959
|
+
crossingRoutesToRip.add(assignment.solvedRoute);
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
return crossingRoutesToRip;
|
|
1963
|
+
}
|
|
1918
1964
|
getNextCandidates(currentCandidate) {
|
|
1919
1965
|
const currentRegion = currentCandidate.nextRegion;
|
|
1920
1966
|
const currentPort = currentCandidate.port;
|
|
@@ -1966,29 +2012,13 @@ var HyperGraphSolver = class extends BaseSolver {
|
|
|
1966
2012
|
solvedRoute.path.unshift(cursorCandidate);
|
|
1967
2013
|
cursorCandidate = cursorCandidate.parent;
|
|
1968
2014
|
}
|
|
1969
|
-
const routesToRip = /* @__PURE__ */ new Set();
|
|
1970
2015
|
if (anyRipsRequired) {
|
|
1971
2016
|
solvedRoute.requiredRip = true;
|
|
1972
|
-
for (const candidate of solvedRoute.path) {
|
|
1973
|
-
if (candidate.port.assignment && candidate.port.assignment.connection.mutuallyConnectedNetworkId !== this.currentConnection.mutuallyConnectedNetworkId) {
|
|
1974
|
-
routesToRip.add(candidate.port.assignment.solvedRoute);
|
|
1975
|
-
}
|
|
1976
|
-
}
|
|
1977
|
-
}
|
|
1978
|
-
for (const candidate of solvedRoute.path) {
|
|
1979
|
-
if (!candidate.lastPort || !candidate.lastRegion) continue;
|
|
1980
|
-
const ripsRequired = this.getRipsRequiredForPortUsage(
|
|
1981
|
-
candidate.lastRegion,
|
|
1982
|
-
candidate.lastPort,
|
|
1983
|
-
candidate.port
|
|
1984
|
-
);
|
|
1985
|
-
for (const assignment of ripsRequired) {
|
|
1986
|
-
routesToRip.add(assignment.solvedRoute);
|
|
1987
|
-
}
|
|
1988
2017
|
}
|
|
1989
|
-
|
|
2018
|
+
const allRoutesToRip = this.computeRoutesToRip(solvedRoute);
|
|
2019
|
+
if (allRoutesToRip.size > 0) {
|
|
1990
2020
|
solvedRoute.requiredRip = true;
|
|
1991
|
-
for (const route of
|
|
2021
|
+
for (const route of allRoutesToRip) {
|
|
1992
2022
|
this.ripSolvedRoute(route);
|
|
1993
2023
|
}
|
|
1994
2024
|
}
|
|
@@ -2117,10 +2147,11 @@ var visualizeJumperGraph = (graph, options) => {
|
|
|
2117
2147
|
points: [],
|
|
2118
2148
|
rects: [],
|
|
2119
2149
|
texts: [],
|
|
2150
|
+
polygons: [],
|
|
2120
2151
|
coordinateSystem: "cartesian"
|
|
2121
2152
|
};
|
|
2122
2153
|
for (const region of graph.regions) {
|
|
2123
|
-
const { bounds, isPad, isThroughJumper, isConnectionRegion } = region.d;
|
|
2154
|
+
const { bounds, isPad, isThroughJumper, isConnectionRegion, polygon } = region.d;
|
|
2124
2155
|
const centerX = (bounds.minX + bounds.maxX) / 2;
|
|
2125
2156
|
const centerY = (bounds.minY + bounds.maxY) / 2;
|
|
2126
2157
|
const width = bounds.maxX - bounds.minX;
|
|
@@ -2135,12 +2166,22 @@ var visualizeJumperGraph = (graph, options) => {
|
|
|
2135
2166
|
} else {
|
|
2136
2167
|
fill = "rgba(200, 200, 255, 0.1)";
|
|
2137
2168
|
}
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2169
|
+
if (polygon && polygon.length >= 3) {
|
|
2170
|
+
const points = polygon;
|
|
2171
|
+
graphics.polygons.push({
|
|
2172
|
+
points,
|
|
2173
|
+
fill,
|
|
2174
|
+
stroke: "rgba(128, 128, 128, 0.5)",
|
|
2175
|
+
strokeWidth: 0.03
|
|
2176
|
+
});
|
|
2177
|
+
} else {
|
|
2178
|
+
graphics.rects.push({
|
|
2179
|
+
center: { x: centerX, y: centerY },
|
|
2180
|
+
width: width - 0.1,
|
|
2181
|
+
height: height - 0.1,
|
|
2182
|
+
fill
|
|
2183
|
+
});
|
|
2184
|
+
}
|
|
2144
2185
|
}
|
|
2145
2186
|
if (!options?.hidePortPoints) {
|
|
2146
2187
|
for (const port of graph.ports) {
|
|
@@ -2590,6 +2631,7 @@ var RegionBuilder = class {
|
|
|
2590
2631
|
this.data = {
|
|
2591
2632
|
id,
|
|
2592
2633
|
bounds: null,
|
|
2634
|
+
polygon: null,
|
|
2593
2635
|
center: null,
|
|
2594
2636
|
width: null,
|
|
2595
2637
|
height: null,
|
|
@@ -2606,6 +2648,35 @@ var RegionBuilder = class {
|
|
|
2606
2648
|
// Geometry methods
|
|
2607
2649
|
rect(b) {
|
|
2608
2650
|
this.data.bounds = { ...b };
|
|
2651
|
+
this.data.polygon = null;
|
|
2652
|
+
this.data.center = null;
|
|
2653
|
+
this.data.width = null;
|
|
2654
|
+
this.data.height = null;
|
|
2655
|
+
return this;
|
|
2656
|
+
}
|
|
2657
|
+
polygon(points) {
|
|
2658
|
+
if (points.length < 3) {
|
|
2659
|
+
throw new TopologyError(
|
|
2660
|
+
`Region "${this.data.id}" has invalid polygon: at least 3 points required`,
|
|
2661
|
+
{
|
|
2662
|
+
regionIds: [this.data.id],
|
|
2663
|
+
suggestion: "Provide at least three polygon vertices"
|
|
2664
|
+
}
|
|
2665
|
+
);
|
|
2666
|
+
}
|
|
2667
|
+
for (const point of points) {
|
|
2668
|
+
if (!Number.isFinite(point.x) || !Number.isFinite(point.y)) {
|
|
2669
|
+
throw new TopologyError(
|
|
2670
|
+
`Region "${this.data.id}" has invalid polygon point`,
|
|
2671
|
+
{
|
|
2672
|
+
regionIds: [this.data.id],
|
|
2673
|
+
suggestion: "Use finite numeric x/y values"
|
|
2674
|
+
}
|
|
2675
|
+
);
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2678
|
+
this.data.polygon = points.map((p) => ({ x: p.x, y: p.y }));
|
|
2679
|
+
this.data.bounds = null;
|
|
2609
2680
|
this.data.center = null;
|
|
2610
2681
|
this.data.width = null;
|
|
2611
2682
|
this.data.height = null;
|
|
@@ -2614,6 +2685,7 @@ var RegionBuilder = class {
|
|
|
2614
2685
|
center(x, y) {
|
|
2615
2686
|
this.data.center = { x, y };
|
|
2616
2687
|
this.data.bounds = null;
|
|
2688
|
+
this.data.polygon = null;
|
|
2617
2689
|
return this;
|
|
2618
2690
|
}
|
|
2619
2691
|
size(w, h, anchor = "center") {
|
|
@@ -2630,6 +2702,7 @@ var RegionBuilder = class {
|
|
|
2630
2702
|
this.data.height = h;
|
|
2631
2703
|
this.data.anchor = anchor;
|
|
2632
2704
|
this.data.bounds = null;
|
|
2705
|
+
this.data.polygon = null;
|
|
2633
2706
|
return this;
|
|
2634
2707
|
}
|
|
2635
2708
|
// Semantic methods
|
|
@@ -2665,6 +2738,20 @@ function computeBoundsFromRegionData(data) {
|
|
|
2665
2738
|
if (data.bounds) {
|
|
2666
2739
|
return data.bounds;
|
|
2667
2740
|
}
|
|
2741
|
+
if (data.polygon && data.polygon.length > 0) {
|
|
2742
|
+
let minX = data.polygon[0].x;
|
|
2743
|
+
let maxX = data.polygon[0].x;
|
|
2744
|
+
let minY = data.polygon[0].y;
|
|
2745
|
+
let maxY = data.polygon[0].y;
|
|
2746
|
+
for (let i = 1; i < data.polygon.length; i++) {
|
|
2747
|
+
const point = data.polygon[i];
|
|
2748
|
+
minX = Math.min(minX, point.x);
|
|
2749
|
+
maxX = Math.max(maxX, point.x);
|
|
2750
|
+
minY = Math.min(minY, point.y);
|
|
2751
|
+
maxY = Math.max(maxY, point.y);
|
|
2752
|
+
}
|
|
2753
|
+
return { minX, maxX, minY, maxY };
|
|
2754
|
+
}
|
|
2668
2755
|
if (data.center && data.width !== null && data.height !== null) {
|
|
2669
2756
|
const halfW = data.width / 2;
|
|
2670
2757
|
const halfH = data.height / 2;
|
|
@@ -3277,6 +3364,7 @@ ${errors.map((e) => ` - ${e}`).join("\n")}`
|
|
|
3277
3364
|
bounds,
|
|
3278
3365
|
center,
|
|
3279
3366
|
isPad: data.isPad,
|
|
3367
|
+
...data.polygon && { polygon: data.polygon },
|
|
3280
3368
|
...data.isThroughJumper && { isThroughJumper: true },
|
|
3281
3369
|
...data.isConnectionRegion && { isConnectionRegion: true },
|
|
3282
3370
|
...data.meta
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tscircuit/hypergraph",
|
|
3
3
|
"main": "dist/index.js",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.21",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"start": "cosmos",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"@tscircuit/math-utils": "^0.0.29",
|
|
20
20
|
"@types/bun": "latest",
|
|
21
21
|
"bun-match-svg": "^0.0.15",
|
|
22
|
-
"graphics-debug": "^0.0.
|
|
22
|
+
"graphics-debug": "^0.0.83",
|
|
23
23
|
"react-cosmos": "^7.1.0",
|
|
24
24
|
"react-cosmos-plugin-vite": "^7.1.0",
|
|
25
25
|
"transformation-matrix": "^3.1.0",
|