@kgdiff/layout 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +46 -2
- package/dist/components.d.ts +11 -0
- package/dist/components.d.ts.map +1 -0
- package/dist/components.js +56 -0
- package/dist/components.js.map +1 -0
- package/dist/engine.d.ts +20 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +336 -0
- package/dist/engine.js.map +1 -0
- package/dist/extend.d.ts +7 -0
- package/dist/extend.d.ts.map +1 -0
- package/dist/extend.js +68 -0
- package/dist/extend.js.map +1 -0
- package/dist/forces/collide-rect.d.ts +50 -0
- package/dist/forces/collide-rect.d.ts.map +1 -0
- package/dist/forces/collide-rect.js +310 -0
- package/dist/forces/collide-rect.js.map +1 -0
- package/dist/forces/free-spot.d.ts +40 -0
- package/dist/forces/free-spot.d.ts.map +1 -0
- package/dist/forces/free-spot.js +189 -0
- package/dist/forces/free-spot.js.map +1 -0
- package/dist/forces/params.d.ts +38 -0
- package/dist/forces/params.d.ts.map +1 -0
- package/dist/forces/params.js +55 -0
- package/dist/forces/params.js.map +1 -0
- package/dist/forces/springs.d.ts +20 -0
- package/dist/forces/springs.d.ts.map +1 -0
- package/dist/forces/springs.js +34 -0
- package/dist/forces/springs.js.map +1 -0
- package/dist/geometry.d.ts +21 -0
- package/dist/geometry.d.ts.map +1 -0
- package/dist/geometry.js +39 -0
- package/dist/geometry.js.map +1 -0
- package/dist/graph.d.ts +38 -0
- package/dist/graph.d.ts.map +1 -0
- package/dist/graph.js +82 -0
- package/dist/graph.js.map +1 -0
- package/dist/index.d.ts +162 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +87 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +28 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +196 -0
- package/dist/init.js.map +1 -0
- package/dist/json.d.ts +4 -0
- package/dist/json.d.ts.map +1 -0
- package/dist/json.js +112 -0
- package/dist/json.js.map +1 -0
- package/dist/layout.d.ts +7 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +70 -0
- package/dist/layout.js.map +1 -0
- package/dist/math.d.ts +29 -0
- package/dist/math.d.ts.map +1 -0
- package/dist/math.js +91 -0
- package/dist/math.js.map +1 -0
- package/dist/options.d.ts +43 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +76 -0
- package/dist/options.js.map +1 -0
- package/dist/pack.d.ts +43 -0
- package/dist/pack.d.ts.map +1 -0
- package/dist/pack.js +168 -0
- package/dist/pack.js.map +1 -0
- package/dist/prng.d.ts +20 -0
- package/dist/prng.d.ts.map +1 -0
- package/dist/prng.js +53 -0
- package/dist/prng.js.map +1 -0
- package/dist/result.d.ts +20 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/result.js +40 -0
- package/dist/result.js.map +1 -0
- package/dist/scope.d.ts +25 -0
- package/dist/scope.d.ts.map +1 -0
- package/dist/scope.js +71 -0
- package/dist/scope.js.map +1 -0
- package/dist/simulate.d.ts +64 -0
- package/dist/simulate.d.ts.map +1 -0
- package/dist/simulate.js +247 -0
- package/dist/simulate.js.map +1 -0
- package/dist/text-metrics.d.ts +32 -0
- package/dist/text-metrics.d.ts.map +1 -0
- package/dist/text-metrics.js +153 -0
- package/dist/text-metrics.js.map +1 -0
- package/dist/union.d.ts +32 -0
- package/dist/union.d.ts.map +1 -0
- package/dist/union.js +106 -0
- package/dist/union.js.map +1 -0
- package/package.json +52 -3
- package/src/components.ts +56 -0
- package/src/engine.ts +444 -0
- package/src/extend.ts +75 -0
- package/src/forces/collide-rect.ts +385 -0
- package/src/forces/free-spot.ts +243 -0
- package/src/forces/params.ts +88 -0
- package/src/forces/springs.ts +50 -0
- package/src/geometry.ts +66 -0
- package/src/graph.ts +120 -0
- package/src/index.ts +193 -0
- package/src/init.ts +246 -0
- package/src/json.ts +131 -0
- package/src/layout.ts +87 -0
- package/src/math.ts +91 -0
- package/src/options.ts +153 -0
- package/src/pack.ts +228 -0
- package/src/prng.ts +59 -0
- package/src/result.ts +63 -0
- package/src/scope.ts +86 -0
- package/src/simulate.ts +328 -0
- package/src/text-metrics.ts +157 -0
- package/src/union.ts +127 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAe,KAAK,KAAK,EAAE,KAAK,OAAO,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAI7E,MAAM,WAAW,qBAAqB;IACpC,gFAAgF;IAChF,QAAQ,CAAC,QAAQ,EAAE,cAAc,GAAG,SAAS,CAAC;IAC9C,gDAAgD;IAChD,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,6CAA6C;IAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,gDAAgD;IAChD,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC7C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,eAAO,MAAM,QAAQ;aACnB,WAAW,EAAE,CAAC;aACd,SAAS,EAAE,EAAE;aACb,mBAAmB,EAAE,IAAI;aACzB,IAAI,EAAE,CAAC;aACP,YAAY,EAAE,EAAE;aAChB,UAAU;aACV,QAAQ,EAAE,EAAE;aACZ,YAAY,EAAE,EAAE;CACR,CAAC;AAwBX,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,aAAkB,GAAG,qBAAqB,CA0EvF;AAMD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAK3D"}
|
package/dist/options.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Option defaults and validation. Every default is documented on `LayoutOptions`.
|
|
3
|
+
*/
|
|
4
|
+
import { KgdiffError } from '@kgdiff/core';
|
|
5
|
+
import { clamp, log2 } from './math.js';
|
|
6
|
+
import { seedState } from './prng.js';
|
|
7
|
+
export const DEFAULTS = {
|
|
8
|
+
contextHops: 1,
|
|
9
|
+
hubDegree: 40,
|
|
10
|
+
largeGraphThreshold: 3000,
|
|
11
|
+
seed: 1,
|
|
12
|
+
linkDistance: 56,
|
|
13
|
+
labelAware: true,
|
|
14
|
+
fontSize: 12,
|
|
15
|
+
componentGap: 96,
|
|
16
|
+
};
|
|
17
|
+
function invalid(option, value, expected) {
|
|
18
|
+
throw new KgdiffError('invalid-input', `Invalid layout option ${option}: expected ${expected}, got ${String(value)}`, { option, value });
|
|
19
|
+
}
|
|
20
|
+
function number(option, value, fallback, check, expected) {
|
|
21
|
+
if (value === undefined)
|
|
22
|
+
return fallback;
|
|
23
|
+
if (typeof value !== 'number' || Number.isNaN(value) || !check(value)) {
|
|
24
|
+
invalid(option, value, expected);
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
export function resolveLayoutOptions(options = {}) {
|
|
29
|
+
const { strategy, scope } = options;
|
|
30
|
+
if (strategy !== undefined && strategy !== 'union' && strategy !== 'anchored') {
|
|
31
|
+
invalid('strategy', strategy, "'union' or 'anchored'");
|
|
32
|
+
}
|
|
33
|
+
if (scope !== undefined && scope !== 'all' && scope !== 'changes') {
|
|
34
|
+
invalid('scope', scope, "'all' or 'changes'");
|
|
35
|
+
}
|
|
36
|
+
const finite = Number.isFinite;
|
|
37
|
+
const pinned = options.pinned ?? new Map();
|
|
38
|
+
for (const [id, p] of pinned) {
|
|
39
|
+
if (!p || !finite(p.x) || !finite(p.y))
|
|
40
|
+
invalid(`pinned[${id}]`, JSON.stringify(p), 'a finite point');
|
|
41
|
+
}
|
|
42
|
+
const iterations = options.iterations === undefined
|
|
43
|
+
? undefined
|
|
44
|
+
: number('iterations', options.iterations, 0, (v) => Number.isInteger(v) && v >= 0, 'an integer ≥ 0');
|
|
45
|
+
return {
|
|
46
|
+
strategy,
|
|
47
|
+
scope,
|
|
48
|
+
contextHops: number('contextHops', options.contextHops, DEFAULTS.contextHops, (v) => Number.isInteger(v) && v >= 0, 'an integer ≥ 0'),
|
|
49
|
+
hubDegree: number('hubDegree', options.hubDegree, DEFAULTS.hubDegree, (v) => v >= 0, 'a number ≥ 0'),
|
|
50
|
+
largeGraphThreshold: number('largeGraphThreshold', options.largeGraphThreshold, DEFAULTS.largeGraphThreshold, (v) => v >= 0, 'a number ≥ 0'),
|
|
51
|
+
seed: seedState(number('seed', options.seed, DEFAULTS.seed, finite, 'a finite number')),
|
|
52
|
+
iterations,
|
|
53
|
+
linkDistance: number('linkDistance', options.linkDistance, DEFAULTS.linkDistance, (v) => finite(v) && v > 0, 'a finite number > 0'),
|
|
54
|
+
labelAware: options.labelAware ?? DEFAULTS.labelAware,
|
|
55
|
+
fontSize: number('fontSize', options.fontSize, DEFAULTS.fontSize, (v) => finite(v) && v > 0, 'a finite number > 0'),
|
|
56
|
+
pinned,
|
|
57
|
+
componentGap: number('componentGap', options.componentGap, DEFAULTS.componentGap, (v) => finite(v) && v >= 0, 'a finite number ≥ 0'),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/** Above this many nodes in one simulation the tick count shrinks (see `defaultIterations`). */
|
|
61
|
+
const LARGE_SIMULATION = 2000;
|
|
62
|
+
const MIN_TICKS = 30;
|
|
63
|
+
/**
|
|
64
|
+
* Default tick count of a simulation over `nodeCount` nodes: `clamp(300 − 25·log₂(N), 120, 300)`
|
|
65
|
+
* (docs/algorithms.md §12) up to 2000 nodes. Beyond, the per-tick cost (Barnes–Hut charge and
|
|
66
|
+
* collision, ~N log N) would blow the time budget, so the work is held at that of a 2000-node
|
|
67
|
+
* run: `max(30, 120 · 2000 / N)` ticks. Large components start from a BFS spiral and get their
|
|
68
|
+
* label room in one step (see simulate.ts), so they need fewer ticks than the formula implies.
|
|
69
|
+
*/
|
|
70
|
+
export function defaultIterations(nodeCount) {
|
|
71
|
+
if (nodeCount > LARGE_SIMULATION) {
|
|
72
|
+
return Math.max(MIN_TICKS, Math.round((120 * LARGE_SIMULATION) / nodeCount));
|
|
73
|
+
}
|
|
74
|
+
return clamp(Math.round(300 - 25 * log2(Math.max(1, nodeCount))), 120, 300);
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=options.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"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"}
|
package/dist/pack.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Packing of laid-out components (docs/algorithms.md §12 "Components").
|
|
3
|
+
*
|
|
4
|
+
* Components are packed with a skyline bottom-left packer (tallest first) into a strip whose
|
|
5
|
+
* width is chosen from a few candidates so the overall picture is closest to a 16:10 viewport;
|
|
6
|
+
* isolated nodes follow as a compact table (aligned columns, rows sharing a baseline) spanning
|
|
7
|
+
* the same width.
|
|
8
|
+
*/
|
|
9
|
+
export interface Size {
|
|
10
|
+
readonly width: number;
|
|
11
|
+
readonly height: number;
|
|
12
|
+
}
|
|
13
|
+
export interface Placement {
|
|
14
|
+
readonly x: number;
|
|
15
|
+
readonly y: number;
|
|
16
|
+
}
|
|
17
|
+
/** A node in the isolated-node grid: its visual box relative to the node center. */
|
|
18
|
+
export interface GridItem {
|
|
19
|
+
readonly halfWidth: number;
|
|
20
|
+
/** Negative: distance from the center to the top of the box. */
|
|
21
|
+
readonly top: number;
|
|
22
|
+
readonly bottom: number;
|
|
23
|
+
}
|
|
24
|
+
export interface PackInput {
|
|
25
|
+
/** Bounding boxes of multi-node components. */
|
|
26
|
+
readonly components: readonly Size[];
|
|
27
|
+
readonly isolated: readonly GridItem[];
|
|
28
|
+
readonly componentGap: number;
|
|
29
|
+
readonly gridGapX: number;
|
|
30
|
+
readonly gridGapY: number;
|
|
31
|
+
/** Target width / height. Default 1.6 (16:10). */
|
|
32
|
+
readonly aspect?: number;
|
|
33
|
+
}
|
|
34
|
+
export interface PackResult {
|
|
35
|
+
/** Top-left corner of each component box, by input index. */
|
|
36
|
+
readonly components: readonly Placement[];
|
|
37
|
+
/** Node center of each isolated item, by input index. */
|
|
38
|
+
readonly isolated: readonly Placement[];
|
|
39
|
+
readonly width: number;
|
|
40
|
+
readonly height: number;
|
|
41
|
+
}
|
|
42
|
+
export declare function pack(input: PackInput): PackResult;
|
|
43
|
+
//# sourceMappingURL=pack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pack.d.ts","sourceRoot":"","sources":["../src/pack.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,IAAI;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,oFAAoF;AACpF,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,gEAAgE;IAChE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,SAAS;IACxB,+CAA+C;IAC/C,QAAQ,CAAC,UAAU,EAAE,SAAS,IAAI,EAAE,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,SAAS,QAAQ,EAAE,CAAC;IACvC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,kDAAkD;IAClD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,6DAA6D;IAC7D,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAC1C,yDAAyD;IACzD,QAAQ,CAAC,QAAQ,EAAE,SAAS,SAAS,EAAE,CAAC;IACxC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AA+HD,wBAAgB,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,UAAU,CAuDjD"}
|
package/dist/pack.js
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Packing of laid-out components (docs/algorithms.md §12 "Components").
|
|
3
|
+
*
|
|
4
|
+
* Components are packed with a skyline bottom-left packer (tallest first) into a strip whose
|
|
5
|
+
* width is chosen from a few candidates so the overall picture is closest to a 16:10 viewport;
|
|
6
|
+
* isolated nodes follow as a compact table (aligned columns, rows sharing a baseline) spanning
|
|
7
|
+
* the same width.
|
|
8
|
+
*/
|
|
9
|
+
const WIDTH_FACTORS = [0.55, 0.7, 0.85, 1, 1.15, 1.3, 1.5, 1.75, 2];
|
|
10
|
+
/** Skyline bottom-left placement into a strip; boxes must not be wider than the strip. */
|
|
11
|
+
function skyline(sizes, order, stripWidth) {
|
|
12
|
+
const positions = new Array(sizes.length);
|
|
13
|
+
let segments = [{ x: 0, y: 0, w: stripWidth }];
|
|
14
|
+
let width = 0;
|
|
15
|
+
let height = 0;
|
|
16
|
+
const eps = 1e-9;
|
|
17
|
+
for (const index of order) {
|
|
18
|
+
const { width: w, height: h } = sizes[index];
|
|
19
|
+
let bestY = Number.POSITIVE_INFINITY;
|
|
20
|
+
let bestX = 0;
|
|
21
|
+
for (let i = 0; i < segments.length; i++) {
|
|
22
|
+
const x = segments[i].x;
|
|
23
|
+
if (x + w > stripWidth + eps && x > 0)
|
|
24
|
+
break;
|
|
25
|
+
let y = 0;
|
|
26
|
+
let covered = 0;
|
|
27
|
+
for (let j = i; j < segments.length && covered < w - eps; j++) {
|
|
28
|
+
y = Math.max(y, segments[j].y);
|
|
29
|
+
covered = segments[j].x + segments[j].w - x;
|
|
30
|
+
}
|
|
31
|
+
if (y < bestY - eps) {
|
|
32
|
+
bestY = y;
|
|
33
|
+
bestX = x;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
positions[index] = { x: bestX, y: bestY };
|
|
37
|
+
width = Math.max(width, bestX + w);
|
|
38
|
+
height = Math.max(height, bestY + h);
|
|
39
|
+
// Raise the skyline over [bestX, bestX + w).
|
|
40
|
+
const left = bestX;
|
|
41
|
+
const right = bestX + w;
|
|
42
|
+
const next = [];
|
|
43
|
+
for (const seg of segments) {
|
|
44
|
+
const segRight = seg.x + seg.w;
|
|
45
|
+
if (segRight <= left + eps || seg.x >= right - eps) {
|
|
46
|
+
next.push(seg);
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (seg.x < left)
|
|
50
|
+
next.push({ x: seg.x, y: seg.y, w: left - seg.x });
|
|
51
|
+
if (segRight > right)
|
|
52
|
+
next.push({ x: right, y: seg.y, w: segRight - right });
|
|
53
|
+
}
|
|
54
|
+
next.push({ x: left, y: bestY + h, w });
|
|
55
|
+
next.sort((a, b) => a.x - b.x);
|
|
56
|
+
segments = [];
|
|
57
|
+
for (const seg of next) {
|
|
58
|
+
const last = segments[segments.length - 1];
|
|
59
|
+
if (last !== undefined && Math.abs(last.y - seg.y) < eps)
|
|
60
|
+
last.w = seg.x + seg.w - last.x;
|
|
61
|
+
else
|
|
62
|
+
segments.push({ ...seg });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return { positions, width, height };
|
|
66
|
+
}
|
|
67
|
+
/** Table layout of isolated nodes with the largest column count that fits `targetWidth`. */
|
|
68
|
+
function grid(items, gapX, gapY, targetWidth) {
|
|
69
|
+
const n = items.length;
|
|
70
|
+
if (n === 0)
|
|
71
|
+
return { centers: [], width: 0, height: 0 };
|
|
72
|
+
const tableWidth = (columns) => {
|
|
73
|
+
let total = gapX * (columns - 1);
|
|
74
|
+
for (let col = 0; col < columns; col++) {
|
|
75
|
+
let w = 0;
|
|
76
|
+
for (let i = col; i < n; i += columns)
|
|
77
|
+
w = Math.max(w, 2 * items[i].halfWidth);
|
|
78
|
+
total += w;
|
|
79
|
+
}
|
|
80
|
+
return total;
|
|
81
|
+
};
|
|
82
|
+
let columns = 1;
|
|
83
|
+
while (columns < n && tableWidth(columns + 1) <= targetWidth)
|
|
84
|
+
columns++;
|
|
85
|
+
const colWidth = [];
|
|
86
|
+
for (let col = 0; col < columns; col++) {
|
|
87
|
+
let w = 0;
|
|
88
|
+
for (let i = col; i < n; i += columns)
|
|
89
|
+
w = Math.max(w, 2 * items[i].halfWidth);
|
|
90
|
+
colWidth.push(w);
|
|
91
|
+
}
|
|
92
|
+
const colCenter = [];
|
|
93
|
+
let cursor = 0;
|
|
94
|
+
for (const w of colWidth) {
|
|
95
|
+
colCenter.push(cursor + w / 2);
|
|
96
|
+
cursor += w + gapX;
|
|
97
|
+
}
|
|
98
|
+
const width = cursor - gapX;
|
|
99
|
+
const centers = new Array(n);
|
|
100
|
+
let top = 0;
|
|
101
|
+
for (let row = 0; row * columns < n; row++) {
|
|
102
|
+
let above = 0;
|
|
103
|
+
let below = 0;
|
|
104
|
+
for (let i = row * columns; i < Math.min(n, (row + 1) * columns); i++) {
|
|
105
|
+
above = Math.max(above, -items[i].top);
|
|
106
|
+
below = Math.max(below, items[i].bottom);
|
|
107
|
+
}
|
|
108
|
+
for (let i = row * columns; i < Math.min(n, (row + 1) * columns); i++) {
|
|
109
|
+
centers[i] = { x: colCenter[i - row * columns], y: top + above };
|
|
110
|
+
}
|
|
111
|
+
top += above + below + gapY;
|
|
112
|
+
}
|
|
113
|
+
return { centers, width, height: top - gapY };
|
|
114
|
+
}
|
|
115
|
+
/** Area of the smallest `aspect` rectangle enclosing width × height. */
|
|
116
|
+
const enclosingArea = (width, height, aspect) => {
|
|
117
|
+
const w = Math.max(width, height * aspect);
|
|
118
|
+
return w * (w / aspect);
|
|
119
|
+
};
|
|
120
|
+
export function pack(input) {
|
|
121
|
+
const aspect = input.aspect ?? 1.6;
|
|
122
|
+
const gap = input.componentGap;
|
|
123
|
+
const inflated = input.components.map((s) => ({ width: s.width + gap, height: s.height + gap }));
|
|
124
|
+
const order = inflated
|
|
125
|
+
.map((_, i) => i)
|
|
126
|
+
.sort((a, b) => inflated[b].height - inflated[a].height ||
|
|
127
|
+
inflated[b].width - inflated[a].width ||
|
|
128
|
+
a - b);
|
|
129
|
+
let area = 0;
|
|
130
|
+
let widest = 0;
|
|
131
|
+
for (const s of inflated) {
|
|
132
|
+
area += s.width * s.height;
|
|
133
|
+
widest = Math.max(widest, s.width);
|
|
134
|
+
}
|
|
135
|
+
for (const item of input.isolated) {
|
|
136
|
+
area += (2 * item.halfWidth + input.gridGapX) * (item.bottom - item.top + input.gridGapY);
|
|
137
|
+
widest = Math.max(widest, 2 * item.halfWidth);
|
|
138
|
+
}
|
|
139
|
+
const base = Math.sqrt(area * aspect);
|
|
140
|
+
let best;
|
|
141
|
+
let bestCost = Number.POSITIVE_INFINITY;
|
|
142
|
+
const tried = new Set();
|
|
143
|
+
for (const factor of WIDTH_FACTORS) {
|
|
144
|
+
const strip = Math.max(widest, base * factor);
|
|
145
|
+
if (tried.has(strip))
|
|
146
|
+
continue;
|
|
147
|
+
tried.add(strip);
|
|
148
|
+
const packed = skyline(inflated, order, strip);
|
|
149
|
+
const compWidth = packed.width > 0 ? packed.width - gap : 0;
|
|
150
|
+
const compHeight = packed.height > 0 ? packed.height - gap : 0;
|
|
151
|
+
const table = grid(input.isolated, input.gridGapX, input.gridGapY, Math.max(strip - gap, compWidth));
|
|
152
|
+
const tableTop = compHeight > 0 && table.height > 0 ? compHeight + gap : 0;
|
|
153
|
+
const width = Math.max(compWidth, table.width);
|
|
154
|
+
const height = table.height > 0 ? tableTop + table.height : compHeight;
|
|
155
|
+
const cost = enclosingArea(width, height, aspect);
|
|
156
|
+
if (cost < bestCost - 1e-6) {
|
|
157
|
+
bestCost = cost;
|
|
158
|
+
best = {
|
|
159
|
+
components: packed.positions,
|
|
160
|
+
isolated: table.centers.map((c) => ({ x: c.x, y: c.y + tableTop })),
|
|
161
|
+
width,
|
|
162
|
+
height,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return best ?? { components: [], isolated: [], width: 0, height: 0 };
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=pack.js.map
|
package/dist/pack.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pack.js","sourceRoot":"","sources":["../src/pack.ts"],"names":[],"mappings":"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"}
|
package/dist/prng.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seeded randomness. The layout never calls `Math.random`: every random choice comes from a
|
|
3
|
+
* mulberry32 stream (fast, 32-bit state, good equidistribution for layout jitter), and per-node
|
|
4
|
+
* choices are derived from a hash of the node id so they do not depend on processing order.
|
|
5
|
+
*/
|
|
6
|
+
export type Random = () => number;
|
|
7
|
+
/** mulberry32: uniform floats in [0, 1) from a 32-bit seed. */
|
|
8
|
+
export declare function mulberry32(seed: number): Random;
|
|
9
|
+
/** FNV-1a over UTF-16 code units, mixed with `salt`. */
|
|
10
|
+
export declare function hashString(text: string, salt?: number): number;
|
|
11
|
+
/** Map any finite seed to a 32-bit state (integers map directly, others via their decimal text). */
|
|
12
|
+
export declare function seedState(seed: number): number;
|
|
13
|
+
/** A stream that depends only on the layout seed and one node id. */
|
|
14
|
+
export declare function nodeRandom(seed: number, id: string): Random;
|
|
15
|
+
/** A uniformly random unit vector, without trigonometry (rejection sampling in the unit disc). */
|
|
16
|
+
export declare function randomDirection(random: Random): {
|
|
17
|
+
readonly x: number;
|
|
18
|
+
readonly y: number;
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=prng.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prng.d.ts","sourceRoot":"","sources":["../src/prng.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC;AAElC,+DAA+D;AAC/D,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAS/C;AAED,wDAAwD;AACxD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,SAAI,GAAG,MAAM,CAazD;AAED,oGAAoG;AACpG,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,qEAAqE;AACrE,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,kGAAkG;AAClG,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAW1F"}
|
package/dist/prng.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seeded randomness. The layout never calls `Math.random`: every random choice comes from a
|
|
3
|
+
* mulberry32 stream (fast, 32-bit state, good equidistribution for layout jitter), and per-node
|
|
4
|
+
* choices are derived from a hash of the node id so they do not depend on processing order.
|
|
5
|
+
*/
|
|
6
|
+
/** mulberry32: uniform floats in [0, 1) from a 32-bit seed. */
|
|
7
|
+
export function mulberry32(seed) {
|
|
8
|
+
let state = seed >>> 0;
|
|
9
|
+
return () => {
|
|
10
|
+
state = (state + 0x6d2b79f5) >>> 0;
|
|
11
|
+
let t = state;
|
|
12
|
+
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
13
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
14
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/** FNV-1a over UTF-16 code units, mixed with `salt`. */
|
|
18
|
+
export function hashString(text, salt = 0) {
|
|
19
|
+
let hash = (0x811c9dc5 ^ salt) >>> 0;
|
|
20
|
+
for (let i = 0; i < text.length; i++) {
|
|
21
|
+
hash ^= text.charCodeAt(i);
|
|
22
|
+
hash = Math.imul(hash, 0x01000193);
|
|
23
|
+
}
|
|
24
|
+
// Final avalanche (murmur3 fmix32) so similar ids give unrelated streams.
|
|
25
|
+
hash ^= hash >>> 16;
|
|
26
|
+
hash = Math.imul(hash, 0x85ebca6b);
|
|
27
|
+
hash ^= hash >>> 13;
|
|
28
|
+
hash = Math.imul(hash, 0xc2b2ae35);
|
|
29
|
+
hash ^= hash >>> 16;
|
|
30
|
+
return hash >>> 0;
|
|
31
|
+
}
|
|
32
|
+
/** Map any finite seed to a 32-bit state (integers map directly, others via their decimal text). */
|
|
33
|
+
export function seedState(seed) {
|
|
34
|
+
return Number.isInteger(seed) ? seed >>> 0 : hashString(String(seed));
|
|
35
|
+
}
|
|
36
|
+
/** A stream that depends only on the layout seed and one node id. */
|
|
37
|
+
export function nodeRandom(seed, id) {
|
|
38
|
+
return mulberry32(hashString(id, seed));
|
|
39
|
+
}
|
|
40
|
+
/** A uniformly random unit vector, without trigonometry (rejection sampling in the unit disc). */
|
|
41
|
+
export function randomDirection(random) {
|
|
42
|
+
for (let i = 0; i < 32; i++) {
|
|
43
|
+
const x = random() * 2 - 1;
|
|
44
|
+
const y = random() * 2 - 1;
|
|
45
|
+
const d2 = x * x + y * y;
|
|
46
|
+
if (d2 > 1e-4 && d2 <= 1) {
|
|
47
|
+
const d = Math.sqrt(d2);
|
|
48
|
+
return { x: x / d, y: y / d };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return { x: 1, y: 0 };
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=prng.js.map
|
package/dist/prng.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prng.js","sourceRoot":"","sources":["../src/prng.ts"],"names":[],"mappings":"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"}
|
package/dist/result.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assembling the immutable `LayoutResult`.
|
|
3
|
+
*/
|
|
4
|
+
import type { Point, UnionId } from '@kgdiff/core';
|
|
5
|
+
import type { LayoutGraph } from './graph.js';
|
|
6
|
+
import type { LayoutResult, LayoutScope, LayoutStrategy } from './index.js';
|
|
7
|
+
export declare const now: () => number;
|
|
8
|
+
export interface AssembleInput {
|
|
9
|
+
readonly graph: LayoutGraph;
|
|
10
|
+
/** Final positions of every graph node (and possibly of nodes outside the graph). */
|
|
11
|
+
readonly positions: ReadonlyMap<UnionId, Point>;
|
|
12
|
+
readonly radii: ReadonlyMap<UnionId, number>;
|
|
13
|
+
readonly hidden: ReadonlyMap<UnionId, number>;
|
|
14
|
+
readonly scope: LayoutScope;
|
|
15
|
+
readonly strategy: LayoutStrategy;
|
|
16
|
+
readonly iterations: number;
|
|
17
|
+
readonly started: number;
|
|
18
|
+
}
|
|
19
|
+
export declare function assemble(input: AssembleInput): LayoutResult;
|
|
20
|
+
//# sourceMappingURL=result.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../src/result.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAmB,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE7F,eAAO,MAAM,GAAG,QAAO,MAGP,CAAC;AAEjB,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,qFAAqF;IACrF,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAChD,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC7C,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC9C,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAKD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,YAAY,CAiC3D"}
|
package/dist/result.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { connectedComponents } from './components.js';
|
|
2
|
+
import { emptyExtent, extentOf, includeNode, toRect } from './geometry.js';
|
|
3
|
+
export const now = () => typeof performance === 'object' && typeof performance.now === 'function'
|
|
4
|
+
? performance.now()
|
|
5
|
+
: Date.now();
|
|
6
|
+
const byId = (map) => new Map([...map].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)));
|
|
7
|
+
export function assemble(input) {
|
|
8
|
+
const { graph } = input;
|
|
9
|
+
const x = new Float64Array(graph.n);
|
|
10
|
+
const y = new Float64Array(graph.n);
|
|
11
|
+
const bounds = emptyExtent();
|
|
12
|
+
for (let i = 0; i < graph.n; i++) {
|
|
13
|
+
const p = input.positions.get(graph.nodes[i].id);
|
|
14
|
+
if (p === undefined)
|
|
15
|
+
continue;
|
|
16
|
+
x[i] = p.x;
|
|
17
|
+
y[i] = p.y;
|
|
18
|
+
includeNode(bounds, graph, i, p.x, p.y);
|
|
19
|
+
}
|
|
20
|
+
const components = connectedComponents(graph).map((members) => Object.freeze({
|
|
21
|
+
nodes: Object.freeze(members.map((i) => graph.nodes[i].id)),
|
|
22
|
+
bounds: toRect(extentOf(graph, members, x, y)),
|
|
23
|
+
}));
|
|
24
|
+
const positions = byId(input.positions);
|
|
25
|
+
return Object.freeze({
|
|
26
|
+
positions,
|
|
27
|
+
radii: byId(input.radii),
|
|
28
|
+
bounds: toRect(bounds),
|
|
29
|
+
scope: input.scope,
|
|
30
|
+
hidden: byId(input.hidden),
|
|
31
|
+
components: Object.freeze(components),
|
|
32
|
+
stats: Object.freeze({
|
|
33
|
+
strategy: input.strategy,
|
|
34
|
+
iterations: input.iterations,
|
|
35
|
+
nodes: positions.size,
|
|
36
|
+
ms: now() - input.started,
|
|
37
|
+
}),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=result.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.js","sourceRoot":"","sources":["../src/result.ts"],"names":[],"mappings":"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"}
|
package/dist/scope.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layout scope: which union nodes get a position (docs/algorithms.md §12 "Scope").
|
|
3
|
+
*/
|
|
4
|
+
import type { UnionId } from '@kgdiff/core';
|
|
5
|
+
import type { LayoutScope } from './index.js';
|
|
6
|
+
import type { UnionGraph } from './union.js';
|
|
7
|
+
export interface ScopeSelection {
|
|
8
|
+
/** 1 = node gets a position, by union index. */
|
|
9
|
+
readonly visible: Uint8Array;
|
|
10
|
+
/** Visible node → number of neighbours left out (only entries > 0), in id order. */
|
|
11
|
+
readonly hidden: ReadonlyMap<UnionId, number>;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* - `all`: every node.
|
|
15
|
+
* - `changes` (docs/algorithms.md §12): changed nodes (distance 0); the unchanged endpoints of
|
|
16
|
+
* changed edges in either version (distance 1, so a relation-only change between two unchanged
|
|
17
|
+
* nodes stays readable); and the nodes within `contextHops` of a changed node, by a
|
|
18
|
+
* multi-source BFS that never expands from a hub (max degree over both versions >
|
|
19
|
+
* `hubDegree`): a hub is visible when reached, but neighbours that could only be reached
|
|
20
|
+
* through it stay out and are counted in `hidden`.
|
|
21
|
+
*/
|
|
22
|
+
export declare function selectScope(graph: UnionGraph, scope: LayoutScope, contextHops: number, hubDegree: number): ScopeSelection;
|
|
23
|
+
/** For every visible node, the number of distinct neighbours that are not visible. */
|
|
24
|
+
export declare function countHidden(graph: UnionGraph, visible: Uint8Array): ReadonlyMap<UnionId, number>;
|
|
25
|
+
//# sourceMappingURL=scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../src/scope.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,WAAW,cAAc;IAC7B,gDAAgD;IAChD,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,oFAAoF;IACpF,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;CAC/C;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,WAAW,EAClB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,cAAc,CA2ChB;AAED,sFAAsF;AACtF,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,GAAG,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,CAWhG"}
|
package/dist/scope.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* - `all`: every node.
|
|
3
|
+
* - `changes` (docs/algorithms.md §12): changed nodes (distance 0); the unchanged endpoints of
|
|
4
|
+
* changed edges in either version (distance 1, so a relation-only change between two unchanged
|
|
5
|
+
* nodes stays readable); and the nodes within `contextHops` of a changed node, by a
|
|
6
|
+
* multi-source BFS that never expands from a hub (max degree over both versions >
|
|
7
|
+
* `hubDegree`): a hub is visible when reached, but neighbours that could only be reached
|
|
8
|
+
* through it stay out and are counted in `hidden`.
|
|
9
|
+
*/
|
|
10
|
+
export function selectScope(graph, scope, contextHops, hubDegree) {
|
|
11
|
+
const n = graph.nodes.length;
|
|
12
|
+
const visible = new Uint8Array(n);
|
|
13
|
+
if (scope === 'all') {
|
|
14
|
+
visible.fill(1);
|
|
15
|
+
return { visible, hidden: new Map() };
|
|
16
|
+
}
|
|
17
|
+
// Distance 0: changed nodes. Distance 1: unchanged endpoints of changed edges (they are
|
|
18
|
+
// needed to draw those edges and are context of them, like any neighbour of a changed node).
|
|
19
|
+
const distance = new Int32Array(n).fill(-1);
|
|
20
|
+
let frontier = [];
|
|
21
|
+
for (let i = 0; i < n; i++) {
|
|
22
|
+
if (graph.nodes[i].status !== 'unchanged') {
|
|
23
|
+
distance[i] = 0;
|
|
24
|
+
frontier.push(i);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const touched = [];
|
|
28
|
+
for (let i = 0; i < n; i++) {
|
|
29
|
+
if (graph.changed[i] && distance[i] === -1) {
|
|
30
|
+
distance[i] = 1;
|
|
31
|
+
touched.push(i);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
for (let hop = 0; hop < contextHops; hop++) {
|
|
35
|
+
const next = hop === 0 ? [...touched] : [];
|
|
36
|
+
for (const u of frontier) {
|
|
37
|
+
const { degree } = graph.nodes[u];
|
|
38
|
+
if (Math.max(degree.before, degree.after) > hubDegree)
|
|
39
|
+
continue;
|
|
40
|
+
for (let k = graph.offsets[u]; k < graph.offsets[u + 1]; k++) {
|
|
41
|
+
const v = graph.adjacency[k];
|
|
42
|
+
if (distance[v] === -1) {
|
|
43
|
+
distance[v] = hop + 1;
|
|
44
|
+
next.push(v);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
frontier = next.sort((a, b) => a - b);
|
|
49
|
+
}
|
|
50
|
+
for (let i = 0; i < n; i++)
|
|
51
|
+
if (distance[i] >= 0)
|
|
52
|
+
visible[i] = 1;
|
|
53
|
+
return { visible, hidden: countHidden(graph, visible) };
|
|
54
|
+
}
|
|
55
|
+
/** For every visible node, the number of distinct neighbours that are not visible. */
|
|
56
|
+
export function countHidden(graph, visible) {
|
|
57
|
+
const hidden = new Map();
|
|
58
|
+
for (let u = 0; u < graph.nodes.length; u++) {
|
|
59
|
+
if (!visible[u])
|
|
60
|
+
continue;
|
|
61
|
+
let count = 0;
|
|
62
|
+
for (let k = graph.offsets[u]; k < graph.offsets[u + 1]; k++) {
|
|
63
|
+
if (!visible[graph.adjacency[k]])
|
|
64
|
+
count++;
|
|
65
|
+
}
|
|
66
|
+
if (count > 0)
|
|
67
|
+
hidden.set(graph.nodes[u].id, count);
|
|
68
|
+
}
|
|
69
|
+
return hidden;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.js","sourceRoot":"","sources":["../src/scope.ts"],"names":[],"mappings":"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"}
|