@kgdiff/layout 0.0.0-stage → 0.2.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
package/dist/init.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic initial placement (docs/algorithms.md §12 "Simulation").
|
|
3
|
+
*
|
|
4
|
+
* Fresh components start on a phyllotaxis (sunflower) spiral filled in BFS order from the
|
|
5
|
+
* highest-degree node, so graph distance ≈ radial distance and the simulation never starts from
|
|
6
|
+
* a hairball. Each BFS layer occupies a contiguous run of spiral slots; within that annulus the
|
|
7
|
+
* nodes are assigned to slots by angle, aligned with the angle of their BFS parent, so subtrees
|
|
8
|
+
* start in the same sector as their parent and parent–child links start out short and uncrossed.
|
|
9
|
+
*
|
|
10
|
+
* Nodes added around fixed ones start at the barycenter of their placed neighbours (or next to
|
|
11
|
+
* their merge / split partner), and a lone new leaf sprouts on the open side of its neighbour.
|
|
12
|
+
*/
|
|
13
|
+
import type { LayoutGraph } from './graph.js';
|
|
14
|
+
import { type Random } from './prng.js';
|
|
15
|
+
/** Merge / split partners of every node, both directions, ascending. */
|
|
16
|
+
export declare function partnerLists(graph: LayoutGraph): readonly (readonly number[])[];
|
|
17
|
+
/**
|
|
18
|
+
* Place one component (ascending indices) on a seeded, BFS-ordered phyllotaxis spiral around the
|
|
19
|
+
* origin. `visited` is shared across components (they are disjoint).
|
|
20
|
+
*/
|
|
21
|
+
export declare function placeOnSpiral(graph: LayoutGraph, partners: readonly (readonly number[])[], members: readonly number[], spacing: number, random: Random, visited: Uint8Array, x: Float64Array, y: Float64Array): void;
|
|
22
|
+
/**
|
|
23
|
+
* Give every unplaced node of `members` that is connected to a placed one a start position,
|
|
24
|
+
* layer by layer outwards from the placed set. Within a layer, positions depend only on earlier
|
|
25
|
+
* layers and on the node's own id, never on processing order.
|
|
26
|
+
*/
|
|
27
|
+
export declare function placeAroundFixed(graph: LayoutGraph, partners: readonly (readonly number[])[], members: readonly number[], placed: Uint8Array, x: Float64Array, y: Float64Array, linkDistance: number, seed: number): void;
|
|
28
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAc,KAAK,MAAM,EAAmB,MAAM,WAAW,CAAC;AAQrE,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAQ/E;AAYD;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,EACxC,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,UAAU,EACnB,CAAC,EAAE,YAAY,EACf,CAAC,EAAE,YAAY,GACd,IAAI,CA0EN;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,EACxC,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,MAAM,EAAE,UAAU,EAClB,CAAC,EAAE,YAAY,EACf,CAAC,EAAE,YAAY,EACf,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,GACX,IAAI,CA6BN"}
|
package/dist/init.js
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { pseudoAngle, pseudoAngleDistance } from './math.js';
|
|
2
|
+
import { nodeRandom, randomDirection } from './prng.js';
|
|
3
|
+
/** Golden angle π(3 − √5) as exact literals: the spiral is built by rotation, without trig. */
|
|
4
|
+
const COS_GOLDEN = -0.7373688780783197;
|
|
5
|
+
const SIN_GOLDEN = 0.6754902942615238;
|
|
6
|
+
/** Offsets tried when aligning a BFS layer with its parents (more are sampled with a stride). */
|
|
7
|
+
const MAX_ALIGN_OFFSETS = 48;
|
|
8
|
+
/** Merge / split partners of every node, both directions, ascending. */
|
|
9
|
+
export function partnerLists(graph) {
|
|
10
|
+
const lists = Array.from({ length: graph.n }, () => []);
|
|
11
|
+
for (const { mover, anchor } of graph.partners) {
|
|
12
|
+
lists[mover].push(anchor);
|
|
13
|
+
lists[anchor].push(mover);
|
|
14
|
+
}
|
|
15
|
+
for (const list of lists)
|
|
16
|
+
list.sort((a, b) => a - b);
|
|
17
|
+
return lists;
|
|
18
|
+
}
|
|
19
|
+
function forEachNeighbour(graph, partners, u, visit) {
|
|
20
|
+
for (let k = graph.offsets[u]; k < graph.offsets[u + 1]; k++)
|
|
21
|
+
visit(graph.adjacency[k]);
|
|
22
|
+
for (const v of partners[u])
|
|
23
|
+
visit(v);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Place one component (ascending indices) on a seeded, BFS-ordered phyllotaxis spiral around the
|
|
27
|
+
* origin. `visited` is shared across components (they are disjoint).
|
|
28
|
+
*/
|
|
29
|
+
export function placeOnSpiral(graph, partners, members, spacing, random, visited, x, y) {
|
|
30
|
+
let root = members[0];
|
|
31
|
+
if (root === undefined)
|
|
32
|
+
return;
|
|
33
|
+
for (const m of members)
|
|
34
|
+
if (graph.degree[m] > graph.degree[root])
|
|
35
|
+
root = m;
|
|
36
|
+
// The whole spiral is rotated by a seeded angle and each slot jittered a little, so different
|
|
37
|
+
// seeds give different (equally good) layouts.
|
|
38
|
+
const phase = randomDirection(random);
|
|
39
|
+
let c = phase.x;
|
|
40
|
+
let s = phase.y;
|
|
41
|
+
const jitter = spacing * 0.3;
|
|
42
|
+
let slot = 1;
|
|
43
|
+
visited[root] = 1;
|
|
44
|
+
x[root] = 0;
|
|
45
|
+
y[root] = 0;
|
|
46
|
+
let layer = [root];
|
|
47
|
+
while (layer.length > 0) {
|
|
48
|
+
const next = [];
|
|
49
|
+
const parentAngle = [];
|
|
50
|
+
for (const u of layer) {
|
|
51
|
+
const children = [];
|
|
52
|
+
forEachNeighbour(graph, partners, u, (v) => {
|
|
53
|
+
if (!visited[v]) {
|
|
54
|
+
visited[v] = 1;
|
|
55
|
+
children.push(v);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
children.sort((a, b) => graph.degree[b] - graph.degree[a] || a - b);
|
|
59
|
+
const angle = pseudoAngle(x[u], y[u]);
|
|
60
|
+
for (const v of children) {
|
|
61
|
+
next.push(v);
|
|
62
|
+
parentAngle.push(angle);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (next.length === 0)
|
|
66
|
+
break;
|
|
67
|
+
const slots = [];
|
|
68
|
+
for (let j = 0; j < next.length; j++, slot++) {
|
|
69
|
+
const r = spacing * Math.sqrt(slot);
|
|
70
|
+
slots.push({ x: r * c, y: r * s, angle: pseudoAngle(c, s) });
|
|
71
|
+
const cr = c * COS_GOLDEN - s * SIN_GOLDEN;
|
|
72
|
+
s = c * SIN_GOLDEN + s * COS_GOLDEN;
|
|
73
|
+
c = cr;
|
|
74
|
+
}
|
|
75
|
+
slots.sort((a, b) => a.angle - b.angle);
|
|
76
|
+
const count = next.length;
|
|
77
|
+
const stride = Math.max(1, Math.ceil(count / MAX_ALIGN_OFFSETS));
|
|
78
|
+
let bestOffset = 0;
|
|
79
|
+
let bestCost = Number.POSITIVE_INFINITY;
|
|
80
|
+
for (let o = 0; o < count; o += stride) {
|
|
81
|
+
let cost = 0;
|
|
82
|
+
for (let i = 0; i < count; i++) {
|
|
83
|
+
cost += pseudoAngleDistance(parentAngle[i], slots[(i + o) % count].angle);
|
|
84
|
+
}
|
|
85
|
+
if (cost < bestCost) {
|
|
86
|
+
bestCost = cost;
|
|
87
|
+
bestOffset = o;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
for (let i = 0; i < count; i++) {
|
|
91
|
+
const v = next[i];
|
|
92
|
+
const target = slots[(i + bestOffset) % count];
|
|
93
|
+
x[v] = target.x + (random() - 0.5) * jitter;
|
|
94
|
+
y[v] = target.y + (random() - 0.5) * jitter;
|
|
95
|
+
}
|
|
96
|
+
layer = next
|
|
97
|
+
.map((v) => ({ v, angle: pseudoAngle(x[v], y[v]) }))
|
|
98
|
+
.sort((a, b) => a.angle - b.angle || a.v - b.v)
|
|
99
|
+
.map((e) => e.v);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Give every unplaced node of `members` that is connected to a placed one a start position,
|
|
104
|
+
* layer by layer outwards from the placed set. Within a layer, positions depend only on earlier
|
|
105
|
+
* layers and on the node's own id, never on processing order.
|
|
106
|
+
*/
|
|
107
|
+
export function placeAroundFixed(graph, partners, members, placed, x, y, linkDistance, seed) {
|
|
108
|
+
const hasPlacedNeighbour = (u) => {
|
|
109
|
+
let found = false;
|
|
110
|
+
forEachNeighbour(graph, partners, u, (v) => {
|
|
111
|
+
if (placed[v])
|
|
112
|
+
found = true;
|
|
113
|
+
});
|
|
114
|
+
return found;
|
|
115
|
+
};
|
|
116
|
+
let layer = members.filter((u) => !placed[u] && hasPlacedNeighbour(u));
|
|
117
|
+
while (layer.length > 0) {
|
|
118
|
+
const starts = layer.map((u) => startNear(graph, partners, u, placed, x, y, linkDistance, seed));
|
|
119
|
+
const queued = new Set();
|
|
120
|
+
for (let i = 0; i < layer.length; i++) {
|
|
121
|
+
const u = layer[i];
|
|
122
|
+
const p = starts[i];
|
|
123
|
+
x[u] = p.x;
|
|
124
|
+
y[u] = p.y;
|
|
125
|
+
placed[u] = 1;
|
|
126
|
+
}
|
|
127
|
+
for (const u of layer) {
|
|
128
|
+
forEachNeighbour(graph, partners, u, (v) => {
|
|
129
|
+
if (!placed[v])
|
|
130
|
+
queued.add(v);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
layer = [...queued].sort((a, b) => a - b);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function startNear(graph, partners, u, placed, x, y, linkDistance, seed) {
|
|
137
|
+
const random = nodeRandom(seed, graph.nodes[u].id);
|
|
138
|
+
const centroid = (list) => {
|
|
139
|
+
let sx = 0;
|
|
140
|
+
let sy = 0;
|
|
141
|
+
for (const v of list) {
|
|
142
|
+
sx += x[v];
|
|
143
|
+
sy += y[v];
|
|
144
|
+
}
|
|
145
|
+
return { x: sx / list.length, y: sy / list.length };
|
|
146
|
+
};
|
|
147
|
+
const placedPartners = partners[u].filter((v) => placed[v]);
|
|
148
|
+
if (placedPartners.length > 0) {
|
|
149
|
+
const base = centroid(placedPartners);
|
|
150
|
+
const dir = randomDirection(random);
|
|
151
|
+
return { x: base.x + dir.x * linkDistance * 0.5, y: base.y + dir.y * linkDistance * 0.5 };
|
|
152
|
+
}
|
|
153
|
+
const neighbours = [];
|
|
154
|
+
for (let k = graph.offsets[u]; k < graph.offsets[u + 1]; k++) {
|
|
155
|
+
const v = graph.adjacency[k];
|
|
156
|
+
if (placed[v])
|
|
157
|
+
neighbours.push(v);
|
|
158
|
+
}
|
|
159
|
+
if (neighbours.length >= 2) {
|
|
160
|
+
const base = centroid(neighbours);
|
|
161
|
+
const dir = randomDirection(random);
|
|
162
|
+
return { x: base.x + dir.x * linkDistance * 0.25, y: base.y + dir.y * linkDistance * 0.25 };
|
|
163
|
+
}
|
|
164
|
+
// A single placed neighbour p: sprout on p's open side (away from p's other placed
|
|
165
|
+
// neighbours), turned by up to ±55° so siblings fan out.
|
|
166
|
+
const p = neighbours[0];
|
|
167
|
+
const others = [];
|
|
168
|
+
for (let k = graph.offsets[p]; k < graph.offsets[p + 1]; k++) {
|
|
169
|
+
const v = graph.adjacency[k];
|
|
170
|
+
if (v !== u && placed[v])
|
|
171
|
+
others.push(v);
|
|
172
|
+
}
|
|
173
|
+
let ax = 0;
|
|
174
|
+
let ay = 0;
|
|
175
|
+
if (others.length > 0) {
|
|
176
|
+
const o = centroid(others);
|
|
177
|
+
ax = x[p] - o.x;
|
|
178
|
+
ay = y[p] - o.y;
|
|
179
|
+
}
|
|
180
|
+
const len = Math.sqrt(ax * ax + ay * ay);
|
|
181
|
+
if (len < 1e-9) {
|
|
182
|
+
const dir = randomDirection(random);
|
|
183
|
+
ax = dir.x;
|
|
184
|
+
ay = dir.y;
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
ax /= len;
|
|
188
|
+
ay /= len;
|
|
189
|
+
}
|
|
190
|
+
const sin = (random() * 2 - 1) * 0.82;
|
|
191
|
+
const cos = Math.sqrt(1 - sin * sin);
|
|
192
|
+
const dx = ax * cos - ay * sin;
|
|
193
|
+
const dy = ax * sin + ay * cos;
|
|
194
|
+
return { x: x[p] + dx * linkDistance, y: y[p] + dy * linkDistance };
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=init.js.map
|
package/dist/init.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAe,eAAe,EAAE,MAAM,WAAW,CAAC;AAErE,+FAA+F;AAC/F,MAAM,UAAU,GAAG,CAAC,kBAAkB,CAAC;AACvC,MAAM,UAAU,GAAG,kBAAkB,CAAC;AACtC,iGAAiG;AACjG,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAE7B,wEAAwE;AACxE,MAAM,UAAU,YAAY,CAAC,KAAkB;IAC7C,MAAM,KAAK,GAAe,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IACpE,KAAK,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC/C,KAAK,CAAC,KAAK,CAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC3B,KAAK,CAAC,MAAM,CAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,KAAK;QAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACrD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,gBAAgB,CACvB,KAAkB,EAClB,QAAwC,EACxC,CAAS,EACT,KAA0B;IAE1B,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAE,EAAE,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAE,EAAE,CAAC,EAAE;QAAE,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAE,CAAC,CAAC;IAC3F,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAE;QAAE,KAAK,CAAC,CAAC,CAAC,CAAC;AACzC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAC3B,KAAkB,EAClB,QAAwC,EACxC,OAA0B,EAC1B,OAAe,EACf,MAAc,EACd,OAAmB,EACnB,CAAe,EACf,CAAe;IAEf,IAAI,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACtB,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO;IAC/B,KAAK,MAAM,CAAC,IAAI,OAAO;QAAE,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAE,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAE;YAAE,IAAI,GAAG,CAAC,CAAC;IAE9E,8FAA8F;IAC9F,+CAA+C;IAC/C,MAAM,KAAK,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACtC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;IAChB,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;IAChB,MAAM,MAAM,GAAG,OAAO,GAAG,GAAG,CAAC;IAC7B,IAAI,IAAI,GAAG,CAAC,CAAC;IAEb,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAClB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACZ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACZ,IAAI,KAAK,GAAG,CAAC,IAAI,CAAC,CAAC;IAEnB,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,GAAa,EAAE,CAAC;QAC1B,MAAM,WAAW,GAAa,EAAE,CAAC;QACjC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,MAAM,QAAQ,GAAa,EAAE,CAAC;YAC9B,gBAAgB,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE;gBACzC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;oBAChB,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;oBACf,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBACnB,CAAC;YACH,CAAC,CAAC,CAAC;YACH,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAE,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YACtE,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAE,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC,CAAC;YACxC,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;gBACzB,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBACb,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM;QAE7B,MAAM,KAAK,GAA8C,EAAE,CAAC;QAC5D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC;YAC7C,MAAM,CAAC,GAAG,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACpC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;YAC7D,MAAM,EAAE,GAAG,CAAC,GAAG,UAAU,GAAG,CAAC,GAAG,UAAU,CAAC;YAC3C,CAAC,GAAG,CAAC,GAAG,UAAU,GAAG,CAAC,GAAG,UAAU,CAAC;YACpC,CAAC,GAAG,EAAE,CAAC;QACT,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QAExC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,iBAAiB,CAAC,CAAC,CAAC;QACjE,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,IAAI,QAAQ,GAAG,MAAM,CAAC,iBAAiB,CAAC;QACxC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,IAAI,MAAM,EAAE,CAAC;YACvC,IAAI,IAAI,GAAG,CAAC,CAAC;YACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC/B,IAAI,IAAI,mBAAmB,CAAC,WAAW,CAAC,CAAC,CAAE,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAE,CAAC,KAAK,CAAC,CAAC;YAC9E,CAAC;YACD,IAAI,IAAI,GAAG,QAAQ,EAAE,CAAC;gBACpB,QAAQ,GAAG,IAAI,CAAC;gBAChB,UAAU,GAAG,CAAC,CAAC;YACjB,CAAC;QACH,CAAC;QACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/B,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAE,CAAC;YACnB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,GAAG,KAAK,CAAE,CAAC;YAChD,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,GAAG,MAAM,CAAC;YAC5C,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,GAAG,MAAM,CAAC;QAC9C,CAAC;QAED,KAAK,GAAG,IAAI;aACT,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAE,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC,EAAE,CAAC,CAAC;aACrD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;aAC9C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrB,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,KAAkB,EAClB,QAAwC,EACxC,OAA0B,EAC1B,MAAkB,EAClB,CAAe,EACf,CAAe,EACf,YAAoB,EACpB,IAAY;IAEZ,MAAM,kBAAkB,GAAG,CAAC,CAAS,EAAW,EAAE;QAChD,IAAI,KAAK,GAAG,KAAK,CAAC;QAClB,gBAAgB,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE;YACzC,IAAI,MAAM,CAAC,CAAC,CAAC;gBAAE,KAAK,GAAG,IAAI,CAAC;QAC9B,CAAC,CAAC,CAAC;QACH,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IACF,IAAI,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC;IAEvE,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC7B,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,CAChE,CAAC;QACF,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;QACjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;YACpB,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAE,CAAC;YACrB,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACX,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACX,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAChB,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,gBAAgB,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE;gBACzC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;oBAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAChC,CAAC,CAAC,CAAC;QACL,CAAC;QACD,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5C,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAChB,KAAkB,EAClB,QAAwC,EACxC,CAAS,EACT,MAAkB,EAClB,CAAe,EACf,CAAe,EACf,YAAoB,EACpB,IAAY;IAEZ,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,EAAE,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,CAAC,IAAuB,EAA4B,EAAE;QACrE,IAAI,EAAE,GAAG,CAAC,CAAC;QACX,IAAI,EAAE,GAAG,CAAC,CAAC;QACX,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;YACrB,EAAE,IAAI,CAAC,CAAC,CAAC,CAAE,CAAC;YACZ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAE,CAAC;QACd,CAAC;QACD,OAAO,EAAE,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;IACtD,CAAC,CAAC;IAEF,MAAM,cAAc,GAAG,QAAQ,CAAC,CAAC,CAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7D,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,QAAQ,CAAC,cAAc,CAAC,CAAC;QACtC,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;QACpC,OAAO,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,YAAY,GAAG,GAAG,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,YAAY,GAAG,GAAG,EAAE,CAAC;IAC5F,CAAC;IAED,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAE,EAAE,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/D,MAAM,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAE,CAAC;QAC9B,IAAI,MAAM,CAAC,CAAC,CAAC;YAAE,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpC,CAAC;IACD,IAAI,UAAU,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;QAClC,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;QACpC,OAAO,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,YAAY,GAAG,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,YAAY,GAAG,IAAI,EAAE,CAAC;IAC9F,CAAC;IAED,mFAAmF;IACnF,yDAAyD;IACzD,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,CAAE,CAAC;IACzB,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAE,EAAE,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/D,MAAM,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAE,CAAC;QAC9B,IAAI,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC3C,CAAC;IACD,IAAI,EAAE,GAAG,CAAC,CAAC;IACX,IAAI,EAAE,GAAG,CAAC,CAAC;IACX,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC3B,EAAE,GAAG,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,CAAC,CAAC,CAAC;QACjB,EAAE,GAAG,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;IACzC,IAAI,GAAG,GAAG,IAAI,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;QACpC,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC;QACX,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC;IACb,CAAC;SAAM,CAAC;QACN,EAAE,IAAI,GAAG,CAAC;QACV,EAAE,IAAI,GAAG,CAAC;IACZ,CAAC;IACD,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC;IACtC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC;IACrC,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,EAAE,GAAG,GAAG,CAAC;IAC/B,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,EAAE,GAAG,GAAG,CAAC;IAC/B,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAE,GAAG,EAAE,GAAG,YAAY,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAE,GAAG,EAAE,GAAG,YAAY,EAAE,CAAC;AACxE,CAAC"}
|
package/dist/json.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAEV,UAAU,EACV,YAAY,EAGb,MAAM,YAAY,CAAC;AAiBpB,wBAAgB,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,UAAU,CAevD;AAiCD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,YAAY,CAuDvD"}
|
package/dist/json.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic JSON transport for layouts (no timing information).
|
|
3
|
+
*/
|
|
4
|
+
import { KgdiffError } from '@kgdiff/core';
|
|
5
|
+
import { round2 } from './math.js';
|
|
6
|
+
const byKey = ([a], [b]) => a < b ? -1 : a > b ? 1 : 0;
|
|
7
|
+
function sortedEntries(entries) {
|
|
8
|
+
return [...entries].sort(byKey);
|
|
9
|
+
}
|
|
10
|
+
const rect = (r) => ({
|
|
11
|
+
x: round2(r.x),
|
|
12
|
+
y: round2(r.y),
|
|
13
|
+
width: round2(r.width),
|
|
14
|
+
height: round2(r.height),
|
|
15
|
+
});
|
|
16
|
+
export function toJSON(layout) {
|
|
17
|
+
return {
|
|
18
|
+
format: 'kgdiff.layout',
|
|
19
|
+
version: 1,
|
|
20
|
+
strategy: layout.stats.strategy,
|
|
21
|
+
scope: layout.scope,
|
|
22
|
+
iterations: layout.stats.iterations,
|
|
23
|
+
positions: Object.fromEntries(sortedEntries(layout.positions).map(([id, p]) => [id, [round2(p.x), round2(p.y)]])),
|
|
24
|
+
radii: Object.fromEntries(sortedEntries(layout.radii).map(([id, r]) => [id, round2(r)])),
|
|
25
|
+
hidden: Object.fromEntries(sortedEntries(layout.hidden)),
|
|
26
|
+
bounds: rect(layout.bounds),
|
|
27
|
+
components: layout.components.map((c) => ({ nodes: [...c.nodes], bounds: rect(c.bounds) })),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function fail(message, details) {
|
|
31
|
+
throw new KgdiffError('invalid-input', `layoutFromJSON: ${message}`, details);
|
|
32
|
+
}
|
|
33
|
+
const isObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
34
|
+
const finite = (v) => typeof v === 'number' && Number.isFinite(v);
|
|
35
|
+
function readRect(value, where) {
|
|
36
|
+
if (!isObject(value) || ![value.x, value.y, value.width, value.height].every(finite)) {
|
|
37
|
+
fail(`${where} must be a rectangle`, { where });
|
|
38
|
+
}
|
|
39
|
+
return Object.freeze({
|
|
40
|
+
x: value.x,
|
|
41
|
+
y: value.y,
|
|
42
|
+
width: value.width,
|
|
43
|
+
height: value.height,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function readNumbers(value, where) {
|
|
47
|
+
if (!isObject(value))
|
|
48
|
+
fail(`${where} must be an object`, { where });
|
|
49
|
+
const out = new Map();
|
|
50
|
+
for (const [id, n] of sortedEntries(Object.entries(value))) {
|
|
51
|
+
if (!finite(n))
|
|
52
|
+
fail(`${where}["${id}"] must be a finite number`, { where, id });
|
|
53
|
+
out.set(id, n);
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
export function fromJSON(json) {
|
|
58
|
+
const raw = json;
|
|
59
|
+
if (!isObject(raw) || raw.format !== 'kgdiff.layout')
|
|
60
|
+
fail('not a kgdiff.layout document');
|
|
61
|
+
if (raw.version !== 1) {
|
|
62
|
+
throw new KgdiffError('unsupported-version', `Unsupported kgdiff.layout version ${String(raw.version)}`, {
|
|
63
|
+
version: raw.version,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
const strategy = raw.strategy;
|
|
67
|
+
if (strategy !== 'union' && strategy !== 'anchored')
|
|
68
|
+
fail('strategy must be union or anchored');
|
|
69
|
+
const scope = raw.scope;
|
|
70
|
+
if (scope !== 'all' && scope !== 'changes')
|
|
71
|
+
fail('scope must be all or changes');
|
|
72
|
+
const iterations = raw.iterations;
|
|
73
|
+
if (!finite(iterations) || iterations < 0)
|
|
74
|
+
fail('iterations must be a number ≥ 0');
|
|
75
|
+
if (!isObject(raw.positions))
|
|
76
|
+
fail('positions must be an object');
|
|
77
|
+
const positions = new Map();
|
|
78
|
+
for (const [id, p] of sortedEntries(Object.entries(raw.positions))) {
|
|
79
|
+
if (!Array.isArray(p) || p.length !== 2 || !finite(p[0]) || !finite(p[1])) {
|
|
80
|
+
fail(`positions["${id}"] must be [x, y]`, { id });
|
|
81
|
+
}
|
|
82
|
+
positions.set(id, Object.freeze({ x: p[0], y: p[1] }));
|
|
83
|
+
}
|
|
84
|
+
const radii = readNumbers(raw.radii, 'radii');
|
|
85
|
+
const hidden = readNumbers(raw.hidden, 'hidden');
|
|
86
|
+
if (!Array.isArray(raw.components))
|
|
87
|
+
fail('components must be an array');
|
|
88
|
+
const components = raw.components.map((c, k) => {
|
|
89
|
+
if (!isObject(c) || !Array.isArray(c.nodes) || !c.nodes.every((id) => typeof id === 'string')) {
|
|
90
|
+
fail(`components[${k}] must have a nodes array`, { index: k });
|
|
91
|
+
}
|
|
92
|
+
return Object.freeze({
|
|
93
|
+
nodes: Object.freeze([...c.nodes]),
|
|
94
|
+
bounds: readRect(c.bounds, `components[${k}].bounds`),
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
return Object.freeze({
|
|
98
|
+
positions,
|
|
99
|
+
radii,
|
|
100
|
+
bounds: readRect(raw.bounds, 'bounds'),
|
|
101
|
+
scope: scope,
|
|
102
|
+
hidden,
|
|
103
|
+
components: Object.freeze(components),
|
|
104
|
+
stats: Object.freeze({
|
|
105
|
+
strategy: strategy,
|
|
106
|
+
iterations,
|
|
107
|
+
nodes: positions.size,
|
|
108
|
+
ms: 0,
|
|
109
|
+
}),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=json.js.map
|
package/dist/json.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.js","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"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"}
|
package/dist/layout.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `layoutDiff`: scope → layout graph → strategy → simulation → packing → result.
|
|
3
|
+
*/
|
|
4
|
+
import type { DiffResult } from '@kgdiff/core';
|
|
5
|
+
import type { LayoutOptions, LayoutResult } from './index.js';
|
|
6
|
+
export declare function computeLayout(diff: DiffResult, options: LayoutOptions | undefined, nodeRadius: (degree: number) => number): LayoutResult;
|
|
7
|
+
//# sourceMappingURL=layout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../src/layout.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,UAAU,EAAkB,MAAM,cAAc,CAAC;AAG/D,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAmC9D,wBAAgB,aAAa,CAC3B,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,aAAa,GAAG,SAAS,EAClC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GACrC,YAAY,CAyCd"}
|
package/dist/layout.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { placeAnchored, placeFree } from './engine.js';
|
|
2
|
+
import { buildLayoutGraph } from './graph.js';
|
|
3
|
+
import { round2 } from './math.js';
|
|
4
|
+
import { resolveLayoutOptions } from './options.js';
|
|
5
|
+
import { assemble, now } from './result.js';
|
|
6
|
+
import { selectScope } from './scope.js';
|
|
7
|
+
import { buildUnionGraph } from './union.js';
|
|
8
|
+
const finitePoint = (p) => p !== undefined && Number.isFinite(p.x) && Number.isFinite(p.y);
|
|
9
|
+
/**
|
|
10
|
+
* Known positions, by priority: explicit pins, then the before version's `position` (the mental
|
|
11
|
+
* map the reviewer already has), then the after version's.
|
|
12
|
+
*/
|
|
13
|
+
function knownPositions(graph, options) {
|
|
14
|
+
const fixed = new Uint8Array(graph.n);
|
|
15
|
+
const x = new Float64Array(graph.n);
|
|
16
|
+
const y = new Float64Array(graph.n);
|
|
17
|
+
let count = 0;
|
|
18
|
+
for (let i = 0; i < graph.n; i++) {
|
|
19
|
+
const node = graph.nodes[i];
|
|
20
|
+
const candidates = [options.pinned.get(node.id), node.before?.position, node.after?.position];
|
|
21
|
+
const p = candidates.find(finitePoint);
|
|
22
|
+
if (p === undefined)
|
|
23
|
+
continue;
|
|
24
|
+
fixed[i] = 1;
|
|
25
|
+
x[i] = p.x;
|
|
26
|
+
y[i] = p.y;
|
|
27
|
+
count++;
|
|
28
|
+
}
|
|
29
|
+
return { fixed, x, y, count };
|
|
30
|
+
}
|
|
31
|
+
export function computeLayout(diff, options, nodeRadius) {
|
|
32
|
+
const started = now();
|
|
33
|
+
const opts = resolveLayoutOptions(options);
|
|
34
|
+
const union = buildUnionGraph(diff);
|
|
35
|
+
const scope = opts.scope ?? (union.nodes.length > opts.largeGraphThreshold ? 'changes' : 'all');
|
|
36
|
+
const selection = selectScope(union, scope, opts.contextHops, opts.hubDegree);
|
|
37
|
+
const graph = buildLayoutGraph(union, selection.visible, { fontSize: opts.fontSize, nodeRadius });
|
|
38
|
+
const known = knownPositions(graph, opts);
|
|
39
|
+
const strategy = opts.strategy ?? (known.count > 0 ? 'anchored' : 'union');
|
|
40
|
+
const anchored = strategy === 'anchored' && known.count > 0;
|
|
41
|
+
const ctx = {
|
|
42
|
+
graph,
|
|
43
|
+
options: opts,
|
|
44
|
+
x: anchored ? known.x : new Float64Array(graph.n),
|
|
45
|
+
y: anchored ? known.y : new Float64Array(graph.n),
|
|
46
|
+
};
|
|
47
|
+
const iterations = anchored ? placeAnchored(ctx, known.fixed) : placeFree(ctx);
|
|
48
|
+
const positions = new Map();
|
|
49
|
+
const radii = new Map();
|
|
50
|
+
for (let i = 0; i < graph.n; i++) {
|
|
51
|
+
const id = graph.nodes[i].id;
|
|
52
|
+
// Known positions are preserved exactly; computed ones are rounded to 0.01.
|
|
53
|
+
const exact = anchored && known.fixed[i] === 1;
|
|
54
|
+
const x = exact ? ctx.x[i] : round2(ctx.x[i]);
|
|
55
|
+
const y = exact ? ctx.y[i] : round2(ctx.y[i]);
|
|
56
|
+
positions.set(id, Object.freeze({ x, y }));
|
|
57
|
+
radii.set(id, graph.radius[i]);
|
|
58
|
+
}
|
|
59
|
+
return assemble({
|
|
60
|
+
graph,
|
|
61
|
+
positions,
|
|
62
|
+
radii,
|
|
63
|
+
hidden: selection.hidden,
|
|
64
|
+
scope,
|
|
65
|
+
strategy,
|
|
66
|
+
iterations,
|
|
67
|
+
started,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=layout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout.js","sourceRoot":"","sources":["../src/layout.ts"],"names":[],"mappings":"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"}
|
package/dist/math.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic arithmetic.
|
|
3
|
+
*
|
|
4
|
+
* ECMAScript only guarantees exact (correctly rounded) results for + − × ÷ and, in every engine
|
|
5
|
+
* in practice, `Math.sqrt`. `Math.log2`, `Math.pow`, `Math.sin`… are implementation-approximated
|
|
6
|
+
* and can differ in the last bit between V8, SpiderMonkey and JavaScriptCore. A force simulation
|
|
7
|
+
* amplifies such differences, so everything that feeds it goes through these helpers to keep
|
|
8
|
+
* layouts byte-identical between the CLI (Node) and the browser.
|
|
9
|
+
*/
|
|
10
|
+
export declare const clamp: (value: number, min: number, max: number) => number;
|
|
11
|
+
/** Round to 0.01 (the layout's output precision); never returns `-0`. */
|
|
12
|
+
export declare function round2(value: number): number;
|
|
13
|
+
/** log₂(x) for x > 0, using only exactly rounded operations. Accurate to ~1e-15. */
|
|
14
|
+
export declare function log2(x: number): number;
|
|
15
|
+
/** bⁿ for integer n ≥ 0 by repeated squaring (exact operation sequence). */
|
|
16
|
+
export declare function powInt(base: number, exponent: number): number;
|
|
17
|
+
/**
|
|
18
|
+
* The per-tick factor `r` with `r^ticks = ratio` (ratio ∈ (0, 1]), found by bisection over
|
|
19
|
+
* `powInt` so it is reproducible everywhere. Used for the alpha cooling schedule.
|
|
20
|
+
*/
|
|
21
|
+
export declare function coolingFactor(ratio: number, ticks: number): number;
|
|
22
|
+
/**
|
|
23
|
+
* Monotonic stand-in for the polar angle of (x, y), in [0, 4). Sorting by it equals sorting by
|
|
24
|
+
* `atan2` without any transcendental function.
|
|
25
|
+
*/
|
|
26
|
+
export declare function pseudoAngle(x: number, y: number): number;
|
|
27
|
+
/** Shortest distance between two pseudo-angles on the [0, 4) circle. */
|
|
28
|
+
export declare function pseudoAngleDistance(a: number, b: number): number;
|
|
29
|
+
//# sourceMappingURL=math.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"math.d.ts","sourceRoot":"","sources":["../src/math.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,KAAK,UAAW,MAAM,OAAO,MAAM,OAAO,MAAM,KAAG,MAC3B,CAAC;AAEtC,yEAAyE;AACzE,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAG5C;AAED,oFAAoF;AACpF,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAwBtC;AAED,4EAA4E;AAC5E,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAU7D;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAUlE;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAKxD;AAED,wEAAwE;AACxE,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAGhE"}
|
package/dist/math.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic arithmetic.
|
|
3
|
+
*
|
|
4
|
+
* ECMAScript only guarantees exact (correctly rounded) results for + − × ÷ and, in every engine
|
|
5
|
+
* in practice, `Math.sqrt`. `Math.log2`, `Math.pow`, `Math.sin`… are implementation-approximated
|
|
6
|
+
* and can differ in the last bit between V8, SpiderMonkey and JavaScriptCore. A force simulation
|
|
7
|
+
* amplifies such differences, so everything that feeds it goes through these helpers to keep
|
|
8
|
+
* layouts byte-identical between the CLI (Node) and the browser.
|
|
9
|
+
*/
|
|
10
|
+
export const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
11
|
+
/** Round to 0.01 (the layout's output precision); never returns `-0`. */
|
|
12
|
+
export function round2(value) {
|
|
13
|
+
const rounded = Math.round(value * 100) / 100;
|
|
14
|
+
return rounded === 0 ? 0 : rounded;
|
|
15
|
+
}
|
|
16
|
+
/** log₂(x) for x > 0, using only exactly rounded operations. Accurate to ~1e-15. */
|
|
17
|
+
export function log2(x) {
|
|
18
|
+
if (!(x > 0))
|
|
19
|
+
return Number.NEGATIVE_INFINITY;
|
|
20
|
+
if (x === Number.POSITIVE_INFINITY)
|
|
21
|
+
return x;
|
|
22
|
+
// x = m·2^e with m ∈ [1, 2); scaling by 2 is exact.
|
|
23
|
+
let m = x;
|
|
24
|
+
let e = 0;
|
|
25
|
+
while (m >= 2) {
|
|
26
|
+
m /= 2;
|
|
27
|
+
e++;
|
|
28
|
+
}
|
|
29
|
+
while (m < 1) {
|
|
30
|
+
m *= 2;
|
|
31
|
+
e--;
|
|
32
|
+
}
|
|
33
|
+
// ln(m) = 2·atanh(s), s = (m − 1)/(m + 1) ∈ [0, 1/3): the series converges by 1/9 per term.
|
|
34
|
+
const s = (m - 1) / (m + 1);
|
|
35
|
+
const s2 = s * s;
|
|
36
|
+
let term = s;
|
|
37
|
+
let sum = 0;
|
|
38
|
+
for (let k = 1; k < 40; k += 2) {
|
|
39
|
+
sum += term / k;
|
|
40
|
+
term *= s2;
|
|
41
|
+
}
|
|
42
|
+
return e + (2 * sum) / Math.LN2;
|
|
43
|
+
}
|
|
44
|
+
/** bⁿ for integer n ≥ 0 by repeated squaring (exact operation sequence). */
|
|
45
|
+
export function powInt(base, exponent) {
|
|
46
|
+
let result = 1;
|
|
47
|
+
let b = base;
|
|
48
|
+
let n = exponent;
|
|
49
|
+
while (n > 0) {
|
|
50
|
+
if (n & 1)
|
|
51
|
+
result *= b;
|
|
52
|
+
b *= b;
|
|
53
|
+
n >>>= 1;
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The per-tick factor `r` with `r^ticks = ratio` (ratio ∈ (0, 1]), found by bisection over
|
|
59
|
+
* `powInt` so it is reproducible everywhere. Used for the alpha cooling schedule.
|
|
60
|
+
*/
|
|
61
|
+
export function coolingFactor(ratio, ticks) {
|
|
62
|
+
if (ticks <= 0 || ratio >= 1)
|
|
63
|
+
return 1;
|
|
64
|
+
let lo = 0;
|
|
65
|
+
let hi = 1;
|
|
66
|
+
for (let i = 0; i < 64; i++) {
|
|
67
|
+
const mid = (lo + hi) / 2;
|
|
68
|
+
if (powInt(mid, ticks) < ratio)
|
|
69
|
+
lo = mid;
|
|
70
|
+
else
|
|
71
|
+
hi = mid;
|
|
72
|
+
}
|
|
73
|
+
return (lo + hi) / 2;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Monotonic stand-in for the polar angle of (x, y), in [0, 4). Sorting by it equals sorting by
|
|
77
|
+
* `atan2` without any transcendental function.
|
|
78
|
+
*/
|
|
79
|
+
export function pseudoAngle(x, y) {
|
|
80
|
+
const d = Math.abs(x) + Math.abs(y);
|
|
81
|
+
if (d === 0)
|
|
82
|
+
return 0;
|
|
83
|
+
const p = x / d;
|
|
84
|
+
return y >= 0 ? 1 - p : 3 + p;
|
|
85
|
+
}
|
|
86
|
+
/** Shortest distance between two pseudo-angles on the [0, 4) circle. */
|
|
87
|
+
export function pseudoAngleDistance(a, b) {
|
|
88
|
+
const d = Math.abs(a - b);
|
|
89
|
+
return d > 2 ? 4 - d : d;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=math.js.map
|
package/dist/math.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"math.js","sourceRoot":"","sources":["../src/math.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Option defaults and validation. Every default is documented on `LayoutOptions`.
|
|
3
|
+
*/
|
|
4
|
+
import { type Point, type UnionId } from '@kgdiff/core';
|
|
5
|
+
import type { LayoutOptions, LayoutScope, LayoutStrategy } from './index.js';
|
|
6
|
+
export interface ResolvedLayoutOptions {
|
|
7
|
+
/** `undefined` = choose from the data (anchored when any position is known). */
|
|
8
|
+
readonly strategy: LayoutStrategy | undefined;
|
|
9
|
+
/** `undefined` = choose from the graph size. */
|
|
10
|
+
readonly scope: LayoutScope | undefined;
|
|
11
|
+
readonly contextHops: number;
|
|
12
|
+
readonly hubDegree: number;
|
|
13
|
+
readonly largeGraphThreshold: number;
|
|
14
|
+
/** 32-bit PRNG state derived from `seed`. */
|
|
15
|
+
readonly seed: number;
|
|
16
|
+
/** `undefined` = choose from the graph size. */
|
|
17
|
+
readonly iterations: number | undefined;
|
|
18
|
+
readonly linkDistance: number;
|
|
19
|
+
readonly labelAware: boolean;
|
|
20
|
+
readonly fontSize: number;
|
|
21
|
+
readonly pinned: ReadonlyMap<UnionId, Point>;
|
|
22
|
+
readonly componentGap: number;
|
|
23
|
+
}
|
|
24
|
+
export declare const DEFAULTS: {
|
|
25
|
+
readonly contextHops: 1;
|
|
26
|
+
readonly hubDegree: 40;
|
|
27
|
+
readonly largeGraphThreshold: 3000;
|
|
28
|
+
readonly seed: 1;
|
|
29
|
+
readonly linkDistance: 56;
|
|
30
|
+
readonly labelAware: true;
|
|
31
|
+
readonly fontSize: 12;
|
|
32
|
+
readonly componentGap: 96;
|
|
33
|
+
};
|
|
34
|
+
export declare function resolveLayoutOptions(options?: LayoutOptions): ResolvedLayoutOptions;
|
|
35
|
+
/**
|
|
36
|
+
* Default tick count of a simulation over `nodeCount` nodes: `clamp(300 − 25·log₂(N), 120, 300)`
|
|
37
|
+
* (docs/algorithms.md §12) up to 2000 nodes. Beyond, the per-tick cost (Barnes–Hut charge and
|
|
38
|
+
* collision, ~N log N) would blow the time budget, so the work is held at that of a 2000-node
|
|
39
|
+
* run: `max(30, 120 · 2000 / N)` ticks. Large components start from a BFS spiral and get their
|
|
40
|
+
* label room in one step (see simulate.ts), so they need fewer ticks than the formula implies.
|
|
41
|
+
*/
|
|
42
|
+
export declare function defaultIterations(nodeCount: number): number;
|
|
43
|
+
//# sourceMappingURL=options.d.ts.map
|