@antv/layout 2.0.0-alpha.2 → 2.0.0-alpha.3

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/dist/index.js CHANGED
@@ -357,1195 +357,1257 @@
357
357
  return result;
358
358
  });
359
359
 
360
- class GraphLib {
361
- constructor(data, options = {}) {
362
- this.edgeIdCounter = new Map();
363
- this.nodeMap = extractNodeData(data.nodes, options.node);
364
- this.edgeMap = extractEdgeData(data.edges || [], options.edge, this.getEdgeId.bind(this));
365
- }
366
- data() {
367
- return { nodes: this.nodeMap, edges: this.edgeMap };
368
- }
369
- replace(result) {
370
- this.nodeMap = result.nodes;
371
- this.edgeMap = result.edges;
372
- this.clearCache();
373
- }
374
- nodes() {
375
- return Array.from(this.nodeMap.values());
376
- }
377
- node(id) {
378
- return this.nodeMap.get(id);
379
- }
380
- nodeAt(index) {
381
- if (!this.indexNodeCache) {
382
- this.buildNodeIndexCache();
383
- }
384
- const nodeId = this.indexNodeCache.get(index);
385
- return nodeId ? this.nodeMap.get(nodeId) : undefined;
386
- }
387
- nodeIndexOf(id) {
388
- var _a;
389
- if (!this.nodeIndexCache) {
390
- this.buildNodeIndexCache();
391
- }
392
- return (_a = this.nodeIndexCache.get(id)) !== null && _a !== void 0 ? _a : -1;
393
- }
394
- firstNode() {
395
- return this.nodeMap.values().next().value;
396
- }
397
- forEachNode(callback) {
398
- let i = 0;
399
- this.nodeMap.forEach((node) => callback(node, i++));
400
- }
401
- originalNode(id) {
402
- const node = this.nodeMap.get(id);
403
- return node === null || node === void 0 ? void 0 : node._original;
404
- }
405
- nodeCount() {
406
- return this.nodeMap.size;
407
- }
408
- edges() {
409
- return Array.from(this.edgeMap.values());
410
- }
411
- edge(id) {
412
- return this.edgeMap.get(id);
413
- }
414
- firstEdge() {
415
- return this.edgeMap.values().next().value;
416
- }
417
- forEachEdge(callback) {
418
- let i = 0;
419
- this.edgeMap.forEach((edge) => callback(edge, i++));
420
- }
421
- originalEdge(id) {
422
- const edge = this.edgeMap.get(id);
423
- return edge === null || edge === void 0 ? void 0 : edge._original;
424
- }
425
- edgeCount() {
426
- return this.edgeMap.size;
427
- }
428
- getEdgeId(edge) {
429
- if (edge.id)
430
- return edge.id;
431
- const baseId = `${edge.source}-${edge.target}`;
432
- const count = this.edgeIdCounter.get(baseId) || 0;
433
- const id = count === 0 ? baseId : `${baseId}-${count}`;
434
- this.edgeIdCounter.set(baseId, count + 1);
435
- return id;
436
- }
437
- degree(nodeId, direction = 'both') {
438
- if (!this.degreeCache) {
439
- this.buildDegreeCache();
440
- }
441
- const degree = this.degreeCache.get(nodeId);
442
- if (!degree)
443
- return 0;
444
- return degree[direction];
445
- }
446
- neighbors(nodeId, direction = 'both') {
447
- if (!this.outAdjacencyCache || !this.inAdjacencyCache) {
448
- this.buildAdjacencyCache();
449
- }
450
- if (direction === 'out') {
451
- return Array.from(this.outAdjacencyCache.get(nodeId) || []);
452
- }
453
- if (direction === 'in') {
454
- return Array.from(this.inAdjacencyCache.get(nodeId) || []);
455
- }
456
- if (this.bothAdjacencyCache) {
457
- return Array.from(this.bothAdjacencyCache.get(nodeId) || []);
360
+ const isArray = Array.isArray;
361
+
362
+ /**
363
+ * Return the layout result for a graph with zero or one node.
364
+ * @param graph original graph
365
+ * @param center the layout center
366
+ * @returns layout result
367
+ */
368
+ function applySingleNodeLayout(model, center, dimensions = 2) {
369
+ const n = model.nodeCount();
370
+ if (n === 1) {
371
+ const first = model.firstNode();
372
+ first.x = center[0];
373
+ first.y = center[1];
374
+ if (dimensions === 3) {
375
+ first.z = center[2] || 0;
458
376
  }
459
- const inSet = this.inAdjacencyCache.get(nodeId);
460
- const outSet = this.outAdjacencyCache.get(nodeId);
461
- if (!inSet && !outSet)
462
- return [];
463
- if (!inSet)
464
- return Array.from(outSet);
465
- if (!outSet)
466
- return Array.from(inSet);
467
- const merged = new Set();
468
- inSet.forEach((id) => merged.add(id));
469
- outSet.forEach((id) => merged.add(id));
470
- return Array.from(merged);
471
- }
472
- successors(nodeId) {
473
- return this.neighbors(nodeId, 'out');
474
- }
475
- predecessors(nodeId) {
476
- return this.neighbors(nodeId, 'in');
477
- }
478
- setNodeOrder(nodes) {
479
- const next = new Map();
480
- for (const node of nodes)
481
- next.set(node.id, node);
482
- this.nodeMap = next;
483
- this.nodeIndexCache = undefined;
484
- this.indexNodeCache = undefined;
485
- }
486
- clearCache() {
487
- this.degreeCache = undefined;
488
- this.inAdjacencyCache = undefined;
489
- this.outAdjacencyCache = undefined;
490
- this.bothAdjacencyCache = undefined;
491
- this.nodeIndexCache = undefined;
492
- this.indexNodeCache = undefined;
493
377
  }
494
- buildDegreeCache() {
495
- this.degreeCache = new Map();
496
- for (const edge of this.edges()) {
497
- const { source, target } = edge;
498
- if (edge.source === edge.target)
499
- continue;
500
- if (!this.degreeCache.has(source)) {
501
- this.degreeCache.set(source, { in: 0, out: 0, both: 0 });
502
- }
503
- const sourceDeg = this.degreeCache.get(edge.source);
504
- if (sourceDeg) {
505
- sourceDeg.out++;
506
- sourceDeg.both++;
507
- }
508
- if (!this.degreeCache.has(target)) {
509
- this.degreeCache.set(target, { in: 0, out: 0, both: 0 });
510
- }
511
- const targetDeg = this.degreeCache.get(edge.target);
512
- if (targetDeg) {
513
- targetDeg.in++;
514
- targetDeg.both++;
515
- }
378
+ }
379
+
380
+ /**
381
+ * Floyd-Warshall algorithm to find shortest paths (but with no negative cycles).
382
+ */
383
+ const floydWarshall = (adjMatrix) => {
384
+ // initialize
385
+ const n = adjMatrix.length;
386
+ const dist = Array.from({ length: n }, () => new Array(n));
387
+ for (let i = 0; i < n; i++) {
388
+ const row = adjMatrix[i];
389
+ const drow = dist[i];
390
+ for (let j = 0; j < n; j++) {
391
+ drow[j] = i === j ? 0 : row[j] > 0 ? row[j] : Infinity;
516
392
  }
517
393
  }
518
- buildAdjacencyCache() {
519
- this.inAdjacencyCache = new Map();
520
- this.outAdjacencyCache = new Map();
521
- for (const edge of this.edges()) {
522
- if (!this.nodeMap.has(edge.source) || !this.nodeMap.has(edge.target))
394
+ // floyd
395
+ for (let k = 0; k < n; k++) {
396
+ const dk = dist[k];
397
+ for (let i = 0; i < n; i++) {
398
+ const di = dist[i];
399
+ const dik = di[k];
400
+ if (dik === Infinity)
523
401
  continue;
524
- if (!this.outAdjacencyCache.has(edge.source)) {
525
- this.outAdjacencyCache.set(edge.source, new Set());
526
- }
527
- this.outAdjacencyCache.get(edge.source).add(edge.target);
528
- if (!this.inAdjacencyCache.has(edge.target)) {
529
- this.inAdjacencyCache.set(edge.target, new Set());
402
+ for (let j = 0; j < n; j++) {
403
+ const dkj = dk[j];
404
+ if (dkj === Infinity)
405
+ continue;
406
+ const next = dik + dkj;
407
+ if (next < di[j]) {
408
+ di[j] = next;
409
+ }
530
410
  }
531
- this.inAdjacencyCache.get(edge.target).add(edge.source);
532
411
  }
533
412
  }
534
- buildNodeIndexCache() {
535
- this.nodeIndexCache = new Map();
536
- this.indexNodeCache = new Map();
537
- let index = 0;
538
- this.nodeMap.forEach((_node, nodeId) => {
539
- this.nodeIndexCache.set(nodeId, index);
540
- this.indexNodeCache.set(index, nodeId);
541
- index++;
542
- });
543
- }
544
- destroy() {
545
- this.clearCache();
546
- this.nodeMap.clear();
547
- this.edgeMap.clear();
548
- this.edgeIdCounter.clear();
549
- }
550
- }
551
- const nodeFields = [
552
- 'id',
553
- 'x',
554
- 'y',
555
- 'z',
556
- 'vx',
557
- 'vy',
558
- 'vz',
559
- 'fx',
560
- 'fy',
561
- 'fz',
562
- 'parentId',
563
- ];
564
- const edgeFields = ['id', 'source', 'target', 'points'];
565
- function extractNodeData(nodes, node) {
566
- if (!nodes) {
567
- throw new Error('Data.nodes is required');
568
- }
569
- const result = new Map();
570
- for (const datum of nodes) {
571
- const nodeData = { _original: datum };
572
- for (const field of nodeFields) {
573
- const value = datum[field];
574
- if (isNil(value))
575
- continue;
576
- nodeData[field] = value;
577
- }
578
- if (node) {
579
- const customFields = node(datum);
580
- for (const key in customFields) {
581
- const value = customFields[key];
582
- if (isNil(value))
583
- continue;
584
- nodeData[key] = value;
585
- }
413
+ return dist;
414
+ };
415
+ /**
416
+ * Get the adjacency matrix of the graph model.
417
+ */
418
+ const getAdjMatrix = (model, directed) => {
419
+ const n = model.nodeCount();
420
+ const matrix = Array.from({ length: n }, () => new Array(n));
421
+ // map node with index in data.nodes
422
+ const nodeMap = {};
423
+ let i = 0;
424
+ model.forEachNode((node) => {
425
+ nodeMap[node.id] = i++;
426
+ });
427
+ model.forEachEdge((e) => {
428
+ const sIndex = nodeMap[e.source];
429
+ const tIndex = nodeMap[e.target];
430
+ if (sIndex === undefined || tIndex === undefined)
431
+ return;
432
+ matrix[sIndex][tIndex] = 1;
433
+ if (!directed) {
434
+ matrix[tIndex][sIndex] = 1;
586
435
  }
587
- if (isNil(nodeData.id)) {
588
- throw new Error(`Node is missing id field`);
436
+ });
437
+ return matrix;
438
+ };
439
+ /**
440
+ * Get the adjacency list of the graph model.
441
+ */
442
+ const getAdjList = (model, directed) => {
443
+ const n = model.nodeCount();
444
+ const adjList = Array.from({ length: n }, () => []);
445
+ // map node with index
446
+ const nodeMap = {};
447
+ let idx = 0;
448
+ model.forEachNode((node) => {
449
+ nodeMap[node.id] = idx++;
450
+ });
451
+ model.forEachEdge((e) => {
452
+ const s = nodeMap[e.source];
453
+ const t = nodeMap[e.target];
454
+ if (s == null || t == null)
455
+ return;
456
+ adjList[s].push(t);
457
+ if (!directed)
458
+ adjList[t].push(s);
459
+ });
460
+ return adjList;
461
+ };
462
+ /**
463
+ * scale matrix
464
+ * @param matrix [ [], [], [] ]
465
+ * @param ratio
466
+ */
467
+ const scaleMatrix = (matrix, ratio) => {
468
+ const n = matrix.length;
469
+ const result = new Array(n);
470
+ for (let i = 0; i < n; i++) {
471
+ const row = matrix[i];
472
+ const m = row.length;
473
+ const newRow = new Array(m);
474
+ for (let j = 0; j < m; j++) {
475
+ newRow[j] = row[j] * ratio;
589
476
  }
590
- result.set(nodeData.id, nodeData);
477
+ result[i] = newRow;
591
478
  }
592
479
  return result;
593
- }
594
- function extractEdgeData(edges, edge, getEdgeId) {
595
- const result = new Map();
596
- for (const datum of edges) {
597
- const edgeData = { _original: datum };
598
- for (const field of edgeFields) {
599
- const value = datum[field];
600
- if (isNil(value))
601
- continue;
602
- edgeData[field] = value;
603
- }
604
- if (edge) {
605
- const customFields = edge(datum);
606
- for (const key in customFields) {
607
- const value = customFields[key];
608
- if (isNil(value))
609
- continue;
610
- edgeData[key] = value;
611
- }
480
+ };
481
+ /**
482
+ * calculate the bounding box for the nodes according to their x, y, and size
483
+ * @param nodes nodes in the layout
484
+ * @returns
485
+ */
486
+ const getLayoutBBox = (nodes) => {
487
+ let minX = Infinity;
488
+ let minY = Infinity;
489
+ let maxX = -Infinity;
490
+ let maxY = -Infinity;
491
+ nodes.forEach((node) => {
492
+ let size = node.data.size;
493
+ if (isArray(size)) {
494
+ if (size.length === 1)
495
+ size = [size[0], size[0]];
612
496
  }
613
- if (isNil(edgeData.source) || isNil(edgeData.target)) {
614
- throw new Error(`Edge is missing source or target field`);
497
+ else if (size === undefined || isNaN(size)) {
498
+ size = [30, 30];
615
499
  }
616
- if (isNil(edgeData.id)) {
617
- edgeData.id = getEdgeId === null || getEdgeId === void 0 ? void 0 : getEdgeId(datum);
500
+ else if (isNumber(size)) {
501
+ size = [size, size];
618
502
  }
619
- result.set(edgeData.id, edgeData);
503
+ const halfSize = [size[0] / 2, size[1] / 2];
504
+ const left = node.data.x - halfSize[0];
505
+ const right = node.data.x + halfSize[0];
506
+ const top = node.data.y - halfSize[1];
507
+ const bottom = node.data.y + halfSize[1];
508
+ if (minX > left)
509
+ minX = left;
510
+ if (minY > top)
511
+ minY = top;
512
+ if (maxX < right)
513
+ maxX = right;
514
+ if (maxY < bottom)
515
+ maxY = bottom;
516
+ });
517
+ return { minX, minY, maxX, maxY };
518
+ };
519
+ /**
520
+ * calculate the euclidean distance form p1 to p2
521
+ * @param p1
522
+ * @param p2
523
+ * @returns
524
+ */
525
+ const getEuclideanDistance = (p1, p2) => Math.sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
526
+ /**
527
+ * Depth first search begin from nodes in graphCore data.
528
+ * @param graphCore graphlib data structure
529
+ * @param nodes begin nodes
530
+ * @param fn will be called while visiting each node
531
+ * @param mode 'TB' - visit from top to bottom; 'BT' - visit from bottom to top;
532
+ * @returns
533
+ */
534
+ const graphTreeDfs = (graph, nodes, fn, mode = 'TB', treeKey, stopFns = {}) => {
535
+ if (!(nodes === null || nodes === void 0 ? void 0 : nodes.length))
536
+ return;
537
+ const { stopBranchFn, stopAllFn } = stopFns;
538
+ for (let i = 0; i < nodes.length; i++) {
539
+ const node = nodes[i];
540
+ if (!graph.hasNode(node.id))
541
+ continue;
542
+ if (stopBranchFn === null || stopBranchFn === void 0 ? void 0 : stopBranchFn(node))
543
+ continue; // Stop this branch
544
+ if (stopAllFn === null || stopAllFn === void 0 ? void 0 : stopAllFn(node))
545
+ return; // Stop all
546
+ if (mode === 'TB')
547
+ fn(node); // Traverse from top to bottom
548
+ graphTreeDfs(graph, graph.getChildren(node.id, treeKey), fn, mode, treeKey, stopFns);
549
+ if (mode !== 'TB')
550
+ fn(node); // Traverse from bottom to top
620
551
  }
621
- return result;
552
+ };
553
+ /**
554
+ * Use Johnson + Dijkstra to compute APSP for sparse graph.
555
+ * Fully compatible with floydWarshall(adjMatrix).
556
+ */
557
+ function johnson(adjList) {
558
+ const n = adjList.length;
559
+ // Step 1: add a dummy node q connected to all nodes with weight 0
560
+ new Array(n).fill(0);
561
+ // Bellman-Ford to compute potentials h(v)
562
+ // 因为权重全是 1,无负边,可直接跳过 BF,h 全 0 即可
563
+ // Step 2: reweight edges
564
+ // 因为 h(u)=h(v)=0,reweight 后仍然是 1,省略 reweight 过程
565
+ // Step 3: run Dijkstra from each node
566
+ const distAll = Array.from({ length: n }, () => new Array(n).fill(Infinity));
567
+ for (let s = 0; s < n; s++) {
568
+ distAll[s] = dijkstra(adjList, s);
569
+ }
570
+ return distAll;
622
571
  }
623
- function initNodePosition(model, width, height, dimensions = 2) {
624
- model.forEachNode((node) => {
625
- if (isNil(node.x)) {
626
- node.x = Math.random() * width;
627
- }
628
- if (isNil(node.y)) {
629
- node.y = Math.random() * height;
630
- }
631
- if (dimensions === 3 && isNil(node.z)) {
632
- node.z = Math.random() * Math.min(width, height);
572
+ /**
573
+ * Dijkstra algorithm to find shortest paths from source to all nodes.
574
+ */
575
+ function dijkstra(adjList, source) {
576
+ const n = adjList.length;
577
+ const dist = new Array(n).fill(Infinity);
578
+ dist[source] = 0;
579
+ // Minimal binary heap
580
+ const heap = new MinHeap();
581
+ heap.push([0, source]); // [distance, node]
582
+ while (!heap.empty()) {
583
+ const [d, u] = heap.pop();
584
+ if (d !== dist[u])
585
+ continue;
586
+ const neighbors = adjList[u];
587
+ for (let i = 0; i < neighbors.length; i++) {
588
+ const v = neighbors[i];
589
+ const nd = d + 1;
590
+ if (nd < dist[v]) {
591
+ dist[v] = nd;
592
+ heap.push([nd, v]);
593
+ }
633
594
  }
634
- });
595
+ }
596
+ return dist;
635
597
  }
636
-
637
- class RuntimeContext {
638
- constructor(data, options = {}) {
639
- this.graph = new GraphLib(data, options);
598
+ class MinHeap {
599
+ constructor() {
600
+ this.data = [];
640
601
  }
641
- export() {
642
- return this.graph.data();
602
+ push(item) {
603
+ this.data.push(item);
604
+ this.bubbleUp(this.data.length - 1);
643
605
  }
644
- replace(result) {
645
- this.graph.replace(result);
606
+ pop() {
607
+ const top = this.data[0];
608
+ const end = this.data.pop();
609
+ if (this.data.length > 0) {
610
+ this.data[0] = end;
611
+ this.bubbleDown(0);
612
+ }
613
+ return top;
646
614
  }
647
- forEachNode(callback) {
648
- this.graph.forEachNode(callback);
615
+ empty() {
616
+ return this.data.length === 0;
649
617
  }
650
- forEachEdge(callback) {
651
- this.graph.forEachEdge((edge, i) => {
652
- edge.sourceNode = this.graph.node(edge.source);
653
- edge.targetNode = this.graph.node(edge.target);
654
- callback(edge, i);
655
- });
618
+ bubbleUp(pos) {
619
+ const data = this.data;
620
+ while (pos > 0) {
621
+ const parent = (pos - 1) >> 1;
622
+ if (data[parent][0] <= data[pos][0])
623
+ break;
624
+ [data[parent], data[pos]] = [data[pos], data[parent]];
625
+ pos = parent;
626
+ }
656
627
  }
657
- destroy() {
658
- this.graph.destroy();
628
+ bubbleDown(pos) {
629
+ const data = this.data;
630
+ const length = data.length;
631
+ while (true) {
632
+ const left = pos * 2 + 1;
633
+ const right = pos * 2 + 2;
634
+ let min = pos;
635
+ if (left < length && data[left][0] < data[min][0])
636
+ min = left;
637
+ if (right < length && data[right][0] < data[min][0])
638
+ min = right;
639
+ if (min === pos)
640
+ break;
641
+ [data[pos], data[min]] = [data[min], data[pos]];
642
+ pos = min;
643
+ }
659
644
  }
660
645
  }
661
646
 
662
647
  /**
663
- * @license
664
- * Copyright 2019 Google LLC
665
- * SPDX-License-Identifier: Apache-2.0
648
+ * Get nested property value
649
+ * For example: getNestedValue(obj, 'a.b.c') will return obj.a.b.c
666
650
  */
667
- const proxyMarker = Symbol("Comlink.proxy");
668
- const createEndpoint = Symbol("Comlink.endpoint");
669
- const releaseProxy = Symbol("Comlink.releaseProxy");
670
- const finalizer = Symbol("Comlink.finalizer");
671
- const throwMarker = Symbol("Comlink.thrown");
672
- const isObject = (val) => (typeof val === "object" && val !== null) || typeof val === "function";
651
+ function getNestedValue(obj, path) {
652
+ const keys = String(path).split('.');
653
+ return get$1(obj, keys);
654
+ }
673
655
  /**
674
- * Internal transfer handle to handle objects marked to proxy.
656
+ * Set nested property value
657
+ * For example: setNestedValue(obj, 'a.b.c', value) will set obj.a.b.c = value
675
658
  */
676
- const proxyTransferHandler = {
677
- canHandle: (val) => isObject(val) && val[proxyMarker],
678
- serialize(obj) {
679
- const { port1, port2 } = new MessageChannel();
680
- expose(obj, port1);
681
- return [port2, [port2]];
682
- },
683
- deserialize(port) {
684
- port.start();
685
- return wrap(port);
686
- },
687
- };
659
+ function setNestedValue(obj, path, value) {
660
+ const keys = String(path).split('.');
661
+ set$1(obj, keys, value);
662
+ }
688
663
  /**
689
- * Internal transfer handler to handle thrown exceptions.
664
+ * Merge objects, but undefined values in source objects will not override existing values
665
+ * @param target - The target object
666
+ * @param sources - Source objects to merge
667
+ * @returns A new merged object
668
+ *
669
+ * @example
670
+ * assignDefined({ a: 1, b: 2 }, { b: undefined, c: 3 })
671
+ * // Returns: { a: 1, b: 2, c: 3 }
690
672
  */
691
- const throwTransferHandler = {
692
- canHandle: (value) => isObject(value) && throwMarker in value,
693
- serialize({ value }) {
694
- let serialized;
695
- if (value instanceof Error) {
696
- serialized = {
697
- isError: true,
698
- value: {
699
- message: value.message,
700
- name: value.name,
701
- stack: value.stack,
702
- },
703
- };
704
- }
705
- else {
706
- serialized = { isError: false, value };
707
- }
708
- return [serialized, []];
709
- },
710
- deserialize(serialized) {
711
- if (serialized.isError) {
712
- throw Object.assign(new Error(serialized.value.message), serialized.value);
673
+ function assignDefined(target, ...sources) {
674
+ sources.forEach((source) => {
675
+ if (source) {
676
+ Object.keys(source).forEach((key) => {
677
+ const value = source[key];
678
+ if (value !== undefined) {
679
+ target[key] = value;
680
+ }
681
+ });
713
682
  }
714
- throw serialized.value;
715
- },
716
- };
683
+ });
684
+ return target;
685
+ }
686
+
717
687
  /**
718
- * Allows customizing the serialization of certain values.
688
+ * 通用排序核心函数
719
689
  */
720
- const transferHandlers = new Map([
721
- ["proxy", proxyTransferHandler],
722
- ["throw", throwTransferHandler],
723
- ]);
724
- function isAllowedOrigin(allowedOrigins, origin) {
725
- for (const allowedOrigin of allowedOrigins) {
726
- if (origin === allowedOrigin || allowedOrigin === "*") {
727
- return true;
728
- }
729
- if (allowedOrigin instanceof RegExp && allowedOrigin.test(origin)) {
730
- return true;
731
- }
732
- }
733
- return false;
690
+ function sort$1(model, compareFn) {
691
+ const nodes = model.nodes();
692
+ nodes.sort(compareFn);
693
+ model.setNodeOrder(nodes);
694
+ return model;
734
695
  }
735
- function expose(obj, ep = globalThis, allowedOrigins = ["*"]) {
736
- ep.addEventListener("message", function callback(ev) {
737
- if (!ev || !ev.data) {
738
- return;
739
- }
740
- if (!isAllowedOrigin(allowedOrigins, ev.origin)) {
741
- console.warn(`Invalid origin '${ev.origin}' for comlink proxy`);
742
- return;
696
+ function orderByDegree(model) {
697
+ return sort$1(model, (nodeA, nodeB) => {
698
+ const degreeA = model.degree(nodeA.id);
699
+ const degreeB = model.degree(nodeB.id);
700
+ return degreeB - degreeA; // descending order
701
+ });
702
+ }
703
+ /**
704
+ * 按 ID 排序
705
+ */
706
+ function orderById(model) {
707
+ return sort$1(model, (nodeA, nodeB) => {
708
+ const idA = nodeA.id;
709
+ const idB = nodeB.id;
710
+ if (typeof idA === 'number' && typeof idB === 'number') {
711
+ return idA - idB;
743
712
  }
744
- const { id, type, path } = Object.assign({ path: [] }, ev.data);
745
- const argumentList = (ev.data.argumentList || []).map(fromWireValue);
746
- let returnValue;
747
- try {
748
- const parent = path.slice(0, -1).reduce((obj, prop) => obj[prop], obj);
749
- const rawValue = path.reduce((obj, prop) => obj[prop], obj);
750
- switch (type) {
751
- case "GET" /* MessageType.GET */:
752
- {
753
- returnValue = rawValue;
754
- }
755
- break;
756
- case "SET" /* MessageType.SET */:
757
- {
758
- parent[path.slice(-1)[0]] = fromWireValue(ev.data.value);
759
- returnValue = true;
760
- }
761
- break;
762
- case "APPLY" /* MessageType.APPLY */:
763
- {
764
- returnValue = rawValue.apply(parent, argumentList);
765
- }
766
- break;
767
- case "CONSTRUCT" /* MessageType.CONSTRUCT */:
768
- {
769
- const value = new rawValue(...argumentList);
770
- returnValue = proxy(value);
771
- }
772
- break;
773
- case "ENDPOINT" /* MessageType.ENDPOINT */:
774
- {
775
- const { port1, port2 } = new MessageChannel();
776
- expose(obj, port2);
777
- returnValue = transfer(port1, [port1]);
778
- }
779
- break;
780
- case "RELEASE" /* MessageType.RELEASE */:
781
- {
782
- returnValue = undefined;
783
- }
784
- break;
785
- default:
786
- return;
713
+ return String(idA).localeCompare(String(idB));
714
+ });
715
+ }
716
+ /**
717
+ * 按自定义比较函数排序
718
+ */
719
+ function orderBySorter(model, sorter) {
720
+ return sort$1(model, (nodeA, nodeB) => {
721
+ const a = model.originalNode(nodeA.id);
722
+ const b = model.originalNode(nodeB.id);
723
+ return sorter(a, b);
724
+ });
725
+ }
726
+ /**
727
+ * Order nodes according to graph topology
728
+ */
729
+ function orderByTopology(model, directed = false) {
730
+ const n = model.nodeCount();
731
+ if (n === 0)
732
+ return model;
733
+ const nodes = model.nodes();
734
+ const orderedNodes = [nodes[0]];
735
+ const pickFlags = {};
736
+ pickFlags[nodes[0].id] = true;
737
+ let k = 0;
738
+ let i = 0;
739
+ model.forEachNode((node) => {
740
+ if (i !== 0) {
741
+ const currentDegree = model.degree(node.id, 'both');
742
+ const nextDegree = i < n - 1 ? model.degree(nodes[i + 1].id, 'both') : 0;
743
+ const currentNodeId = orderedNodes[k].id;
744
+ const isNeighbor = model
745
+ .neighbors(currentNodeId, 'both')
746
+ .includes(node.id);
747
+ if ((i === n - 1 || currentDegree !== nextDegree || isNeighbor) &&
748
+ !pickFlags[node.id]) {
749
+ orderedNodes.push(node);
750
+ pickFlags[node.id] = true;
751
+ k++;
787
752
  }
788
- }
789
- catch (value) {
790
- returnValue = { value, [throwMarker]: 0 };
791
- }
792
- Promise.resolve(returnValue)
793
- .catch((value) => {
794
- return { value, [throwMarker]: 0 };
795
- })
796
- .then((returnValue) => {
797
- const [wireValue, transferables] = toWireValue(returnValue);
798
- ep.postMessage(Object.assign(Object.assign({}, wireValue), { id }), transferables);
799
- if (type === "RELEASE" /* MessageType.RELEASE */) {
800
- // detach and deactive after sending release response above.
801
- ep.removeEventListener("message", callback);
802
- closeEndPoint(ep);
803
- if (finalizer in obj && typeof obj[finalizer] === "function") {
804
- obj[finalizer]();
753
+ else {
754
+ const children = directed
755
+ ? model.successors(currentNodeId)
756
+ : model.neighbors(currentNodeId);
757
+ let foundChild = false;
758
+ for (let j = 0; j < children.length; j++) {
759
+ const childId = children[j];
760
+ const child = model.node(childId);
761
+ if (child &&
762
+ model.degree(childId) === model.degree(node.id) &&
763
+ !pickFlags[childId]) {
764
+ orderedNodes.push(child);
765
+ pickFlags[childId] = true;
766
+ foundChild = true;
767
+ break;
768
+ }
769
+ }
770
+ let ii = 0;
771
+ while (!foundChild) {
772
+ if (!pickFlags[nodes[ii].id]) {
773
+ orderedNodes.push(nodes[ii]);
774
+ pickFlags[nodes[ii].id] = true;
775
+ foundChild = true;
776
+ }
777
+ ii++;
778
+ if (ii === n) {
779
+ break;
780
+ }
805
781
  }
806
782
  }
807
- })
808
- .catch((error) => {
809
- // Send Serialization Error To Caller
810
- const [wireValue, transferables] = toWireValue({
811
- value: new TypeError("Unserializable return value"),
812
- [throwMarker]: 0,
813
- });
814
- ep.postMessage(Object.assign(Object.assign({}, wireValue), { id }), transferables);
815
- });
783
+ }
784
+ i++;
816
785
  });
817
- if (ep.start) {
818
- ep.start();
819
- }
786
+ // Update model with ordered nodes
787
+ model.setNodeOrder(orderedNodes);
788
+ return model;
820
789
  }
821
- function isMessagePort(endpoint) {
822
- return endpoint.constructor.name === "MessagePort";
790
+
791
+ function parsePoint(point) {
792
+ var _a;
793
+ return [point.x, point.y, (_a = point.z) !== null && _a !== void 0 ? _a : 0];
823
794
  }
824
- function closeEndPoint(endpoint) {
825
- if (isMessagePort(endpoint))
826
- endpoint.close();
795
+ function toPointObject(point) {
796
+ var _a;
797
+ return { x: point[0], y: point[1], z: (_a = point[2]) !== null && _a !== void 0 ? _a : 0 };
827
798
  }
828
- function wrap(ep, target) {
829
- const pendingListeners = new Map();
830
- ep.addEventListener("message", function handleMessage(ev) {
831
- const { data } = ev;
832
- if (!data || !data.id) {
833
- return;
834
- }
835
- const resolver = pendingListeners.get(data.id);
836
- if (!resolver) {
837
- return;
838
- }
839
- try {
840
- resolver(data);
841
- }
842
- finally {
843
- pendingListeners.delete(data.id);
844
- }
845
- });
846
- return createProxy(ep, pendingListeners, [], target);
799
+
800
+ function parseSize(size) {
801
+ if (!size)
802
+ return [0, 0, 0];
803
+ if (isNumber(size))
804
+ return [size, size, size];
805
+ else if (Array.isArray(size) && size.length === 0)
806
+ return [0, 0, 0];
807
+ const [x, y = x, z = x] = size;
808
+ return [x, y, z];
847
809
  }
848
- function throwIfProxyReleased(isReleased) {
849
- if (isReleased) {
850
- throw new Error("Proxy has been released and is not useable");
810
+
811
+ /**
812
+ * Viewport configuration such as width, height and center point.
813
+ */
814
+ const normalizeViewport = (options) => {
815
+ const { width, height, center } = options;
816
+ const normalizedWidth = width !== null && width !== void 0 ? width : (typeof window !== 'undefined' ? window.innerWidth : 0);
817
+ const normalizedHeight = height !== null && height !== void 0 ? height : (typeof window !== 'undefined' ? window.innerHeight : 0);
818
+ const centerPoint = center !== null && center !== void 0 ? center : [normalizedWidth / 2, normalizedHeight / 2];
819
+ return {
820
+ width: normalizedWidth,
821
+ height: normalizedHeight,
822
+ center: centerPoint,
823
+ };
824
+ };
825
+
826
+ /**
827
+ * Format value with multiple types into a function that returns a number
828
+ * @param value The value to be formatted
829
+ * @param defaultValue The default value when value is invalid
830
+ * @returns A function that returns a number
831
+ */
832
+ function formatNumberFn(value, defaultValue) {
833
+ // If value is a function, return it directly
834
+ if (isFunction(value)) {
835
+ return value;
836
+ }
837
+ // If value is a number, return a function that returns this number
838
+ if (isNumber(value)) {
839
+ return () => value;
851
840
  }
841
+ // For other cases (undefined or invalid values), return default value function
842
+ return () => defaultValue;
852
843
  }
853
- function releaseEndpoint(ep) {
854
- return requestResponseMessage(ep, new Map(), {
855
- type: "RELEASE" /* MessageType.RELEASE */,
856
- }).then(() => {
857
- closeEndPoint(ep);
858
- });
844
+ /**
845
+ * Format size config with multiple types into a function that returns a size
846
+ * @param value The value to be formatted
847
+ * @param defaultValue The default value when value is invalid
848
+ * @param resultIsNumber Whether to return a number (max of width/height) or size array
849
+ * @returns A function that returns a size
850
+ */
851
+ function formatSizeFn(value, defaultValue = 10) {
852
+ // If value is undefined, return default value function
853
+ if (!value) {
854
+ return () => defaultValue;
855
+ }
856
+ // If value is a function, return it directly
857
+ if (isFunction(value)) {
858
+ return value;
859
+ }
860
+ // If value is a number, return a function that returns this number
861
+ if (isNumber(value)) {
862
+ return () => value;
863
+ }
864
+ // If value is an array, return max or the array itself
865
+ if (Array.isArray(value)) {
866
+ return () => value;
867
+ }
868
+ // If value is an object with width and height
869
+ if (isObject$1(value) && value.width && value.height) {
870
+ return () => [value.width, value.height];
871
+ }
872
+ return () => defaultValue;
859
873
  }
860
- const proxyCounter = new WeakMap();
861
- const proxyFinalizers = "FinalizationRegistry" in globalThis &&
862
- new FinalizationRegistry((ep) => {
863
- const newCount = (proxyCounter.get(ep) || 0) - 1;
864
- proxyCounter.set(ep, newCount);
865
- if (newCount === 0) {
866
- releaseEndpoint(ep);
874
+ /**
875
+ * Format nodeSize and nodeSpacing into a function that returns the total size
876
+ * @param nodeSize The size of the node
877
+ * @param nodeSpacing The spacing around the node
878
+ * @param defaultNodeSize The default node size when value is invalid
879
+ * @returns A function that returns the total size (node size + spacing)
880
+ */
881
+ const formatNodeSizeFn = (nodeSize, nodeSpacing, defaultNodeSize = 10) => {
882
+ const nodeSpacingFunc = formatNumberFn(nodeSpacing, 0);
883
+ const nodeSizeFunc = formatSizeFn(nodeSize, defaultNodeSize);
884
+ return (node) => {
885
+ const size = nodeSizeFunc(node);
886
+ const spacing = nodeSpacingFunc(node);
887
+ return Math.max(...parseSize(size)) + spacing;
888
+ };
889
+ };
890
+
891
+ class GraphLib {
892
+ constructor(data, options = {}) {
893
+ this.edgeIdCounter = new Map();
894
+ this.nodeMap = extractNodeData(data.nodes, options.node);
895
+ this.edgeMap = extractEdgeData(data.edges || [], options.edge, this.getEdgeId.bind(this));
896
+ }
897
+ data() {
898
+ return { nodes: this.nodeMap, edges: this.edgeMap };
899
+ }
900
+ replace(result) {
901
+ this.nodeMap = result.nodes;
902
+ this.edgeMap = result.edges;
903
+ this.clearCache();
904
+ }
905
+ nodes() {
906
+ return Array.from(this.nodeMap.values());
907
+ }
908
+ node(id) {
909
+ return this.nodeMap.get(id);
910
+ }
911
+ nodeAt(index) {
912
+ if (!this.indexNodeCache) {
913
+ this.buildNodeIndexCache();
914
+ }
915
+ const nodeId = this.indexNodeCache.get(index);
916
+ return nodeId ? this.nodeMap.get(nodeId) : undefined;
917
+ }
918
+ nodeIndexOf(id) {
919
+ var _a;
920
+ if (!this.nodeIndexCache) {
921
+ this.buildNodeIndexCache();
922
+ }
923
+ return (_a = this.nodeIndexCache.get(id)) !== null && _a !== void 0 ? _a : -1;
924
+ }
925
+ firstNode() {
926
+ return this.nodeMap.values().next().value;
927
+ }
928
+ forEachNode(callback) {
929
+ let i = 0;
930
+ this.nodeMap.forEach((node) => callback(node, i++));
931
+ }
932
+ originalNode(id) {
933
+ const node = this.nodeMap.get(id);
934
+ return node === null || node === void 0 ? void 0 : node._original;
935
+ }
936
+ nodeCount() {
937
+ return this.nodeMap.size;
938
+ }
939
+ edges() {
940
+ return Array.from(this.edgeMap.values());
941
+ }
942
+ edge(id) {
943
+ return this.edgeMap.get(id);
944
+ }
945
+ firstEdge() {
946
+ return this.edgeMap.values().next().value;
947
+ }
948
+ forEachEdge(callback) {
949
+ let i = 0;
950
+ this.edgeMap.forEach((edge) => callback(edge, i++));
951
+ }
952
+ originalEdge(id) {
953
+ const edge = this.edgeMap.get(id);
954
+ return edge === null || edge === void 0 ? void 0 : edge._original;
955
+ }
956
+ edgeCount() {
957
+ return this.edgeMap.size;
958
+ }
959
+ getEdgeId(edge) {
960
+ if (edge.id)
961
+ return edge.id;
962
+ const baseId = `${edge.source}-${edge.target}`;
963
+ const count = this.edgeIdCounter.get(baseId) || 0;
964
+ const id = count === 0 ? baseId : `${baseId}-${count}`;
965
+ this.edgeIdCounter.set(baseId, count + 1);
966
+ return id;
967
+ }
968
+ degree(nodeId, direction = 'both') {
969
+ if (!this.degreeCache) {
970
+ this.buildDegreeCache();
971
+ }
972
+ const degree = this.degreeCache.get(nodeId);
973
+ if (!degree)
974
+ return 0;
975
+ return degree[direction];
976
+ }
977
+ neighbors(nodeId, direction = 'both') {
978
+ if (!this.outAdjacencyCache || !this.inAdjacencyCache) {
979
+ this.buildAdjacencyCache();
867
980
  }
868
- });
869
- function registerProxy(proxy, ep) {
870
- const newCount = (proxyCounter.get(ep) || 0) + 1;
871
- proxyCounter.set(ep, newCount);
872
- if (proxyFinalizers) {
873
- proxyFinalizers.register(proxy, ep, proxy);
981
+ if (direction === 'out') {
982
+ return Array.from(this.outAdjacencyCache.get(nodeId) || []);
983
+ }
984
+ if (direction === 'in') {
985
+ return Array.from(this.inAdjacencyCache.get(nodeId) || []);
986
+ }
987
+ if (this.bothAdjacencyCache) {
988
+ return Array.from(this.bothAdjacencyCache.get(nodeId) || []);
989
+ }
990
+ const inSet = this.inAdjacencyCache.get(nodeId);
991
+ const outSet = this.outAdjacencyCache.get(nodeId);
992
+ if (!inSet && !outSet)
993
+ return [];
994
+ if (!inSet)
995
+ return Array.from(outSet);
996
+ if (!outSet)
997
+ return Array.from(inSet);
998
+ const merged = new Set();
999
+ inSet.forEach((id) => merged.add(id));
1000
+ outSet.forEach((id) => merged.add(id));
1001
+ return Array.from(merged);
874
1002
  }
875
- }
876
- function unregisterProxy(proxy) {
877
- if (proxyFinalizers) {
878
- proxyFinalizers.unregister(proxy);
1003
+ successors(nodeId) {
1004
+ return this.neighbors(nodeId, 'out');
879
1005
  }
880
- }
881
- function createProxy(ep, pendingListeners, path = [], target = function () { }) {
882
- let isProxyReleased = false;
883
- const proxy = new Proxy(target, {
884
- get(_target, prop) {
885
- throwIfProxyReleased(isProxyReleased);
886
- if (prop === releaseProxy) {
887
- return () => {
888
- unregisterProxy(proxy);
889
- releaseEndpoint(ep);
890
- pendingListeners.clear();
891
- isProxyReleased = true;
892
- };
1006
+ predecessors(nodeId) {
1007
+ return this.neighbors(nodeId, 'in');
1008
+ }
1009
+ setNodeOrder(nodes) {
1010
+ const next = new Map();
1011
+ for (const node of nodes)
1012
+ next.set(node.id, node);
1013
+ this.nodeMap = next;
1014
+ this.nodeIndexCache = undefined;
1015
+ this.indexNodeCache = undefined;
1016
+ }
1017
+ clearCache() {
1018
+ this.degreeCache = undefined;
1019
+ this.inAdjacencyCache = undefined;
1020
+ this.outAdjacencyCache = undefined;
1021
+ this.bothAdjacencyCache = undefined;
1022
+ this.nodeIndexCache = undefined;
1023
+ this.indexNodeCache = undefined;
1024
+ }
1025
+ buildDegreeCache() {
1026
+ this.degreeCache = new Map();
1027
+ for (const edge of this.edges()) {
1028
+ const { source, target } = edge;
1029
+ if (edge.source === edge.target)
1030
+ continue;
1031
+ if (!this.degreeCache.has(source)) {
1032
+ this.degreeCache.set(source, { in: 0, out: 0, both: 0 });
893
1033
  }
894
- if (prop === "then") {
895
- if (path.length === 0) {
896
- return { then: () => proxy };
897
- }
898
- const r = requestResponseMessage(ep, pendingListeners, {
899
- type: "GET" /* MessageType.GET */,
900
- path: path.map((p) => p.toString()),
901
- }).then(fromWireValue);
902
- return r.then.bind(r);
1034
+ const sourceDeg = this.degreeCache.get(edge.source);
1035
+ if (sourceDeg) {
1036
+ sourceDeg.out++;
1037
+ sourceDeg.both++;
903
1038
  }
904
- return createProxy(ep, pendingListeners, [...path, prop]);
905
- },
906
- set(_target, prop, rawValue) {
907
- throwIfProxyReleased(isProxyReleased);
908
- // FIXME: ES6 Proxy Handler `set` methods are supposed to return a
909
- // boolean. To show good will, we return true asynchronously ¯\_(ツ)_/¯
910
- const [value, transferables] = toWireValue(rawValue);
911
- return requestResponseMessage(ep, pendingListeners, {
912
- type: "SET" /* MessageType.SET */,
913
- path: [...path, prop].map((p) => p.toString()),
914
- value,
915
- }, transferables).then(fromWireValue);
916
- },
917
- apply(_target, _thisArg, rawArgumentList) {
918
- throwIfProxyReleased(isProxyReleased);
919
- const last = path[path.length - 1];
920
- if (last === createEndpoint) {
921
- return requestResponseMessage(ep, pendingListeners, {
922
- type: "ENDPOINT" /* MessageType.ENDPOINT */,
923
- }).then(fromWireValue);
1039
+ if (!this.degreeCache.has(target)) {
1040
+ this.degreeCache.set(target, { in: 0, out: 0, both: 0 });
924
1041
  }
925
- // We just pretend that `bind()` didn’t happen.
926
- if (last === "bind") {
927
- return createProxy(ep, pendingListeners, path.slice(0, -1));
1042
+ const targetDeg = this.degreeCache.get(edge.target);
1043
+ if (targetDeg) {
1044
+ targetDeg.in++;
1045
+ targetDeg.both++;
928
1046
  }
929
- const [argumentList, transferables] = processArguments(rawArgumentList);
930
- return requestResponseMessage(ep, pendingListeners, {
931
- type: "APPLY" /* MessageType.APPLY */,
932
- path: path.map((p) => p.toString()),
933
- argumentList,
934
- }, transferables).then(fromWireValue);
935
- },
936
- construct(_target, rawArgumentList) {
937
- throwIfProxyReleased(isProxyReleased);
938
- const [argumentList, transferables] = processArguments(rawArgumentList);
939
- return requestResponseMessage(ep, pendingListeners, {
940
- type: "CONSTRUCT" /* MessageType.CONSTRUCT */,
941
- path: path.map((p) => p.toString()),
942
- argumentList,
943
- }, transferables).then(fromWireValue);
944
- },
945
- });
946
- registerProxy(proxy, ep);
947
- return proxy;
948
- }
949
- function myFlat(arr) {
950
- return Array.prototype.concat.apply([], arr);
951
- }
952
- function processArguments(argumentList) {
953
- const processed = argumentList.map(toWireValue);
954
- return [processed.map((v) => v[0]), myFlat(processed.map((v) => v[1]))];
955
- }
956
- const transferCache = new WeakMap();
957
- function transfer(obj, transfers) {
958
- transferCache.set(obj, transfers);
959
- return obj;
960
- }
961
- function proxy(obj) {
962
- return Object.assign(obj, { [proxyMarker]: true });
963
- }
964
- function toWireValue(value) {
965
- for (const [name, handler] of transferHandlers) {
966
- if (handler.canHandle(value)) {
967
- const [serializedValue, transferables] = handler.serialize(value);
968
- return [
969
- {
970
- type: "HANDLER" /* WireValueType.HANDLER */,
971
- name,
972
- value: serializedValue,
973
- },
974
- transferables,
975
- ];
976
- }
977
- }
978
- return [
979
- {
980
- type: "RAW" /* WireValueType.RAW */,
981
- value,
982
- },
983
- transferCache.get(value) || [],
984
- ];
985
- }
986
- function fromWireValue(value) {
987
- switch (value.type) {
988
- case "HANDLER" /* WireValueType.HANDLER */:
989
- return transferHandlers.get(value.name).deserialize(value.value);
990
- case "RAW" /* WireValueType.RAW */:
991
- return value.value;
992
- }
993
- }
994
- function requestResponseMessage(ep, pendingListeners, msg, transfers) {
995
- return new Promise((resolve) => {
996
- const id = generateUUID();
997
- pendingListeners.set(id, resolve);
998
- if (ep.start) {
999
- ep.start();
1000
1047
  }
1001
- ep.postMessage(Object.assign({ id }, msg), transfers);
1002
- });
1003
- }
1004
- function generateUUID() {
1005
- return new Array(4)
1006
- .fill(0)
1007
- .map(() => Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(16))
1008
- .join("-");
1009
- }
1010
-
1011
- class Supervisor {
1012
- constructor() {
1013
- this.worker = null;
1014
- this.workerApi = null;
1015
1048
  }
1016
- /**
1017
- * Execute layout in worker
1018
- */
1019
- execute(layoutId, data, options) {
1020
- return __awaiter(this, void 0, void 0, function* () {
1021
- if (!this.worker) {
1022
- yield this.initWorker();
1049
+ buildAdjacencyCache() {
1050
+ this.inAdjacencyCache = new Map();
1051
+ this.outAdjacencyCache = new Map();
1052
+ for (const edge of this.edges()) {
1053
+ if (!this.nodeMap.has(edge.source) || !this.nodeMap.has(edge.target))
1054
+ continue;
1055
+ if (!this.outAdjacencyCache.has(edge.source)) {
1056
+ this.outAdjacencyCache.set(edge.source, new Set());
1023
1057
  }
1024
- if (!this.workerApi) {
1025
- throw new Error('Worker API not initialized');
1058
+ this.outAdjacencyCache.get(edge.source).add(edge.target);
1059
+ if (!this.inAdjacencyCache.has(edge.target)) {
1060
+ this.inAdjacencyCache.set(edge.target, new Set());
1026
1061
  }
1027
- return yield this.workerApi.execute(layoutId, data, options);
1028
- });
1029
- }
1030
- /**
1031
- * Destroy worker
1032
- */
1033
- destroy() {
1034
- if (this.workerApi) {
1035
- this.workerApi.destroy();
1036
- }
1037
- if (this.worker) {
1038
- this.worker.terminate();
1039
- this.worker = null;
1040
- this.workerApi = null;
1062
+ this.inAdjacencyCache.get(edge.target).add(edge.source);
1041
1063
  }
1042
1064
  }
1043
- /**
1044
- * Initialize worker
1045
- */
1046
- initWorker() {
1047
- return __awaiter(this, void 0, void 0, function* () {
1048
- const workerPath = this.resolveWorkerPath();
1049
- const isESM = workerPath.includes('/lib/') || workerPath.endsWith('.mjs');
1050
- const type = isESM ? 'module' : 'classic';
1051
- this.worker = new Worker(workerPath, { type });
1052
- this.workerApi = wrap(this.worker);
1065
+ buildNodeIndexCache() {
1066
+ this.nodeIndexCache = new Map();
1067
+ this.indexNodeCache = new Map();
1068
+ let index = 0;
1069
+ this.nodeMap.forEach((_node, nodeId) => {
1070
+ this.nodeIndexCache.set(nodeId, index);
1071
+ this.indexNodeCache.set(index, nodeId);
1072
+ index++;
1053
1073
  });
1054
1074
  }
1055
- /**
1056
- * Resolve worker script path which works in both ESM and UMD environments
1057
- */
1058
- resolveWorkerPath() {
1059
- if (typeof ({ url: (typeof document === 'undefined' && typeof location === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : typeof document === 'undefined' ? location.href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.js', document.baseURI).href)) }) !== 'undefined' && (typeof document === 'undefined' && typeof location === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : typeof document === 'undefined' ? location.href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.js', document.baseURI).href))) {
1060
- const currentUrl = new URL((typeof document === 'undefined' && typeof location === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : typeof document === 'undefined' ? location.href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.js', document.baseURI).href)));
1061
- // e.g. `.../lib/runtime/supervisor.js` -> `.../lib/worker.js`
1062
- const asRoot = currentUrl.href.replace(/\/runtime\/[^/]+\.js$/, '/worker.js');
1063
- if (asRoot !== currentUrl.href)
1064
- return asRoot;
1065
- // Fallback: keep legacy behavior (same directory)
1066
- return currentUrl.href.replace(/\/[^/]+\.js$/, '/worker.js');
1075
+ destroy() {
1076
+ this.clearCache();
1077
+ this.nodeMap.clear();
1078
+ this.edgeMap.clear();
1079
+ this.edgeIdCounter.clear();
1080
+ }
1081
+ }
1082
+ const nodeFields = [
1083
+ 'id',
1084
+ 'x',
1085
+ 'y',
1086
+ 'z',
1087
+ 'vx',
1088
+ 'vy',
1089
+ 'vz',
1090
+ 'fx',
1091
+ 'fy',
1092
+ 'fz',
1093
+ 'parentId',
1094
+ ];
1095
+ const edgeFields = ['id', 'source', 'target', 'points'];
1096
+ function extractNodeData(nodes, node) {
1097
+ if (!nodes) {
1098
+ throw new Error('Data.nodes is required');
1099
+ }
1100
+ const result = new Map();
1101
+ for (const datum of nodes) {
1102
+ const nodeData = { _original: datum };
1103
+ for (const field of nodeFields) {
1104
+ const value = datum[field];
1105
+ if (isNil(value))
1106
+ continue;
1107
+ nodeData[field] = value;
1067
1108
  }
1068
- if (typeof document !== 'undefined') {
1069
- const scripts = document.getElementsByTagName('script');
1070
- for (let i = scripts.length - 1; i >= 0; i--) {
1071
- const src = scripts[i].src;
1072
- if (src && (src.includes('index.js') || src.includes('index.min.js'))) {
1073
- return src.replace(/index(\.min)?\.js/, 'worker.js');
1074
- }
1109
+ if (node) {
1110
+ const customFields = node(datum);
1111
+ for (const key in customFields) {
1112
+ const value = customFields[key];
1113
+ if (isNil(value))
1114
+ continue;
1115
+ nodeData[key] = value;
1075
1116
  }
1076
1117
  }
1077
- return './worker.js';
1078
- }
1079
- }
1080
-
1081
- const isArray = Array.isArray;
1082
-
1083
- /**
1084
- * Return the layout result for a graph with zero or one node.
1085
- * @param graph original graph
1086
- * @param center the layout center
1087
- * @returns layout result
1088
- */
1089
- function applySingleNodeLayout(model, center, dimensions = 2) {
1090
- const n = model.nodeCount();
1091
- if (n === 1) {
1092
- const first = model.firstNode();
1093
- first.x = center[0];
1094
- first.y = center[1];
1095
- if (dimensions === 3) {
1096
- first.z = center[2] || 0;
1118
+ if (isNil(nodeData.id)) {
1119
+ throw new Error(`Node is missing id field`);
1097
1120
  }
1121
+ result.set(nodeData.id, nodeData);
1098
1122
  }
1123
+ return result;
1099
1124
  }
1100
-
1101
- /**
1102
- * Floyd-Warshall algorithm to find shortest paths (but with no negative cycles).
1103
- */
1104
- const floydWarshall = (adjMatrix) => {
1105
- // initialize
1106
- const n = adjMatrix.length;
1107
- const dist = Array.from({ length: n }, () => new Array(n));
1108
- for (let i = 0; i < n; i++) {
1109
- const row = adjMatrix[i];
1110
- const drow = dist[i];
1111
- for (let j = 0; j < n; j++) {
1112
- drow[j] = i === j ? 0 : row[j] > 0 ? row[j] : Infinity;
1113
- }
1114
- }
1115
- // floyd
1116
- for (let k = 0; k < n; k++) {
1117
- const dk = dist[k];
1118
- for (let i = 0; i < n; i++) {
1119
- const di = dist[i];
1120
- const dik = di[k];
1121
- if (dik === Infinity)
1125
+ function extractEdgeData(edges, edge, getEdgeId) {
1126
+ const result = new Map();
1127
+ for (const datum of edges) {
1128
+ const edgeData = { _original: datum };
1129
+ for (const field of edgeFields) {
1130
+ const value = datum[field];
1131
+ if (isNil(value))
1122
1132
  continue;
1123
- for (let j = 0; j < n; j++) {
1124
- const dkj = dk[j];
1125
- if (dkj === Infinity)
1133
+ edgeData[field] = value;
1134
+ }
1135
+ if (edge) {
1136
+ const customFields = edge(datum);
1137
+ for (const key in customFields) {
1138
+ const value = customFields[key];
1139
+ if (isNil(value))
1126
1140
  continue;
1127
- const next = dik + dkj;
1128
- if (next < di[j]) {
1129
- di[j] = next;
1130
- }
1141
+ edgeData[key] = value;
1131
1142
  }
1132
1143
  }
1133
- }
1134
- return dist;
1135
- };
1136
- /**
1137
- * Get the adjacency matrix of the graph model.
1138
- */
1139
- const getAdjMatrix = (model, directed) => {
1140
- const n = model.nodeCount();
1141
- const matrix = Array.from({ length: n }, () => new Array(n));
1142
- // map node with index in data.nodes
1143
- const nodeMap = {};
1144
- let i = 0;
1145
- model.forEachNode((node) => {
1146
- nodeMap[node.id] = i++;
1147
- });
1148
- model.forEachEdge((e) => {
1149
- const sIndex = nodeMap[e.source];
1150
- const tIndex = nodeMap[e.target];
1151
- if (sIndex === undefined || tIndex === undefined)
1152
- return;
1153
- matrix[sIndex][tIndex] = 1;
1154
- if (!directed) {
1155
- matrix[tIndex][sIndex] = 1;
1144
+ if (isNil(edgeData.source) || isNil(edgeData.target)) {
1145
+ throw new Error(`Edge is missing source or target field`);
1156
1146
  }
1157
- });
1158
- return matrix;
1159
- };
1160
- /**
1161
- * Get the adjacency list of the graph model.
1162
- */
1163
- const getAdjList = (model, directed) => {
1164
- const n = model.nodeCount();
1165
- const adjList = Array.from({ length: n }, () => []);
1166
- // map node with index
1167
- const nodeMap = {};
1168
- let idx = 0;
1169
- model.forEachNode((node) => {
1170
- nodeMap[node.id] = idx++;
1171
- });
1172
- model.forEachEdge((e) => {
1173
- const s = nodeMap[e.source];
1174
- const t = nodeMap[e.target];
1175
- if (s == null || t == null)
1176
- return;
1177
- adjList[s].push(t);
1178
- if (!directed)
1179
- adjList[t].push(s);
1180
- });
1181
- return adjList;
1182
- };
1183
- /**
1184
- * scale matrix
1185
- * @param matrix [ [], [], [] ]
1186
- * @param ratio
1187
- */
1188
- const scaleMatrix = (matrix, ratio) => {
1189
- const n = matrix.length;
1190
- const result = new Array(n);
1191
- for (let i = 0; i < n; i++) {
1192
- const row = matrix[i];
1193
- const m = row.length;
1194
- const newRow = new Array(m);
1195
- for (let j = 0; j < m; j++) {
1196
- newRow[j] = row[j] * ratio;
1147
+ if (isNil(edgeData.id)) {
1148
+ edgeData.id = getEdgeId === null || getEdgeId === void 0 ? void 0 : getEdgeId(datum);
1197
1149
  }
1198
- result[i] = newRow;
1150
+ result.set(edgeData.id, edgeData);
1199
1151
  }
1200
1152
  return result;
1201
- };
1202
- /**
1203
- * calculate the bounding box for the nodes according to their x, y, and size
1204
- * @param nodes nodes in the layout
1205
- * @returns
1206
- */
1207
- const getLayoutBBox = (nodes) => {
1208
- let minX = Infinity;
1209
- let minY = Infinity;
1210
- let maxX = -Infinity;
1211
- let maxY = -Infinity;
1212
- nodes.forEach((node) => {
1213
- let size = node.data.size;
1214
- if (isArray(size)) {
1215
- if (size.length === 1)
1216
- size = [size[0], size[0]];
1153
+ }
1154
+ function initNodePosition(model, width, height, dimensions = 2) {
1155
+ model.forEachNode((node) => {
1156
+ if (isNil(node.x)) {
1157
+ node.x = Math.random() * width;
1217
1158
  }
1218
- else if (size === undefined || isNaN(size)) {
1219
- size = [30, 30];
1159
+ if (isNil(node.y)) {
1160
+ node.y = Math.random() * height;
1220
1161
  }
1221
- else if (isNumber(size)) {
1222
- size = [size, size];
1162
+ if (dimensions === 3 && isNil(node.z)) {
1163
+ node.z = Math.random() * Math.min(width, height);
1223
1164
  }
1224
- const halfSize = [size[0] / 2, size[1] / 2];
1225
- const left = node.data.x - halfSize[0];
1226
- const right = node.data.x + halfSize[0];
1227
- const top = node.data.y - halfSize[1];
1228
- const bottom = node.data.y + halfSize[1];
1229
- if (minX > left)
1230
- minX = left;
1231
- if (minY > top)
1232
- minY = top;
1233
- if (maxX < right)
1234
- maxX = right;
1235
- if (maxY < bottom)
1236
- maxY = bottom;
1237
1165
  });
1238
- return { minX, minY, maxX, maxY };
1239
- };
1166
+ }
1167
+
1168
+ class RuntimeContext {
1169
+ constructor(data, options = {}) {
1170
+ this.graph = new GraphLib(data, options);
1171
+ }
1172
+ export() {
1173
+ return this.graph.data();
1174
+ }
1175
+ replace(result) {
1176
+ this.graph.replace(result);
1177
+ }
1178
+ forEachNode(callback) {
1179
+ this.graph.forEachNode(callback);
1180
+ }
1181
+ forEachEdge(callback) {
1182
+ this.graph.forEachEdge((edge, i) => {
1183
+ edge.sourceNode = this.graph.node(edge.source);
1184
+ edge.targetNode = this.graph.node(edge.target);
1185
+ callback(edge, i);
1186
+ });
1187
+ }
1188
+ destroy() {
1189
+ this.graph.destroy();
1190
+ }
1191
+ }
1192
+
1240
1193
  /**
1241
- * calculate the euclidean distance form p1 to p2
1242
- * @param p1
1243
- * @param p2
1244
- * @returns
1194
+ * @license
1195
+ * Copyright 2019 Google LLC
1196
+ * SPDX-License-Identifier: Apache-2.0
1245
1197
  */
1246
- const getEuclideanDistance = (p1, p2) => Math.sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
1198
+ const proxyMarker = Symbol("Comlink.proxy");
1199
+ const createEndpoint = Symbol("Comlink.endpoint");
1200
+ const releaseProxy = Symbol("Comlink.releaseProxy");
1201
+ const finalizer = Symbol("Comlink.finalizer");
1202
+ const throwMarker = Symbol("Comlink.thrown");
1203
+ const isObject = (val) => (typeof val === "object" && val !== null) || typeof val === "function";
1247
1204
  /**
1248
- * Depth first search begin from nodes in graphCore data.
1249
- * @param graphCore graphlib data structure
1250
- * @param nodes begin nodes
1251
- * @param fn will be called while visiting each node
1252
- * @param mode 'TB' - visit from top to bottom; 'BT' - visit from bottom to top;
1253
- * @returns
1205
+ * Internal transfer handle to handle objects marked to proxy.
1254
1206
  */
1255
- const graphTreeDfs = (graph, nodes, fn, mode = 'TB', treeKey, stopFns = {}) => {
1256
- if (!(nodes === null || nodes === void 0 ? void 0 : nodes.length))
1257
- return;
1258
- const { stopBranchFn, stopAllFn } = stopFns;
1259
- for (let i = 0; i < nodes.length; i++) {
1260
- const node = nodes[i];
1261
- if (!graph.hasNode(node.id))
1262
- continue;
1263
- if (stopBranchFn === null || stopBranchFn === void 0 ? void 0 : stopBranchFn(node))
1264
- continue; // Stop this branch
1265
- if (stopAllFn === null || stopAllFn === void 0 ? void 0 : stopAllFn(node))
1266
- return; // Stop all
1267
- if (mode === 'TB')
1268
- fn(node); // Traverse from top to bottom
1269
- graphTreeDfs(graph, graph.getChildren(node.id, treeKey), fn, mode, treeKey, stopFns);
1270
- if (mode !== 'TB')
1271
- fn(node); // Traverse from bottom to top
1272
- }
1207
+ const proxyTransferHandler = {
1208
+ canHandle: (val) => isObject(val) && val[proxyMarker],
1209
+ serialize(obj) {
1210
+ const { port1, port2 } = new MessageChannel();
1211
+ expose(obj, port1);
1212
+ return [port2, [port2]];
1213
+ },
1214
+ deserialize(port) {
1215
+ port.start();
1216
+ return wrap(port);
1217
+ },
1273
1218
  };
1274
1219
  /**
1275
- * Use Johnson + Dijkstra to compute APSP for sparse graph.
1276
- * Fully compatible with floydWarshall(adjMatrix).
1220
+ * Internal transfer handler to handle thrown exceptions.
1277
1221
  */
1278
- function johnson(adjList) {
1279
- const n = adjList.length;
1280
- // Step 1: add a dummy node q connected to all nodes with weight 0
1281
- new Array(n).fill(0);
1282
- // Bellman-Ford to compute potentials h(v)
1283
- // 因为权重全是 1,无负边,可直接跳过 BF,h 全 0 即可
1284
- // Step 2: reweight edges
1285
- // 因为 h(u)=h(v)=0,reweight 后仍然是 1,省略 reweight 过程
1286
- // Step 3: run Dijkstra from each node
1287
- const distAll = Array.from({ length: n }, () => new Array(n).fill(Infinity));
1288
- for (let s = 0; s < n; s++) {
1289
- distAll[s] = dijkstra(adjList, s);
1290
- }
1291
- return distAll;
1292
- }
1222
+ const throwTransferHandler = {
1223
+ canHandle: (value) => isObject(value) && throwMarker in value,
1224
+ serialize({ value }) {
1225
+ let serialized;
1226
+ if (value instanceof Error) {
1227
+ serialized = {
1228
+ isError: true,
1229
+ value: {
1230
+ message: value.message,
1231
+ name: value.name,
1232
+ stack: value.stack,
1233
+ },
1234
+ };
1235
+ }
1236
+ else {
1237
+ serialized = { isError: false, value };
1238
+ }
1239
+ return [serialized, []];
1240
+ },
1241
+ deserialize(serialized) {
1242
+ if (serialized.isError) {
1243
+ throw Object.assign(new Error(serialized.value.message), serialized.value);
1244
+ }
1245
+ throw serialized.value;
1246
+ },
1247
+ };
1293
1248
  /**
1294
- * Dijkstra algorithm to find shortest paths from source to all nodes.
1249
+ * Allows customizing the serialization of certain values.
1295
1250
  */
1296
- function dijkstra(adjList, source) {
1297
- const n = adjList.length;
1298
- const dist = new Array(n).fill(Infinity);
1299
- dist[source] = 0;
1300
- // Minimal binary heap
1301
- const heap = new MinHeap();
1302
- heap.push([0, source]); // [distance, node]
1303
- while (!heap.empty()) {
1304
- const [d, u] = heap.pop();
1305
- if (d !== dist[u])
1306
- continue;
1307
- const neighbors = adjList[u];
1308
- for (let i = 0; i < neighbors.length; i++) {
1309
- const v = neighbors[i];
1310
- const nd = d + 1;
1311
- if (nd < dist[v]) {
1312
- dist[v] = nd;
1313
- heap.push([nd, v]);
1314
- }
1251
+ const transferHandlers = new Map([
1252
+ ["proxy", proxyTransferHandler],
1253
+ ["throw", throwTransferHandler],
1254
+ ]);
1255
+ function isAllowedOrigin(allowedOrigins, origin) {
1256
+ for (const allowedOrigin of allowedOrigins) {
1257
+ if (origin === allowedOrigin || allowedOrigin === "*") {
1258
+ return true;
1259
+ }
1260
+ if (allowedOrigin instanceof RegExp && allowedOrigin.test(origin)) {
1261
+ return true;
1315
1262
  }
1316
1263
  }
1317
- return dist;
1264
+ return false;
1318
1265
  }
1319
- class MinHeap {
1320
- constructor() {
1321
- this.data = [];
1322
- }
1323
- push(item) {
1324
- this.data.push(item);
1325
- this.bubbleUp(this.data.length - 1);
1326
- }
1327
- pop() {
1328
- const top = this.data[0];
1329
- const end = this.data.pop();
1330
- if (this.data.length > 0) {
1331
- this.data[0] = end;
1332
- this.bubbleDown(0);
1266
+ function expose(obj, ep = globalThis, allowedOrigins = ["*"]) {
1267
+ ep.addEventListener("message", function callback(ev) {
1268
+ if (!ev || !ev.data) {
1269
+ return;
1333
1270
  }
1334
- return top;
1335
- }
1336
- empty() {
1337
- return this.data.length === 0;
1338
- }
1339
- bubbleUp(pos) {
1340
- const data = this.data;
1341
- while (pos > 0) {
1342
- const parent = (pos - 1) >> 1;
1343
- if (data[parent][0] <= data[pos][0])
1344
- break;
1345
- [data[parent], data[pos]] = [data[pos], data[parent]];
1346
- pos = parent;
1271
+ if (!isAllowedOrigin(allowedOrigins, ev.origin)) {
1272
+ console.warn(`Invalid origin '${ev.origin}' for comlink proxy`);
1273
+ return;
1347
1274
  }
1348
- }
1349
- bubbleDown(pos) {
1350
- const data = this.data;
1351
- const length = data.length;
1352
- while (true) {
1353
- const left = pos * 2 + 1;
1354
- const right = pos * 2 + 2;
1355
- let min = pos;
1356
- if (left < length && data[left][0] < data[min][0])
1357
- min = left;
1358
- if (right < length && data[right][0] < data[min][0])
1359
- min = right;
1360
- if (min === pos)
1361
- break;
1362
- [data[pos], data[min]] = [data[min], data[pos]];
1363
- pos = min;
1275
+ const { id, type, path } = Object.assign({ path: [] }, ev.data);
1276
+ const argumentList = (ev.data.argumentList || []).map(fromWireValue);
1277
+ let returnValue;
1278
+ try {
1279
+ const parent = path.slice(0, -1).reduce((obj, prop) => obj[prop], obj);
1280
+ const rawValue = path.reduce((obj, prop) => obj[prop], obj);
1281
+ switch (type) {
1282
+ case "GET" /* MessageType.GET */:
1283
+ {
1284
+ returnValue = rawValue;
1285
+ }
1286
+ break;
1287
+ case "SET" /* MessageType.SET */:
1288
+ {
1289
+ parent[path.slice(-1)[0]] = fromWireValue(ev.data.value);
1290
+ returnValue = true;
1291
+ }
1292
+ break;
1293
+ case "APPLY" /* MessageType.APPLY */:
1294
+ {
1295
+ returnValue = rawValue.apply(parent, argumentList);
1296
+ }
1297
+ break;
1298
+ case "CONSTRUCT" /* MessageType.CONSTRUCT */:
1299
+ {
1300
+ const value = new rawValue(...argumentList);
1301
+ returnValue = proxy(value);
1302
+ }
1303
+ break;
1304
+ case "ENDPOINT" /* MessageType.ENDPOINT */:
1305
+ {
1306
+ const { port1, port2 } = new MessageChannel();
1307
+ expose(obj, port2);
1308
+ returnValue = transfer(port1, [port1]);
1309
+ }
1310
+ break;
1311
+ case "RELEASE" /* MessageType.RELEASE */:
1312
+ {
1313
+ returnValue = undefined;
1314
+ }
1315
+ break;
1316
+ default:
1317
+ return;
1318
+ }
1319
+ }
1320
+ catch (value) {
1321
+ returnValue = { value, [throwMarker]: 0 };
1364
1322
  }
1323
+ Promise.resolve(returnValue)
1324
+ .catch((value) => {
1325
+ return { value, [throwMarker]: 0 };
1326
+ })
1327
+ .then((returnValue) => {
1328
+ const [wireValue, transferables] = toWireValue(returnValue);
1329
+ ep.postMessage(Object.assign(Object.assign({}, wireValue), { id }), transferables);
1330
+ if (type === "RELEASE" /* MessageType.RELEASE */) {
1331
+ // detach and deactive after sending release response above.
1332
+ ep.removeEventListener("message", callback);
1333
+ closeEndPoint(ep);
1334
+ if (finalizer in obj && typeof obj[finalizer] === "function") {
1335
+ obj[finalizer]();
1336
+ }
1337
+ }
1338
+ })
1339
+ .catch((error) => {
1340
+ // Send Serialization Error To Caller
1341
+ const [wireValue, transferables] = toWireValue({
1342
+ value: new TypeError("Unserializable return value"),
1343
+ [throwMarker]: 0,
1344
+ });
1345
+ ep.postMessage(Object.assign(Object.assign({}, wireValue), { id }), transferables);
1346
+ });
1347
+ });
1348
+ if (ep.start) {
1349
+ ep.start();
1365
1350
  }
1366
1351
  }
1367
-
1368
- /**
1369
- * Get nested property value
1370
- * For example: getNestedValue(obj, 'a.b.c') will return obj.a.b.c
1371
- */
1372
- function getNestedValue(obj, path) {
1373
- const keys = String(path).split('.');
1374
- return get$1(obj, keys);
1352
+ function isMessagePort(endpoint) {
1353
+ return endpoint.constructor.name === "MessagePort";
1375
1354
  }
1376
- /**
1377
- * Set nested property value
1378
- * For example: setNestedValue(obj, 'a.b.c', value) will set obj.a.b.c = value
1379
- */
1380
- function setNestedValue(obj, path, value) {
1381
- const keys = String(path).split('.');
1382
- set$1(obj, keys, value);
1355
+ function closeEndPoint(endpoint) {
1356
+ if (isMessagePort(endpoint))
1357
+ endpoint.close();
1383
1358
  }
1384
- /**
1385
- * Merge objects, but undefined values in source objects will not override existing values
1386
- * @param target - The target object
1387
- * @param sources - Source objects to merge
1388
- * @returns A new merged object
1389
- *
1390
- * @example
1391
- * assignDefined({ a: 1, b: 2 }, { b: undefined, c: 3 })
1392
- * // Returns: { a: 1, b: 2, c: 3 }
1393
- */
1394
- function assignDefined(target, ...sources) {
1395
- sources.forEach((source) => {
1396
- if (source) {
1397
- Object.keys(source).forEach((key) => {
1398
- const value = source[key];
1399
- if (value !== undefined) {
1400
- target[key] = value;
1401
- }
1402
- });
1359
+ function wrap(ep, target) {
1360
+ const pendingListeners = new Map();
1361
+ ep.addEventListener("message", function handleMessage(ev) {
1362
+ const { data } = ev;
1363
+ if (!data || !data.id) {
1364
+ return;
1365
+ }
1366
+ const resolver = pendingListeners.get(data.id);
1367
+ if (!resolver) {
1368
+ return;
1369
+ }
1370
+ try {
1371
+ resolver(data);
1372
+ }
1373
+ finally {
1374
+ pendingListeners.delete(data.id);
1403
1375
  }
1404
1376
  });
1405
- return target;
1406
- }
1407
- function mergeOptions(base, patch) {
1408
- return Object.assign({}, base, patch || {});
1377
+ return createProxy(ep, pendingListeners, [], target);
1409
1378
  }
1410
-
1411
- /**
1412
- * 通用排序核心函数
1413
- */
1414
- function sort$1(model, compareFn) {
1415
- const nodes = model.nodes();
1416
- nodes.sort(compareFn);
1417
- model.setNodeOrder(nodes);
1418
- return model;
1379
+ function throwIfProxyReleased(isReleased) {
1380
+ if (isReleased) {
1381
+ throw new Error("Proxy has been released and is not useable");
1382
+ }
1419
1383
  }
1420
- function orderByDegree(model) {
1421
- return sort$1(model, (nodeA, nodeB) => {
1422
- const degreeA = model.degree(nodeA.id);
1423
- const degreeB = model.degree(nodeB.id);
1424
- return degreeB - degreeA; // descending order
1384
+ function releaseEndpoint(ep) {
1385
+ return requestResponseMessage(ep, new Map(), {
1386
+ type: "RELEASE" /* MessageType.RELEASE */,
1387
+ }).then(() => {
1388
+ closeEndPoint(ep);
1425
1389
  });
1426
1390
  }
1427
- /**
1428
- * ID 排序
1429
- */
1430
- function orderById(model) {
1431
- return sort$1(model, (nodeA, nodeB) => {
1432
- const idA = nodeA.id;
1433
- const idB = nodeB.id;
1434
- if (typeof idA === 'number' && typeof idB === 'number') {
1435
- return idA - idB;
1391
+ const proxyCounter = new WeakMap();
1392
+ const proxyFinalizers = "FinalizationRegistry" in globalThis &&
1393
+ new FinalizationRegistry((ep) => {
1394
+ const newCount = (proxyCounter.get(ep) || 0) - 1;
1395
+ proxyCounter.set(ep, newCount);
1396
+ if (newCount === 0) {
1397
+ releaseEndpoint(ep);
1436
1398
  }
1437
- return String(idA).localeCompare(String(idB));
1438
1399
  });
1400
+ function registerProxy(proxy, ep) {
1401
+ const newCount = (proxyCounter.get(ep) || 0) + 1;
1402
+ proxyCounter.set(ep, newCount);
1403
+ if (proxyFinalizers) {
1404
+ proxyFinalizers.register(proxy, ep, proxy);
1405
+ }
1439
1406
  }
1440
- /**
1441
- * 按自定义比较函数排序
1442
- */
1443
- function orderBySorter(model, sorter) {
1444
- return sort$1(model, (nodeA, nodeB) => {
1445
- const a = model.originalNode(nodeA.id);
1446
- const b = model.originalNode(nodeB.id);
1447
- return sorter(a, b);
1448
- });
1407
+ function unregisterProxy(proxy) {
1408
+ if (proxyFinalizers) {
1409
+ proxyFinalizers.unregister(proxy);
1410
+ }
1449
1411
  }
1450
- /**
1451
- * Order nodes according to graph topology
1452
- */
1453
- function orderByTopology(model, directed = false) {
1454
- const n = model.nodeCount();
1455
- if (n === 0)
1456
- return model;
1457
- const nodes = model.nodes();
1458
- const orderedNodes = [nodes[0]];
1459
- const pickFlags = {};
1460
- pickFlags[nodes[0].id] = true;
1461
- let k = 0;
1462
- let i = 0;
1463
- model.forEachNode((node) => {
1464
- if (i !== 0) {
1465
- const currentDegree = model.degree(node.id, 'both');
1466
- const nextDegree = i < n - 1 ? model.degree(nodes[i + 1].id, 'both') : 0;
1467
- const currentNodeId = orderedNodes[k].id;
1468
- const isNeighbor = model
1469
- .neighbors(currentNodeId, 'both')
1470
- .includes(node.id);
1471
- if ((i === n - 1 || currentDegree !== nextDegree || isNeighbor) &&
1472
- !pickFlags[node.id]) {
1473
- orderedNodes.push(node);
1474
- pickFlags[node.id] = true;
1475
- k++;
1412
+ function createProxy(ep, pendingListeners, path = [], target = function () { }) {
1413
+ let isProxyReleased = false;
1414
+ const proxy = new Proxy(target, {
1415
+ get(_target, prop) {
1416
+ throwIfProxyReleased(isProxyReleased);
1417
+ if (prop === releaseProxy) {
1418
+ return () => {
1419
+ unregisterProxy(proxy);
1420
+ releaseEndpoint(ep);
1421
+ pendingListeners.clear();
1422
+ isProxyReleased = true;
1423
+ };
1476
1424
  }
1477
- else {
1478
- const children = directed
1479
- ? model.successors(currentNodeId)
1480
- : model.neighbors(currentNodeId);
1481
- let foundChild = false;
1482
- for (let j = 0; j < children.length; j++) {
1483
- const childId = children[j];
1484
- const child = model.node(childId);
1485
- if (child &&
1486
- model.degree(childId) === model.degree(node.id) &&
1487
- !pickFlags[childId]) {
1488
- orderedNodes.push(child);
1489
- pickFlags[childId] = true;
1490
- foundChild = true;
1491
- break;
1492
- }
1493
- }
1494
- let ii = 0;
1495
- while (!foundChild) {
1496
- if (!pickFlags[nodes[ii].id]) {
1497
- orderedNodes.push(nodes[ii]);
1498
- pickFlags[nodes[ii].id] = true;
1499
- foundChild = true;
1500
- }
1501
- ii++;
1502
- if (ii === n) {
1503
- break;
1504
- }
1425
+ if (prop === "then") {
1426
+ if (path.length === 0) {
1427
+ return { then: () => proxy };
1505
1428
  }
1429
+ const r = requestResponseMessage(ep, pendingListeners, {
1430
+ type: "GET" /* MessageType.GET */,
1431
+ path: path.map((p) => p.toString()),
1432
+ }).then(fromWireValue);
1433
+ return r.then.bind(r);
1434
+ }
1435
+ return createProxy(ep, pendingListeners, [...path, prop]);
1436
+ },
1437
+ set(_target, prop, rawValue) {
1438
+ throwIfProxyReleased(isProxyReleased);
1439
+ // FIXME: ES6 Proxy Handler `set` methods are supposed to return a
1440
+ // boolean. To show good will, we return true asynchronously ¯\_(ツ)_/¯
1441
+ const [value, transferables] = toWireValue(rawValue);
1442
+ return requestResponseMessage(ep, pendingListeners, {
1443
+ type: "SET" /* MessageType.SET */,
1444
+ path: [...path, prop].map((p) => p.toString()),
1445
+ value,
1446
+ }, transferables).then(fromWireValue);
1447
+ },
1448
+ apply(_target, _thisArg, rawArgumentList) {
1449
+ throwIfProxyReleased(isProxyReleased);
1450
+ const last = path[path.length - 1];
1451
+ if (last === createEndpoint) {
1452
+ return requestResponseMessage(ep, pendingListeners, {
1453
+ type: "ENDPOINT" /* MessageType.ENDPOINT */,
1454
+ }).then(fromWireValue);
1455
+ }
1456
+ // We just pretend that `bind()` didn’t happen.
1457
+ if (last === "bind") {
1458
+ return createProxy(ep, pendingListeners, path.slice(0, -1));
1506
1459
  }
1460
+ const [argumentList, transferables] = processArguments(rawArgumentList);
1461
+ return requestResponseMessage(ep, pendingListeners, {
1462
+ type: "APPLY" /* MessageType.APPLY */,
1463
+ path: path.map((p) => p.toString()),
1464
+ argumentList,
1465
+ }, transferables).then(fromWireValue);
1466
+ },
1467
+ construct(_target, rawArgumentList) {
1468
+ throwIfProxyReleased(isProxyReleased);
1469
+ const [argumentList, transferables] = processArguments(rawArgumentList);
1470
+ return requestResponseMessage(ep, pendingListeners, {
1471
+ type: "CONSTRUCT" /* MessageType.CONSTRUCT */,
1472
+ path: path.map((p) => p.toString()),
1473
+ argumentList,
1474
+ }, transferables).then(fromWireValue);
1475
+ },
1476
+ });
1477
+ registerProxy(proxy, ep);
1478
+ return proxy;
1479
+ }
1480
+ function myFlat(arr) {
1481
+ return Array.prototype.concat.apply([], arr);
1482
+ }
1483
+ function processArguments(argumentList) {
1484
+ const processed = argumentList.map(toWireValue);
1485
+ return [processed.map((v) => v[0]), myFlat(processed.map((v) => v[1]))];
1486
+ }
1487
+ const transferCache = new WeakMap();
1488
+ function transfer(obj, transfers) {
1489
+ transferCache.set(obj, transfers);
1490
+ return obj;
1491
+ }
1492
+ function proxy(obj) {
1493
+ return Object.assign(obj, { [proxyMarker]: true });
1494
+ }
1495
+ function toWireValue(value) {
1496
+ for (const [name, handler] of transferHandlers) {
1497
+ if (handler.canHandle(value)) {
1498
+ const [serializedValue, transferables] = handler.serialize(value);
1499
+ return [
1500
+ {
1501
+ type: "HANDLER" /* WireValueType.HANDLER */,
1502
+ name,
1503
+ value: serializedValue,
1504
+ },
1505
+ transferables,
1506
+ ];
1507
1507
  }
1508
- i++;
1509
- });
1510
- // Update model with ordered nodes
1511
- model.setNodeOrder(orderedNodes);
1512
- return model;
1508
+ }
1509
+ return [
1510
+ {
1511
+ type: "RAW" /* WireValueType.RAW */,
1512
+ value,
1513
+ },
1514
+ transferCache.get(value) || [],
1515
+ ];
1513
1516
  }
1514
-
1515
- function parsePoint(point) {
1516
- var _a;
1517
- return [point.x, point.y, (_a = point.z) !== null && _a !== void 0 ? _a : 0];
1517
+ function fromWireValue(value) {
1518
+ switch (value.type) {
1519
+ case "HANDLER" /* WireValueType.HANDLER */:
1520
+ return transferHandlers.get(value.name).deserialize(value.value);
1521
+ case "RAW" /* WireValueType.RAW */:
1522
+ return value.value;
1523
+ }
1518
1524
  }
1519
- function toPointObject(point) {
1520
- var _a;
1521
- return { x: point[0], y: point[1], z: (_a = point[2]) !== null && _a !== void 0 ? _a : 0 };
1525
+ function requestResponseMessage(ep, pendingListeners, msg, transfers) {
1526
+ return new Promise((resolve) => {
1527
+ const id = generateUUID();
1528
+ pendingListeners.set(id, resolve);
1529
+ if (ep.start) {
1530
+ ep.start();
1531
+ }
1532
+ ep.postMessage(Object.assign({ id }, msg), transfers);
1533
+ });
1522
1534
  }
1523
-
1524
- function parseSize(size) {
1525
- if (!size)
1526
- return [0, 0, 0];
1527
- if (isNumber(size))
1528
- return [size, size, size];
1529
- else if (Array.isArray(size) && size.length === 0)
1530
- return [0, 0, 0];
1531
- const [x, y = x, z = x] = size;
1532
- return [x, y, z];
1535
+ function generateUUID() {
1536
+ return new Array(4)
1537
+ .fill(0)
1538
+ .map(() => Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(16))
1539
+ .join("-");
1533
1540
  }
1534
1541
 
1535
- /**
1536
- * Viewport configuration such as width, height and center point.
1537
- */
1538
- const normalizeViewport = (options) => {
1539
- const { width, height, center } = options;
1540
- const normalizedWidth = width !== null && width !== void 0 ? width : (typeof window !== 'undefined' ? window.innerWidth : 0);
1541
- const normalizedHeight = height !== null && height !== void 0 ? height : (typeof window !== 'undefined' ? window.innerHeight : 0);
1542
- const centerPoint = center !== null && center !== void 0 ? center : [normalizedWidth / 2, normalizedHeight / 2];
1543
- return {
1544
- width: normalizedWidth,
1545
- height: normalizedHeight,
1546
- center: centerPoint,
1547
- };
1548
- };
1542
+ class Supervisor {
1543
+ constructor() {
1544
+ this.worker = null;
1545
+ this.workerApi = null;
1546
+ }
1547
+ /**
1548
+ * Execute layout in worker
1549
+ */
1550
+ execute(layoutId, data, options) {
1551
+ return __awaiter(this, void 0, void 0, function* () {
1552
+ if (!this.worker) {
1553
+ yield this.initWorker();
1554
+ }
1555
+ if (!this.workerApi) {
1556
+ throw new Error('Worker API not initialized');
1557
+ }
1558
+ return yield this.workerApi.execute(layoutId, data, options);
1559
+ });
1560
+ }
1561
+ /**
1562
+ * Destroy worker
1563
+ */
1564
+ destroy() {
1565
+ if (this.workerApi) {
1566
+ this.workerApi.destroy();
1567
+ }
1568
+ if (this.worker) {
1569
+ this.worker.terminate();
1570
+ this.worker = null;
1571
+ this.workerApi = null;
1572
+ }
1573
+ }
1574
+ /**
1575
+ * Initialize worker
1576
+ */
1577
+ initWorker() {
1578
+ return __awaiter(this, void 0, void 0, function* () {
1579
+ const workerPath = this.resolveWorkerPath();
1580
+ const isESM = workerPath.includes('/lib/') || workerPath.endsWith('.mjs');
1581
+ const type = isESM ? 'module' : 'classic';
1582
+ this.worker = new Worker(workerPath, { type });
1583
+ this.workerApi = wrap(this.worker);
1584
+ });
1585
+ }
1586
+ /**
1587
+ * Resolve worker script path which works in both ESM and UMD environments
1588
+ */
1589
+ resolveWorkerPath() {
1590
+ if (typeof ({ url: (typeof document === 'undefined' && typeof location === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : typeof document === 'undefined' ? location.href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.js', document.baseURI).href)) }) !== 'undefined' && (typeof document === 'undefined' && typeof location === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : typeof document === 'undefined' ? location.href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.js', document.baseURI).href))) {
1591
+ const currentUrl = new URL((typeof document === 'undefined' && typeof location === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : typeof document === 'undefined' ? location.href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.js', document.baseURI).href)));
1592
+ // e.g. `.../lib/runtime/supervisor.js` -> `.../lib/worker.js`
1593
+ const asRoot = currentUrl.href.replace(/\/runtime\/[^/]+\.js$/, '/worker.js');
1594
+ if (asRoot !== currentUrl.href)
1595
+ return asRoot;
1596
+ // Fallback: keep legacy behavior (same directory)
1597
+ return currentUrl.href.replace(/\/[^/]+\.js$/, '/worker.js');
1598
+ }
1599
+ if (typeof document !== 'undefined') {
1600
+ const scripts = document.getElementsByTagName('script');
1601
+ for (let i = scripts.length - 1; i >= 0; i--) {
1602
+ const src = scripts[i].src;
1603
+ if (src && (src.includes('index.js') || src.includes('index.min.js'))) {
1604
+ return src.replace(/index(\.min)?\.js/, 'worker.js');
1605
+ }
1606
+ }
1607
+ }
1608
+ return './worker.js';
1609
+ }
1610
+ }
1549
1611
 
1550
1612
  /**
1551
1613
  * <zh/> 布局基类
@@ -1555,14 +1617,17 @@
1555
1617
  class BaseLayout {
1556
1618
  constructor(options) {
1557
1619
  this.supervisor = null;
1558
- this.initialOptions = mergeOptions(this.getDefaultOptions(), options);
1620
+ this.initialOptions = this.mergeOptions(this.getDefaultOptions(), options);
1559
1621
  }
1560
1622
  get options() {
1561
1623
  return this.runtimeOptions || this.initialOptions;
1562
1624
  }
1625
+ mergeOptions(base, patch) {
1626
+ return Object.assign({}, base, patch || {});
1627
+ }
1563
1628
  execute(data, userOptions) {
1564
1629
  return __awaiter(this, void 0, void 0, function* () {
1565
- this.runtimeOptions = mergeOptions(this.initialOptions, userOptions);
1630
+ this.runtimeOptions = this.mergeOptions(this.initialOptions, userOptions);
1566
1631
  const { node, edge, enableWorker } = this.runtimeOptions;
1567
1632
  this.context = new RuntimeContext(data, { node, edge });
1568
1633
  this.model = this.context.graph;
@@ -1627,71 +1692,6 @@
1627
1692
  return !!layout.tick && !!layout.stop;
1628
1693
  }
1629
1694
 
1630
- /**
1631
- * Format value with multiple types into a function that returns a number
1632
- * @param value The value to be formatted
1633
- * @param defaultValue The default value when value is invalid
1634
- * @returns A function that returns a number
1635
- */
1636
- function formatNumberFn(value, defaultValue) {
1637
- // If value is a function, return it directly
1638
- if (isFunction(value)) {
1639
- return value;
1640
- }
1641
- // If value is a number, return a function that returns this number
1642
- if (isNumber(value)) {
1643
- return () => value;
1644
- }
1645
- // For other cases (undefined or invalid values), return default value function
1646
- return () => defaultValue;
1647
- }
1648
- /**
1649
- * Format size config with multiple types into a function that returns a size
1650
- * @param value The value to be formatted
1651
- * @param defaultValue The default value when value is invalid
1652
- * @param resultIsNumber Whether to return a number (max of width/height) or size array
1653
- * @returns A function that returns a size
1654
- */
1655
- function formatSizeFn(value, defaultValue = 10) {
1656
- // If value is undefined, return default value function
1657
- if (!value) {
1658
- return () => defaultValue;
1659
- }
1660
- // If value is a function, return it directly
1661
- if (isFunction(value)) {
1662
- return value;
1663
- }
1664
- // If value is a number, return a function that returns this number
1665
- if (isNumber(value)) {
1666
- return () => value;
1667
- }
1668
- // If value is an array, return max or the array itself
1669
- if (Array.isArray(value)) {
1670
- return () => value;
1671
- }
1672
- // If value is an object with width and height
1673
- if (isObject$1(value) && value.width && value.height) {
1674
- return () => [value.width, value.height];
1675
- }
1676
- return () => defaultValue;
1677
- }
1678
- /**
1679
- * Format nodeSize and nodeSpacing into a function that returns the total size
1680
- * @param nodeSize The size of the node
1681
- * @param nodeSpacing The spacing around the node
1682
- * @param defaultNodeSize The default node size when value is invalid
1683
- * @returns A function that returns the total size (node size + spacing)
1684
- */
1685
- const formatNodeSizeFn = (nodeSize, nodeSpacing, defaultNodeSize = 10) => {
1686
- const nodeSpacingFunc = formatNumberFn(nodeSpacing, 0);
1687
- const nodeSizeFunc = formatSizeFn(nodeSize, defaultNodeSize);
1688
- return (node) => {
1689
- const size = nodeSizeFunc(node);
1690
- const spacing = nodeSpacingFunc(node);
1691
- return Math.max(...parseSize(size)) + spacing;
1692
- };
1693
- };
1694
-
1695
1695
  /**
1696
1696
  * <zh/> 内部图数据结构,用于 antv-dagre 布局算法
1697
1697
  *
@@ -5131,9 +5131,9 @@
5131
5131
  return __awaiter(this, void 0, void 0, function* () {
5132
5132
  const { nodeSize, align, rankdir = 'TB', ranksep, nodesep, edgeLabelSpace, ranker = 'tight-tree', nodeOrder, begin, controlPoints, radial, sortByCombo,
5133
5133
  // focusNode,
5134
- preset, } = options;
5135
- const ranksepfunc = formatNumberFn(ranksep, DEFAULTS_LAYOUT_OPTIONS$8.ranksep);
5136
- const nodesepfunc = formatNumberFn(nodesep, DEFAULTS_LAYOUT_OPTIONS$8.nodesep);
5134
+ preset, ranksepFunc, nodesepFunc, } = options;
5135
+ const ranksepfunc = formatNumberFn(ranksepFunc, ranksep !== null && ranksep !== void 0 ? ranksep : 50);
5136
+ const nodesepfunc = formatNumberFn(nodesepFunc, nodesep !== null && nodesep !== void 0 ? nodesep : 50);
5137
5137
  let horisep = nodesepfunc;
5138
5138
  let vertisep = ranksepfunc;
5139
5139
  if (rankdir === 'LR' || rankdir === 'RL') {
@@ -5148,9 +5148,10 @@
5148
5148
  const edges = this.model.edges();
5149
5149
  nodes.forEach((node) => {
5150
5150
  var _a;
5151
- const size = parseSize(nodeSizeFunc(node));
5152
- const verti = vertisep(node);
5153
- const hori = horisep(node);
5151
+ const raw = node._original;
5152
+ const size = parseSize(nodeSizeFunc(raw));
5153
+ const verti = vertisep(raw);
5154
+ const hori = horisep(raw);
5154
5155
  const width = size[0] + 2 * hori;
5155
5156
  const height = size[1] + 2 * verti;
5156
5157
  const layer = (_a = node.data) === null || _a === void 0 ? void 0 : _a.layer;
@@ -5216,6 +5217,7 @@
5216
5217
  acyclicer: 'greedy',
5217
5218
  ranker,
5218
5219
  rankdir,
5220
+ nodesep,
5219
5221
  align,
5220
5222
  });
5221
5223
  const layoutTopLeft = [0, 0];
@@ -7320,25 +7322,17 @@
7320
7322
  }
7321
7323
 
7322
7324
  const DEFAULTS_LAYOUT_OPTIONS$6 = {
7323
- centerStrength: 1,
7324
- edgeId: (d) => String(d.id),
7325
- linkDistance: 30,
7326
- edgeStrength: undefined,
7327
- edgeIterations: 1,
7325
+ link: {
7326
+ id: (d) => String(d.id),
7327
+ },
7328
+ manyBody: {
7329
+ strength: -30,
7330
+ },
7328
7331
  preventOverlap: false,
7329
7332
  nodeSize: 10,
7330
7333
  nodeSpacing: 0,
7331
- collideStrength: 1,
7332
- collideIterations: 1,
7333
- nodeStrength: -30,
7334
- distanceMin: undefined,
7335
- distanceMax: undefined,
7336
- theta: undefined,
7337
- alpha: 1,
7338
- alphaMin: 0.001,
7339
- alphaDecay: 1 - Math.pow(0.001, 1 / 300),
7340
- alphaTarget: 0,
7341
- velocityDecay: 0.4,
7334
+ x: false,
7335
+ y: false,
7342
7336
  clustering: false,
7343
7337
  clusterNodeStrength: -1,
7344
7338
  clusterEdgeStrength: 0.1,
@@ -7555,20 +7549,17 @@
7555
7549
  this.setupClusterForce(simulation, options);
7556
7550
  }
7557
7551
  getCenterOptions(options) {
7558
- if (!options.width ||
7559
- !options.height ||
7560
- options.centerStrength !== undefined) {
7561
- const viewport = normalizeViewport({
7562
- width: options.width,
7563
- height: options.height,
7564
- });
7565
- return assignDefined({}, options.center || {}, {
7566
- x: viewport.width / 2,
7567
- y: viewport.height / 2,
7568
- strength: options.centerStrength,
7569
- });
7570
- }
7571
- return undefined;
7552
+ if (options.center === false)
7553
+ return undefined;
7554
+ const viewport = normalizeViewport({
7555
+ width: options.width,
7556
+ height: options.height,
7557
+ });
7558
+ return assignDefined({}, options.center || {}, {
7559
+ x: viewport.width / 2,
7560
+ y: viewport.height / 2,
7561
+ strength: options.centerStrength,
7562
+ });
7572
7563
  }
7573
7564
  setupCenterForce(simulation, options) {
7574
7565
  const center = this.getCenterOptions(options);
@@ -7592,19 +7583,14 @@
7592
7583
  }
7593
7584
  }
7594
7585
  getManyBodyOptions(options) {
7595
- if (options.manyBody !== undefined ||
7596
- options.nodeStrength !== undefined ||
7597
- options.distanceMin !== undefined ||
7598
- options.distanceMax !== undefined ||
7599
- options.theta !== undefined) {
7600
- return assignDefined({}, options.manyBody || {}, {
7601
- strength: options.nodeStrength,
7602
- distanceMin: options.distanceMin,
7603
- distanceMax: options.distanceMax,
7604
- theta: options.theta,
7605
- });
7606
- }
7607
- return undefined;
7586
+ if (options.manyBody === false)
7587
+ return undefined;
7588
+ return assignDefined({}, options.manyBody || {}, {
7589
+ strength: options.nodeStrength,
7590
+ distanceMin: options.distanceMin,
7591
+ distanceMax: options.distanceMax,
7592
+ theta: options.theta,
7593
+ });
7608
7594
  }
7609
7595
  setupManyBodyForce(simulation, options) {
7610
7596
  const manyBody = this.getManyBodyOptions(options);
@@ -7630,19 +7616,14 @@
7630
7616
  }
7631
7617
  }
7632
7618
  getLinkOptions(options) {
7633
- if (options.link ||
7634
- options.edgeId !== undefined ||
7635
- options.linkDistance !== undefined ||
7636
- options.edgeStrength !== undefined ||
7637
- options.edgeIterations !== undefined) {
7638
- return assignDefined({}, options.link || {}, {
7639
- id: options.edgeId,
7640
- distance: options.linkDistance,
7641
- strength: options.edgeStrength,
7642
- iterations: options.edgeIterations,
7643
- });
7644
- }
7645
- return undefined;
7619
+ if (options.link === false)
7620
+ return undefined;
7621
+ return assignDefined({}, options.link || {}, {
7622
+ id: options.edgeId,
7623
+ distance: options.linkDistance,
7624
+ strength: options.edgeStrength,
7625
+ iterations: options.edgeIterations,
7626
+ });
7646
7627
  }
7647
7628
  setupLinkForce(simulation, options) {
7648
7629
  const edges = this.model.edges();
@@ -7669,23 +7650,16 @@
7669
7650
  }
7670
7651
  }
7671
7652
  getCollisionOptions(options) {
7672
- if (!options.preventOverlap)
7653
+ if (options.preventOverlap === false || options.collide === false)
7673
7654
  return undefined;
7674
- if (options.collide !== undefined ||
7675
- options.nodeSize !== undefined ||
7676
- options.nodeSpacing !== undefined ||
7677
- options.collideStrength !== undefined ||
7678
- options.collideIterations !== undefined) {
7679
- const radius = options.nodeSize || options.nodeSpacing
7680
- ? (d) => formatNodeSizeFn(options.nodeSize, options.nodeSpacing)(d._original) / 2
7681
- : undefined;
7682
- return assignDefined({}, options.collide || {}, {
7683
- radius,
7684
- strength: options.collideStrength,
7685
- iterations: options.collideIterations,
7686
- });
7687
- }
7688
- return undefined;
7655
+ const radius = options.nodeSize || options.nodeSpacing
7656
+ ? (d) => formatNodeSizeFn(options.nodeSize, options.nodeSpacing)(d._original) / 2
7657
+ : undefined;
7658
+ return assignDefined({}, options.collide || {}, {
7659
+ radius: options.collide || radius,
7660
+ strength: options.collideStrength,
7661
+ iterations: options.collideIterations,
7662
+ });
7689
7663
  }
7690
7664
  setupCollisionForce(simulation, options) {
7691
7665
  const collide = this.getCollisionOptions(options);
@@ -7710,18 +7684,13 @@
7710
7684
  }
7711
7685
  getXForceOptions(options) {
7712
7686
  var _a;
7713
- if (options.x !== undefined ||
7714
- options.forceXPosition !== undefined ||
7715
- options.forceXStrength !== undefined ||
7716
- options.width !== undefined ||
7717
- options.height !== undefined) {
7718
- const center = this.getCenterOptions(options);
7719
- return assignDefined({}, options.x || {}, {
7720
- x: (_a = options.forceXPosition) !== null && _a !== void 0 ? _a : (center && center.x),
7721
- strength: options.forceXStrength,
7722
- });
7723
- }
7724
- return undefined;
7687
+ if (options.x === false)
7688
+ return undefined;
7689
+ const center = this.getCenterOptions(options);
7690
+ return assignDefined({}, options.x || {}, {
7691
+ x: (_a = options.forceXPosition) !== null && _a !== void 0 ? _a : (center && center.x),
7692
+ strength: options.forceXStrength,
7693
+ });
7725
7694
  }
7726
7695
  setupXForce(simulation, options) {
7727
7696
  const x = this.getXForceOptions(options);
@@ -7744,18 +7713,13 @@
7744
7713
  }
7745
7714
  getYForceOptions(options) {
7746
7715
  var _a;
7747
- if (options.y !== undefined ||
7748
- options.forceYPosition !== undefined ||
7749
- options.forceYStrength !== undefined ||
7750
- options.width !== undefined ||
7751
- options.height !== undefined) {
7752
- const center = this.getCenterOptions(options);
7753
- return assignDefined({}, options.y || {}, {
7754
- y: (_a = options.forceYPosition) !== null && _a !== void 0 ? _a : (center && center.y),
7755
- strength: options.forceYStrength,
7756
- });
7757
- }
7758
- return undefined;
7716
+ if (options.y === false)
7717
+ return undefined;
7718
+ const center = this.getCenterOptions(options);
7719
+ return assignDefined({}, options.y || {}, {
7720
+ y: (_a = options.forceYPosition) !== null && _a !== void 0 ? _a : (center && center.y),
7721
+ strength: options.forceYStrength,
7722
+ });
7759
7723
  }
7760
7724
  setupYForce(simulation, options) {
7761
7725
  const y = this.getYForceOptions(options);
@@ -32799,7 +32763,6 @@ ${indent}columns: ${matrix.columns}
32799
32763
  exports.isArray = isArray;
32800
32764
  exports.isLayoutWithIterations = isLayoutWithIterations;
32801
32765
  exports.johnson = johnson;
32802
- exports.mergeOptions = mergeOptions;
32803
32766
  exports.normalizeViewport = normalizeViewport;
32804
32767
  exports.orderByDegree = orderByDegree;
32805
32768
  exports.orderById = orderById;