@kortexya/nodus 0.1.5 → 0.1.7
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/assets/hypergraphWorker-D-ulsXRM.js +1 -0
- package/assets/layoutWorker-C9xWGcvY.js +285 -0
- package/assets/nodus_wasm-DxYJZXfN.js +1 -0
- package/nodus.src.bundle.js +4177 -6075
- package/nodus_render_wasm-C4PGMrSp.js +2617 -0
- package/nodus_wasm-PO2Iq59v.js +2883 -0
- package/package.json +1 -1
- package/types/algorithms/force/SEC.d.ts +8 -8
- package/types/geometry/index.d.ts +3 -1
- package/types/hypergraph/Hypergraph.d.ts +9 -1
- package/types/hypergraph/index.d.ts +1 -4
- package/types/hypergraph/layout/Optimizer.d.ts +33 -1
- package/types/hypergraph/render/Interaction.d.ts +7 -0
- package/types/hypergraph/render/PolygonRenderer.d.ts +12 -0
- package/types/hypergraph/types.d.ts +16 -0
- package/types/internals/Hypergraph.d.ts +5 -0
- package/types/internals/Tooltip.d.ts +9 -1
- package/types/internals/TooltipPrimitives.d.ts +2 -0
- package/types/internals/algorithmExports.d.ts +4 -2
- package/types/internals/algorithmHelpers.d.ts +1 -11
- package/types/internals/helpers.d.ts +0 -1
- package/types/internals/labels/helpers.d.ts +6 -2
- package/types/internals/rendering/float16.d.ts +2 -1
- package/types/internals/rendering/packing.d.ts +5 -5
- package/types/modules/ToolsAPI.d.ts +1 -0
- package/types/tools/TooltipAPI.d.ts +13 -0
- package/assets/hypergraphWorker-DV0aFI3L.js +0 -1
- package/assets/layoutWorker-DwDJwbgr.js +0 -285
- package/assets/nodus_wasm-C0vDfO5K.js +0 -1
- package/nodus_render_wasm-Bs6hlsx-.js +0 -2617
- package/nodus_wasm-DKYQVSUZ.js +0 -2789
- package/types/algorithms/hierarchical/sugiyama.d.ts +0 -144
- package/types/hypergraph/layout/Adam.d.ts +0 -12
- package/types/hypergraph/layout/Energy.d.ts +0 -55
- package/types/hypergraph/layout/LBFGS.d.ts +0 -32
package/package.json
CHANGED
|
@@ -3,12 +3,12 @@ export interface Circle {
|
|
|
3
3
|
y: number;
|
|
4
4
|
r: number;
|
|
5
5
|
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Smallest enclosing circle (Welzl) of the discs `ids`, looked up via getX/getY/getR. Runs in
|
|
8
|
+
* Rust (algo_smallest_enclosing_circle) — DETERMINISTIC: the former TS `med` shuffled with
|
|
9
|
+
* Math.random (non-reproducible) only to give Welzl expected-O(n) order; the circle is the
|
|
10
|
+
* unique minimum either way, so dropping the shuffle makes the layout reproducible. The
|
|
11
|
+
* getX/getY/getR callbacks are pre-applied here (per id, order-independent) so nothing crosses
|
|
12
|
+
* the wasm boundary as a callback. Byte-exact with the no-shuffle TS reference (sec-parity.mjs).
|
|
13
|
+
*/
|
|
14
14
|
export declare function med(ids: number[], getX: (i: number) => number, getY: (i: number) => number, getR: (i: number) => number): Circle;
|
|
@@ -132,7 +132,9 @@ export declare function getClosestPointOnLine(px: number, py: number, ax: number
|
|
|
132
132
|
*/
|
|
133
133
|
export declare function isPointInPolygon(px: number, py: number, points: Point[]): boolean;
|
|
134
134
|
/**
|
|
135
|
-
* Test if two convex polygons intersect using SAT.
|
|
135
|
+
* Test if two convex polygons intersect using SAT. Runs in the Rust `geo_polygons_intersect_sat4`
|
|
136
|
+
* kernel (crates/nodus-core/src/geometry.rs): four perpendicular axes from the first two edges of
|
|
137
|
+
* each polygon. Each polygon needs ≥ 3 points; flattened to `[x0,y0,x1,y1,…]` at the boundary.
|
|
136
138
|
*/
|
|
137
139
|
export declare function polygonsIntersect(v1: Point[], v2: Point[]): boolean;
|
|
138
140
|
/**
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* a set of incident vertex ids; we maintain a reverse index `incidence`
|
|
6
6
|
* (vertex id → set of hyperedge ids) so neighborhood queries are O(1).
|
|
7
7
|
*/
|
|
8
|
-
import type { HypergraphData, HypergraphHyperedge, HypergraphId, HypergraphVertex } from './types';
|
|
8
|
+
import type { ForbiddenSubgraph, HypergraphData, HypergraphHyperedge, HypergraphId, HypergraphVertex } from './types';
|
|
9
9
|
export declare class Hypergraph<VData = any, EData = any> {
|
|
10
10
|
private _vertices;
|
|
11
11
|
private _hyperedges;
|
|
@@ -71,4 +71,12 @@ export declare class Hypergraph<VData = any, EData = any> {
|
|
|
71
71
|
vertex?: HypergraphId;
|
|
72
72
|
hyperedge?: HypergraphId;
|
|
73
73
|
}): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* All forbidden sub-hypergraphs (paper Fig. 6: 3a2 / 2a3 / strangledV / strangledE), via the
|
|
76
|
+
* Rust kernel (HyperStruct.detect_forbidden) — byte-exact with the former TS detectors
|
|
77
|
+
* (forbidden-parity.mjs, ordered detection list). The kernel emits a flat
|
|
78
|
+
* `[kind, nV, nE, internedVerts…, internedEdges…]` per detection (kinds 0=3a2,1=2a3,
|
|
79
|
+
* 2=strangledV,3=strangledE); we demap the interned i64 ids back to the original ids.
|
|
80
|
+
*/
|
|
81
|
+
detectForbidden(): ForbiddenSubgraph[];
|
|
74
82
|
}
|
|
@@ -9,10 +9,7 @@ export { VertexRemoval, HyperedgeRemoval, VertexMerger, HyperedgeMerger, enumera
|
|
|
9
9
|
export { detectAllForbidden, forbiddenInFootprint, ALL_FORBIDDEN_KINDS, detectThreeAdjacentCluster2, detectTwoAdjacentCluster3, detectStrangledVertex, detectStrangledHyperedge, } from './forbidden';
|
|
10
10
|
export { buildDual } from './forbidden/StrangledHyperedge';
|
|
11
11
|
export { Simplifier, type SimplifierResult } from './Simplifier';
|
|
12
|
-
export {
|
|
13
|
-
export { minimizeLBFGS, type LBFGSOptions, type LBFGSResult } from './layout/LBFGS';
|
|
14
|
-
export { minimizeAdam, type AdamOptions } from './layout/Adam';
|
|
15
|
-
export { optimize, buildModel, refreshModelOrdering, type OptimizerInput, type OptimizerResult, } from './layout/Optimizer';
|
|
12
|
+
export { optimize, buildModel, refreshModelOrdering, type LayoutModel, type OptimizerInput, type OptimizerResult, } from './layout/Optimizer';
|
|
16
13
|
export { reverse, type ReverserInput, type ReverserResult, } from './layout/Reverser';
|
|
17
14
|
export { createPolygonDraw, applyDefaults, type PolygonRenderState, } from './render/PolygonRenderer';
|
|
18
15
|
export { createSVGDraw } from './render/SVGPolygonRenderer';
|
|
@@ -6,9 +6,41 @@
|
|
|
6
6
|
* Phase 2 (regularity): all five terms active with paper weights.
|
|
7
7
|
*/
|
|
8
8
|
import type { Hypergraph } from '../Hypergraph';
|
|
9
|
-
import { type LayoutModel } from './Energy';
|
|
10
9
|
import type { LayoutOptions, HypergraphId } from '../types';
|
|
11
10
|
import { sortPointsCCW } from '../../geometry/index';
|
|
11
|
+
/**
|
|
12
|
+
* Per-call layout model consumed by the wasm two-phase optimizer (and the frozen TS energy
|
|
13
|
+
* oracle). Built by `buildModel`; the heavy compute (energy + L-BFGS/Adam) runs in Rust over a
|
|
14
|
+
* CSR encoding of this — see HyperStruct.layout_optimize / hyper_reverse_step.
|
|
15
|
+
*/
|
|
16
|
+
export interface LayoutModel {
|
|
17
|
+
/** vertex id → flat index 0..|V|-1 */
|
|
18
|
+
vIndex: Map<string | number, number>;
|
|
19
|
+
/** hyperedge id → flat index 0..|E|-1 */
|
|
20
|
+
eIndex: Map<string | number, number>;
|
|
21
|
+
vCount: number;
|
|
22
|
+
eCount: number;
|
|
23
|
+
/** Per-hyperedge: list of vertex flat-indices (sorted CCW around centroid). */
|
|
24
|
+
hyperedgeVerts: number[][];
|
|
25
|
+
/** Per-hyperedge: cardinality (equal to hyperedgeVerts[i].length, cached). */
|
|
26
|
+
cardinality: number[];
|
|
27
|
+
/** Per-hyperedge: cardinality used for E_area / E_sep in the regularity phase. Paper §5.3:
|
|
28
|
+
* "we use the corresponding cardinalities saved from the original scale H_0" — for
|
|
29
|
+
* hyperedges that have absorbed others via merger, this is the sum of merged H_0
|
|
30
|
+
* cardinalities; for hyperedges intact since H_0, this equals the H_0 cardinality. */
|
|
31
|
+
originalCardinality: number[];
|
|
32
|
+
/** Per-hyperedge: target polygon area A*(k). Computed from originalCardinality, not the
|
|
33
|
+
* current k. */
|
|
34
|
+
targetArea: number[];
|
|
35
|
+
/** Per-vertex: list of hyperedge flat-indices that contain it. */
|
|
36
|
+
vertexInHyperedges: number[][];
|
|
37
|
+
/** Buffer distance d_b for separation energy. */
|
|
38
|
+
bufferDistance: number;
|
|
39
|
+
/** Which phase the energy evaluator should use for the separation d_0 formula. 'separation'
|
|
40
|
+
* uses Eq. 3 (½ sum of current diameters + d_b); 'regularity' uses the circumradius of the
|
|
41
|
+
* regular polygon with originalCardinality and targetArea. */
|
|
42
|
+
phase: 'separation' | 'regularity';
|
|
43
|
+
}
|
|
12
44
|
export interface OptimizerInput {
|
|
13
45
|
H: Hypergraph;
|
|
14
46
|
/** Optional initial vertex positions; missing vertices placed on a circle. */
|
|
@@ -13,6 +13,13 @@ export interface HitTestState {
|
|
|
13
13
|
dualPositions?: Map<HypergraphId, Vec2>;
|
|
14
14
|
showVertices: boolean;
|
|
15
15
|
vertexRadius: number;
|
|
16
|
+
/** When true, hit-test vertices even if their dots are hidden, so the hover
|
|
17
|
+
* spotlight can detect a member-node hover (the bubble's nodes are the real
|
|
18
|
+
* Nodus nodes, drawn by the host renderer, not the polygon layer). */
|
|
19
|
+
spotlight?: boolean;
|
|
20
|
+
/** Per-vertex screen-px hit radius — the bound core-node radius — so a member
|
|
21
|
+
* hit-zone matches the actual node. Falls back to `vertexRadius`. */
|
|
22
|
+
perVertexRadius?: Map<HypergraphId, number>;
|
|
16
23
|
/** 'primal' | 'dual' | 'both' — which set of polygons to hit-test. */
|
|
17
24
|
view: 'primal' | 'dual' | 'both';
|
|
18
25
|
/** Bubble-set boundary loops per hyperedge — when present, the cursor is
|
|
@@ -32,6 +32,18 @@ export interface PolygonRenderState {
|
|
|
32
32
|
/** Selection sets. */
|
|
33
33
|
selectedHyperedges?: Set<HypergraphId>;
|
|
34
34
|
selectedVertices?: Set<HypergraphId>;
|
|
35
|
+
/** Vertex id → bound Nodus core node id. Lets the spotlight size each member
|
|
36
|
+
* glow to the live node radius (and reach the node at all when the polygon
|
|
37
|
+
* layer renders below the graph with its own vertices hidden). */
|
|
38
|
+
nodeBindings?: Map<HypergraphId, string | number>;
|
|
39
|
+
/** 0→1 brighten of the bubble whose body is hovered (its members light up). */
|
|
40
|
+
spotGlow?: number;
|
|
41
|
+
/** 0→1 recede of the bubble(s) whose member node is hovered. */
|
|
42
|
+
spotFade?: number;
|
|
43
|
+
/** The bubble the glow applies to (kept through ramp-out). */
|
|
44
|
+
spotGlowHE?: HypergraphId | null;
|
|
45
|
+
/** The bubbles the fade applies to (kept through ramp-out). */
|
|
46
|
+
spotFadeHEs?: Set<HypergraphId>;
|
|
35
47
|
}
|
|
36
48
|
export declare function applyDefaults(opts?: HypergraphRenderOptions): PolygonRenderState['options'];
|
|
37
49
|
/**
|
|
@@ -154,6 +154,22 @@ export interface HypergraphRenderOptions {
|
|
|
154
154
|
* encloses any non-member that falls between its corners.
|
|
155
155
|
* 2.5D and dual views always use the convex hull. */
|
|
156
156
|
boundary?: 'bubble' | 'concave' | 'hull';
|
|
157
|
+
/** When true (default), the hover interaction follows the Bubble-Sets / Ogma
|
|
158
|
+
* "spotlight" pattern so a bubble and the nodes inside it never fight:
|
|
159
|
+
* - hovering a hyperedge's body draws a soft glow behind each of its member
|
|
160
|
+
* nodes, so you can see at a glance what the fact is made of;
|
|
161
|
+
* - hovering a member node fades that hyperedge's fill back (and drops its
|
|
162
|
+
* bright-hover emphasis), so the node underneath reads clearly.
|
|
163
|
+
* Set `false` to keep the plain hover-highlight with no member coupling. */
|
|
164
|
+
spotlight?: boolean;
|
|
165
|
+
/** Multiplier applied to a hyperedge's fill opacity while one of its member
|
|
166
|
+
* nodes is hovered, so the bubble recedes behind the node. Default 0.3. */
|
|
167
|
+
spotlightFadeScale?: number;
|
|
168
|
+
/** CSS color of the member glow. Defaults to the hyperedge's palette color. */
|
|
169
|
+
spotlightColor?: string;
|
|
170
|
+
/** Screen-pixel margin the member glow extends beyond each node's radius.
|
|
171
|
+
* Default 7. */
|
|
172
|
+
spotlightGlowMargin?: number;
|
|
157
173
|
}
|
|
158
174
|
export interface InteractionEvent {
|
|
159
175
|
/** Hyperedge id under cursor, if any. */
|
|
@@ -45,6 +45,8 @@ export declare class Hypergraph extends Module {
|
|
|
45
45
|
hoveredVertex: HypergraphId | null;
|
|
46
46
|
selectedHyperedges: Set<HypergraphId>;
|
|
47
47
|
selectedVertices: Set<HypergraphId>;
|
|
48
|
+
private _spot;
|
|
49
|
+
private _spotTimer;
|
|
48
50
|
private _onMouseMove;
|
|
49
51
|
private _onClick;
|
|
50
52
|
private _onMouseOut;
|
|
@@ -119,6 +121,9 @@ export declare class Hypergraph extends Module {
|
|
|
119
121
|
private _unbindInteraction;
|
|
120
122
|
private _hitState;
|
|
121
123
|
private _updateCursor;
|
|
124
|
+
private _spotTargets;
|
|
125
|
+
private _animateSpot;
|
|
126
|
+
private _spotStep;
|
|
122
127
|
setSelected(opts: {
|
|
123
128
|
hyperedges?: HypergraphId[];
|
|
124
129
|
vertices?: HypergraphId[];
|
|
@@ -8,16 +8,24 @@ export declare class Tooltip extends Module {
|
|
|
8
8
|
private _isHover;
|
|
9
9
|
private _elt;
|
|
10
10
|
private _timeout;
|
|
11
|
+
private _hideTimer;
|
|
12
|
+
private _hyperedgeWired;
|
|
11
13
|
onMounted(): void;
|
|
14
|
+
private _wireHyperedgeTooltips;
|
|
15
|
+
_onHyperedgeHover(evt: any): void;
|
|
16
|
+
_onHyperedgeOut(): void;
|
|
12
17
|
hide(): void;
|
|
13
18
|
isShown(): boolean;
|
|
14
19
|
refresh(): void;
|
|
15
|
-
show(e: any, { position: t, autoAdjust: s, className: o, x: c, y: p }?: any): void;
|
|
20
|
+
show(e: any, { position: t, autoAdjust: s, className: o, animationDuration: a, interactive: iv, offset: ofs, x: c, y: p, }?: any): void;
|
|
16
21
|
setHandler(e: string, t: any, s?: any): void;
|
|
17
22
|
_hideTooltip(e: boolean): void;
|
|
18
23
|
_createInteraction(e: string, t?: boolean): any;
|
|
19
24
|
_refreshTooltip(): void;
|
|
20
25
|
_schedule(e: () => void, t: number): void;
|
|
26
|
+
private _activeHideDelay;
|
|
27
|
+
private _scheduleHide;
|
|
28
|
+
private _cancelHide;
|
|
21
29
|
_showTooltip(e: any, t: any, s?: {
|
|
22
30
|
x: number;
|
|
23
31
|
y: number;
|
|
@@ -8,6 +8,8 @@ export declare const DEFAULT_OPTIONS11: {
|
|
|
8
8
|
position: string;
|
|
9
9
|
autoAdjust: boolean;
|
|
10
10
|
delay: number;
|
|
11
|
+
hideDelay: number;
|
|
12
|
+
offset: number;
|
|
11
13
|
};
|
|
12
14
|
export declare const CONDITIONS: Record<string, (info: any) => boolean>;
|
|
13
15
|
export declare function makeHandlerName(type: string, info: any): string;
|
|
@@ -182,8 +182,10 @@ export declare function stressMinimizationStepGeneral(graphDist: Float32Array[],
|
|
|
182
182
|
y: Float32Array;
|
|
183
183
|
};
|
|
184
184
|
/**
|
|
185
|
-
* Computes the convex hull of a set of 2D points.
|
|
186
|
-
*
|
|
185
|
+
* Computes the convex hull of a set of 2D points (Andrew's monotone chain). Routes to the Rust
|
|
186
|
+
* `algo_convex_hull` kernel, which returns the hull as original point indices in the exact TS
|
|
187
|
+
* order (byte-exact — convex-hull-parity.mjs); we map those back to the input point objects so
|
|
188
|
+
* identity is preserved.
|
|
187
189
|
*
|
|
188
190
|
* @param points - Array of [x, y] coordinate pairs
|
|
189
191
|
* @returns Array of points forming the convex hull, or null if fewer than 3 points
|
|
@@ -6,17 +6,7 @@
|
|
|
6
6
|
export declare const distance: (x1: number, y1: number, x2: number, y2: number) => number;
|
|
7
7
|
/** Squared Euclidean distance (avoids sqrt). */
|
|
8
8
|
export declare function squaredDistance(x1: number, y1: number, x2: number, y2: number): number;
|
|
9
|
-
|
|
10
|
-
* Finds intersection points of two circles.
|
|
11
|
-
* Returns null if the circles don't intersect.
|
|
12
|
-
* Returns an array of two [x, y] intersection points otherwise.
|
|
13
|
-
*/
|
|
14
|
-
export declare function circleCircleIntersection(cx1: number, cy1: number, r1: number, cx2: number, cy2: number, r2: number): [number, number][] | null;
|
|
15
|
-
/**
|
|
16
|
-
* Compares two points for angular sorting around a center point.
|
|
17
|
-
* Used for ordering nodes around a radial layout center.
|
|
18
|
-
*/
|
|
19
|
-
export declare function circleSortCompare(x1: number, y1: number, x2: number, y2: number, cx: number, cy: number): number;
|
|
9
|
+
export { circleCircleIntersection, circleSortCompare } from '../geometry/index';
|
|
20
10
|
/**
|
|
21
11
|
* Maps an index array (e.g. from Delaunator) into an array of the referenced elements.
|
|
22
12
|
*/
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared internal helper functions used across multiple Nodus modules.
|
|
3
|
-
* These are standalone utility functions with no cross-module dependencies.
|
|
4
3
|
*/
|
|
5
4
|
/** Flatten a nested object to dot-separated keys. */
|
|
6
5
|
export declare function flatten(obj: any, leafTest?: (v: any) => boolean, sep?: string): Record<string, any>;
|
|
@@ -122,6 +122,10 @@ export declare function getOBBPoints(x: number, y: number, w: number, h: number,
|
|
|
122
122
|
x: number;
|
|
123
123
|
y: number;
|
|
124
124
|
}[];
|
|
125
|
+
/**
|
|
126
|
+
* Test if two convex polygons intersect (all-edge Separating Axis Theorem). Runs in the Rust
|
|
127
|
+
* `geo_polygons_intersect` kernel; each polygon is flattened to `[x0,y0,x1,y1,…]` at the boundary.
|
|
128
|
+
*/
|
|
125
129
|
export declare function polygonsIntersect(a: {
|
|
126
130
|
x: number;
|
|
127
131
|
y: number;
|
|
@@ -130,8 +134,8 @@ export declare function polygonsIntersect(a: {
|
|
|
130
134
|
y: number;
|
|
131
135
|
}[]): boolean;
|
|
132
136
|
/**
|
|
133
|
-
* Check if two OBBs (oriented bounding boxes) collide.
|
|
134
|
-
*
|
|
137
|
+
* Check if two OBBs (oriented bounding boxes) collide. Each OBB is `[x, y, w, h, angle]`. Runs in
|
|
138
|
+
* the Rust `geo_check_obb_collision` kernel (builds each OBB's corner polygon + SAT internally).
|
|
135
139
|
*/
|
|
136
140
|
export declare function checkOBBCollision(a: number[], b: number[]): boolean;
|
|
137
141
|
export declare function moveAABB(bbox: number[], angle?: number, dist?: number, out?: number[]): number[];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* IEEE 754 half-precision (float16) conversion utilities and a simple
|
|
3
|
-
* `isNumber` guard.
|
|
3
|
+
* `isNumber` guard. toFloat16/fromFloat16 route to the Rust render_to_float16/render_from_float16
|
|
4
|
+
* kernels (crates/nodus-core/src/float16.rs); isNumber stays a trivial TS guard.
|
|
4
5
|
*/
|
|
5
6
|
/**
|
|
6
7
|
* Check whether a value is a finite number.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* GPU instance-buffer packing primitives (the renderer's CPU-side data layer). Each routes to the
|
|
3
|
+
* Rust render_* kernel (crates/nodus-core::render_pack), byte-exact across JS and WASM. The four
|
|
4
|
+
* `packWordN` bit-shift assemblers have only a COMBINED kernel (render_pack_words) and are packed
|
|
5
|
+
* in one crossing at their MemoryManager call site; `unsignedToSignedByte`/`unsignedToSignedInt16`
|
|
6
|
+
* are trivial inverse decodes with no kernel and stay inline TS.
|
|
7
7
|
*/
|
|
8
8
|
/** Extract the bit field `[low, high)` from `value`. `(value % 2^high) >> low`. */
|
|
9
9
|
export declare function extractBits(value: number, low: number, high: number): number;
|
|
@@ -42,6 +42,7 @@ declare class TooltipAPI extends APIModule {
|
|
|
42
42
|
onNodeRightClick(handler: any, options?: any): any;
|
|
43
43
|
onNodeDoubleClick(handler: any, options?: any): any;
|
|
44
44
|
onEdgeHover(handler: any, options?: any): any;
|
|
45
|
+
onHyperedgeHover(handler: any, options?: any): any;
|
|
45
46
|
onEdgeClick(handler: any, options?: any): any;
|
|
46
47
|
onEdgeRightClick(handler: any, options?: any): any;
|
|
47
48
|
onEdgeDoubleClick(handler: any, options?: any): any;
|
|
@@ -26,6 +26,8 @@ export declare class TooltipAPI<ND = any, ED = any> extends Module {
|
|
|
26
26
|
private _refreshTooltip;
|
|
27
27
|
private _schedule;
|
|
28
28
|
private _showTooltip;
|
|
29
|
+
private _onHyperedgeHover;
|
|
30
|
+
private _onHyperedgeOut;
|
|
29
31
|
/**
|
|
30
32
|
* Display a tooltip when a node is hovered.
|
|
31
33
|
*/
|
|
@@ -71,6 +73,17 @@ export declare class TooltipAPI<ND = any, ED = any> extends Module {
|
|
|
71
73
|
delay?: number;
|
|
72
74
|
className?: string;
|
|
73
75
|
}): void;
|
|
76
|
+
/**
|
|
77
|
+
* Display a tooltip when a hyperedge (polygon / bubble) is hovered. Wired to
|
|
78
|
+
* the hypergraph's own hover events; the handler receives the hovered
|
|
79
|
+
* hyperedge id. Yields to a node/edge tooltip when the cursor is on one.
|
|
80
|
+
*/
|
|
81
|
+
onHyperedgeHover(handler: (hyperedge: any) => string | Promise<string>, options?: {
|
|
82
|
+
position?: TooltipPosition;
|
|
83
|
+
autoAdjust?: boolean;
|
|
84
|
+
delay?: number;
|
|
85
|
+
className?: string;
|
|
86
|
+
}): void;
|
|
74
87
|
/**
|
|
75
88
|
* Display a tooltip when an edge is left-clicked.
|
|
76
89
|
*/
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const e=[`geometry`,`graph`,`attr`,`spatial`,`transform`,`layout`,`hyper`,`render`];function t(){let t={};for(let n of e)t[n]=`ts`;return t}let n=t();function r(r){if(n=t(),r){if(r.all===`ts`||r.all===`wasm`)for(let t of e)n[t]=r.all;for(let t of e){let e=r[t];(e===`ts`||e===`wasm`)&&(n[t]=e)}}}function i(e){return n[e]===`wasm`}let a=null;function o(){return a}function s(e){a=e}var c=class e{_vertices=new Map;_hyperedges=new Map;_incidence=new Map;_struct=null;_intern=new Map;_nextIntern=1n;_internOf(e){let t=this._intern.get(e);return t===void 0&&(t=this._nextIntern++,this._intern.set(e,t)),t}_hs(){if(!this._struct){let e=o();this._struct=new e.HyperStruct}return this._struct}constructor(e){this._hs(),e&&this.load(e)}load(e){this._struct?.free?.(),this._struct=null,this._intern.clear(),this._nextIntern=1n,this._hs(),this._vertices.clear(),this._hyperedges.clear(),this._incidence.clear();for(let t of e.vertices)this.addVertex(t);for(let t of e.hyperedges)this.addHyperedge(t);return this}toData(){return{vertices:Array.from(this._vertices.values()).map(e=>({...e})),hyperedges:Array.from(this._hyperedges.values()).map(e=>({...e,vertices:e.vertices.slice()}))}}clone(){return new e(this.toData())}vertexCount(){return this._vertices.size}hyperedgeCount(){return this._hyperedges.size}hasVertex(e){return this._vertices.has(e)}hasHyperedge(e){return this._hyperedges.has(e)}getVertex(e){return this._vertices.get(e)}getHyperedge(e){return this._hyperedges.get(e)}vertices(){return this._vertices.values()}hyperedges(){return this._hyperedges.values()}vertexIds(){return this._vertices.keys()}hyperedgeIds(){return this._hyperedges.keys()}addVertex(e){if(this._vertices.has(e.id))throw Error(`Vertex ${e.id} already exists`);return this._hs().add_vertex(this._internOf(e.id)),this._vertices.set(e.id,{...e}),this._incidence.set(e.id,new Set),this}addHyperedge(e){if(this._hyperedges.has(e.id))throw Error(`Hyperedge ${e.id} already exists`);let t=e.vertices.slice();{let n=new BigInt64Array(t.length);for(let e=0;e<t.length;e++)n[e]=this._internOf(t[e]);this._hs().add_hyperedge(this._internOf(e.id),n)}this._hyperedges.set(e.id,{...e,vertices:t});for(let n of t){let t=this._incidence.get(n);t||(t=new Set,this._incidence.set(n,t)),t.add(e.id)}return this}removeVertex(e){if(!this._vertices.get(e))throw Error(`Vertex ${e} not found`);this._hs().remove_vertex(this._internOf(e));let t=Array.from(this._incidence.get(e)??[]);for(let n of t){let t=this._hyperedges.get(n);t.vertices=t.vertices.filter(t=>t!==e)}return this._vertices.delete(e),this._incidence.delete(e),{incidentHyperedges:t}}removeHyperedge(e){let t=this._hyperedges.get(e);if(!t)throw Error(`Hyperedge ${e} not found`);this._hs().remove_hyperedge(this._internOf(e));let n=t.vertices.slice();for(let t of n){let n=this._incidence.get(t);n&&n.delete(e)}return this._hyperedges.delete(e),{incidentVertices:n}}setHyperedgeVertices(e,t){let n=this._hyperedges.get(e);if(!n)throw Error(`Hyperedge ${e} not found`);{let n=new BigInt64Array(t.length);for(let e=0;e<t.length;e++)n[e]=this._internOf(t[e]);this._hs().set_hyperedge_vertices(this._internOf(e),n)}for(let t of n.vertices){let n=this._incidence.get(t);n&&n.delete(e)}n.vertices=t.slice();for(let n of t){let t=this._incidence.get(n);t||(t=new Set,this._incidence.set(n,t)),t.add(e)}}incidentHyperedges(e){let t=this._incidence.get(e);return t?Array.from(t):[]}hyperedgeVertices(e){return this._hyperedges.get(e)?.vertices.slice()??[]}vertexAdjacent(e,t){if(e===t)return!1;let n=this._incidence.get(e),r=this._incidence.get(t);if(!n||!r)return!1;if(n.size>r.size)return this.vertexAdjacent(t,e);for(let e of n)if(r.has(e))return!0;return!1}hyperedgeAdjacent(e,t){if(e===t)return!1;let n=this._hyperedges.get(e)?.vertices,r=this._hyperedges.get(t)?.vertices;if(!n||!r)return!1;let i=new Set(r);for(let e of n)if(i.has(e))return!0;return!1}sharedHyperedges(e,t){if(e===t)return[];let n=this._incidence.get(e),r=this._incidence.get(t);if(!n||!r)return[];let i=[];for(let e of n)r.has(e)&&i.push(e);return i}sharedVertices(e,t){if(e===t)return[];let n=this._hyperedges.get(e)?.vertices,r=this._hyperedges.get(t)?.vertices;if(!n||!r)return[];let i=new Set(r);return n.filter(e=>i.has(e))}adjacentVertices(e){let t=new Set;for(let n of this._incidence.get(e)??[]){let r=this._hyperedges.get(n);if(r)for(let n of r.vertices)n!==e&&t.add(n)}return Array.from(t)}adjacentHyperedges(e){let t=new Set,n=this._hyperedges.get(e);if(!n)return[];for(let r of n.vertices)for(let n of this._incidence.get(r)??[])n!==e&&t.add(n);return Array.from(t)}degree(e){return this._incidence.get(e)?.size??0}cardinality(e){return this._hyperedges.get(e)?.vertices.length??0}isLinear(){return this._hs().is_linear()}isConnected(){return this._hs().is_connected()}isConnectedAfterRemoval(e){if(e.vertex!==void 0&&!this._vertices.has(e.vertex)||e.hyperedge!==void 0&&!this._hyperedges.has(e.hyperedge))return this.isConnected();let t=e.vertex===void 0?NaN:Number(this._internOf(e.vertex)),n=e.hyperedge===void 0?NaN:Number(this._internOf(e.hyperedge));return this._hs().is_connected_after_removal(t,n)}};function l(e,t,n){let{vCount:r,eCount:i}=e,a=t,o=2*r,s=t.length,c=new Float64Array(s),l=0;if(n.coordination>0){for(let r=0;r<i;r++){let i=e.hyperedgeVerts[r],s=i.length;if(s===0)continue;let u=0,d=0;for(let e of i)u+=a[2*e],d+=a[2*e+1];u/=s,d/=s;let f=o+2*r,p=t[f]-u,m=t[f+1]-d;l+=n.coordination*(p*p+m*m),c[f]+=n.coordination*2*p,c[f+1]+=n.coordination*2*m;let h=-n.coordination*2/s;for(let e of i)c[2*e]+=h*p,c[2*e+1]+=h*m}for(let i=0;i<r;i++){let r=e.vertexInHyperedges[i],s=r.length;if(s===0)continue;let u=0,d=0;for(let e of r)u+=t[o+2*e],d+=t[o+2*e+1];u/=s,d/=s;let f=2*i,p=a[f]-u,m=a[f+1]-d;l+=n.coordination*(p*p+m*m),c[f]+=n.coordination*2*p,c[f+1]+=n.coordination*2*m;let h=-n.coordination*2/s;for(let e of r)c[o+2*e]+=h*p,c[o+2*e+1]+=h*m}}for(let t=0;t<i;t++){let r=e.hyperedgeVerts[t],i=r.length;if(i<3)continue;let o=new Float64Array(i),s=new Float64Array(i);for(let e=0;e<i;e++)o[e]=a[2*r[e]],s[e]=a[2*r[e]+1];let u=0,d=0;for(let e=0;e<i;e++)u+=o[e],d+=s[e];if(u/=i,d/=i,n.area>0){let a=e.targetArea[t],u=0;for(let e=0;e<i;e++){let t=(e+1)%i;u+=o[e]*s[t]-o[t]*s[e]}u*=.5;let d=(Math.abs(u)-a)/Math.max(1,a);l+=n.area*d*d;let f=u>=0?1:-1,p=n.area*2*d*f/Math.max(1,a);for(let e=0;e<i;e++){let t=(e+1)%i,n=(e-1+i)%i,a=.5*(s[t]-s[n]),l=.5*(o[n]-o[t]);c[2*r[e]]+=p*a,c[2*r[e]+1]+=p*l}}if(n.regularity>0){let e=new Float64Array(i),t=new Float64Array(i),a=new Float64Array(i),u=0;for(let n=0;n<i;n++){let r=(n+1)%i;e[n]=o[r]-o[n],t[n]=s[r]-s[n],a[n]=Math.sqrt(e[n]*e[n]+t[n]*t[n])||1e-9,u+=a[n]}u/=i;for(let o=0;o<i;o++){let s=a[o]-u;l+=n.regularity*s*s;let d=2*n.regularity*s,f=(o+1)%i,p=e[o]/a[o],m=t[o]/a[o];c[2*r[o]]+=-d*p,c[2*r[o]+1]+=-d*m,c[2*r[f]]+=d*p,c[2*r[f]+1]+=d*m}}if(n.intersection>0)for(let e=0;e<i;e++){let t=(e+1)%i,a=o[e]-u,f=s[e]-d,p=o[t]-u,m=a*(s[t]-d)-f*p;if(m<0){l+=n.intersection*m*m;let i=2*n.intersection*m;c[2*r[e]]+=i*(s[t]-d),c[2*r[e]+1]+=i*-(o[t]-u),c[2*r[t]]+=i*-(s[e]-d),c[2*r[t]+1]+=i*(o[e]-u)}}}if(n.separation>0){let t=new Float64Array(i),r=new Float64Array(i),o=new Float64Array(i);for(let n=0;n<i;n++){let i=e.hyperedgeVerts[n],s=i.length;if(s===0)continue;let c=0,l=0;for(let e of i)c+=a[2*e],l+=a[2*e+1];t[n]=c/s,r[n]=l/s;let u=e.originalCardinality[n],d=Math.sqrt(u>=3?2*e.targetArea[n]/(u*Math.sin(2*Math.PI/u)):e.targetArea[n]/Math.PI);if(e.phase===`separation`){let e=0;for(let t=0;t<i.length;t++){let n=i[t];for(let r=t+1;r<i.length;r++){let t=i[r],o=a[2*n]-a[2*t],s=a[2*n+1]-a[2*t+1],c=o*o+s*s;c>e&&(e=c)}}let t=Math.sqrt(e)*.5;o[n]=Math.max(t,d)}else o[n]=d}for(let a=0;a<i;a++)for(let s=a+1;s<i;s++){let i=e.hyperedgeVerts[a],d=e.hyperedgeVerts[s];if(u(i,d))continue;let f=t[s]-t[a],p=r[s]-r[a],m=Math.sqrt(f*f+p*p)||1e-9,h=o[a]+o[s]+e.bufferDistance-m;if(h>0){l+=n.separation*h*h;let e=-2*n.separation*h/m,t=e*-f,r=e*-p,a=e*f,o=e*p,s=i.length,u=d.length;for(let e of i)c[2*e]+=t/s,c[2*e+1]+=r/s;for(let e of d)c[2*e]+=a/u,c[2*e+1]+=o/u}}}return{energy:l,grad:c}}function u(e,t){let n=new Set(e);for(let e of t)if(n.has(e))return!0;return!1}function d(e,t,n={}){let r=n.m??8,i=n.maxIter??200,a=n.tol??1e-5,o=n.c1??1e-4,s=n.alphaInit??1,c=n.alphaShrink??.5,l=n.maxLineSearch??25,u=t.length,d=new Float64Array(t),{energy:g,grad:_}=e(d);h(_,n.mask);let v=[],y=[],b=[];for(let t=0;t<i;t++){let i=p(_);if(n.onIter&&n.onIter(t,g,i),i<a)return{x:d,energy:g,iterations:t,converged:!0};let x=new Float64Array(_),S=Array(v.length);for(let e=v.length-1;e>=0;e--){let t=b[e]*f(v[e],x);S[e]=t,m(-t,y[e],x)}let C=1;if(v.length>0){let e=v[v.length-1],t=y[y.length-1];C=f(e,t)/(f(t,t)||1e-12)}let w=new Float64Array(x.length);for(let e=0;e<x.length;e++)w[e]=C*x[e];for(let e=0;e<v.length;e++){let t=b[e]*f(y[e],w);m(S[e]-t,v[e],w)}let T=new Float64Array(w.length);for(let e=0;e<w.length;e++)T[e]=-w[e];h(T,n.mask);let E=f(_,T);if(E>=0){v.length=0,y.length=0,b.length=0;for(let e=0;e<T.length;e++)T[e]=-_[e];h(T,n.mask)}let D=s,O=new Float64Array(u),k,A=_,j=!1;for(let t=0;t<l;t++){for(let e=0;e<u;e++)O[e]=d[e]+D*T[e];let t=e(O);if(k=t.energy,A=t.grad,k<=g+o*D*E){j=!0;break}D*=c}if(!j)return{x:d,energy:g,iterations:t,converged:!1};let M=new Float64Array(u),N=new Float64Array(u);for(let e=0;e<u;e++)M[e]=O[e]-d[e],N[e]=A[e]-_[e];let P=f(M,N);P>1e-12&&(v.push(M),y.push(N),b.push(1/P),v.length>r&&(v.shift(),y.shift(),b.shift()));for(let e=0;e<u;e++)d[e]=O[e];g=k,_=A,h(_,n.mask)}return{x:d,energy:g,iterations:i,converged:!1}}function f(e,t){let n=0;for(let r=0;r<e.length;r++)n+=e[r]*t[r];return n}function p(e){return Math.sqrt(f(e,e))}function m(e,t,n){for(let r=0;r<t.length;r++)n[r]+=e*t[r]}function h(e,t){if(t)for(let n=0;n<e.length;n++)t[n]||(e[n]=0)}function g(e,t,n={}){let r=n.lr??.01,i=n.beta1??.9,a=n.beta2??.999,o=n.eps??1e-8,s=n.maxIter??1e3,c=n.tol??1e-5,l=t.length,u=new Float64Array(t),d=new Float64Array(l),f=new Float64Array(l),p=0;for(let t=1;t<=s;t++){let s=e(u);p=s.energy;let m=s.grad;if(n.mask)for(let e=0;e<l;e++)n.mask[e]||(m[e]=0);let h=0;for(let e=0;e<l;e++)h+=m[e]*m[e];if(h=Math.sqrt(h),n.onIter&&n.onIter(t,p,h),h<c)return{x:u,energy:p,iterations:t,converged:!0};let g=1-i**+t,_=1-a**+t;for(let e=0;e<l;e++){d[e]=i*d[e]+(1-i)*m[e],f[e]=a*f[e]+(1-a)*m[e]*m[e];let t=d[e]/g,n=f[e]/_;u[e]-=r*t/(Math.sqrt(n)+o)}}return{x:u,energy:p,iterations:s,converged:!1}}const _={regularity:1,area:1,separation:.5,intersection:2,coordination:1},v={regularity:0,area:0,separation:1,intersection:0,coordination:1};function y(e){let{H:t,options:n}=e,r={..._,...n.weights??{}},a=n.targetAreaPerCardinality??(e=>80*Math.max(1,e)),o=n.bufferDistance??10,s=x(t,a,o,e.originalCardinality),c=new Float64Array(2*(s.vCount+s.eCount)),u=2*s.vCount,f=Array.from(t.vertexIds());for(let t=0;t<f.length;t++){let n=f[t],r=s.vIndex.get(n),i=e.initialPositions?.get(n);if(i)c[2*r]=i.x,c[2*r+1]=i.y;else{let e=t/f.length*2*Math.PI;c[2*r]=Math.cos(e)*200,c[2*r+1]=Math.sin(e)*200}}for(let n of t.hyperedges()){let t=s.eIndex.get(n.id),r=e.initialDualPositions?.get(n.id);if(r)c[u+2*t]=r.x,c[u+2*t+1]=r.y;else{let e=0,n=0,r=s.hyperedgeVerts[t];for(let t of r)e+=c[2*t],n+=c[2*t+1];let i=r.length||1;c[u+2*t]=e/i,c[u+2*t+1]=n/i}}let p=n.solver??`lbfgs`;if(i(`hyper`)){let e=b(t,s,c,r,o,n,f,p);if(e)return e}let m=0,h=0,y=(e,t)=>{n.onProgress?.({scale:0,phase:`separation`,iter:e,energy:t})};s.phase=`separation`;let T=e=>(S(s,e),l(s,e,v)),E=p===`lbfgs`?d(T,c,{maxIter:n.separationIters??200,onIter:y}):g(T,c,{maxIter:n.separationIters??200,onIter:y});c.set(E.x),m+=E.iterations;let D=(e,t)=>{n.onProgress?.({scale:0,phase:`regularity`,iter:e,energy:t})};s.phase=`regularity`;let O=e=>(S(s,e),l(s,e,r)),k=p===`lbfgs`?d(O,c,{maxIter:n.regularityIters??300,onIter:D}):g(O,c,{maxIter:n.regularityIters??300,onIter:D});return c.set(k.x),m+=k.iterations,h=k.energy,{vertexPositions:C(c,s,t),dualPositions:w(c,s,t),energy:h,iterations:m,state:c,model:s}}function b(e,t,n,r,i,a,s,c){let l=o();if(!l||typeof l.HyperStruct!=`function`)return null;let u=Array.from(e.hyperedgeIds()),d=new Map,f=0n,p=e=>{let t=d.get(e);return t===void 0&&(t=f++,d.set(e,t)),t},m=new l.HyperStruct;for(let e of s)m.add_vertex(p(e));for(let t of u){let n=e.hyperedgeVertices(t),r=new BigInt64Array(n.length);for(let e=0;e<n.length;e++)r[e]=p(n[e]);m.add_hyperedge(p(t),r)}let h=new Float64Array(u.length);for(let e=0;e<u.length;e++){let n=t.originalCardinality[e],r=t.targetArea[e];h[e]=Math.sqrt(n>=3?2*r/(n*Math.sin(2*Math.PI/n)):r/Math.PI)}let g=n.slice(0,2*t.vCount),_=n.slice(2*t.vCount),v=a.separationIters??200,y=a.regularityIters??300,b=a.onProgress,x=b?(e,t,n)=>b({scale:0,phase:e===0?`separation`:`regularity`,iter:t,energy:n}):void 0,S=m.layout_optimize(r.regularity,r.area,r.separation,r.intersection,r.coordination,i,v,y,g,_,Float64Array.from(t.targetArea),h,+(c===`adam`),x),C=2*(t.vCount+t.eCount),w=S.slice(0,C),T=S[C],E=S[C+1],D=2*t.vCount,O=new Map;for(let e=0;e<s.length;e++)O.set(s[e],{x:w[2*e],y:w[2*e+1]});let k=new Map;for(let e=0;e<u.length;e++)k.set(u[e],{x:w[D+2*e],y:w[D+2*e+1]});return{vertexPositions:O,dualPositions:k,energy:T,iterations:E,state:w,model:t}}function x(e,t,n,r){let i=Array.from(e.vertexIds()),a=Array.from(e.hyperedgeIds()),o=new Map,s=new Map;for(let e=0;e<i.length;e++)o.set(i[e],e);for(let e=0;e<a.length;e++)s.set(a[e],e);let c=a.map(t=>e.hyperedgeVertices(t).map(e=>o.get(e)).filter(e=>e!==void 0)),l=c.map(e=>e.length),u=a.map((e,t)=>r?.get(e)??l[t]),d=u.map(e=>t(e)),f=i.map(()=>[]);for(let e=0;e<a.length;e++)for(let t of c[e])f[t].push(e);return{vIndex:o,eIndex:s,vCount:i.length,eCount:a.length,hyperedgeVerts:c,cardinality:l,originalCardinality:u,targetArea:d,vertexInHyperedges:f,bufferDistance:n,phase:`regularity`}}function S(e,t){for(let n=0;n<e.eCount;n++){let r=e.hyperedgeVerts[n];if(r.length<3)continue;let i=0,a=0;for(let e of r)i+=t[2*e],a+=t[2*e+1];i/=r.length,a/=r.length;let o=r.map(e=>({vi:e,a:Math.atan2(t[2*e+1]-a,t[2*e]-i)})).sort((e,t)=>e.a-t.a).map(e=>e.vi);for(let e=0;e<r.length;e++)r[e]=o[e]}}function C(e,t,n){let r=new Map;for(let i of n.vertices()){let n=t.vIndex.get(i.id);r.set(i.id,{x:e[2*n],y:e[2*n+1]})}return r}function w(e,t,n){let r=2*t.vCount,i=new Map;for(let a of n.hyperedges()){let n=t.eIndex.get(a.id);i.set(a.id,{x:e[r+2*n],y:e[r+2*n+1]})}return i}function T(e,t,n,r,i){let a=e.ranges.degree.max-e.ranges.degree.min,o=e.ranges.cardinality.max-e.ranges.cardinality.min,s=Math.max(a,o)||1,c=Math.min(e.ranges.degree.min,e.ranges.cardinality.min),l=e.ranges.adjacency.min,u=e.ranges.adjacency.max-l||1,d=e.ranges.betweenness.min,f=e.ranges.betweenness.max-d||1,p=(n-c)/s,m=(r-l)/u,h=(i-d)/f;return t.alpha*p+t.beta*m-t.gamma*h}var E=class{kind=`vertexRemoval`;constructor(e){this.vid=e}isLegal(e){if(!e.hasVertex(this.vid))return!1;let t=e.incidentHyperedges(this.vid);for(let n=0;n<t.length;n++)for(let r=n+1;r<t.length;r++){let i=t[n],a=t[r];if(e.sharedVertices(i,a).filter(e=>e!==this.vid).length===0)return!1}return!!e.isConnectedAfterRemoval({vertex:this.vid})}apply(e){let t=e.getVertex(this.vid);if(!t)throw Error(`Vertex ${this.vid} not found`);let n=this.footprint(e),r=e.incidentHyperedges(this.vid),i={};for(let t of r)i[String(t)]=e.hyperedgeVertices(t);let a={vertex:{...t},incident:r,incidentLists:i};return e.removeVertex(this.vid),{kind:this.kind,vertices:[this.vid],hyperedges:[],footprintVertices:n.vertices,footprintHyperedges:n.hyperedges,snapshot:a}}invert(e,t){let n=t.snapshot;e.addVertex(n.vertex);for(let t of n.incident){let r=e.getHyperedge(t);if(!r)continue;let i=n.incidentLists?.[String(t)];e.setHyperedgeVertices(t,i??[...r.vertices,this.vid])}}footprint(e){let t=e.incidentHyperedges(this.vid),n=new Set([this.vid]);for(let r of t){let t=e.getHyperedge(r);if(t)for(let e of t.vertices)n.add(e)}return{vertices:Array.from(n),hyperedges:t}}priority(e,t,n){let r=this.footprint(e),i=0;for(let e of r.vertices)i=Math.max(i,t.degree.get(e)??0);for(let e of r.hyperedges)i=Math.max(i,t.cardinality.get(e)??0);let a=t.adjacencyFactor.get(this.vid)??0,o=t.betweenness.get(this.vid)??0;return T(t,n,i,a,o)}},D=class{kind=`hyperedgeRemoval`;constructor(e){this.eid=e}isLegal(e){if(!e.hasHyperedge(this.eid))return!1;let t=e.hyperedgeVertices(this.eid);for(let n=0;n<t.length;n++)for(let r=n+1;r<t.length;r++){let i=t[n],a=t[r];if(e.sharedHyperedges(i,a).filter(e=>e!==this.eid).length===0)return!1}return!!e.isConnectedAfterRemoval({hyperedge:this.eid})}apply(e){let t=e.getHyperedge(this.eid);if(!t)throw Error(`Hyperedge ${this.eid} not found`);let n=this.footprint(e),r={hyperedge:{...t,vertices:t.vertices.slice()}};return e.removeHyperedge(this.eid),{kind:this.kind,vertices:[],hyperedges:[this.eid],footprintVertices:n.vertices,footprintHyperedges:n.hyperedges,snapshot:r}}invert(e,t){e.addHyperedge(t.snapshot.hyperedge)}footprint(e){let t=e.hyperedgeVertices(this.eid),n=new Set([this.eid]);for(let r of t)for(let t of e.incidentHyperedges(r))n.add(t);return{vertices:t,hyperedges:Array.from(n)}}priority(e,t,n){let r=this.footprint(e),i=0;for(let e of r.vertices)i=Math.max(i,t.degree.get(e)??0);for(let e of r.hyperedges)i=Math.max(i,t.cardinality.get(e)??0);let a=t.adjacencyFactor.get(this.eid)??0,o=t.betweenness.get(this.eid)??0;return T(t,n,i,a,o)}},O=class{kind=`vertexMerger`;constructor(e,t){this.retained=e,this.absorbed=t}isLegal(e){return!e.hasVertex(this.retained)||!e.hasVertex(this.absorbed)||this.retained===this.absorbed?!1:e.sharedHyperedges(this.retained,this.absorbed).length>=2}apply(e){if(!e.hasVertex(this.retained)||!e.hasVertex(this.absorbed))throw Error(`Merger operands missing`);let t=this.footprint(e),n={...e.getVertex(this.absorbed)},r=e.incidentHyperedges(this.absorbed),i=new Set(e.incidentHyperedges(this.retained)),a={},o=new Set([...r,...i]);for(let t of o)a[String(t)]=e.hyperedgeVertices(t);for(let t of r){let n=e.getHyperedge(t);n&&(i.has(t)?e.setHyperedgeVertices(t,n.vertices.filter(e=>e!==this.absorbed)):e.setHyperedgeVertices(t,n.vertices.map(e=>e===this.absorbed?this.retained:e)))}return e.removeVertex(this.absorbed),{kind:this.kind,vertices:[this.retained,this.absorbed],hyperedges:[],footprintVertices:t.vertices,footprintHyperedges:t.hyperedges,snapshot:{absorbedVertex:n,incidentAbsorbed:r,incidentLists:a}}}invert(e,t){let n=t.snapshot;e.addVertex(n.absorbedVertex);for(let[t,r]of Object.entries(n.incidentLists??{}))e.hasHyperedge(t)&&e.setHyperedgeVertices(t,r)}footprint(e){let t=new Set([this.retained,this.absorbed]),n=new Set;for(let t of e.incidentHyperedges(this.retained))n.add(t);for(let t of e.incidentHyperedges(this.absorbed))n.add(t);for(let r of n){let n=e.getHyperedge(r);if(n)for(let e of n.vertices)t.add(e)}return{vertices:Array.from(t),hyperedges:Array.from(n)}}priority(e,t,n){let r=this.footprint(e),i=0;for(let e of r.vertices)i=Math.max(i,t.degree.get(e)??0);for(let e of r.hyperedges)i=Math.max(i,t.cardinality.get(e)??0);let a=t.adjacencyFactor.get(this.retained)??0,o=t.adjacencyFactor.get(this.absorbed)??0,s=t.betweenness.get(this.retained)??0,c=t.betweenness.get(this.absorbed)??0;return T(t,n,i,(a+o)/2,(s+c)/2)}},k=class{kind=`hyperedgeMerger`;constructor(e,t){this.retained=e,this.absorbed=t}isLegal(e){return!e.hasHyperedge(this.retained)||!e.hasHyperedge(this.absorbed)||this.retained===this.absorbed?!1:e.sharedVertices(this.retained,this.absorbed).length>=2}apply(e){let t=this.footprint(e),n={...e.getHyperedge(this.absorbed),vertices:e.hyperedgeVertices(this.absorbed)},r=new Set(e.hyperedgeVertices(this.retained)),i=[...r];for(let e of n.vertices)r.has(e)||i.push(e);return e.setHyperedgeVertices(this.retained,i),e.removeHyperedge(this.absorbed),{kind:this.kind,vertices:[],hyperedges:[this.retained,this.absorbed],footprintVertices:t.vertices,footprintHyperedges:t.hyperedges,snapshot:{absorbedHE:n,retainedOriginalVerts:Array.from(r)}}}invert(e,t){let n=t.snapshot;e.setHyperedgeVertices(this.retained,n.retainedOriginalVerts),e.addHyperedge(n.absorbedHE)}footprint(e){let t=new Set;for(let n of e.hyperedgeVertices(this.retained))t.add(n);for(let n of e.hyperedgeVertices(this.absorbed))t.add(n);let n=new Set([this.retained,this.absorbed]);for(let r of t)for(let t of e.incidentHyperedges(r))n.add(t);return{vertices:Array.from(t),hyperedges:Array.from(n)}}priority(e,t,n){let r=this.footprint(e),i=0;for(let e of r.vertices)i=Math.max(i,t.degree.get(e)??0);for(let e of r.hyperedges)i=Math.max(i,t.cardinality.get(e)??0);let a=t.adjacencyFactor.get(this.retained)??0,o=t.adjacencyFactor.get(this.absorbed)??0,s=t.betweenness.get(this.retained)??0,c=t.betweenness.get(this.absorbed)??0;return T(t,n,i,(a+o)/2,(s+c)/2)}};function A(e){switch(e.kind){case`vertexRemoval`:return new E(e.vertices[0]);case`hyperedgeRemoval`:return new D(e.hyperedges[0]);case`vertexMerger`:return new O(e.vertices[0],e.vertices[1]);case`hyperedgeMerger`:return new k(e.hyperedges[0],e.hyperedges[1])}}const j={regularity:1,area:1,separation:.5,intersection:2,coordination:1};function M(e){let t={...j,...e.options.weights??{}},n=e.options.targetAreaPerCardinality??(e=>80*Math.max(1,e)),r=e.options.bufferDistance??10,i=e.options.solver??`lbfgs`,a=e.options.onProgress,o=new Map(e.vertexPositions),s=new Map(e.dualPositions);for(let c=e.operations.length-1;c>=0;c--){let u=e.operations[c];if(A(u).invert(e.H,u),e.originalCardinality){if(u.kind===`hyperedgeMerger`){let[,t]=u.hyperedges,n=u.snapshot?.absorbedHE?.vertices?.length??0;e.originalCardinality.set(t,n)}else if(u.kind===`hyperedgeRemoval`){let t=u.hyperedges[0],n=e.H.getHyperedge(t);n&&e.originalCardinality.set(t,n.vertices.length)}}N(e.H,u,o,s);let f=new Set(u.footprintVertices),p=new Set(u.footprintHyperedges),m=x(e.H,n,r,e.originalCardinality);m.phase=`regularity`;let h=new Float64Array(2*(m.vCount+m.eCount)),_=2*m.vCount;for(let[e,t]of o){let n=m.vIndex.get(e);n!==void 0&&(h[2*n]=t.x,h[2*n+1]=t.y)}for(let[e,t]of s){let n=m.eIndex.get(e);n!==void 0&&(h[_+2*n]=t.x,h[_+2*n+1]=t.y)}S(m,h);let v=new Uint8Array(h.length);for(let e of f){let t=m.vIndex.get(e);t!==void 0&&(v[2*t]=1,v[2*t+1]=1)}for(let e of p){let t=m.eIndex.get(e);t!==void 0&&(v[_+2*t]=1,v[_+2*t+1]=1)}let y=e=>(S(m,e),l(m,e,t)),b=(e,t)=>{a?.({scale:c,phase:`reversal`,iter:e,energy:t})},C=i===`lbfgs`?d(y,h,{maxIter:80,mask:v,onIter:b}):g(y,h,{maxIter:200,mask:v,onIter:b});h.set(C.x),o=new Map,s=new Map;for(let t of e.H.vertices()){let e=m.vIndex.get(t.id);o.set(t.id,{x:h[2*e],y:h[2*e+1]})}for(let t of e.H.hyperedges()){let e=m.eIndex.get(t.id);s.set(t.id,{x:h[_+2*e],y:h[_+2*e+1]})}}return{vertexPositions:o,dualPositions:s}}function N(e,t,n,r){for(let i of t.vertices){if(n.has(i))continue;let t=e.incidentHyperedges(i);if(t.length===0){n.set(i,{x:0,y:0});continue}let a=0,o=0,s=0;for(let e of t){let t=r.get(e);t&&(a+=t.x,o+=t.y,s++)}s===0?n.set(i,{x:0,y:0}):n.set(i,{x:a/s,y:o/s})}for(let i of t.hyperedges){if(r.has(i))continue;let t=e.hyperedgeVertices(i);if(t.length===0){r.set(i,{x:0,y:0});continue}let a=0,o=0,s=0;for(let e of t){let t=n.get(e);t&&(a+=t.x,o+=t.y,s++)}s===0?r.set(i,{x:0,y:0}):r.set(i,{x:a/s,y:o/s})}}let P=null;function F(){return P||=import(`./nodus_wasm-C0vDfO5K.js`).then(e=>{s(e),r({graph:`wasm`,attr:`wasm`,layout:`wasm`,hyper:`wasm`,geometry:`wasm`,spatial:`wasm`})}).catch(()=>{P=null}),P}const I=new Set;self.addEventListener(`message`,e=>{let t=e.data;if(!(!t||typeof t!=`object`)){if(t.type===`cancel`){I.add(t.id);return}t.type===`run`&&F().then(()=>L(t))}});function L(e){let{id:t}=e,n=Math.max(1,e.progressEvery??25),r=0,i=e=>{r++,(e.iter===0||r%n===0)&&self.postMessage({type:`progress`,id:t,info:e})};try{let n=new c(e.hypergraph),r=new Map(e.originalCardinality),a=e.initialPositions?new Map(e.initialPositions):void 0,o=e.initialDualPositions?new Map(e.initialDualPositions):void 0,s=y({H:n,options:{...e.options,onProgress:i},initialPositions:a,initialDualPositions:o,originalCardinality:r});if(I.has(t)){I.delete(t),self.postMessage({type:`error`,id:t,error:`cancelled`});return}let l=s.vertexPositions,u=s.dualPositions;if(e.operations.length>0){let t=M({H:n,operations:e.operations,vertexPositions:l,dualPositions:u,originalCardinality:r,options:{...e.options,onProgress:i}});l=t.vertexPositions,u=t.dualPositions}if(I.has(t)){I.delete(t),self.postMessage({type:`error`,id:t,error:`cancelled`});return}self.postMessage({type:`done`,id:t,finalHypergraph:n.toData(),vertexPositions:Array.from(l.entries()),dualPositions:Array.from(u.entries()),energy:s.energy})}catch(e){self.postMessage({type:`error`,id:t,error:e?.message??String(e)})}}
|