xcode-graph 0.2.0 → 1.34.5-1

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.
@@ -1,1420 +1 @@
1
- /**
2
- * Cluster Types - Cluster layout and positioning types
3
- *
4
- * Pure TypeScript enums, interfaces, and constants for cluster grouping,
5
- * node roles, and layout configuration. No Zod dependency.
6
- *
7
- * @module schemas/cluster
8
- */
9
- /**
10
- * Node role enum - determines positioning strategy within a cluster
11
- *
12
- * @public
13
- */
14
- var NodeRole;
15
- (function (NodeRole) {
16
- NodeRole["Entry"] = "entry";
17
- NodeRole["InternalFramework"] = "internal-framework";
18
- NodeRole["InternalLib"] = "internal-lib";
19
- NodeRole["Utility"] = "utility";
20
- NodeRole["Test"] = "test";
21
- NodeRole["Tool"] = "tool";
22
- })(NodeRole || (NodeRole = {}));
23
- /**
24
- * Cluster type enum - distinguishes local projects from packages
25
- *
26
- * @public
27
- */
28
- var ClusterType;
29
- (function (ClusterType) {
30
- ClusterType["Project"] = "project";
31
- ClusterType["Package"] = "package";
32
- })(ClusterType || (ClusterType = {}));
33
- /**
34
- * ELK Hierarchy Handling strategy
35
- *
36
- * @public
37
- */
38
- var ElkHierarchyHandling;
39
- (function (ElkHierarchyHandling) {
40
- ElkHierarchyHandling["Inherit"] = "INHERIT";
41
- ElkHierarchyHandling["IncludeChildren"] = "INCLUDE_CHILDREN";
42
- ElkHierarchyHandling["SeparateChildren"] = "SEPARATE_CHILDREN";
43
- })(ElkHierarchyHandling || (ElkHierarchyHandling = {}));
44
- /**
45
- * All node role values for iteration
46
- *
47
- * @public
48
- */
49
- Object.values(NodeRole);
50
- /**
51
- * All cluster type values for iteration
52
- *
53
- * @public
54
- */
55
- Object.values(ClusterType);
56
-
57
- function forceCenter(x, y) {
58
- var nodes, strength = 1;
59
-
60
- if (x == null) x = 0;
61
- if (y == null) y = 0;
62
-
63
- function force() {
64
- var i,
65
- n = nodes.length,
66
- node,
67
- sx = 0,
68
- sy = 0;
69
-
70
- for (i = 0; i < n; ++i) {
71
- node = nodes[i], sx += node.x, sy += node.y;
72
- }
73
-
74
- for (sx = (sx / n - x) * strength, sy = (sy / n - y) * strength, i = 0; i < n; ++i) {
75
- node = nodes[i], node.x -= sx, node.y -= sy;
76
- }
77
- }
78
-
79
- force.initialize = function(_) {
80
- nodes = _;
81
- };
82
-
83
- force.x = function(_) {
84
- return arguments.length ? (x = +_, force) : x;
85
- };
86
-
87
- force.y = function(_) {
88
- return arguments.length ? (y = +_, force) : y;
89
- };
90
-
91
- force.strength = function(_) {
92
- return arguments.length ? (strength = +_, force) : strength;
93
- };
94
-
95
- return force;
96
- }
97
-
98
- function tree_add(d) {
99
- const x = +this._x.call(null, d),
100
- y = +this._y.call(null, d);
101
- return add(this.cover(x, y), x, y, d);
102
- }
103
-
104
- function add(tree, x, y, d) {
105
- if (isNaN(x) || isNaN(y)) return tree; // ignore invalid points
106
-
107
- var parent,
108
- node = tree._root,
109
- leaf = {data: d},
110
- x0 = tree._x0,
111
- y0 = tree._y0,
112
- x1 = tree._x1,
113
- y1 = tree._y1,
114
- xm,
115
- ym,
116
- xp,
117
- yp,
118
- right,
119
- bottom,
120
- i,
121
- j;
122
-
123
- // If the tree is empty, initialize the root as a leaf.
124
- if (!node) return tree._root = leaf, tree;
125
-
126
- // Find the existing leaf for the new point, or add it.
127
- while (node.length) {
128
- if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;
129
- if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;
130
- if (parent = node, !(node = node[i = bottom << 1 | right])) return parent[i] = leaf, tree;
131
- }
132
-
133
- // Is the new point is exactly coincident with the existing point?
134
- xp = +tree._x.call(null, node.data);
135
- yp = +tree._y.call(null, node.data);
136
- if (x === xp && y === yp) return leaf.next = node, parent ? parent[i] = leaf : tree._root = leaf, tree;
137
-
138
- // Otherwise, split the leaf node until the old and new point are separated.
139
- do {
140
- parent = parent ? parent[i] = new Array(4) : tree._root = new Array(4);
141
- if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;
142
- if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;
143
- } while ((i = bottom << 1 | right) === (j = (yp >= ym) << 1 | (xp >= xm)));
144
- return parent[j] = node, parent[i] = leaf, tree;
145
- }
146
-
147
- function addAll(data) {
148
- var d, i, n = data.length,
149
- x,
150
- y,
151
- xz = new Array(n),
152
- yz = new Array(n),
153
- x0 = Infinity,
154
- y0 = Infinity,
155
- x1 = -Infinity,
156
- y1 = -Infinity;
157
-
158
- // Compute the points and their extent.
159
- for (i = 0; i < n; ++i) {
160
- if (isNaN(x = +this._x.call(null, d = data[i])) || isNaN(y = +this._y.call(null, d))) continue;
161
- xz[i] = x;
162
- yz[i] = y;
163
- if (x < x0) x0 = x;
164
- if (x > x1) x1 = x;
165
- if (y < y0) y0 = y;
166
- if (y > y1) y1 = y;
167
- }
168
-
169
- // If there were no (valid) points, abort.
170
- if (x0 > x1 || y0 > y1) return this;
171
-
172
- // Expand the tree to cover the new points.
173
- this.cover(x0, y0).cover(x1, y1);
174
-
175
- // Add the new points.
176
- for (i = 0; i < n; ++i) {
177
- add(this, xz[i], yz[i], data[i]);
178
- }
179
-
180
- return this;
181
- }
182
-
183
- function tree_cover(x, y) {
184
- if (isNaN(x = +x) || isNaN(y = +y)) return this; // ignore invalid points
185
-
186
- var x0 = this._x0,
187
- y0 = this._y0,
188
- x1 = this._x1,
189
- y1 = this._y1;
190
-
191
- // If the quadtree has no extent, initialize them.
192
- // Integer extent are necessary so that if we later double the extent,
193
- // the existing quadrant boundaries don’t change due to floating point error!
194
- if (isNaN(x0)) {
195
- x1 = (x0 = Math.floor(x)) + 1;
196
- y1 = (y0 = Math.floor(y)) + 1;
197
- }
198
-
199
- // Otherwise, double repeatedly to cover.
200
- else {
201
- var z = x1 - x0 || 1,
202
- node = this._root,
203
- parent,
204
- i;
205
-
206
- while (x0 > x || x >= x1 || y0 > y || y >= y1) {
207
- i = (y < y0) << 1 | (x < x0);
208
- parent = new Array(4), parent[i] = node, node = parent, z *= 2;
209
- switch (i) {
210
- case 0: x1 = x0 + z, y1 = y0 + z; break;
211
- case 1: x0 = x1 - z, y1 = y0 + z; break;
212
- case 2: x1 = x0 + z, y0 = y1 - z; break;
213
- case 3: x0 = x1 - z, y0 = y1 - z; break;
214
- }
215
- }
216
-
217
- if (this._root && this._root.length) this._root = node;
218
- }
219
-
220
- this._x0 = x0;
221
- this._y0 = y0;
222
- this._x1 = x1;
223
- this._y1 = y1;
224
- return this;
225
- }
226
-
227
- function tree_data() {
228
- var data = [];
229
- this.visit(function(node) {
230
- if (!node.length) do data.push(node.data); while (node = node.next)
231
- });
232
- return data;
233
- }
234
-
235
- function tree_extent(_) {
236
- return arguments.length
237
- ? this.cover(+_[0][0], +_[0][1]).cover(+_[1][0], +_[1][1])
238
- : isNaN(this._x0) ? undefined : [[this._x0, this._y0], [this._x1, this._y1]];
239
- }
240
-
241
- function Quad(node, x0, y0, x1, y1) {
242
- this.node = node;
243
- this.x0 = x0;
244
- this.y0 = y0;
245
- this.x1 = x1;
246
- this.y1 = y1;
247
- }
248
-
249
- function tree_find(x, y, radius) {
250
- var data,
251
- x0 = this._x0,
252
- y0 = this._y0,
253
- x1,
254
- y1,
255
- x2,
256
- y2,
257
- x3 = this._x1,
258
- y3 = this._y1,
259
- quads = [],
260
- node = this._root,
261
- q,
262
- i;
263
-
264
- if (node) quads.push(new Quad(node, x0, y0, x3, y3));
265
- if (radius == null) radius = Infinity;
266
- else {
267
- x0 = x - radius, y0 = y - radius;
268
- x3 = x + radius, y3 = y + radius;
269
- radius *= radius;
270
- }
271
-
272
- while (q = quads.pop()) {
273
-
274
- // Stop searching if this quadrant can’t contain a closer node.
275
- if (!(node = q.node)
276
- || (x1 = q.x0) > x3
277
- || (y1 = q.y0) > y3
278
- || (x2 = q.x1) < x0
279
- || (y2 = q.y1) < y0) continue;
280
-
281
- // Bisect the current quadrant.
282
- if (node.length) {
283
- var xm = (x1 + x2) / 2,
284
- ym = (y1 + y2) / 2;
285
-
286
- quads.push(
287
- new Quad(node[3], xm, ym, x2, y2),
288
- new Quad(node[2], x1, ym, xm, y2),
289
- new Quad(node[1], xm, y1, x2, ym),
290
- new Quad(node[0], x1, y1, xm, ym)
291
- );
292
-
293
- // Visit the closest quadrant first.
294
- if (i = (y >= ym) << 1 | (x >= xm)) {
295
- q = quads[quads.length - 1];
296
- quads[quads.length - 1] = quads[quads.length - 1 - i];
297
- quads[quads.length - 1 - i] = q;
298
- }
299
- }
300
-
301
- // Visit this point. (Visiting coincident points isn’t necessary!)
302
- else {
303
- var dx = x - +this._x.call(null, node.data),
304
- dy = y - +this._y.call(null, node.data),
305
- d2 = dx * dx + dy * dy;
306
- if (d2 < radius) {
307
- var d = Math.sqrt(radius = d2);
308
- x0 = x - d, y0 = y - d;
309
- x3 = x + d, y3 = y + d;
310
- data = node.data;
311
- }
312
- }
313
- }
314
-
315
- return data;
316
- }
317
-
318
- function tree_remove(d) {
319
- if (isNaN(x = +this._x.call(null, d)) || isNaN(y = +this._y.call(null, d))) return this; // ignore invalid points
320
-
321
- var parent,
322
- node = this._root,
323
- retainer,
324
- previous,
325
- next,
326
- x0 = this._x0,
327
- y0 = this._y0,
328
- x1 = this._x1,
329
- y1 = this._y1,
330
- x,
331
- y,
332
- xm,
333
- ym,
334
- right,
335
- bottom,
336
- i,
337
- j;
338
-
339
- // If the tree is empty, initialize the root as a leaf.
340
- if (!node) return this;
341
-
342
- // Find the leaf node for the point.
343
- // While descending, also retain the deepest parent with a non-removed sibling.
344
- if (node.length) while (true) {
345
- if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;
346
- if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;
347
- if (!(parent = node, node = node[i = bottom << 1 | right])) return this;
348
- if (!node.length) break;
349
- if (parent[(i + 1) & 3] || parent[(i + 2) & 3] || parent[(i + 3) & 3]) retainer = parent, j = i;
350
- }
351
-
352
- // Find the point to remove.
353
- while (node.data !== d) if (!(previous = node, node = node.next)) return this;
354
- if (next = node.next) delete node.next;
355
-
356
- // If there are multiple coincident points, remove just the point.
357
- if (previous) return (next ? previous.next = next : delete previous.next), this;
358
-
359
- // If this is the root point, remove it.
360
- if (!parent) return this._root = next, this;
361
-
362
- // Remove this leaf.
363
- next ? parent[i] = next : delete parent[i];
364
-
365
- // If the parent now contains exactly one leaf, collapse superfluous parents.
366
- if ((node = parent[0] || parent[1] || parent[2] || parent[3])
367
- && node === (parent[3] || parent[2] || parent[1] || parent[0])
368
- && !node.length) {
369
- if (retainer) retainer[j] = node;
370
- else this._root = node;
371
- }
372
-
373
- return this;
374
- }
375
-
376
- function removeAll(data) {
377
- for (var i = 0, n = data.length; i < n; ++i) this.remove(data[i]);
378
- return this;
379
- }
380
-
381
- function tree_root() {
382
- return this._root;
383
- }
384
-
385
- function tree_size() {
386
- var size = 0;
387
- this.visit(function(node) {
388
- if (!node.length) do ++size; while (node = node.next)
389
- });
390
- return size;
391
- }
392
-
393
- function tree_visit(callback) {
394
- var quads = [], q, node = this._root, child, x0, y0, x1, y1;
395
- if (node) quads.push(new Quad(node, this._x0, this._y0, this._x1, this._y1));
396
- while (q = quads.pop()) {
397
- if (!callback(node = q.node, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1) && node.length) {
398
- var xm = (x0 + x1) / 2, ym = (y0 + y1) / 2;
399
- if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1));
400
- if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1));
401
- if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym));
402
- if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym));
403
- }
404
- }
405
- return this;
406
- }
407
-
408
- function tree_visitAfter(callback) {
409
- var quads = [], next = [], q;
410
- if (this._root) quads.push(new Quad(this._root, this._x0, this._y0, this._x1, this._y1));
411
- while (q = quads.pop()) {
412
- var node = q.node;
413
- if (node.length) {
414
- var child, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1, xm = (x0 + x1) / 2, ym = (y0 + y1) / 2;
415
- if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym));
416
- if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym));
417
- if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1));
418
- if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1));
419
- }
420
- next.push(q);
421
- }
422
- while (q = next.pop()) {
423
- callback(q.node, q.x0, q.y0, q.x1, q.y1);
424
- }
425
- return this;
426
- }
427
-
428
- function defaultX(d) {
429
- return d[0];
430
- }
431
-
432
- function tree_x(_) {
433
- return arguments.length ? (this._x = _, this) : this._x;
434
- }
435
-
436
- function defaultY(d) {
437
- return d[1];
438
- }
439
-
440
- function tree_y(_) {
441
- return arguments.length ? (this._y = _, this) : this._y;
442
- }
443
-
444
- function quadtree(nodes, x, y) {
445
- var tree = new Quadtree(x == null ? defaultX : x, y == null ? defaultY : y, NaN, NaN, NaN, NaN);
446
- return nodes == null ? tree : tree.addAll(nodes);
447
- }
448
-
449
- function Quadtree(x, y, x0, y0, x1, y1) {
450
- this._x = x;
451
- this._y = y;
452
- this._x0 = x0;
453
- this._y0 = y0;
454
- this._x1 = x1;
455
- this._y1 = y1;
456
- this._root = undefined;
457
- }
458
-
459
- function leaf_copy(leaf) {
460
- var copy = {data: leaf.data}, next = copy;
461
- while (leaf = leaf.next) next = next.next = {data: leaf.data};
462
- return copy;
463
- }
464
-
465
- var treeProto = quadtree.prototype = Quadtree.prototype;
466
-
467
- treeProto.copy = function() {
468
- var copy = new Quadtree(this._x, this._y, this._x0, this._y0, this._x1, this._y1),
469
- node = this._root,
470
- nodes,
471
- child;
472
-
473
- if (!node) return copy;
474
-
475
- if (!node.length) return copy._root = leaf_copy(node), copy;
476
-
477
- nodes = [{source: node, target: copy._root = new Array(4)}];
478
- while (node = nodes.pop()) {
479
- for (var i = 0; i < 4; ++i) {
480
- if (child = node.source[i]) {
481
- if (child.length) nodes.push({source: child, target: node.target[i] = new Array(4)});
482
- else node.target[i] = leaf_copy(child);
483
- }
484
- }
485
- }
486
-
487
- return copy;
488
- };
489
-
490
- treeProto.add = tree_add;
491
- treeProto.addAll = addAll;
492
- treeProto.cover = tree_cover;
493
- treeProto.data = tree_data;
494
- treeProto.extent = tree_extent;
495
- treeProto.find = tree_find;
496
- treeProto.remove = tree_remove;
497
- treeProto.removeAll = removeAll;
498
- treeProto.root = tree_root;
499
- treeProto.size = tree_size;
500
- treeProto.visit = tree_visit;
501
- treeProto.visitAfter = tree_visitAfter;
502
- treeProto.x = tree_x;
503
- treeProto.y = tree_y;
504
-
505
- function constant(x) {
506
- return function() {
507
- return x;
508
- };
509
- }
510
-
511
- function jiggle(random) {
512
- return (random() - 0.5) * 1e-6;
513
- }
514
-
515
- function x$1(d) {
516
- return d.x + d.vx;
517
- }
518
-
519
- function y$1(d) {
520
- return d.y + d.vy;
521
- }
522
-
523
- function forceCollide(radius) {
524
- var nodes,
525
- radii,
526
- random,
527
- strength = 1,
528
- iterations = 1;
529
-
530
- if (typeof radius !== "function") radius = constant(radius == null ? 1 : +radius);
531
-
532
- function force() {
533
- var i, n = nodes.length,
534
- tree,
535
- node,
536
- xi,
537
- yi,
538
- ri,
539
- ri2;
540
-
541
- for (var k = 0; k < iterations; ++k) {
542
- tree = quadtree(nodes, x$1, y$1).visitAfter(prepare);
543
- for (i = 0; i < n; ++i) {
544
- node = nodes[i];
545
- ri = radii[node.index], ri2 = ri * ri;
546
- xi = node.x + node.vx;
547
- yi = node.y + node.vy;
548
- tree.visit(apply);
549
- }
550
- }
551
-
552
- function apply(quad, x0, y0, x1, y1) {
553
- var data = quad.data, rj = quad.r, r = ri + rj;
554
- if (data) {
555
- if (data.index > node.index) {
556
- var x = xi - data.x - data.vx,
557
- y = yi - data.y - data.vy,
558
- l = x * x + y * y;
559
- if (l < r * r) {
560
- if (x === 0) x = jiggle(random), l += x * x;
561
- if (y === 0) y = jiggle(random), l += y * y;
562
- l = (r - (l = Math.sqrt(l))) / l * strength;
563
- node.vx += (x *= l) * (r = (rj *= rj) / (ri2 + rj));
564
- node.vy += (y *= l) * r;
565
- data.vx -= x * (r = 1 - r);
566
- data.vy -= y * r;
567
- }
568
- }
569
- return;
570
- }
571
- return x0 > xi + r || x1 < xi - r || y0 > yi + r || y1 < yi - r;
572
- }
573
- }
574
-
575
- function prepare(quad) {
576
- if (quad.data) return quad.r = radii[quad.data.index];
577
- for (var i = quad.r = 0; i < 4; ++i) {
578
- if (quad[i] && quad[i].r > quad.r) {
579
- quad.r = quad[i].r;
580
- }
581
- }
582
- }
583
-
584
- function initialize() {
585
- if (!nodes) return;
586
- var i, n = nodes.length, node;
587
- radii = new Array(n);
588
- for (i = 0; i < n; ++i) node = nodes[i], radii[node.index] = +radius(node, i, nodes);
589
- }
590
-
591
- force.initialize = function(_nodes, _random) {
592
- nodes = _nodes;
593
- random = _random;
594
- initialize();
595
- };
596
-
597
- force.iterations = function(_) {
598
- return arguments.length ? (iterations = +_, force) : iterations;
599
- };
600
-
601
- force.strength = function(_) {
602
- return arguments.length ? (strength = +_, force) : strength;
603
- };
604
-
605
- force.radius = function(_) {
606
- return arguments.length ? (radius = typeof _ === "function" ? _ : constant(+_), initialize(), force) : radius;
607
- };
608
-
609
- return force;
610
- }
611
-
612
- var noop = {value: () => {}};
613
-
614
- function dispatch() {
615
- for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) {
616
- if (!(t = arguments[i] + "") || (t in _) || /[\s.]/.test(t)) throw new Error("illegal type: " + t);
617
- _[t] = [];
618
- }
619
- return new Dispatch(_);
620
- }
621
-
622
- function Dispatch(_) {
623
- this._ = _;
624
- }
625
-
626
- function parseTypenames(typenames, types) {
627
- return typenames.trim().split(/^|\s+/).map(function(t) {
628
- var name = "", i = t.indexOf(".");
629
- if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
630
- if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t);
631
- return {type: t, name: name};
632
- });
633
- }
634
-
635
- Dispatch.prototype = dispatch.prototype = {
636
- constructor: Dispatch,
637
- on: function(typename, callback) {
638
- var _ = this._,
639
- T = parseTypenames(typename + "", _),
640
- t,
641
- i = -1,
642
- n = T.length;
643
-
644
- // If no callback was specified, return the callback of the given type and name.
645
- if (arguments.length < 2) {
646
- while (++i < n) if ((t = (typename = T[i]).type) && (t = get(_[t], typename.name))) return t;
647
- return;
648
- }
649
-
650
- // If a type was specified, set the callback for the given type and name.
651
- // Otherwise, if a null callback was specified, remove callbacks of the given name.
652
- if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback);
653
- while (++i < n) {
654
- if (t = (typename = T[i]).type) _[t] = set(_[t], typename.name, callback);
655
- else if (callback == null) for (t in _) _[t] = set(_[t], typename.name, null);
656
- }
657
-
658
- return this;
659
- },
660
- copy: function() {
661
- var copy = {}, _ = this._;
662
- for (var t in _) copy[t] = _[t].slice();
663
- return new Dispatch(copy);
664
- },
665
- call: function(type, that) {
666
- if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2];
667
- if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
668
- for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);
669
- },
670
- apply: function(type, that, args) {
671
- if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
672
- for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);
673
- }
674
- };
675
-
676
- function get(type, name) {
677
- for (var i = 0, n = type.length, c; i < n; ++i) {
678
- if ((c = type[i]).name === name) {
679
- return c.value;
680
- }
681
- }
682
- }
683
-
684
- function set(type, name, callback) {
685
- for (var i = 0, n = type.length; i < n; ++i) {
686
- if (type[i].name === name) {
687
- type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1));
688
- break;
689
- }
690
- }
691
- if (callback != null) type.push({name: name, value: callback});
692
- return type;
693
- }
694
-
695
- var frame = 0, // is an animation frame pending?
696
- timeout = 0, // is a timeout pending?
697
- interval = 0, // are any timers active?
698
- pokeDelay = 1000, // how frequently we check for clock skew
699
- taskHead,
700
- taskTail,
701
- clockLast = 0,
702
- clockNow = 0,
703
- clockSkew = 0,
704
- clock = typeof performance === "object" && performance.now ? performance : Date,
705
- setFrame = typeof window === "object" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function(f) { setTimeout(f, 17); };
706
-
707
- function now() {
708
- return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew);
709
- }
710
-
711
- function clearNow() {
712
- clockNow = 0;
713
- }
714
-
715
- function Timer() {
716
- this._call =
717
- this._time =
718
- this._next = null;
719
- }
720
-
721
- Timer.prototype = timer.prototype = {
722
- constructor: Timer,
723
- restart: function(callback, delay, time) {
724
- if (typeof callback !== "function") throw new TypeError("callback is not a function");
725
- time = (time == null ? now() : +time) + (delay == null ? 0 : +delay);
726
- if (!this._next && taskTail !== this) {
727
- if (taskTail) taskTail._next = this;
728
- else taskHead = this;
729
- taskTail = this;
730
- }
731
- this._call = callback;
732
- this._time = time;
733
- sleep();
734
- },
735
- stop: function() {
736
- if (this._call) {
737
- this._call = null;
738
- this._time = Infinity;
739
- sleep();
740
- }
741
- }
742
- };
743
-
744
- function timer(callback, delay, time) {
745
- var t = new Timer;
746
- t.restart(callback, delay, time);
747
- return t;
748
- }
749
-
750
- function timerFlush() {
751
- now(); // Get the current time, if not already set.
752
- ++frame; // Pretend we’ve set an alarm, if we haven’t already.
753
- var t = taskHead, e;
754
- while (t) {
755
- if ((e = clockNow - t._time) >= 0) t._call.call(undefined, e);
756
- t = t._next;
757
- }
758
- --frame;
759
- }
760
-
761
- function wake() {
762
- clockNow = (clockLast = clock.now()) + clockSkew;
763
- frame = timeout = 0;
764
- try {
765
- timerFlush();
766
- } finally {
767
- frame = 0;
768
- nap();
769
- clockNow = 0;
770
- }
771
- }
772
-
773
- function poke() {
774
- var now = clock.now(), delay = now - clockLast;
775
- if (delay > pokeDelay) clockSkew -= delay, clockLast = now;
776
- }
777
-
778
- function nap() {
779
- var t0, t1 = taskHead, t2, time = Infinity;
780
- while (t1) {
781
- if (t1._call) {
782
- if (time > t1._time) time = t1._time;
783
- t0 = t1, t1 = t1._next;
784
- } else {
785
- t2 = t1._next, t1._next = null;
786
- t1 = t0 ? t0._next = t2 : taskHead = t2;
787
- }
788
- }
789
- taskTail = t0;
790
- sleep(time);
791
- }
792
-
793
- function sleep(time) {
794
- if (frame) return; // Soonest alarm already set, or will be.
795
- if (timeout) timeout = clearTimeout(timeout);
796
- var delay = time - clockNow; // Strictly less than if we recomputed clockNow.
797
- if (delay > 24) {
798
- if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew);
799
- if (interval) interval = clearInterval(interval);
800
- } else {
801
- if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay);
802
- frame = 1, setFrame(wake);
803
- }
804
- }
805
-
806
- // https://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_use
807
- const a = 1664525;
808
- const c = 1013904223;
809
- const m = 4294967296; // 2^32
810
-
811
- function lcg() {
812
- let s = 1;
813
- return () => (s = (a * s + c) % m) / m;
814
- }
815
-
816
- function x(d) {
817
- return d.x;
818
- }
819
-
820
- function y(d) {
821
- return d.y;
822
- }
823
-
824
- var initialRadius = 10,
825
- initialAngle = Math.PI * (3 - Math.sqrt(5));
826
-
827
- function forceSimulation(nodes) {
828
- var simulation,
829
- alpha = 1,
830
- alphaMin = 0.001,
831
- alphaDecay = 1 - Math.pow(alphaMin, 1 / 300),
832
- alphaTarget = 0,
833
- velocityDecay = 0.6,
834
- forces = new Map(),
835
- stepper = timer(step),
836
- event = dispatch("tick", "end"),
837
- random = lcg();
838
-
839
- if (nodes == null) nodes = [];
840
-
841
- function step() {
842
- tick();
843
- event.call("tick", simulation);
844
- if (alpha < alphaMin) {
845
- stepper.stop();
846
- event.call("end", simulation);
847
- }
848
- }
849
-
850
- function tick(iterations) {
851
- var i, n = nodes.length, node;
852
-
853
- if (iterations === undefined) iterations = 1;
854
-
855
- for (var k = 0; k < iterations; ++k) {
856
- alpha += (alphaTarget - alpha) * alphaDecay;
857
-
858
- forces.forEach(function(force) {
859
- force(alpha);
860
- });
861
-
862
- for (i = 0; i < n; ++i) {
863
- node = nodes[i];
864
- if (node.fx == null) node.x += node.vx *= velocityDecay;
865
- else node.x = node.fx, node.vx = 0;
866
- if (node.fy == null) node.y += node.vy *= velocityDecay;
867
- else node.y = node.fy, node.vy = 0;
868
- }
869
- }
870
-
871
- return simulation;
872
- }
873
-
874
- function initializeNodes() {
875
- for (var i = 0, n = nodes.length, node; i < n; ++i) {
876
- node = nodes[i], node.index = i;
877
- if (node.fx != null) node.x = node.fx;
878
- if (node.fy != null) node.y = node.fy;
879
- if (isNaN(node.x) || isNaN(node.y)) {
880
- var radius = initialRadius * Math.sqrt(0.5 + i), angle = i * initialAngle;
881
- node.x = radius * Math.cos(angle);
882
- node.y = radius * Math.sin(angle);
883
- }
884
- if (isNaN(node.vx) || isNaN(node.vy)) {
885
- node.vx = node.vy = 0;
886
- }
887
- }
888
- }
889
-
890
- function initializeForce(force) {
891
- if (force.initialize) force.initialize(nodes, random);
892
- return force;
893
- }
894
-
895
- initializeNodes();
896
-
897
- return simulation = {
898
- tick: tick,
899
-
900
- restart: function() {
901
- return stepper.restart(step), simulation;
902
- },
903
-
904
- stop: function() {
905
- return stepper.stop(), simulation;
906
- },
907
-
908
- nodes: function(_) {
909
- return arguments.length ? (nodes = _, initializeNodes(), forces.forEach(initializeForce), simulation) : nodes;
910
- },
911
-
912
- alpha: function(_) {
913
- return arguments.length ? (alpha = +_, simulation) : alpha;
914
- },
915
-
916
- alphaMin: function(_) {
917
- return arguments.length ? (alphaMin = +_, simulation) : alphaMin;
918
- },
919
-
920
- alphaDecay: function(_) {
921
- return arguments.length ? (alphaDecay = +_, simulation) : +alphaDecay;
922
- },
923
-
924
- alphaTarget: function(_) {
925
- return arguments.length ? (alphaTarget = +_, simulation) : alphaTarget;
926
- },
927
-
928
- velocityDecay: function(_) {
929
- return arguments.length ? (velocityDecay = 1 - _, simulation) : 1 - velocityDecay;
930
- },
931
-
932
- randomSource: function(_) {
933
- return arguments.length ? (random = _, forces.forEach(initializeForce), simulation) : random;
934
- },
935
-
936
- force: function(name, _) {
937
- return arguments.length > 1 ? ((_ == null ? forces.delete(name) : forces.set(name, initializeForce(_))), simulation) : forces.get(name);
938
- },
939
-
940
- find: function(x, y, radius) {
941
- var i = 0,
942
- n = nodes.length,
943
- dx,
944
- dy,
945
- d2,
946
- node,
947
- closest;
948
-
949
- if (radius == null) radius = Infinity;
950
- else radius *= radius;
951
-
952
- for (i = 0; i < n; ++i) {
953
- node = nodes[i];
954
- dx = x - node.x;
955
- dy = y - node.y;
956
- d2 = dx * dx + dy * dy;
957
- if (d2 < radius) closest = node, radius = d2;
958
- }
959
-
960
- return closest;
961
- },
962
-
963
- on: function(name, _) {
964
- return arguments.length > 1 ? (event.on(name, _), simulation) : event.on(name);
965
- }
966
- };
967
- }
968
-
969
- function forceManyBody() {
970
- var nodes,
971
- node,
972
- random,
973
- alpha,
974
- strength = constant(-30),
975
- strengths,
976
- distanceMin2 = 1,
977
- distanceMax2 = Infinity,
978
- theta2 = 0.81;
979
-
980
- function force(_) {
981
- var i, n = nodes.length, tree = quadtree(nodes, x, y).visitAfter(accumulate);
982
- for (alpha = _, i = 0; i < n; ++i) node = nodes[i], tree.visit(apply);
983
- }
984
-
985
- function initialize() {
986
- if (!nodes) return;
987
- var i, n = nodes.length, node;
988
- strengths = new Array(n);
989
- for (i = 0; i < n; ++i) node = nodes[i], strengths[node.index] = +strength(node, i, nodes);
990
- }
991
-
992
- function accumulate(quad) {
993
- var strength = 0, q, c, weight = 0, x, y, i;
994
-
995
- // For internal nodes, accumulate forces from child quadrants.
996
- if (quad.length) {
997
- for (x = y = i = 0; i < 4; ++i) {
998
- if ((q = quad[i]) && (c = Math.abs(q.value))) {
999
- strength += q.value, weight += c, x += c * q.x, y += c * q.y;
1000
- }
1001
- }
1002
- quad.x = x / weight;
1003
- quad.y = y / weight;
1004
- }
1005
-
1006
- // For leaf nodes, accumulate forces from coincident quadrants.
1007
- else {
1008
- q = quad;
1009
- q.x = q.data.x;
1010
- q.y = q.data.y;
1011
- do strength += strengths[q.data.index];
1012
- while (q = q.next);
1013
- }
1014
-
1015
- quad.value = strength;
1016
- }
1017
-
1018
- function apply(quad, x1, _, x2) {
1019
- if (!quad.value) return true;
1020
-
1021
- var x = quad.x - node.x,
1022
- y = quad.y - node.y,
1023
- w = x2 - x1,
1024
- l = x * x + y * y;
1025
-
1026
- // Apply the Barnes-Hut approximation if possible.
1027
- // Limit forces for very close nodes; randomize direction if coincident.
1028
- if (w * w / theta2 < l) {
1029
- if (l < distanceMax2) {
1030
- if (x === 0) x = jiggle(random), l += x * x;
1031
- if (y === 0) y = jiggle(random), l += y * y;
1032
- if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l);
1033
- node.vx += x * quad.value * alpha / l;
1034
- node.vy += y * quad.value * alpha / l;
1035
- }
1036
- return true;
1037
- }
1038
-
1039
- // Otherwise, process points directly.
1040
- else if (quad.length || l >= distanceMax2) return;
1041
-
1042
- // Limit forces for very close nodes; randomize direction if coincident.
1043
- if (quad.data !== node || quad.next) {
1044
- if (x === 0) x = jiggle(random), l += x * x;
1045
- if (y === 0) y = jiggle(random), l += y * y;
1046
- if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l);
1047
- }
1048
-
1049
- do if (quad.data !== node) {
1050
- w = strengths[quad.data.index] * alpha / l;
1051
- node.vx += x * w;
1052
- node.vy += y * w;
1053
- } while (quad = quad.next);
1054
- }
1055
-
1056
- force.initialize = function(_nodes, _random) {
1057
- nodes = _nodes;
1058
- random = _random;
1059
- initialize();
1060
- };
1061
-
1062
- force.strength = function(_) {
1063
- return arguments.length ? (strength = typeof _ === "function" ? _ : constant(+_), initialize(), force) : strength;
1064
- };
1065
-
1066
- force.distanceMin = function(_) {
1067
- return arguments.length ? (distanceMin2 = _ * _, force) : Math.sqrt(distanceMin2);
1068
- };
1069
-
1070
- force.distanceMax = function(_) {
1071
- return arguments.length ? (distanceMax2 = _ * _, force) : Math.sqrt(distanceMax2);
1072
- };
1073
-
1074
- force.theta = function(_) {
1075
- return arguments.length ? (theta2 = _ * _, force) : Math.sqrt(theta2);
1076
- };
1077
-
1078
- return force;
1079
- }
1080
-
1081
- function forceX(x) {
1082
- var strength = constant(0.1),
1083
- nodes,
1084
- strengths,
1085
- xz;
1086
-
1087
- if (typeof x !== "function") x = constant(x == null ? 0 : +x);
1088
-
1089
- function force(alpha) {
1090
- for (var i = 0, n = nodes.length, node; i < n; ++i) {
1091
- node = nodes[i], node.vx += (xz[i] - node.x) * strengths[i] * alpha;
1092
- }
1093
- }
1094
-
1095
- function initialize() {
1096
- if (!nodes) return;
1097
- var i, n = nodes.length;
1098
- strengths = new Array(n);
1099
- xz = new Array(n);
1100
- for (i = 0; i < n; ++i) {
1101
- strengths[i] = isNaN(xz[i] = +x(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes);
1102
- }
1103
- }
1104
-
1105
- force.initialize = function(_) {
1106
- nodes = _;
1107
- initialize();
1108
- };
1109
-
1110
- force.strength = function(_) {
1111
- return arguments.length ? (strength = typeof _ === "function" ? _ : constant(+_), initialize(), force) : strength;
1112
- };
1113
-
1114
- force.x = function(_) {
1115
- return arguments.length ? (x = typeof _ === "function" ? _ : constant(+_), initialize(), force) : x;
1116
- };
1117
-
1118
- return force;
1119
- }
1120
-
1121
- function forceY(y) {
1122
- var strength = constant(0.1),
1123
- nodes,
1124
- strengths,
1125
- yz;
1126
-
1127
- if (typeof y !== "function") y = constant(y == null ? 0 : +y);
1128
-
1129
- function force(alpha) {
1130
- for (var i = 0, n = nodes.length, node; i < n; ++i) {
1131
- node = nodes[i], node.vy += (yz[i] - node.y) * strengths[i] * alpha;
1132
- }
1133
- }
1134
-
1135
- function initialize() {
1136
- if (!nodes) return;
1137
- var i, n = nodes.length;
1138
- strengths = new Array(n);
1139
- yz = new Array(n);
1140
- for (i = 0; i < n; ++i) {
1141
- strengths[i] = isNaN(yz[i] = +y(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes);
1142
- }
1143
- }
1144
-
1145
- force.initialize = function(_) {
1146
- nodes = _;
1147
- initialize();
1148
- };
1149
-
1150
- force.strength = function(_) {
1151
- return arguments.length ? (strength = typeof _ === "function" ? _ : constant(+_), initialize(), force) : strength;
1152
- };
1153
-
1154
- force.y = function(_) {
1155
- return arguments.length ? (y = typeof _ === "function" ? _ : constant(+_), initialize(), force) : y;
1156
- };
1157
-
1158
- return force;
1159
- }
1160
-
1161
- /** Node count above which extra congestion padding is applied */
1162
- const CONGESTION_THRESHOLD = 15;
1163
- /** Per-node padding multiplier for clusters exceeding CONGESTION_THRESHOLD */
1164
- const CONGESTION_FACTOR = 2.0;
1165
- /** Base padding added to every cluster radius (pixels) */
1166
- const BASE_MICRO_PADDING = 50;
1167
- /**
1168
- * Role order for dynamic band calculation (center to outside)
1169
- */
1170
- const ROLE_ORDER = [
1171
- NodeRole.Entry,
1172
- NodeRole.InternalFramework,
1173
- NodeRole.InternalLib,
1174
- NodeRole.Utility,
1175
- NodeRole.Test,
1176
- NodeRole.Tool,
1177
- ];
1178
- /**
1179
- * Compute micro-layout for a single cluster using "Solar System" physics.
1180
- * Nodes are placed in concentric bands based on their role, with anchors at the center.
1181
- * Uses D3 force simulation with orbit, collision, center gravity, and charge forces.
1182
- *
1183
- * @param cluster - The cluster to compute interior layout for
1184
- * @param config - Layout configuration with force parameters
1185
- * @returns Micro layout result with cluster dimensions and relative node positions
1186
- */
1187
- function computeClusterInterior(cluster, config) {
1188
- const nodes = cluster.nodes;
1189
- const n = nodes.length;
1190
- if (n === 0) {
1191
- return {
1192
- clusterId: cluster.id,
1193
- width: config.minClusterSize,
1194
- height: config.minClusterSize,
1195
- relativePositions: new Map(),
1196
- };
1197
- }
1198
- // 1. Calculate Dynamic Bands based on node distribution
1199
- // Each role gets an area proportional to its node count
1200
- const roleCounts = new Map();
1201
- for (const node of nodes) {
1202
- const role = cluster.metadata?.get(node.id)?.role ?? NodeRole.InternalLib;
1203
- roleCounts.set(role, (roleCounts.get(role) ?? 0) + 1);
1204
- }
1205
- let cumulativeCount = 0;
1206
- const dynamicBands = new Map();
1207
- for (const role of ROLE_ORDER) {
1208
- const count = roleCounts.get(role) ?? 0;
1209
- const prevR = Math.sqrt(cumulativeCount / n);
1210
- cumulativeCount += count;
1211
- const nextR = Math.sqrt(cumulativeCount / n);
1212
- // Add a tiny buffer to empty bands to prevent division by zero in force calculations
1213
- // if a node somehow ends up there or just for safety
1214
- if (count === 0) {
1215
- dynamicBands.set(role, { min: prevR, max: prevR });
1216
- }
1217
- else {
1218
- dynamicBands.set(role, { min: prevR, max: nextR });
1219
- }
1220
- }
1221
- // 2. Determine Cluster Size
1222
- // Use power 0.6 instead of 0.5 (sqrt) to make large clusters sparser (more breathing room)
1223
- // This scales area slightly super-linearly with N, reducing density as N grows
1224
- const spacingFactor = 1.3;
1225
- const nodeSpace = config.nodeRadius * 2 + config.clusterNodeSpacing;
1226
- const baseR = n ** 0.6 * nodeSpace * spacingFactor;
1227
- // Add extra padding for large clusters
1228
- const congestionPadding = Math.max(0, n - CONGESTION_THRESHOLD) * CONGESTION_FACTOR;
1229
- const padding = BASE_MICRO_PADDING + congestionPadding;
1230
- const radius = Math.max(config.minClusterSize / 2, baseR + padding);
1231
- const size = radius * 2;
1232
- // 3. Initialize Simulation Nodes
1233
- const simNodes = nodes.map((node) => ({
1234
- id: node.id,
1235
- x: (Math.random() - 0.5) * 10, // Start near center
1236
- y: (Math.random() - 0.5) * 10,
1237
- vx: 0,
1238
- vy: 0,
1239
- role: cluster.metadata?.get(node.id)?.role ?? NodeRole.InternalLib,
1240
- radius: config.nodeRadius,
1241
- }));
1242
- // 4. Create Forces
1243
- const simulation = forceSimulation(simNodes)
1244
- // A. Collision (prevent overlap)
1245
- .force('collide', forceCollide().radius((d) => d.radius + config.nodeCollisionPadding))
1246
- // B. Solar System Orbit (Band-based positioning)
1247
- .force('orbit', (alpha) => {
1248
- const k = alpha * 0.8; // Strong confinement to band
1249
- for (const node of simNodes) {
1250
- const band = dynamicBands.get(node.role) ?? { min: 0, max: 1 };
1251
- const minR = band.min * (radius - 20);
1252
- const maxR = band.max * (radius - 20);
1253
- // Current distance
1254
- const d = Math.hypot(node.x, node.y) || 1e-6;
1255
- // Only apply force if outside band
1256
- if (d < minR) {
1257
- const diff = d - minR; // Negative
1258
- // Push out
1259
- const fx = (node.x / d) * diff * k;
1260
- const fy = (node.y / d) * diff * k;
1261
- node.vx -= fx;
1262
- node.vy -= fy;
1263
- }
1264
- else if (d > maxR) {
1265
- const diff = d - maxR; // Positive
1266
- // Pull in
1267
- const fx = (node.x / d) * diff * k;
1268
- const fy = (node.y / d) * diff * k;
1269
- node.vx -= fx;
1270
- node.vy -= fy;
1271
- }
1272
- // Inside band? Drift freely (repulsion handles spacing)
1273
- }
1274
- })
1275
- // C. Center Gravity (keep things coherent but loose)
1276
- .force('center', forceCenter(0, 0).strength(0.02))
1277
- // D. Many Body (stronger repulsion to use available space)
1278
- .force('charge', forceManyBody().strength(config.nodeCharge));
1279
- // 5. Run Simulation
1280
- // Micro-layout is small (dozens of nodes), so we can run enough ticks quickly
1281
- // Break early if the simulation converges before hitting the tick limit
1282
- const TICKS = 150;
1283
- for (let i = 0; i < TICKS; i++) {
1284
- simulation.tick();
1285
- if (simulation.alpha() < 0.001)
1286
- break;
1287
- }
1288
- simulation.stop();
1289
- // 6. Extract Positions
1290
- const relativePositions = new Map();
1291
- for (const node of simNodes) {
1292
- relativePositions.set(node.id, {
1293
- id: node.id,
1294
- clusterId: cluster.id,
1295
- x: node.x,
1296
- y: node.y,
1297
- vx: 0,
1298
- vy: 0,
1299
- radius: node.radius,
1300
- });
1301
- }
1302
- return {
1303
- clusterId: cluster.id,
1304
- width: size,
1305
- height: size,
1306
- relativePositions,
1307
- };
1308
- }
1309
-
1310
- /**
1311
- * Apply a gentle force-directed "massage" to nodes within a cluster.
1312
- * Runs after the main "Solar System" layout to improve spacing and resolve
1313
- * local congestions while preserving the overall band structure.
1314
- *
1315
- * @param micro - Micro layout result from `computeClusterInterior`
1316
- * @param config - Layout configuration with collision and charge parameters
1317
- * @returns Updated micro layout with refined node positions and possibly expanded dimensions
1318
- */
1319
- function applyNodeMassage(micro, config) {
1320
- const nodes = Array.from(micro.relativePositions.values()).map((pos) => ({
1321
- ...pos,
1322
- // Store original positions for tethering
1323
- ox: pos.x,
1324
- oy: pos.y,
1325
- }));
1326
- if (nodes.length === 0)
1327
- return micro;
1328
- const simulation = forceSimulation(nodes)
1329
- // 1. Anchor to original positions (preserve Solar System bands)
1330
- .force('x', forceX((d) => d.ox).strength(0.1))
1331
- .force('y', forceY((d) => d.oy).strength(0.1))
1332
- // 2. Gentle repulsion to "fluff" the cluster
1333
- .force('charge', forceManyBody().strength(-30))
1334
- // 3. Hard collision to ensure no overlaps
1335
- .force('collide', forceCollide()
1336
- .radius((d) => (d.radius ?? config.nodeRadius) + config.clusterNodeSpacing)
1337
- .strength(0.9)
1338
- .iterations(2))
1339
- .stop();
1340
- // Run a short burst, with early exit on convergence
1341
- const TICKS = 50;
1342
- for (let i = 0; i < TICKS; i++) {
1343
- simulation.tick();
1344
- if (simulation.alpha() < 0.001)
1345
- break;
1346
- }
1347
- // Update positions and recalculate bounds
1348
- const newPositions = new Map();
1349
- let minX = Infinity, maxX = -Infinity;
1350
- let minY = Infinity, maxY = -Infinity;
1351
- for (const node of nodes) {
1352
- const existingPos = micro.relativePositions.get(node.id);
1353
- if (existingPos) {
1354
- newPositions.set(node.id, {
1355
- ...existingPos,
1356
- x: node.x,
1357
- y: node.y,
1358
- });
1359
- }
1360
- if (node.x < minX)
1361
- minX = node.x;
1362
- if (node.x > maxX)
1363
- maxX = node.x;
1364
- if (node.y < minY)
1365
- minY = node.y;
1366
- if (node.y > maxY)
1367
- maxY = node.y;
1368
- }
1369
- // Ensure we don't shrink below original calculated size (which had "breathing room" logic)
1370
- // but do expand if needed.
1371
- // Actually, let's keep it simple: Expand if bounding box exceeds original size.
1372
- // Assuming 0,0 center, the radius is max(abs(min), abs(max))
1373
- const maxDim = Math.max(Math.abs(minX), Math.abs(maxX), Math.abs(minY), Math.abs(maxY));
1374
- const neededSize = maxDim * 2 + 100; // + padding
1375
- const finalSize = Math.max(micro.width, neededSize);
1376
- return {
1377
- ...micro,
1378
- relativePositions: newPositions,
1379
- width: finalSize,
1380
- height: finalSize,
1381
- };
1382
- }
1383
-
1384
- /**
1385
- * Micro-Layout Web Worker
1386
- *
1387
- * Runs computeClusterInterior + applyNodeMassage for a single cluster
1388
- * off the main thread. Used by parallel-micro.ts to parallelize
1389
- * micro-layout computation across multiple workers.
1390
- */
1391
- /**
1392
- * Converts a serialized cluster (with arrays) back to the internal Cluster type (with Maps).
1393
- * @param sc - Serialized cluster received from the main thread
1394
- * @returns Deserialized Cluster instance
1395
- */
1396
- function deserializeCluster(sc) {
1397
- return {
1398
- ...sc,
1399
- metadata: new Map(sc.metadata),
1400
- };
1401
- }
1402
- const workerApi = {
1403
- computeMicro(serializedCluster, config) {
1404
- const cluster = deserializeCluster(serializedCluster);
1405
- let micro = computeClusterInterior(cluster, config);
1406
- micro = applyNodeMassage(micro, config);
1407
- return {
1408
- clusterId: micro.clusterId,
1409
- width: micro.width,
1410
- height: micro.height,
1411
- relativePositions: Array.from(micro.relativePositions.entries()),
1412
- };
1413
- },
1414
- };
1415
- /* v8 ignore start */
1416
- self.onmessage = (e) => {
1417
- const result = workerApi.computeMicro(e.data.cluster, e.data.config);
1418
- self.postMessage(result);
1419
- };
1420
- /* v8 ignore stop */
1
+ var e;(function(e){e.Entry=`entry`,e.InternalFramework=`internal-framework`,e.InternalLib=`internal-lib`,e.Utility=`utility`,e.Test=`test`,e.Tool=`tool`})(e||={});var t;(function(e){e.Project=`project`,e.Package=`package`})(t||={});var n;(function(e){e.Inherit=`INHERIT`,e.IncludeChildren=`INCLUDE_CHILDREN`,e.SeparateChildren=`SEPARATE_CHILDREN`})(n||={}),Object.values(e),Object.values(t);function r(e,t){var n,r=1;e??=0,t??=0;function i(){var i,a=n.length,o,s=0,c=0;for(i=0;i<a;++i)o=n[i],s+=o.x,c+=o.y;for(s=(s/a-e)*r,c=(c/a-t)*r,i=0;i<a;++i)o=n[i],o.x-=s,o.y-=c}return i.initialize=function(e){n=e},i.x=function(t){return arguments.length?(e=+t,i):e},i.y=function(e){return arguments.length?(t=+e,i):t},i.strength=function(e){return arguments.length?(r=+e,i):r},i}function i(e){let t=+this._x.call(null,e),n=+this._y.call(null,e);return a(this.cover(t,n),t,n,e)}function a(e,t,n,r){if(isNaN(t)||isNaN(n))return e;var i,a=e._root,o={data:r},s=e._x0,c=e._y0,l=e._x1,u=e._y1,d,f,p,m,h,g,_,v;if(!a)return e._root=o,e;for(;a.length;)if((h=t>=(d=(s+l)/2))?s=d:l=d,(g=n>=(f=(c+u)/2))?c=f:u=f,i=a,!(a=a[_=g<<1|h]))return i[_]=o,e;if(p=+e._x.call(null,a.data),m=+e._y.call(null,a.data),t===p&&n===m)return o.next=a,i?i[_]=o:e._root=o,e;do i=i?i[_]=[,,,,]:e._root=[,,,,],(h=t>=(d=(s+l)/2))?s=d:l=d,(g=n>=(f=(c+u)/2))?c=f:u=f;while((_=g<<1|h)==(v=(m>=f)<<1|p>=d));return i[v]=a,i[_]=o,e}function o(e){var t,n,r=e.length,i,o,s=Array(r),c=Array(r),l=1/0,u=1/0,d=-1/0,f=-1/0;for(n=0;n<r;++n)isNaN(i=+this._x.call(null,t=e[n]))||isNaN(o=+this._y.call(null,t))||(s[n]=i,c[n]=o,i<l&&(l=i),i>d&&(d=i),o<u&&(u=o),o>f&&(f=o));if(l>d||u>f)return this;for(this.cover(l,u).cover(d,f),n=0;n<r;++n)a(this,s[n],c[n],e[n]);return this}function s(e,t){if(isNaN(e=+e)||isNaN(t=+t))return this;var n=this._x0,r=this._y0,i=this._x1,a=this._y1;if(isNaN(n))i=(n=Math.floor(e))+1,a=(r=Math.floor(t))+1;else{for(var o=i-n||1,s=this._root,c,l;n>e||e>=i||r>t||t>=a;)switch(l=(t<r)<<1|e<n,c=[,,,,],c[l]=s,s=c,o*=2,l){case 0:i=n+o,a=r+o;break;case 1:n=i-o,a=r+o;break;case 2:i=n+o,r=a-o;break;case 3:n=i-o,r=a-o;break}this._root&&this._root.length&&(this._root=s)}return this._x0=n,this._y0=r,this._x1=i,this._y1=a,this}function c(){var e=[];return this.visit(function(t){if(!t.length)do e.push(t.data);while(t=t.next)}),e}function l(e){return arguments.length?this.cover(+e[0][0],+e[0][1]).cover(+e[1][0],+e[1][1]):isNaN(this._x0)?void 0:[[this._x0,this._y0],[this._x1,this._y1]]}function u(e,t,n,r,i){this.node=e,this.x0=t,this.y0=n,this.x1=r,this.y1=i}function d(e,t,n){var r,i=this._x0,a=this._y0,o,s,c,l,d=this._x1,f=this._y1,p=[],m=this._root,h,g;for(m&&p.push(new u(m,i,a,d,f)),n==null?n=1/0:(i=e-n,a=t-n,d=e+n,f=t+n,n*=n);h=p.pop();)if(!(!(m=h.node)||(o=h.x0)>d||(s=h.y0)>f||(c=h.x1)<i||(l=h.y1)<a))if(m.length){var _=(o+c)/2,v=(s+l)/2;p.push(new u(m[3],_,v,c,l),new u(m[2],o,v,_,l),new u(m[1],_,s,c,v),new u(m[0],o,s,_,v)),(g=(t>=v)<<1|e>=_)&&(h=p[p.length-1],p[p.length-1]=p[p.length-1-g],p[p.length-1-g]=h)}else{var y=e-+this._x.call(null,m.data),b=t-+this._y.call(null,m.data),x=y*y+b*b;if(x<n){var S=Math.sqrt(n=x);i=e-S,a=t-S,d=e+S,f=t+S,r=m.data}}return r}function f(e){if(isNaN(u=+this._x.call(null,e))||isNaN(d=+this._y.call(null,e)))return this;var t,n=this._root,r,i,a,o=this._x0,s=this._y0,c=this._x1,l=this._y1,u,d,f,p,m,h,g,_;if(!n)return this;if(n.length)for(;;){if((m=u>=(f=(o+c)/2))?o=f:c=f,(h=d>=(p=(s+l)/2))?s=p:l=p,t=n,!(n=n[g=h<<1|m]))return this;if(!n.length)break;(t[g+1&3]||t[g+2&3]||t[g+3&3])&&(r=t,_=g)}for(;n.data!==e;)if(i=n,!(n=n.next))return this;return(a=n.next)&&delete n.next,i?(a?i.next=a:delete i.next,this):t?(a?t[g]=a:delete t[g],(n=t[0]||t[1]||t[2]||t[3])&&n===(t[3]||t[2]||t[1]||t[0])&&!n.length&&(r?r[_]=n:this._root=n),this):(this._root=a,this)}function p(e){for(var t=0,n=e.length;t<n;++t)this.remove(e[t]);return this}function m(){return this._root}function h(){var e=0;return this.visit(function(t){if(!t.length)do++e;while(t=t.next)}),e}function g(e){var t=[],n,r=this._root,i,a,o,s,c;for(r&&t.push(new u(r,this._x0,this._y0,this._x1,this._y1));n=t.pop();)if(!e(r=n.node,a=n.x0,o=n.y0,s=n.x1,c=n.y1)&&r.length){var l=(a+s)/2,d=(o+c)/2;(i=r[3])&&t.push(new u(i,l,d,s,c)),(i=r[2])&&t.push(new u(i,a,d,l,c)),(i=r[1])&&t.push(new u(i,l,o,s,d)),(i=r[0])&&t.push(new u(i,a,o,l,d))}return this}function _(e){var t=[],n=[],r;for(this._root&&t.push(new u(this._root,this._x0,this._y0,this._x1,this._y1));r=t.pop();){var i=r.node;if(i.length){var a,o=r.x0,s=r.y0,c=r.x1,l=r.y1,d=(o+c)/2,f=(s+l)/2;(a=i[0])&&t.push(new u(a,o,s,d,f)),(a=i[1])&&t.push(new u(a,d,s,c,f)),(a=i[2])&&t.push(new u(a,o,f,d,l)),(a=i[3])&&t.push(new u(a,d,f,c,l))}n.push(r)}for(;r=n.pop();)e(r.node,r.x0,r.y0,r.x1,r.y1);return this}function v(e){return e[0]}function y(e){return arguments.length?(this._x=e,this):this._x}function b(e){return e[1]}function x(e){return arguments.length?(this._y=e,this):this._y}function S(e,t,n){var r=new C(t??v,n??b,NaN,NaN,NaN,NaN);return e==null?r:r.addAll(e)}function C(e,t,n,r,i,a){this._x=e,this._y=t,this._x0=n,this._y0=r,this._x1=i,this._y1=a,this._root=void 0}function w(e){for(var t={data:e.data},n=t;e=e.next;)n=n.next={data:e.data};return t}var T=S.prototype=C.prototype;T.copy=function(){var e=new C(this._x,this._y,this._x0,this._y0,this._x1,this._y1),t=this._root,n,r;if(!t)return e;if(!t.length)return e._root=w(t),e;for(n=[{source:t,target:e._root=[,,,,]}];t=n.pop();)for(var i=0;i<4;++i)(r=t.source[i])&&(r.length?n.push({source:r,target:t.target[i]=[,,,,]}):t.target[i]=w(r));return e},T.add=i,T.addAll=o,T.cover=s,T.data=c,T.extent=l,T.find=d,T.remove=f,T.removeAll=p,T.root=m,T.size=h,T.visit=g,T.visitAfter=_,T.x=y,T.y=x;function E(e){return function(){return e}}function D(e){return(e()-.5)*1e-6}function ee(e){return e.x+e.vx}function te(e){return e.y+e.vy}function O(e){var t,n,r,i=1,a=1;typeof e!=`function`&&(e=E(e==null?1:+e));function o(){for(var e,o=t.length,c,l,u,d,f,p,m=0;m<a;++m)for(c=S(t,ee,te).visitAfter(s),e=0;e<o;++e)l=t[e],f=n[l.index],p=f*f,u=l.x+l.vx,d=l.y+l.vy,c.visit(h);function h(e,t,n,a,o){var s=e.data,c=e.r,m=f+c;if(s){if(s.index>l.index){var h=u-s.x-s.vx,g=d-s.y-s.vy,_=h*h+g*g;_<m*m&&(h===0&&(h=D(r),_+=h*h),g===0&&(g=D(r),_+=g*g),_=(m-(_=Math.sqrt(_)))/_*i,l.vx+=(h*=_)*(m=(c*=c)/(p+c)),l.vy+=(g*=_)*m,s.vx-=h*(m=1-m),s.vy-=g*m)}return}return t>u+m||a<u-m||n>d+m||o<d-m}}function s(e){if(e.data)return e.r=n[e.data.index];for(var t=e.r=0;t<4;++t)e[t]&&e[t].r>e.r&&(e.r=e[t].r)}function c(){if(t){var r,i=t.length,a;for(n=Array(i),r=0;r<i;++r)a=t[r],n[a.index]=+e(a,r,t)}}return o.initialize=function(e,n){t=e,r=n,c()},o.iterations=function(e){return arguments.length?(a=+e,o):a},o.strength=function(e){return arguments.length?(i=+e,o):i},o.radius=function(t){return arguments.length?(e=typeof t==`function`?t:E(+t),c(),o):e},o}var k={value:()=>{}};function A(){for(var e=0,t=arguments.length,n={},r;e<t;++e){if(!(r=arguments[e]+``)||r in n||/[\s.]/.test(r))throw Error(`illegal type: `+r);n[r]=[]}return new j(n)}function j(e){this._=e}function M(e,t){return e.trim().split(/^|\s+/).map(function(e){var n=``,r=e.indexOf(`.`);if(r>=0&&(n=e.slice(r+1),e=e.slice(0,r)),e&&!t.hasOwnProperty(e))throw Error(`unknown type: `+e);return{type:e,name:n}})}j.prototype=A.prototype={constructor:j,on:function(e,t){var n=this._,r=M(e+``,n),i,a=-1,o=r.length;if(arguments.length<2){for(;++a<o;)if((i=(e=r[a]).type)&&(i=N(n[i],e.name)))return i;return}if(t!=null&&typeof t!=`function`)throw Error(`invalid callback: `+t);for(;++a<o;)if(i=(e=r[a]).type)n[i]=P(n[i],e.name,t);else if(t==null)for(i in n)n[i]=P(n[i],e.name,null);return this},copy:function(){var e={},t=this._;for(var n in t)e[n]=t[n].slice();return new j(e)},call:function(e,t){if((i=arguments.length-2)>0)for(var n=Array(i),r=0,i,a;r<i;++r)n[r]=arguments[r+2];if(!this._.hasOwnProperty(e))throw Error(`unknown type: `+e);for(a=this._[e],r=0,i=a.length;r<i;++r)a[r].value.apply(t,n)},apply:function(e,t,n){if(!this._.hasOwnProperty(e))throw Error(`unknown type: `+e);for(var r=this._[e],i=0,a=r.length;i<a;++i)r[i].value.apply(t,n)}};function N(e,t){for(var n=0,r=e.length,i;n<r;++n)if((i=e[n]).name===t)return i.value}function P(e,t,n){for(var r=0,i=e.length;r<i;++r)if(e[r].name===t){e[r]=k,e=e.slice(0,r).concat(e.slice(r+1));break}return n!=null&&e.push({name:t,value:n}),e}var F=0,I=0,L=0,R=1e3,z,B,V=0,H=0,U=0,W=typeof performance==`object`&&performance.now?performance:Date,G=typeof window==`object`&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(e){setTimeout(e,17)};function K(){return H||=(G(ne),W.now()+U)}function ne(){H=0}function q(){this._call=this._time=this._next=null}q.prototype=J.prototype={constructor:q,restart:function(e,t,n){if(typeof e!=`function`)throw TypeError(`callback is not a function`);n=(n==null?K():+n)+(t==null?0:+t),!this._next&&B!==this&&(B?B._next=this:z=this,B=this),this._call=e,this._time=n,X()},stop:function(){this._call&&(this._call=null,this._time=1/0,X())}};function J(e,t,n){var r=new q;return r.restart(e,t,n),r}function re(){K(),++F;for(var e=z,t;e;)(t=H-e._time)>=0&&e._call.call(void 0,t),e=e._next;--F}function Y(){H=(V=W.now())+U,F=I=0;try{re()}finally{F=0,ae(),H=0}}function ie(){var e=W.now(),t=e-V;t>R&&(U-=t,V=e)}function ae(){for(var e,t=z,n,r=1/0;t;)t._call?(r>t._time&&(r=t._time),e=t,t=t._next):(n=t._next,t._next=null,t=e?e._next=n:z=n);B=e,X(r)}function X(e){F||(I&&=clearTimeout(I),e-H>24?(e<1/0&&(I=setTimeout(Y,e-W.now()-U)),L&&=clearInterval(L)):(L||=(V=W.now(),setInterval(ie,R)),F=1,G(Y)))}const Z=4294967296;function oe(){let e=1;return()=>(e=(1664525*e+1013904223)%Z)/Z}function se(e){return e.x}function ce(e){return e.y}var le=10,ue=Math.PI*(3-Math.sqrt(5));function Q(e){var t,n=1,r=.001,i=1-r**(1/300),a=0,o=.6,s=new Map,c=J(d),l=A(`tick`,`end`),u=oe();e??=[];function d(){f(),l.call(`tick`,t),n<r&&(c.stop(),l.call(`end`,t))}function f(r){var c,l=e.length,u;r===void 0&&(r=1);for(var d=0;d<r;++d)for(n+=(a-n)*i,s.forEach(function(e){e(n)}),c=0;c<l;++c)u=e[c],u.fx==null?u.x+=u.vx*=o:(u.x=u.fx,u.vx=0),u.fy==null?u.y+=u.vy*=o:(u.y=u.fy,u.vy=0);return t}function p(){for(var t=0,n=e.length,r;t<n;++t){if(r=e[t],r.index=t,r.fx!=null&&(r.x=r.fx),r.fy!=null&&(r.y=r.fy),isNaN(r.x)||isNaN(r.y)){var i=le*Math.sqrt(.5+t),a=t*ue;r.x=i*Math.cos(a),r.y=i*Math.sin(a)}(isNaN(r.vx)||isNaN(r.vy))&&(r.vx=r.vy=0)}}function m(t){return t.initialize&&t.initialize(e,u),t}return p(),t={tick:f,restart:function(){return c.restart(d),t},stop:function(){return c.stop(),t},nodes:function(n){return arguments.length?(e=n,p(),s.forEach(m),t):e},alpha:function(e){return arguments.length?(n=+e,t):n},alphaMin:function(e){return arguments.length?(r=+e,t):r},alphaDecay:function(e){return arguments.length?(i=+e,t):+i},alphaTarget:function(e){return arguments.length?(a=+e,t):a},velocityDecay:function(e){return arguments.length?(o=1-e,t):1-o},randomSource:function(e){return arguments.length?(u=e,s.forEach(m),t):u},force:function(e,n){return arguments.length>1?(n==null?s.delete(e):s.set(e,m(n)),t):s.get(e)},find:function(t,n,r){var i=0,a=e.length,o,s,c,l,u;for(r==null?r=1/0:r*=r,i=0;i<a;++i)l=e[i],o=t-l.x,s=n-l.y,c=o*o+s*s,c<r&&(u=l,r=c);return u},on:function(e,n){return arguments.length>1?(l.on(e,n),t):l.on(e)}}}function $(){var e,t,n,r,i=E(-30),a,o=1,s=1/0,c=.81;function l(n){var i,a=e.length,o=S(e,se,ce).visitAfter(d);for(r=n,i=0;i<a;++i)t=e[i],o.visit(f)}function u(){if(e){var t,n=e.length,r;for(a=Array(n),t=0;t<n;++t)r=e[t],a[r.index]=+i(r,t,e)}}function d(e){var t=0,n,r,i=0,o,s,c;if(e.length){for(o=s=c=0;c<4;++c)(n=e[c])&&(r=Math.abs(n.value))&&(t+=n.value,i+=r,o+=r*n.x,s+=r*n.y);e.x=o/i,e.y=s/i}else{n=e,n.x=n.data.x,n.y=n.data.y;do t+=a[n.data.index];while(n=n.next)}e.value=t}function f(e,i,l,u){if(!e.value)return!0;var d=e.x-t.x,f=e.y-t.y,p=u-i,m=d*d+f*f;if(p*p/c<m)return m<s&&(d===0&&(d=D(n),m+=d*d),f===0&&(f=D(n),m+=f*f),m<o&&(m=Math.sqrt(o*m)),t.vx+=d*e.value*r/m,t.vy+=f*e.value*r/m),!0;if(!(e.length||m>=s)){(e.data!==t||e.next)&&(d===0&&(d=D(n),m+=d*d),f===0&&(f=D(n),m+=f*f),m<o&&(m=Math.sqrt(o*m)));do e.data!==t&&(p=a[e.data.index]*r/m,t.vx+=d*p,t.vy+=f*p);while(e=e.next)}}return l.initialize=function(t,r){e=t,n=r,u()},l.strength=function(e){return arguments.length?(i=typeof e==`function`?e:E(+e),u(),l):i},l.distanceMin=function(e){return arguments.length?(o=e*e,l):Math.sqrt(o)},l.distanceMax=function(e){return arguments.length?(s=e*e,l):Math.sqrt(s)},l.theta=function(e){return arguments.length?(c=e*e,l):Math.sqrt(c)},l}function de(e){var t=E(.1),n,r,i;typeof e!=`function`&&(e=E(e==null?0:+e));function a(e){for(var t=0,a=n.length,o;t<a;++t)o=n[t],o.vx+=(i[t]-o.x)*r[t]*e}function o(){if(n){var a,o=n.length;for(r=Array(o),i=Array(o),a=0;a<o;++a)r[a]=isNaN(i[a]=+e(n[a],a,n))?0:+t(n[a],a,n)}}return a.initialize=function(e){n=e,o()},a.strength=function(e){return arguments.length?(t=typeof e==`function`?e:E(+e),o(),a):t},a.x=function(t){return arguments.length?(e=typeof t==`function`?t:E(+t),o(),a):e},a}function fe(e){var t=E(.1),n,r,i;typeof e!=`function`&&(e=E(e==null?0:+e));function a(e){for(var t=0,a=n.length,o;t<a;++t)o=n[t],o.vy+=(i[t]-o.y)*r[t]*e}function o(){if(n){var a,o=n.length;for(r=Array(o),i=Array(o),a=0;a<o;++a)r[a]=isNaN(i[a]=+e(n[a],a,n))?0:+t(n[a],a,n)}}return a.initialize=function(e){n=e,o()},a.strength=function(e){return arguments.length?(t=typeof e==`function`?e:E(+e),o(),a):t},a.y=function(t){return arguments.length?(e=typeof t==`function`?t:E(+t),o(),a):e},a}const pe=[e.Entry,e.InternalFramework,e.InternalLib,e.Utility,e.Test,e.Tool];function me(t,n){let i=t.nodes,a=i.length;if(a===0)return{clusterId:t.id,width:n.minClusterSize,height:n.minClusterSize,relativePositions:new Map};let o=new Map;for(let n of i){let r=t.metadata?.get(n.id)?.role??e.InternalLib;o.set(r,(o.get(r)??0)+1)}let s=0,c=new Map;for(let e of pe){let t=o.get(e)??0,n=Math.sqrt(s/a);s+=t;let r=Math.sqrt(s/a);t===0?c.set(e,{min:n,max:n}):c.set(e,{min:n,max:r})}let l=n.nodeRadius*2+n.clusterNodeSpacing,u=a**.6*l*1.3,d=50+Math.max(0,a-15)*2,f=Math.max(n.minClusterSize/2,u+d),p=f*2,m=i.map(r=>({id:r.id,x:(Math.random()-.5)*10,y:(Math.random()-.5)*10,vx:0,vy:0,role:t.metadata?.get(r.id)?.role??e.InternalLib,radius:n.nodeRadius})),h=Q(m).force(`collide`,O().radius(e=>e.radius+n.nodeCollisionPadding)).force(`orbit`,e=>{let t=e*.8;for(let e of m){let n=c.get(e.role)??{min:0,max:1},r=n.min*(f-20),i=n.max*(f-20),a=Math.hypot(e.x,e.y)||1e-6;if(a<r){let n=a-r,i=e.x/a*n*t,o=e.y/a*n*t;e.vx-=i,e.vy-=o}else if(a>i){let n=a-i,r=e.x/a*n*t,o=e.y/a*n*t;e.vx-=r,e.vy-=o}}}).force(`center`,r(0,0).strength(.02)).force(`charge`,$().strength(n.nodeCharge));for(let e=0;e<150&&(h.tick(),!(h.alpha()<.001));e++);h.stop();let g=new Map;for(let e of m)g.set(e.id,{id:e.id,clusterId:t.id,x:e.x,y:e.y,vx:0,vy:0,radius:e.radius});return{clusterId:t.id,width:p,height:p,relativePositions:g}}function he(e,t){let n=Array.from(e.relativePositions.values()).map(e=>({...e,ox:e.x,oy:e.y}));if(n.length===0)return e;let r=Q(n).force(`x`,de(e=>e.ox).strength(.1)).force(`y`,fe(e=>e.oy).strength(.1)).force(`charge`,$().strength(-30)).force(`collide`,O().radius(e=>(e.radius??t.nodeRadius)+t.clusterNodeSpacing).strength(.9).iterations(2)).stop();for(let e=0;e<50&&(r.tick(),!(r.alpha()<.001));e++);let i=new Map,a=1/0,o=-1/0,s=1/0,c=-1/0;for(let t of n){let n=e.relativePositions.get(t.id);n&&i.set(t.id,{...n,x:t.x,y:t.y}),t.x<a&&(a=t.x),t.x>o&&(o=t.x),t.y<s&&(s=t.y),t.y>c&&(c=t.y)}let l=Math.max(Math.abs(a),Math.abs(o),Math.abs(s),Math.abs(c))*2+100,u=Math.max(e.width,l);return{...e,relativePositions:i,width:u,height:u}}function ge(e){return{...e,metadata:new Map(e.metadata)}}const _e={computeMicro(e,t){let n=me(ge(e),t);return n=he(n,t),{clusterId:n.clusterId,width:n.width,height:n.height,relativePositions:Array.from(n.relativePositions.entries())}}};self.onmessage=e=>{let t=_e.computeMicro(e.data.cluster,e.data.config);self.postMessage(t)};