@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.
Files changed (35) hide show
  1. package/assets/hypergraphWorker-D-ulsXRM.js +1 -0
  2. package/assets/layoutWorker-C9xWGcvY.js +285 -0
  3. package/assets/nodus_wasm-DxYJZXfN.js +1 -0
  4. package/nodus.src.bundle.js +4177 -6075
  5. package/nodus_render_wasm-C4PGMrSp.js +2617 -0
  6. package/nodus_wasm-PO2Iq59v.js +2883 -0
  7. package/package.json +1 -1
  8. package/types/algorithms/force/SEC.d.ts +8 -8
  9. package/types/geometry/index.d.ts +3 -1
  10. package/types/hypergraph/Hypergraph.d.ts +9 -1
  11. package/types/hypergraph/index.d.ts +1 -4
  12. package/types/hypergraph/layout/Optimizer.d.ts +33 -1
  13. package/types/hypergraph/render/Interaction.d.ts +7 -0
  14. package/types/hypergraph/render/PolygonRenderer.d.ts +12 -0
  15. package/types/hypergraph/types.d.ts +16 -0
  16. package/types/internals/Hypergraph.d.ts +5 -0
  17. package/types/internals/Tooltip.d.ts +9 -1
  18. package/types/internals/TooltipPrimitives.d.ts +2 -0
  19. package/types/internals/algorithmExports.d.ts +4 -2
  20. package/types/internals/algorithmHelpers.d.ts +1 -11
  21. package/types/internals/helpers.d.ts +0 -1
  22. package/types/internals/labels/helpers.d.ts +6 -2
  23. package/types/internals/rendering/float16.d.ts +2 -1
  24. package/types/internals/rendering/packing.d.ts +5 -5
  25. package/types/modules/ToolsAPI.d.ts +1 -0
  26. package/types/tools/TooltipAPI.d.ts +13 -0
  27. package/assets/hypergraphWorker-DV0aFI3L.js +0 -1
  28. package/assets/layoutWorker-DwDJwbgr.js +0 -285
  29. package/assets/nodus_wasm-C0vDfO5K.js +0 -1
  30. package/nodus_render_wasm-Bs6hlsx-.js +0 -2617
  31. package/nodus_wasm-DKYQVSUZ.js +0 -2789
  32. package/types/algorithms/hierarchical/sugiyama.d.ts +0 -144
  33. package/types/hypergraph/layout/Adam.d.ts +0 -12
  34. package/types/hypergraph/layout/Energy.d.ts +0 -55
  35. package/types/hypergraph/layout/LBFGS.d.ts +0 -32
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kortexya/nodus",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Nodus — a high-performance graph visualization engine (WebGL/WebGPU/Canvas/SVG).",
5
5
  "homepage": "https://kortexya.com",
6
6
  "author": "Kortexya <david.loiret@kortexya.com>",
@@ -3,12 +3,12 @@ export interface Circle {
3
3
  y: number;
4
4
  r: number;
5
5
  }
6
- export declare function from1(x: number, y: number, r: number): Circle;
7
- export declare function from2(x1: number, y1: number, r1: number, x2: number, y2: number, r2: number): Circle;
8
- export declare function from3(x1: number, y1: number, r1: number, x2: number, y2: number, r2: number, x3: number, y3: number, r3: number): Circle;
9
- export declare function contains(c: Circle, x: number, y: number, r: number): boolean;
10
- export declare function containsWeak(c: Circle, x: number, y: number, r: number): boolean;
11
- export declare function containsWeakAll(c: Circle, ids: number[], getX: (i: number) => number, getY: (i: number) => number, getR: (i: number) => number): boolean;
12
- export declare function enclose(ids: number[], getX: (i: number) => number, getY: (i: number) => number, getR: (i: number) => number): Circle;
13
- export declare function shuffle<T>(array: T[]): T[];
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 { evaluateEnergy, type LayoutModel, } from './layout/Energy';
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
- * Uses Andrew's monotone chain algorithm.
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
- * Each OBB is represented as [x, y, w, h, angle].
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
- * Pure GPU instance-buffer packing primitives (the renderer's CPU-side data layer).
3
- *
4
- * Extracted from MemoryManager so they are a single, exported, testable source of truth and
5
- * can be differentially gated against the Rust port (`nodus-core::render_pack`). Each is pure
6
- * integer/bit arithmetic no DOM, no store so it is byte-exact across JS and WASM.
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)})}}