@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 +1202 -1239
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +2 -2
- package/dist/index.min.js.map +1 -1
- package/dist/worker.js +1 -1
- package/dist/worker.js.map +1 -1
- package/lib/algorithm/antv-dagre/index.js +10 -8
- package/lib/algorithm/antv-dagre/index.js.map +1 -1
- package/lib/algorithm/antv-dagre/types.d.ts +25 -3
- package/lib/algorithm/base-layout.d.ts +1 -0
- package/lib/algorithm/base-layout.js +5 -3
- package/lib/algorithm/base-layout.js.map +1 -1
- package/lib/algorithm/d3-force/index.d.ts +2 -2
- package/lib/algorithm/d3-force/index.js +59 -97
- package/lib/algorithm/d3-force/index.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/util/object.d.ts +1 -2
- package/lib/util/object.js +1 -4
- package/lib/util/object.js.map +1 -1
- package/lib/worker.js +763 -799
- package/lib/worker.js.map +1 -1
- package/package.json +1 -1
- package/src/algorithm/antv-dagre/index.ts +11 -12
- package/src/algorithm/antv-dagre/types.ts +25 -3
- package/src/algorithm/base-layout.ts +13 -8
- package/src/algorithm/d3-force/index.ts +67 -117
- package/src/util/object.ts +0 -4
package/lib/worker.js
CHANGED
|
@@ -663,701 +663,763 @@ var pick = (function (object, keys) {
|
|
|
663
663
|
return result;
|
|
664
664
|
});
|
|
665
665
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
nodes() {
|
|
681
|
-
return Array.from(this.nodeMap.values());
|
|
682
|
-
}
|
|
683
|
-
node(id) {
|
|
684
|
-
return this.nodeMap.get(id);
|
|
685
|
-
}
|
|
686
|
-
nodeAt(index) {
|
|
687
|
-
if (!this.indexNodeCache) {
|
|
688
|
-
this.buildNodeIndexCache();
|
|
666
|
+
/**
|
|
667
|
+
* Return the layout result for a graph with zero or one node.
|
|
668
|
+
* @param graph original graph
|
|
669
|
+
* @param center the layout center
|
|
670
|
+
* @returns layout result
|
|
671
|
+
*/
|
|
672
|
+
function applySingleNodeLayout(model, center, dimensions = 2) {
|
|
673
|
+
const n = model.nodeCount();
|
|
674
|
+
if (n === 1) {
|
|
675
|
+
const first = model.firstNode();
|
|
676
|
+
first.x = center[0];
|
|
677
|
+
first.y = center[1];
|
|
678
|
+
if (dimensions === 3) {
|
|
679
|
+
first.z = center[2] || 0;
|
|
689
680
|
}
|
|
690
|
-
const nodeId = this.indexNodeCache.get(index);
|
|
691
|
-
return nodeId ? this.nodeMap.get(nodeId) : undefined;
|
|
692
681
|
}
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Get the adjacency list of the graph model.
|
|
686
|
+
*/
|
|
687
|
+
const getAdjList = (model, directed) => {
|
|
688
|
+
const n = model.nodeCount();
|
|
689
|
+
const adjList = Array.from({ length: n }, () => []);
|
|
690
|
+
// map node with index
|
|
691
|
+
const nodeMap = {};
|
|
692
|
+
let idx = 0;
|
|
693
|
+
model.forEachNode((node) => {
|
|
694
|
+
nodeMap[node.id] = idx++;
|
|
695
|
+
});
|
|
696
|
+
model.forEachEdge((e) => {
|
|
697
|
+
const s = nodeMap[e.source];
|
|
698
|
+
const t = nodeMap[e.target];
|
|
699
|
+
if (s == null || t == null)
|
|
700
|
+
return;
|
|
701
|
+
adjList[s].push(t);
|
|
702
|
+
adjList[t].push(s);
|
|
703
|
+
});
|
|
704
|
+
return adjList;
|
|
705
|
+
};
|
|
706
|
+
/**
|
|
707
|
+
* scale matrix
|
|
708
|
+
* @param matrix [ [], [], [] ]
|
|
709
|
+
* @param ratio
|
|
710
|
+
*/
|
|
711
|
+
const scaleMatrix = (matrix, ratio) => {
|
|
712
|
+
const n = matrix.length;
|
|
713
|
+
const result = new Array(n);
|
|
714
|
+
for (let i = 0; i < n; i++) {
|
|
715
|
+
const row = matrix[i];
|
|
716
|
+
const m = row.length;
|
|
717
|
+
const newRow = new Array(m);
|
|
718
|
+
for (let j = 0; j < m; j++) {
|
|
719
|
+
newRow[j] = row[j] * ratio;
|
|
697
720
|
}
|
|
698
|
-
|
|
699
|
-
}
|
|
700
|
-
firstNode() {
|
|
701
|
-
return this.nodeMap.values().next().value;
|
|
702
|
-
}
|
|
703
|
-
forEachNode(callback) {
|
|
704
|
-
let i = 0;
|
|
705
|
-
this.nodeMap.forEach((node) => callback(node, i++));
|
|
706
|
-
}
|
|
707
|
-
originalNode(id) {
|
|
708
|
-
const node = this.nodeMap.get(id);
|
|
709
|
-
return node === null || node === void 0 ? void 0 : node._original;
|
|
710
|
-
}
|
|
711
|
-
nodeCount() {
|
|
712
|
-
return this.nodeMap.size;
|
|
713
|
-
}
|
|
714
|
-
edges() {
|
|
715
|
-
return Array.from(this.edgeMap.values());
|
|
721
|
+
result[i] = newRow;
|
|
716
722
|
}
|
|
717
|
-
|
|
718
|
-
|
|
723
|
+
return result;
|
|
724
|
+
};
|
|
725
|
+
/**
|
|
726
|
+
* Use Johnson + Dijkstra to compute APSP for sparse graph.
|
|
727
|
+
* Fully compatible with floydWarshall(adjMatrix).
|
|
728
|
+
*/
|
|
729
|
+
function johnson(adjList) {
|
|
730
|
+
const n = adjList.length;
|
|
731
|
+
// Step 1: add a dummy node q connected to all nodes with weight 0
|
|
732
|
+
new Array(n).fill(0);
|
|
733
|
+
// Bellman-Ford to compute potentials h(v)
|
|
734
|
+
// 因为权重全是 1,无负边,可直接跳过 BF,h 全 0 即可
|
|
735
|
+
// Step 2: reweight edges
|
|
736
|
+
// 因为 h(u)=h(v)=0,reweight 后仍然是 1,省略 reweight 过程
|
|
737
|
+
// Step 3: run Dijkstra from each node
|
|
738
|
+
const distAll = Array.from({ length: n }, () => new Array(n).fill(Infinity));
|
|
739
|
+
for (let s = 0; s < n; s++) {
|
|
740
|
+
distAll[s] = dijkstra(adjList, s);
|
|
719
741
|
}
|
|
720
|
-
|
|
721
|
-
|
|
742
|
+
return distAll;
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Dijkstra algorithm to find shortest paths from source to all nodes.
|
|
746
|
+
*/
|
|
747
|
+
function dijkstra(adjList, source) {
|
|
748
|
+
const n = adjList.length;
|
|
749
|
+
const dist = new Array(n).fill(Infinity);
|
|
750
|
+
dist[source] = 0;
|
|
751
|
+
// Minimal binary heap
|
|
752
|
+
const heap = new MinHeap();
|
|
753
|
+
heap.push([0, source]); // [distance, node]
|
|
754
|
+
while (!heap.empty()) {
|
|
755
|
+
const [d, u] = heap.pop();
|
|
756
|
+
if (d !== dist[u])
|
|
757
|
+
continue;
|
|
758
|
+
const neighbors = adjList[u];
|
|
759
|
+
for (let i = 0; i < neighbors.length; i++) {
|
|
760
|
+
const v = neighbors[i];
|
|
761
|
+
const nd = d + 1;
|
|
762
|
+
if (nd < dist[v]) {
|
|
763
|
+
dist[v] = nd;
|
|
764
|
+
heap.push([nd, v]);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
722
767
|
}
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
768
|
+
return dist;
|
|
769
|
+
}
|
|
770
|
+
class MinHeap {
|
|
771
|
+
constructor() {
|
|
772
|
+
this.data = [];
|
|
726
773
|
}
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
774
|
+
push(item) {
|
|
775
|
+
this.data.push(item);
|
|
776
|
+
this.bubbleUp(this.data.length - 1);
|
|
730
777
|
}
|
|
731
|
-
|
|
732
|
-
|
|
778
|
+
pop() {
|
|
779
|
+
const top = this.data[0];
|
|
780
|
+
const end = this.data.pop();
|
|
781
|
+
if (this.data.length > 0) {
|
|
782
|
+
this.data[0] = end;
|
|
783
|
+
this.bubbleDown(0);
|
|
784
|
+
}
|
|
785
|
+
return top;
|
|
733
786
|
}
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
return edge.id;
|
|
737
|
-
const baseId = `${edge.source}-${edge.target}`;
|
|
738
|
-
const count = this.edgeIdCounter.get(baseId) || 0;
|
|
739
|
-
const id = count === 0 ? baseId : `${baseId}-${count}`;
|
|
740
|
-
this.edgeIdCounter.set(baseId, count + 1);
|
|
741
|
-
return id;
|
|
787
|
+
empty() {
|
|
788
|
+
return this.data.length === 0;
|
|
742
789
|
}
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
790
|
+
bubbleUp(pos) {
|
|
791
|
+
const data = this.data;
|
|
792
|
+
while (pos > 0) {
|
|
793
|
+
const parent = (pos - 1) >> 1;
|
|
794
|
+
if (data[parent][0] <= data[pos][0])
|
|
795
|
+
break;
|
|
796
|
+
[data[parent], data[pos]] = [data[pos], data[parent]];
|
|
797
|
+
pos = parent;
|
|
746
798
|
}
|
|
747
|
-
const degree = this.degreeCache.get(nodeId);
|
|
748
|
-
if (!degree)
|
|
749
|
-
return 0;
|
|
750
|
-
return degree[direction];
|
|
751
799
|
}
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
800
|
+
bubbleDown(pos) {
|
|
801
|
+
const data = this.data;
|
|
802
|
+
const length = data.length;
|
|
803
|
+
while (true) {
|
|
804
|
+
const left = pos * 2 + 1;
|
|
805
|
+
const right = pos * 2 + 2;
|
|
806
|
+
let min = pos;
|
|
807
|
+
if (left < length && data[left][0] < data[min][0])
|
|
808
|
+
min = left;
|
|
809
|
+
if (right < length && data[right][0] < data[min][0])
|
|
810
|
+
min = right;
|
|
811
|
+
if (min === pos)
|
|
812
|
+
break;
|
|
813
|
+
[data[pos], data[min]] = [data[min], data[pos]];
|
|
814
|
+
pos = min;
|
|
764
815
|
}
|
|
765
|
-
const inSet = this.inAdjacencyCache.get(nodeId);
|
|
766
|
-
const outSet = this.outAdjacencyCache.get(nodeId);
|
|
767
|
-
if (!inSet && !outSet)
|
|
768
|
-
return [];
|
|
769
|
-
if (!inSet)
|
|
770
|
-
return Array.from(outSet);
|
|
771
|
-
if (!outSet)
|
|
772
|
-
return Array.from(inSet);
|
|
773
|
-
const merged = new Set();
|
|
774
|
-
inSet.forEach((id) => merged.add(id));
|
|
775
|
-
outSet.forEach((id) => merged.add(id));
|
|
776
|
-
return Array.from(merged);
|
|
777
|
-
}
|
|
778
|
-
successors(nodeId) {
|
|
779
|
-
return this.neighbors(nodeId, 'out');
|
|
780
|
-
}
|
|
781
|
-
predecessors(nodeId) {
|
|
782
|
-
return this.neighbors(nodeId, 'in');
|
|
783
|
-
}
|
|
784
|
-
setNodeOrder(nodes) {
|
|
785
|
-
const next = new Map();
|
|
786
|
-
for (const node of nodes)
|
|
787
|
-
next.set(node.id, node);
|
|
788
|
-
this.nodeMap = next;
|
|
789
|
-
this.nodeIndexCache = undefined;
|
|
790
|
-
this.indexNodeCache = undefined;
|
|
791
816
|
}
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
}
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
* Get nested property value
|
|
821
|
+
* For example: getNestedValue(obj, 'a.b.c') will return obj.a.b.c
|
|
822
|
+
*/
|
|
823
|
+
function getNestedValue(obj, path) {
|
|
824
|
+
const keys = String(path).split('.');
|
|
825
|
+
return get$1(obj, keys);
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Merge objects, but undefined values in source objects will not override existing values
|
|
829
|
+
* @param target - The target object
|
|
830
|
+
* @param sources - Source objects to merge
|
|
831
|
+
* @returns A new merged object
|
|
832
|
+
*
|
|
833
|
+
* @example
|
|
834
|
+
* assignDefined({ a: 1, b: 2 }, { b: undefined, c: 3 })
|
|
835
|
+
* // Returns: { a: 1, b: 2, c: 3 }
|
|
836
|
+
*/
|
|
837
|
+
function assignDefined(target, ...sources) {
|
|
838
|
+
sources.forEach((source) => {
|
|
839
|
+
if (source) {
|
|
840
|
+
Object.keys(source).forEach((key) => {
|
|
841
|
+
const value = source[key];
|
|
842
|
+
if (value !== undefined) {
|
|
843
|
+
target[key] = value;
|
|
844
|
+
}
|
|
845
|
+
});
|
|
822
846
|
}
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
847
|
+
});
|
|
848
|
+
return target;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
/**
|
|
852
|
+
* 通用排序核心函数
|
|
853
|
+
*/
|
|
854
|
+
function sort$1(model, compareFn) {
|
|
855
|
+
const nodes = model.nodes();
|
|
856
|
+
nodes.sort(compareFn);
|
|
857
|
+
model.setNodeOrder(nodes);
|
|
858
|
+
return model;
|
|
859
|
+
}
|
|
860
|
+
function orderByDegree(model) {
|
|
861
|
+
return sort$1(model, (nodeA, nodeB) => {
|
|
862
|
+
const degreeA = model.degree(nodeA.id);
|
|
863
|
+
const degreeB = model.degree(nodeB.id);
|
|
864
|
+
return degreeB - degreeA; // descending order
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
/**
|
|
868
|
+
* 按 ID 排序
|
|
869
|
+
*/
|
|
870
|
+
function orderById(model) {
|
|
871
|
+
return sort$1(model, (nodeA, nodeB) => {
|
|
872
|
+
const idA = nodeA.id;
|
|
873
|
+
const idB = nodeB.id;
|
|
874
|
+
if (typeof idA === 'number' && typeof idB === 'number') {
|
|
875
|
+
return idA - idB;
|
|
876
|
+
}
|
|
877
|
+
return String(idA).localeCompare(String(idB));
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
/**
|
|
881
|
+
* 按自定义比较函数排序
|
|
882
|
+
*/
|
|
883
|
+
function orderBySorter(model, sorter) {
|
|
884
|
+
return sort$1(model, (nodeA, nodeB) => {
|
|
885
|
+
const a = model.originalNode(nodeA.id);
|
|
886
|
+
const b = model.originalNode(nodeB.id);
|
|
887
|
+
return sorter(a, b);
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* Order nodes according to graph topology
|
|
892
|
+
*/
|
|
893
|
+
function orderByTopology(model, directed = false) {
|
|
894
|
+
const n = model.nodeCount();
|
|
895
|
+
if (n === 0)
|
|
896
|
+
return model;
|
|
897
|
+
const nodes = model.nodes();
|
|
898
|
+
const orderedNodes = [nodes[0]];
|
|
899
|
+
const pickFlags = {};
|
|
900
|
+
pickFlags[nodes[0].id] = true;
|
|
901
|
+
let k = 0;
|
|
902
|
+
let i = 0;
|
|
903
|
+
model.forEachNode((node) => {
|
|
904
|
+
if (i !== 0) {
|
|
905
|
+
const currentDegree = model.degree(node.id, 'both');
|
|
906
|
+
const nextDegree = i < n - 1 ? model.degree(nodes[i + 1].id, 'both') : 0;
|
|
907
|
+
const currentNodeId = orderedNodes[k].id;
|
|
908
|
+
const isNeighbor = model
|
|
909
|
+
.neighbors(currentNodeId, 'both')
|
|
910
|
+
.includes(node.id);
|
|
911
|
+
if ((i === n - 1 || currentDegree !== nextDegree || isNeighbor) &&
|
|
912
|
+
!pickFlags[node.id]) {
|
|
913
|
+
orderedNodes.push(node);
|
|
914
|
+
pickFlags[node.id] = true;
|
|
915
|
+
k++;
|
|
832
916
|
}
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
917
|
+
else {
|
|
918
|
+
const children = directed
|
|
919
|
+
? model.successors(currentNodeId)
|
|
920
|
+
: model.neighbors(currentNodeId);
|
|
921
|
+
let foundChild = false;
|
|
922
|
+
for (let j = 0; j < children.length; j++) {
|
|
923
|
+
const childId = children[j];
|
|
924
|
+
const child = model.node(childId);
|
|
925
|
+
if (child &&
|
|
926
|
+
model.degree(childId) === model.degree(node.id) &&
|
|
927
|
+
!pickFlags[childId]) {
|
|
928
|
+
orderedNodes.push(child);
|
|
929
|
+
pickFlags[childId] = true;
|
|
930
|
+
foundChild = true;
|
|
931
|
+
break;
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
let ii = 0;
|
|
935
|
+
while (!foundChild) {
|
|
936
|
+
if (!pickFlags[nodes[ii].id]) {
|
|
937
|
+
orderedNodes.push(nodes[ii]);
|
|
938
|
+
pickFlags[nodes[ii].id] = true;
|
|
939
|
+
foundChild = true;
|
|
940
|
+
}
|
|
941
|
+
ii++;
|
|
942
|
+
if (ii === n) {
|
|
943
|
+
break;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
836
946
|
}
|
|
837
|
-
this.inAdjacencyCache.get(edge.target).add(edge.source);
|
|
838
947
|
}
|
|
948
|
+
i++;
|
|
949
|
+
});
|
|
950
|
+
// Update model with ordered nodes
|
|
951
|
+
model.setNodeOrder(orderedNodes);
|
|
952
|
+
return model;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
function parsePoint(point) {
|
|
956
|
+
var _a;
|
|
957
|
+
return [point.x, point.y, (_a = point.z) !== null && _a !== void 0 ? _a : 0];
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
function parseSize(size) {
|
|
961
|
+
if (!size)
|
|
962
|
+
return [0, 0, 0];
|
|
963
|
+
if (isNumber(size))
|
|
964
|
+
return [size, size, size];
|
|
965
|
+
else if (Array.isArray(size) && size.length === 0)
|
|
966
|
+
return [0, 0, 0];
|
|
967
|
+
const [x, y = x, z = x] = size;
|
|
968
|
+
return [x, y, z];
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
/**
|
|
972
|
+
* Viewport configuration such as width, height and center point.
|
|
973
|
+
*/
|
|
974
|
+
const normalizeViewport = (options) => {
|
|
975
|
+
const { width, height, center } = options;
|
|
976
|
+
const normalizedWidth = width !== null && width !== void 0 ? width : (typeof window !== 'undefined' ? window.innerWidth : 0);
|
|
977
|
+
const normalizedHeight = height !== null && height !== void 0 ? height : (typeof window !== 'undefined' ? window.innerHeight : 0);
|
|
978
|
+
const centerPoint = center !== null && center !== void 0 ? center : [normalizedWidth / 2, normalizedHeight / 2];
|
|
979
|
+
return {
|
|
980
|
+
width: normalizedWidth,
|
|
981
|
+
height: normalizedHeight,
|
|
982
|
+
center: centerPoint,
|
|
983
|
+
};
|
|
984
|
+
};
|
|
985
|
+
|
|
986
|
+
/**
|
|
987
|
+
* Format value with multiple types into a function that returns a number
|
|
988
|
+
* @param value The value to be formatted
|
|
989
|
+
* @param defaultValue The default value when value is invalid
|
|
990
|
+
* @returns A function that returns a number
|
|
991
|
+
*/
|
|
992
|
+
function formatNumberFn(value, defaultValue) {
|
|
993
|
+
// If value is a function, return it directly
|
|
994
|
+
if (isFunction(value)) {
|
|
995
|
+
return value;
|
|
839
996
|
}
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
let index = 0;
|
|
844
|
-
this.nodeMap.forEach((_node, nodeId) => {
|
|
845
|
-
this.nodeIndexCache.set(nodeId, index);
|
|
846
|
-
this.indexNodeCache.set(index, nodeId);
|
|
847
|
-
index++;
|
|
848
|
-
});
|
|
849
|
-
}
|
|
850
|
-
destroy() {
|
|
851
|
-
this.clearCache();
|
|
852
|
-
this.nodeMap.clear();
|
|
853
|
-
this.edgeMap.clear();
|
|
854
|
-
this.edgeIdCounter.clear();
|
|
997
|
+
// If value is a number, return a function that returns this number
|
|
998
|
+
if (isNumber(value)) {
|
|
999
|
+
return () => value;
|
|
855
1000
|
}
|
|
1001
|
+
// For other cases (undefined or invalid values), return default value function
|
|
1002
|
+
return () => defaultValue;
|
|
856
1003
|
}
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
'parentId',
|
|
869
|
-
];
|
|
870
|
-
const edgeFields = ['id', 'source', 'target', 'points'];
|
|
871
|
-
function extractNodeData(nodes, node) {
|
|
872
|
-
if (!nodes) {
|
|
873
|
-
throw new Error('Data.nodes is required');
|
|
1004
|
+
/**
|
|
1005
|
+
* Format size config with multiple types into a function that returns a size
|
|
1006
|
+
* @param value The value to be formatted
|
|
1007
|
+
* @param defaultValue The default value when value is invalid
|
|
1008
|
+
* @param resultIsNumber Whether to return a number (max of width/height) or size array
|
|
1009
|
+
* @returns A function that returns a size
|
|
1010
|
+
*/
|
|
1011
|
+
function formatSizeFn(value, defaultValue = 10) {
|
|
1012
|
+
// If value is undefined, return default value function
|
|
1013
|
+
if (!value) {
|
|
1014
|
+
return () => defaultValue;
|
|
874
1015
|
}
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
for (const field of nodeFields) {
|
|
879
|
-
const value = datum[field];
|
|
880
|
-
if (isNil(value))
|
|
881
|
-
continue;
|
|
882
|
-
nodeData[field] = value;
|
|
883
|
-
}
|
|
884
|
-
if (node) {
|
|
885
|
-
const customFields = node(datum);
|
|
886
|
-
for (const key in customFields) {
|
|
887
|
-
const value = customFields[key];
|
|
888
|
-
if (isNil(value))
|
|
889
|
-
continue;
|
|
890
|
-
nodeData[key] = value;
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
if (isNil(nodeData.id)) {
|
|
894
|
-
throw new Error(`Node is missing id field`);
|
|
895
|
-
}
|
|
896
|
-
result.set(nodeData.id, nodeData);
|
|
1016
|
+
// If value is a function, return it directly
|
|
1017
|
+
if (isFunction(value)) {
|
|
1018
|
+
return value;
|
|
897
1019
|
}
|
|
898
|
-
return
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
const result = new Map();
|
|
902
|
-
for (const datum of edges) {
|
|
903
|
-
const edgeData = { _original: datum };
|
|
904
|
-
for (const field of edgeFields) {
|
|
905
|
-
const value = datum[field];
|
|
906
|
-
if (isNil(value))
|
|
907
|
-
continue;
|
|
908
|
-
edgeData[field] = value;
|
|
909
|
-
}
|
|
910
|
-
if (edge) {
|
|
911
|
-
const customFields = edge(datum);
|
|
912
|
-
for (const key in customFields) {
|
|
913
|
-
const value = customFields[key];
|
|
914
|
-
if (isNil(value))
|
|
915
|
-
continue;
|
|
916
|
-
edgeData[key] = value;
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
if (isNil(edgeData.source) || isNil(edgeData.target)) {
|
|
920
|
-
throw new Error(`Edge is missing source or target field`);
|
|
921
|
-
}
|
|
922
|
-
if (isNil(edgeData.id)) {
|
|
923
|
-
edgeData.id = getEdgeId === null || getEdgeId === void 0 ? void 0 : getEdgeId(datum);
|
|
924
|
-
}
|
|
925
|
-
result.set(edgeData.id, edgeData);
|
|
1020
|
+
// If value is a number, return a function that returns this number
|
|
1021
|
+
if (isNumber(value)) {
|
|
1022
|
+
return () => value;
|
|
926
1023
|
}
|
|
927
|
-
return
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
}
|
|
937
|
-
if (dimensions === 3 && isNil(node.z)) {
|
|
938
|
-
node.z = Math.random() * Math.min(width, height);
|
|
939
|
-
}
|
|
940
|
-
});
|
|
1024
|
+
// If value is an array, return max or the array itself
|
|
1025
|
+
if (Array.isArray(value)) {
|
|
1026
|
+
return () => value;
|
|
1027
|
+
}
|
|
1028
|
+
// If value is an object with width and height
|
|
1029
|
+
if (isObject(value) && value.width && value.height) {
|
|
1030
|
+
return () => [value.width, value.height];
|
|
1031
|
+
}
|
|
1032
|
+
return () => defaultValue;
|
|
941
1033
|
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Format nodeSize and nodeSpacing into a function that returns the total size
|
|
1036
|
+
* @param nodeSize The size of the node
|
|
1037
|
+
* @param nodeSpacing The spacing around the node
|
|
1038
|
+
* @param defaultNodeSize The default node size when value is invalid
|
|
1039
|
+
* @returns A function that returns the total size (node size + spacing)
|
|
1040
|
+
*/
|
|
1041
|
+
const formatNodeSizeFn = (nodeSize, nodeSpacing, defaultNodeSize = 10) => {
|
|
1042
|
+
const nodeSpacingFunc = formatNumberFn(nodeSpacing, 0);
|
|
1043
|
+
const nodeSizeFunc = formatSizeFn(nodeSize, defaultNodeSize);
|
|
1044
|
+
return (node) => {
|
|
1045
|
+
const size = nodeSizeFunc(node);
|
|
1046
|
+
const spacing = nodeSpacingFunc(node);
|
|
1047
|
+
return Math.max(...parseSize(size)) + spacing;
|
|
1048
|
+
};
|
|
1049
|
+
};
|
|
942
1050
|
|
|
943
|
-
class
|
|
1051
|
+
class GraphLib {
|
|
944
1052
|
constructor(data, options = {}) {
|
|
945
|
-
this.
|
|
1053
|
+
this.edgeIdCounter = new Map();
|
|
1054
|
+
this.nodeMap = extractNodeData(data.nodes, options.node);
|
|
1055
|
+
this.edgeMap = extractEdgeData(data.edges || [], options.edge, this.getEdgeId.bind(this));
|
|
1056
|
+
}
|
|
1057
|
+
data() {
|
|
1058
|
+
return { nodes: this.nodeMap, edges: this.edgeMap };
|
|
1059
|
+
}
|
|
1060
|
+
replace(result) {
|
|
1061
|
+
this.nodeMap = result.nodes;
|
|
1062
|
+
this.edgeMap = result.edges;
|
|
1063
|
+
this.clearCache();
|
|
1064
|
+
}
|
|
1065
|
+
nodes() {
|
|
1066
|
+
return Array.from(this.nodeMap.values());
|
|
1067
|
+
}
|
|
1068
|
+
node(id) {
|
|
1069
|
+
return this.nodeMap.get(id);
|
|
1070
|
+
}
|
|
1071
|
+
nodeAt(index) {
|
|
1072
|
+
if (!this.indexNodeCache) {
|
|
1073
|
+
this.buildNodeIndexCache();
|
|
1074
|
+
}
|
|
1075
|
+
const nodeId = this.indexNodeCache.get(index);
|
|
1076
|
+
return nodeId ? this.nodeMap.get(nodeId) : undefined;
|
|
1077
|
+
}
|
|
1078
|
+
nodeIndexOf(id) {
|
|
1079
|
+
var _a;
|
|
1080
|
+
if (!this.nodeIndexCache) {
|
|
1081
|
+
this.buildNodeIndexCache();
|
|
1082
|
+
}
|
|
1083
|
+
return (_a = this.nodeIndexCache.get(id)) !== null && _a !== void 0 ? _a : -1;
|
|
1084
|
+
}
|
|
1085
|
+
firstNode() {
|
|
1086
|
+
return this.nodeMap.values().next().value;
|
|
1087
|
+
}
|
|
1088
|
+
forEachNode(callback) {
|
|
1089
|
+
let i = 0;
|
|
1090
|
+
this.nodeMap.forEach((node) => callback(node, i++));
|
|
1091
|
+
}
|
|
1092
|
+
originalNode(id) {
|
|
1093
|
+
const node = this.nodeMap.get(id);
|
|
1094
|
+
return node === null || node === void 0 ? void 0 : node._original;
|
|
1095
|
+
}
|
|
1096
|
+
nodeCount() {
|
|
1097
|
+
return this.nodeMap.size;
|
|
946
1098
|
}
|
|
947
|
-
|
|
948
|
-
return this.
|
|
1099
|
+
edges() {
|
|
1100
|
+
return Array.from(this.edgeMap.values());
|
|
949
1101
|
}
|
|
950
|
-
|
|
951
|
-
this.
|
|
1102
|
+
edge(id) {
|
|
1103
|
+
return this.edgeMap.get(id);
|
|
952
1104
|
}
|
|
953
|
-
|
|
954
|
-
this.
|
|
1105
|
+
firstEdge() {
|
|
1106
|
+
return this.edgeMap.values().next().value;
|
|
955
1107
|
}
|
|
956
1108
|
forEachEdge(callback) {
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
edge.targetNode = this.graph.node(edge.target);
|
|
960
|
-
callback(edge, i);
|
|
961
|
-
});
|
|
1109
|
+
let i = 0;
|
|
1110
|
+
this.edgeMap.forEach((edge) => callback(edge, i++));
|
|
962
1111
|
}
|
|
963
|
-
|
|
964
|
-
this.
|
|
1112
|
+
originalEdge(id) {
|
|
1113
|
+
const edge = this.edgeMap.get(id);
|
|
1114
|
+
return edge === null || edge === void 0 ? void 0 : edge._original;
|
|
965
1115
|
}
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
class Supervisor {
|
|
969
|
-
constructor() {
|
|
970
|
-
this.worker = null;
|
|
971
|
-
this.workerApi = null;
|
|
1116
|
+
edgeCount() {
|
|
1117
|
+
return this.edgeMap.size;
|
|
972
1118
|
}
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
if (!this.workerApi) {
|
|
982
|
-
throw new Error('Worker API not initialized');
|
|
983
|
-
}
|
|
984
|
-
return yield this.workerApi.execute(layoutId, data, options);
|
|
985
|
-
});
|
|
1119
|
+
getEdgeId(edge) {
|
|
1120
|
+
if (edge.id)
|
|
1121
|
+
return edge.id;
|
|
1122
|
+
const baseId = `${edge.source}-${edge.target}`;
|
|
1123
|
+
const count = this.edgeIdCounter.get(baseId) || 0;
|
|
1124
|
+
const id = count === 0 ? baseId : `${baseId}-${count}`;
|
|
1125
|
+
this.edgeIdCounter.set(baseId, count + 1);
|
|
1126
|
+
return id;
|
|
986
1127
|
}
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
destroy() {
|
|
991
|
-
if (this.workerApi) {
|
|
992
|
-
this.workerApi.destroy();
|
|
993
|
-
}
|
|
994
|
-
if (this.worker) {
|
|
995
|
-
this.worker.terminate();
|
|
996
|
-
this.worker = null;
|
|
997
|
-
this.workerApi = null;
|
|
1128
|
+
degree(nodeId, direction = 'both') {
|
|
1129
|
+
if (!this.degreeCache) {
|
|
1130
|
+
this.buildDegreeCache();
|
|
998
1131
|
}
|
|
1132
|
+
const degree = this.degreeCache.get(nodeId);
|
|
1133
|
+
if (!degree)
|
|
1134
|
+
return 0;
|
|
1135
|
+
return degree[direction];
|
|
999
1136
|
}
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
initWorker() {
|
|
1004
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1005
|
-
const workerPath = this.resolveWorkerPath();
|
|
1006
|
-
const isESM = workerPath.includes('/lib/') || workerPath.endsWith('.mjs');
|
|
1007
|
-
const type = isESM ? 'module' : 'classic';
|
|
1008
|
-
this.worker = new Worker(workerPath, { type });
|
|
1009
|
-
this.workerApi = wrap(this.worker);
|
|
1010
|
-
});
|
|
1011
|
-
}
|
|
1012
|
-
/**
|
|
1013
|
-
* Resolve worker script path which works in both ESM and UMD environments
|
|
1014
|
-
*/
|
|
1015
|
-
resolveWorkerPath() {
|
|
1016
|
-
if (typeof import.meta !== 'undefined' && import.meta.url) {
|
|
1017
|
-
const currentUrl = new URL(import.meta.url);
|
|
1018
|
-
// e.g. `.../lib/runtime/supervisor.js` -> `.../lib/worker.js`
|
|
1019
|
-
const asRoot = currentUrl.href.replace(/\/runtime\/[^/]+\.js$/, '/worker.js');
|
|
1020
|
-
if (asRoot !== currentUrl.href)
|
|
1021
|
-
return asRoot;
|
|
1022
|
-
// Fallback: keep legacy behavior (same directory)
|
|
1023
|
-
return currentUrl.href.replace(/\/[^/]+\.js$/, '/worker.js');
|
|
1137
|
+
neighbors(nodeId, direction = 'both') {
|
|
1138
|
+
if (!this.outAdjacencyCache || !this.inAdjacencyCache) {
|
|
1139
|
+
this.buildAdjacencyCache();
|
|
1024
1140
|
}
|
|
1025
|
-
if (
|
|
1026
|
-
|
|
1027
|
-
for (let i = scripts.length - 1; i >= 0; i--) {
|
|
1028
|
-
const src = scripts[i].src;
|
|
1029
|
-
if (src && (src.includes('index.js') || src.includes('index.min.js'))) {
|
|
1030
|
-
return src.replace(/index(\.min)?\.js/, 'worker.js');
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1141
|
+
if (direction === 'out') {
|
|
1142
|
+
return Array.from(this.outAdjacencyCache.get(nodeId) || []);
|
|
1033
1143
|
}
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
/**
|
|
1039
|
-
* Return the layout result for a graph with zero or one node.
|
|
1040
|
-
* @param graph original graph
|
|
1041
|
-
* @param center the layout center
|
|
1042
|
-
* @returns layout result
|
|
1043
|
-
*/
|
|
1044
|
-
function applySingleNodeLayout(model, center, dimensions = 2) {
|
|
1045
|
-
const n = model.nodeCount();
|
|
1046
|
-
if (n === 1) {
|
|
1047
|
-
const first = model.firstNode();
|
|
1048
|
-
first.x = center[0];
|
|
1049
|
-
first.y = center[1];
|
|
1050
|
-
if (dimensions === 3) {
|
|
1051
|
-
first.z = center[2] || 0;
|
|
1144
|
+
if (direction === 'in') {
|
|
1145
|
+
return Array.from(this.inAdjacencyCache.get(nodeId) || []);
|
|
1052
1146
|
}
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
/**
|
|
1057
|
-
* Get the adjacency list of the graph model.
|
|
1058
|
-
*/
|
|
1059
|
-
const getAdjList = (model, directed) => {
|
|
1060
|
-
const n = model.nodeCount();
|
|
1061
|
-
const adjList = Array.from({ length: n }, () => []);
|
|
1062
|
-
// map node with index
|
|
1063
|
-
const nodeMap = {};
|
|
1064
|
-
let idx = 0;
|
|
1065
|
-
model.forEachNode((node) => {
|
|
1066
|
-
nodeMap[node.id] = idx++;
|
|
1067
|
-
});
|
|
1068
|
-
model.forEachEdge((e) => {
|
|
1069
|
-
const s = nodeMap[e.source];
|
|
1070
|
-
const t = nodeMap[e.target];
|
|
1071
|
-
if (s == null || t == null)
|
|
1072
|
-
return;
|
|
1073
|
-
adjList[s].push(t);
|
|
1074
|
-
adjList[t].push(s);
|
|
1075
|
-
});
|
|
1076
|
-
return adjList;
|
|
1077
|
-
};
|
|
1078
|
-
/**
|
|
1079
|
-
* scale matrix
|
|
1080
|
-
* @param matrix [ [], [], [] ]
|
|
1081
|
-
* @param ratio
|
|
1082
|
-
*/
|
|
1083
|
-
const scaleMatrix = (matrix, ratio) => {
|
|
1084
|
-
const n = matrix.length;
|
|
1085
|
-
const result = new Array(n);
|
|
1086
|
-
for (let i = 0; i < n; i++) {
|
|
1087
|
-
const row = matrix[i];
|
|
1088
|
-
const m = row.length;
|
|
1089
|
-
const newRow = new Array(m);
|
|
1090
|
-
for (let j = 0; j < m; j++) {
|
|
1091
|
-
newRow[j] = row[j] * ratio;
|
|
1147
|
+
if (this.bothAdjacencyCache) {
|
|
1148
|
+
return Array.from(this.bothAdjacencyCache.get(nodeId) || []);
|
|
1092
1149
|
}
|
|
1093
|
-
|
|
1150
|
+
const inSet = this.inAdjacencyCache.get(nodeId);
|
|
1151
|
+
const outSet = this.outAdjacencyCache.get(nodeId);
|
|
1152
|
+
if (!inSet && !outSet)
|
|
1153
|
+
return [];
|
|
1154
|
+
if (!inSet)
|
|
1155
|
+
return Array.from(outSet);
|
|
1156
|
+
if (!outSet)
|
|
1157
|
+
return Array.from(inSet);
|
|
1158
|
+
const merged = new Set();
|
|
1159
|
+
inSet.forEach((id) => merged.add(id));
|
|
1160
|
+
outSet.forEach((id) => merged.add(id));
|
|
1161
|
+
return Array.from(merged);
|
|
1094
1162
|
}
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
/**
|
|
1098
|
-
* Use Johnson + Dijkstra to compute APSP for sparse graph.
|
|
1099
|
-
* Fully compatible with floydWarshall(adjMatrix).
|
|
1100
|
-
*/
|
|
1101
|
-
function johnson(adjList) {
|
|
1102
|
-
const n = adjList.length;
|
|
1103
|
-
// Step 1: add a dummy node q connected to all nodes with weight 0
|
|
1104
|
-
new Array(n).fill(0);
|
|
1105
|
-
// Bellman-Ford to compute potentials h(v)
|
|
1106
|
-
// 因为权重全是 1,无负边,可直接跳过 BF,h 全 0 即可
|
|
1107
|
-
// Step 2: reweight edges
|
|
1108
|
-
// 因为 h(u)=h(v)=0,reweight 后仍然是 1,省略 reweight 过程
|
|
1109
|
-
// Step 3: run Dijkstra from each node
|
|
1110
|
-
const distAll = Array.from({ length: n }, () => new Array(n).fill(Infinity));
|
|
1111
|
-
for (let s = 0; s < n; s++) {
|
|
1112
|
-
distAll[s] = dijkstra(adjList, s);
|
|
1163
|
+
successors(nodeId) {
|
|
1164
|
+
return this.neighbors(nodeId, 'out');
|
|
1113
1165
|
}
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1166
|
+
predecessors(nodeId) {
|
|
1167
|
+
return this.neighbors(nodeId, 'in');
|
|
1168
|
+
}
|
|
1169
|
+
setNodeOrder(nodes) {
|
|
1170
|
+
const next = new Map();
|
|
1171
|
+
for (const node of nodes)
|
|
1172
|
+
next.set(node.id, node);
|
|
1173
|
+
this.nodeMap = next;
|
|
1174
|
+
this.nodeIndexCache = undefined;
|
|
1175
|
+
this.indexNodeCache = undefined;
|
|
1176
|
+
}
|
|
1177
|
+
clearCache() {
|
|
1178
|
+
this.degreeCache = undefined;
|
|
1179
|
+
this.inAdjacencyCache = undefined;
|
|
1180
|
+
this.outAdjacencyCache = undefined;
|
|
1181
|
+
this.bothAdjacencyCache = undefined;
|
|
1182
|
+
this.nodeIndexCache = undefined;
|
|
1183
|
+
this.indexNodeCache = undefined;
|
|
1184
|
+
}
|
|
1185
|
+
buildDegreeCache() {
|
|
1186
|
+
this.degreeCache = new Map();
|
|
1187
|
+
for (const edge of this.edges()) {
|
|
1188
|
+
const { source, target } = edge;
|
|
1189
|
+
if (edge.source === edge.target)
|
|
1190
|
+
continue;
|
|
1191
|
+
if (!this.degreeCache.has(source)) {
|
|
1192
|
+
this.degreeCache.set(source, { in: 0, out: 0, both: 0 });
|
|
1193
|
+
}
|
|
1194
|
+
const sourceDeg = this.degreeCache.get(edge.source);
|
|
1195
|
+
if (sourceDeg) {
|
|
1196
|
+
sourceDeg.out++;
|
|
1197
|
+
sourceDeg.both++;
|
|
1198
|
+
}
|
|
1199
|
+
if (!this.degreeCache.has(target)) {
|
|
1200
|
+
this.degreeCache.set(target, { in: 0, out: 0, both: 0 });
|
|
1201
|
+
}
|
|
1202
|
+
const targetDeg = this.degreeCache.get(edge.target);
|
|
1203
|
+
if (targetDeg) {
|
|
1204
|
+
targetDeg.in++;
|
|
1205
|
+
targetDeg.both++;
|
|
1137
1206
|
}
|
|
1138
1207
|
}
|
|
1139
1208
|
}
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1209
|
+
buildAdjacencyCache() {
|
|
1210
|
+
this.inAdjacencyCache = new Map();
|
|
1211
|
+
this.outAdjacencyCache = new Map();
|
|
1212
|
+
for (const edge of this.edges()) {
|
|
1213
|
+
if (!this.nodeMap.has(edge.source) || !this.nodeMap.has(edge.target))
|
|
1214
|
+
continue;
|
|
1215
|
+
if (!this.outAdjacencyCache.has(edge.source)) {
|
|
1216
|
+
this.outAdjacencyCache.set(edge.source, new Set());
|
|
1217
|
+
}
|
|
1218
|
+
this.outAdjacencyCache.get(edge.source).add(edge.target);
|
|
1219
|
+
if (!this.inAdjacencyCache.has(edge.target)) {
|
|
1220
|
+
this.inAdjacencyCache.set(edge.target, new Set());
|
|
1221
|
+
}
|
|
1222
|
+
this.inAdjacencyCache.get(edge.target).add(edge.source);
|
|
1223
|
+
}
|
|
1145
1224
|
}
|
|
1146
|
-
|
|
1147
|
-
this.
|
|
1148
|
-
this.
|
|
1225
|
+
buildNodeIndexCache() {
|
|
1226
|
+
this.nodeIndexCache = new Map();
|
|
1227
|
+
this.indexNodeCache = new Map();
|
|
1228
|
+
let index = 0;
|
|
1229
|
+
this.nodeMap.forEach((_node, nodeId) => {
|
|
1230
|
+
this.nodeIndexCache.set(nodeId, index);
|
|
1231
|
+
this.indexNodeCache.set(index, nodeId);
|
|
1232
|
+
index++;
|
|
1233
|
+
});
|
|
1149
1234
|
}
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
this.bubbleDown(0);
|
|
1156
|
-
}
|
|
1157
|
-
return top;
|
|
1235
|
+
destroy() {
|
|
1236
|
+
this.clearCache();
|
|
1237
|
+
this.nodeMap.clear();
|
|
1238
|
+
this.edgeMap.clear();
|
|
1239
|
+
this.edgeIdCounter.clear();
|
|
1158
1240
|
}
|
|
1159
|
-
|
|
1160
|
-
|
|
1241
|
+
}
|
|
1242
|
+
const nodeFields = [
|
|
1243
|
+
'id',
|
|
1244
|
+
'x',
|
|
1245
|
+
'y',
|
|
1246
|
+
'z',
|
|
1247
|
+
'vx',
|
|
1248
|
+
'vy',
|
|
1249
|
+
'vz',
|
|
1250
|
+
'fx',
|
|
1251
|
+
'fy',
|
|
1252
|
+
'fz',
|
|
1253
|
+
'parentId',
|
|
1254
|
+
];
|
|
1255
|
+
const edgeFields = ['id', 'source', 'target', 'points'];
|
|
1256
|
+
function extractNodeData(nodes, node) {
|
|
1257
|
+
if (!nodes) {
|
|
1258
|
+
throw new Error('Data.nodes is required');
|
|
1161
1259
|
}
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1260
|
+
const result = new Map();
|
|
1261
|
+
for (const datum of nodes) {
|
|
1262
|
+
const nodeData = { _original: datum };
|
|
1263
|
+
for (const field of nodeFields) {
|
|
1264
|
+
const value = datum[field];
|
|
1265
|
+
if (isNil(value))
|
|
1266
|
+
continue;
|
|
1267
|
+
nodeData[field] = value;
|
|
1170
1268
|
}
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
if (left < length && data[left][0] < data[min][0])
|
|
1180
|
-
min = left;
|
|
1181
|
-
if (right < length && data[right][0] < data[min][0])
|
|
1182
|
-
min = right;
|
|
1183
|
-
if (min === pos)
|
|
1184
|
-
break;
|
|
1185
|
-
[data[pos], data[min]] = [data[min], data[pos]];
|
|
1186
|
-
pos = min;
|
|
1269
|
+
if (node) {
|
|
1270
|
+
const customFields = node(datum);
|
|
1271
|
+
for (const key in customFields) {
|
|
1272
|
+
const value = customFields[key];
|
|
1273
|
+
if (isNil(value))
|
|
1274
|
+
continue;
|
|
1275
|
+
nodeData[key] = value;
|
|
1276
|
+
}
|
|
1187
1277
|
}
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
/**
|
|
1192
|
-
* Get nested property value
|
|
1193
|
-
* For example: getNestedValue(obj, 'a.b.c') will return obj.a.b.c
|
|
1194
|
-
*/
|
|
1195
|
-
function getNestedValue(obj, path) {
|
|
1196
|
-
const keys = String(path).split('.');
|
|
1197
|
-
return get$1(obj, keys);
|
|
1198
|
-
}
|
|
1199
|
-
/**
|
|
1200
|
-
* Merge objects, but undefined values in source objects will not override existing values
|
|
1201
|
-
* @param target - The target object
|
|
1202
|
-
* @param sources - Source objects to merge
|
|
1203
|
-
* @returns A new merged object
|
|
1204
|
-
*
|
|
1205
|
-
* @example
|
|
1206
|
-
* assignDefined({ a: 1, b: 2 }, { b: undefined, c: 3 })
|
|
1207
|
-
* // Returns: { a: 1, b: 2, c: 3 }
|
|
1208
|
-
*/
|
|
1209
|
-
function assignDefined(target, ...sources) {
|
|
1210
|
-
sources.forEach((source) => {
|
|
1211
|
-
if (source) {
|
|
1212
|
-
Object.keys(source).forEach((key) => {
|
|
1213
|
-
const value = source[key];
|
|
1214
|
-
if (value !== undefined) {
|
|
1215
|
-
target[key] = value;
|
|
1216
|
-
}
|
|
1217
|
-
});
|
|
1278
|
+
if (isNil(nodeData.id)) {
|
|
1279
|
+
throw new Error(`Node is missing id field`);
|
|
1218
1280
|
}
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
function mergeOptions(base, patch) {
|
|
1223
|
-
return Object.assign({}, base, patch || {});
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
/**
|
|
1227
|
-
* 通用排序核心函数
|
|
1228
|
-
*/
|
|
1229
|
-
function sort$1(model, compareFn) {
|
|
1230
|
-
const nodes = model.nodes();
|
|
1231
|
-
nodes.sort(compareFn);
|
|
1232
|
-
model.setNodeOrder(nodes);
|
|
1233
|
-
return model;
|
|
1234
|
-
}
|
|
1235
|
-
function orderByDegree(model) {
|
|
1236
|
-
return sort$1(model, (nodeA, nodeB) => {
|
|
1237
|
-
const degreeA = model.degree(nodeA.id);
|
|
1238
|
-
const degreeB = model.degree(nodeB.id);
|
|
1239
|
-
return degreeB - degreeA; // descending order
|
|
1240
|
-
});
|
|
1281
|
+
result.set(nodeData.id, nodeData);
|
|
1282
|
+
}
|
|
1283
|
+
return result;
|
|
1241
1284
|
}
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1285
|
+
function extractEdgeData(edges, edge, getEdgeId) {
|
|
1286
|
+
const result = new Map();
|
|
1287
|
+
for (const datum of edges) {
|
|
1288
|
+
const edgeData = { _original: datum };
|
|
1289
|
+
for (const field of edgeFields) {
|
|
1290
|
+
const value = datum[field];
|
|
1291
|
+
if (isNil(value))
|
|
1292
|
+
continue;
|
|
1293
|
+
edgeData[field] = value;
|
|
1251
1294
|
}
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
return sort$1(model, (nodeA, nodeB) => {
|
|
1260
|
-
const a = model.originalNode(nodeA.id);
|
|
1261
|
-
const b = model.originalNode(nodeB.id);
|
|
1262
|
-
return sorter(a, b);
|
|
1263
|
-
});
|
|
1264
|
-
}
|
|
1265
|
-
/**
|
|
1266
|
-
* Order nodes according to graph topology
|
|
1267
|
-
*/
|
|
1268
|
-
function orderByTopology(model, directed = false) {
|
|
1269
|
-
const n = model.nodeCount();
|
|
1270
|
-
if (n === 0)
|
|
1271
|
-
return model;
|
|
1272
|
-
const nodes = model.nodes();
|
|
1273
|
-
const orderedNodes = [nodes[0]];
|
|
1274
|
-
const pickFlags = {};
|
|
1275
|
-
pickFlags[nodes[0].id] = true;
|
|
1276
|
-
let k = 0;
|
|
1277
|
-
let i = 0;
|
|
1278
|
-
model.forEachNode((node) => {
|
|
1279
|
-
if (i !== 0) {
|
|
1280
|
-
const currentDegree = model.degree(node.id, 'both');
|
|
1281
|
-
const nextDegree = i < n - 1 ? model.degree(nodes[i + 1].id, 'both') : 0;
|
|
1282
|
-
const currentNodeId = orderedNodes[k].id;
|
|
1283
|
-
const isNeighbor = model
|
|
1284
|
-
.neighbors(currentNodeId, 'both')
|
|
1285
|
-
.includes(node.id);
|
|
1286
|
-
if ((i === n - 1 || currentDegree !== nextDegree || isNeighbor) &&
|
|
1287
|
-
!pickFlags[node.id]) {
|
|
1288
|
-
orderedNodes.push(node);
|
|
1289
|
-
pickFlags[node.id] = true;
|
|
1290
|
-
k++;
|
|
1291
|
-
}
|
|
1292
|
-
else {
|
|
1293
|
-
const children = directed
|
|
1294
|
-
? model.successors(currentNodeId)
|
|
1295
|
-
: model.neighbors(currentNodeId);
|
|
1296
|
-
let foundChild = false;
|
|
1297
|
-
for (let j = 0; j < children.length; j++) {
|
|
1298
|
-
const childId = children[j];
|
|
1299
|
-
const child = model.node(childId);
|
|
1300
|
-
if (child &&
|
|
1301
|
-
model.degree(childId) === model.degree(node.id) &&
|
|
1302
|
-
!pickFlags[childId]) {
|
|
1303
|
-
orderedNodes.push(child);
|
|
1304
|
-
pickFlags[childId] = true;
|
|
1305
|
-
foundChild = true;
|
|
1306
|
-
break;
|
|
1307
|
-
}
|
|
1308
|
-
}
|
|
1309
|
-
let ii = 0;
|
|
1310
|
-
while (!foundChild) {
|
|
1311
|
-
if (!pickFlags[nodes[ii].id]) {
|
|
1312
|
-
orderedNodes.push(nodes[ii]);
|
|
1313
|
-
pickFlags[nodes[ii].id] = true;
|
|
1314
|
-
foundChild = true;
|
|
1315
|
-
}
|
|
1316
|
-
ii++;
|
|
1317
|
-
if (ii === n) {
|
|
1318
|
-
break;
|
|
1319
|
-
}
|
|
1320
|
-
}
|
|
1295
|
+
if (edge) {
|
|
1296
|
+
const customFields = edge(datum);
|
|
1297
|
+
for (const key in customFields) {
|
|
1298
|
+
const value = customFields[key];
|
|
1299
|
+
if (isNil(value))
|
|
1300
|
+
continue;
|
|
1301
|
+
edgeData[key] = value;
|
|
1321
1302
|
}
|
|
1322
1303
|
}
|
|
1323
|
-
|
|
1304
|
+
if (isNil(edgeData.source) || isNil(edgeData.target)) {
|
|
1305
|
+
throw new Error(`Edge is missing source or target field`);
|
|
1306
|
+
}
|
|
1307
|
+
if (isNil(edgeData.id)) {
|
|
1308
|
+
edgeData.id = getEdgeId === null || getEdgeId === void 0 ? void 0 : getEdgeId(datum);
|
|
1309
|
+
}
|
|
1310
|
+
result.set(edgeData.id, edgeData);
|
|
1311
|
+
}
|
|
1312
|
+
return result;
|
|
1313
|
+
}
|
|
1314
|
+
function initNodePosition(model, width, height, dimensions = 2) {
|
|
1315
|
+
model.forEachNode((node) => {
|
|
1316
|
+
if (isNil(node.x)) {
|
|
1317
|
+
node.x = Math.random() * width;
|
|
1318
|
+
}
|
|
1319
|
+
if (isNil(node.y)) {
|
|
1320
|
+
node.y = Math.random() * height;
|
|
1321
|
+
}
|
|
1322
|
+
if (dimensions === 3 && isNil(node.z)) {
|
|
1323
|
+
node.z = Math.random() * Math.min(width, height);
|
|
1324
|
+
}
|
|
1324
1325
|
});
|
|
1325
|
-
// Update model with ordered nodes
|
|
1326
|
-
model.setNodeOrder(orderedNodes);
|
|
1327
|
-
return model;
|
|
1328
1326
|
}
|
|
1329
1327
|
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1328
|
+
class RuntimeContext {
|
|
1329
|
+
constructor(data, options = {}) {
|
|
1330
|
+
this.graph = new GraphLib(data, options);
|
|
1331
|
+
}
|
|
1332
|
+
export() {
|
|
1333
|
+
return this.graph.data();
|
|
1334
|
+
}
|
|
1335
|
+
replace(result) {
|
|
1336
|
+
this.graph.replace(result);
|
|
1337
|
+
}
|
|
1338
|
+
forEachNode(callback) {
|
|
1339
|
+
this.graph.forEachNode(callback);
|
|
1340
|
+
}
|
|
1341
|
+
forEachEdge(callback) {
|
|
1342
|
+
this.graph.forEachEdge((edge, i) => {
|
|
1343
|
+
edge.sourceNode = this.graph.node(edge.source);
|
|
1344
|
+
edge.targetNode = this.graph.node(edge.target);
|
|
1345
|
+
callback(edge, i);
|
|
1346
|
+
});
|
|
1347
|
+
}
|
|
1348
|
+
destroy() {
|
|
1349
|
+
this.graph.destroy();
|
|
1350
|
+
}
|
|
1333
1351
|
}
|
|
1334
1352
|
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1353
|
+
class Supervisor {
|
|
1354
|
+
constructor() {
|
|
1355
|
+
this.worker = null;
|
|
1356
|
+
this.workerApi = null;
|
|
1357
|
+
}
|
|
1358
|
+
/**
|
|
1359
|
+
* Execute layout in worker
|
|
1360
|
+
*/
|
|
1361
|
+
execute(layoutId, data, options) {
|
|
1362
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1363
|
+
if (!this.worker) {
|
|
1364
|
+
yield this.initWorker();
|
|
1365
|
+
}
|
|
1366
|
+
if (!this.workerApi) {
|
|
1367
|
+
throw new Error('Worker API not initialized');
|
|
1368
|
+
}
|
|
1369
|
+
return yield this.workerApi.execute(layoutId, data, options);
|
|
1370
|
+
});
|
|
1371
|
+
}
|
|
1372
|
+
/**
|
|
1373
|
+
* Destroy worker
|
|
1374
|
+
*/
|
|
1375
|
+
destroy() {
|
|
1376
|
+
if (this.workerApi) {
|
|
1377
|
+
this.workerApi.destroy();
|
|
1378
|
+
}
|
|
1379
|
+
if (this.worker) {
|
|
1380
|
+
this.worker.terminate();
|
|
1381
|
+
this.worker = null;
|
|
1382
|
+
this.workerApi = null;
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
/**
|
|
1386
|
+
* Initialize worker
|
|
1387
|
+
*/
|
|
1388
|
+
initWorker() {
|
|
1389
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1390
|
+
const workerPath = this.resolveWorkerPath();
|
|
1391
|
+
const isESM = workerPath.includes('/lib/') || workerPath.endsWith('.mjs');
|
|
1392
|
+
const type = isESM ? 'module' : 'classic';
|
|
1393
|
+
this.worker = new Worker(workerPath, { type });
|
|
1394
|
+
this.workerApi = wrap(this.worker);
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
/**
|
|
1398
|
+
* Resolve worker script path which works in both ESM and UMD environments
|
|
1399
|
+
*/
|
|
1400
|
+
resolveWorkerPath() {
|
|
1401
|
+
if (typeof import.meta !== 'undefined' && import.meta.url) {
|
|
1402
|
+
const currentUrl = new URL(import.meta.url);
|
|
1403
|
+
// e.g. `.../lib/runtime/supervisor.js` -> `.../lib/worker.js`
|
|
1404
|
+
const asRoot = currentUrl.href.replace(/\/runtime\/[^/]+\.js$/, '/worker.js');
|
|
1405
|
+
if (asRoot !== currentUrl.href)
|
|
1406
|
+
return asRoot;
|
|
1407
|
+
// Fallback: keep legacy behavior (same directory)
|
|
1408
|
+
return currentUrl.href.replace(/\/[^/]+\.js$/, '/worker.js');
|
|
1409
|
+
}
|
|
1410
|
+
if (typeof document !== 'undefined') {
|
|
1411
|
+
const scripts = document.getElementsByTagName('script');
|
|
1412
|
+
for (let i = scripts.length - 1; i >= 0; i--) {
|
|
1413
|
+
const src = scripts[i].src;
|
|
1414
|
+
if (src && (src.includes('index.js') || src.includes('index.min.js'))) {
|
|
1415
|
+
return src.replace(/index(\.min)?\.js/, 'worker.js');
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
return './worker.js';
|
|
1420
|
+
}
|
|
1344
1421
|
}
|
|
1345
1422
|
|
|
1346
|
-
/**
|
|
1347
|
-
* Viewport configuration such as width, height and center point.
|
|
1348
|
-
*/
|
|
1349
|
-
const normalizeViewport = (options) => {
|
|
1350
|
-
const { width, height, center } = options;
|
|
1351
|
-
const normalizedWidth = width !== null && width !== void 0 ? width : (typeof window !== 'undefined' ? window.innerWidth : 0);
|
|
1352
|
-
const normalizedHeight = height !== null && height !== void 0 ? height : (typeof window !== 'undefined' ? window.innerHeight : 0);
|
|
1353
|
-
const centerPoint = center !== null && center !== void 0 ? center : [normalizedWidth / 2, normalizedHeight / 2];
|
|
1354
|
-
return {
|
|
1355
|
-
width: normalizedWidth,
|
|
1356
|
-
height: normalizedHeight,
|
|
1357
|
-
center: centerPoint,
|
|
1358
|
-
};
|
|
1359
|
-
};
|
|
1360
|
-
|
|
1361
1423
|
/**
|
|
1362
1424
|
* <zh/> 布局基类
|
|
1363
1425
|
*
|
|
@@ -1366,14 +1428,17 @@ const normalizeViewport = (options) => {
|
|
|
1366
1428
|
class BaseLayout {
|
|
1367
1429
|
constructor(options) {
|
|
1368
1430
|
this.supervisor = null;
|
|
1369
|
-
this.initialOptions = mergeOptions(this.getDefaultOptions(), options);
|
|
1431
|
+
this.initialOptions = this.mergeOptions(this.getDefaultOptions(), options);
|
|
1370
1432
|
}
|
|
1371
1433
|
get options() {
|
|
1372
1434
|
return this.runtimeOptions || this.initialOptions;
|
|
1373
1435
|
}
|
|
1436
|
+
mergeOptions(base, patch) {
|
|
1437
|
+
return Object.assign({}, base, patch || {});
|
|
1438
|
+
}
|
|
1374
1439
|
execute(data, userOptions) {
|
|
1375
1440
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1376
|
-
this.runtimeOptions = mergeOptions(this.initialOptions, userOptions);
|
|
1441
|
+
this.runtimeOptions = this.mergeOptions(this.initialOptions, userOptions);
|
|
1377
1442
|
const { node, edge, enableWorker } = this.runtimeOptions;
|
|
1378
1443
|
this.context = new RuntimeContext(data, { node, edge });
|
|
1379
1444
|
this.model = this.context.graph;
|
|
@@ -1430,71 +1495,6 @@ class BaseLayout {
|
|
|
1430
1495
|
class BaseLayoutWithIterations extends BaseLayout {
|
|
1431
1496
|
}
|
|
1432
1497
|
|
|
1433
|
-
/**
|
|
1434
|
-
* Format value with multiple types into a function that returns a number
|
|
1435
|
-
* @param value The value to be formatted
|
|
1436
|
-
* @param defaultValue The default value when value is invalid
|
|
1437
|
-
* @returns A function that returns a number
|
|
1438
|
-
*/
|
|
1439
|
-
function formatNumberFn(value, defaultValue) {
|
|
1440
|
-
// If value is a function, return it directly
|
|
1441
|
-
if (isFunction(value)) {
|
|
1442
|
-
return value;
|
|
1443
|
-
}
|
|
1444
|
-
// If value is a number, return a function that returns this number
|
|
1445
|
-
if (isNumber(value)) {
|
|
1446
|
-
return () => value;
|
|
1447
|
-
}
|
|
1448
|
-
// For other cases (undefined or invalid values), return default value function
|
|
1449
|
-
return () => defaultValue;
|
|
1450
|
-
}
|
|
1451
|
-
/**
|
|
1452
|
-
* Format size config with multiple types into a function that returns a size
|
|
1453
|
-
* @param value The value to be formatted
|
|
1454
|
-
* @param defaultValue The default value when value is invalid
|
|
1455
|
-
* @param resultIsNumber Whether to return a number (max of width/height) or size array
|
|
1456
|
-
* @returns A function that returns a size
|
|
1457
|
-
*/
|
|
1458
|
-
function formatSizeFn(value, defaultValue = 10) {
|
|
1459
|
-
// If value is undefined, return default value function
|
|
1460
|
-
if (!value) {
|
|
1461
|
-
return () => defaultValue;
|
|
1462
|
-
}
|
|
1463
|
-
// If value is a function, return it directly
|
|
1464
|
-
if (isFunction(value)) {
|
|
1465
|
-
return value;
|
|
1466
|
-
}
|
|
1467
|
-
// If value is a number, return a function that returns this number
|
|
1468
|
-
if (isNumber(value)) {
|
|
1469
|
-
return () => value;
|
|
1470
|
-
}
|
|
1471
|
-
// If value is an array, return max or the array itself
|
|
1472
|
-
if (Array.isArray(value)) {
|
|
1473
|
-
return () => value;
|
|
1474
|
-
}
|
|
1475
|
-
// If value is an object with width and height
|
|
1476
|
-
if (isObject(value) && value.width && value.height) {
|
|
1477
|
-
return () => [value.width, value.height];
|
|
1478
|
-
}
|
|
1479
|
-
return () => defaultValue;
|
|
1480
|
-
}
|
|
1481
|
-
/**
|
|
1482
|
-
* Format nodeSize and nodeSpacing into a function that returns the total size
|
|
1483
|
-
* @param nodeSize The size of the node
|
|
1484
|
-
* @param nodeSpacing The spacing around the node
|
|
1485
|
-
* @param defaultNodeSize The default node size when value is invalid
|
|
1486
|
-
* @returns A function that returns the total size (node size + spacing)
|
|
1487
|
-
*/
|
|
1488
|
-
const formatNodeSizeFn = (nodeSize, nodeSpacing, defaultNodeSize = 10) => {
|
|
1489
|
-
const nodeSpacingFunc = formatNumberFn(nodeSpacing, 0);
|
|
1490
|
-
const nodeSizeFunc = formatSizeFn(nodeSize, defaultNodeSize);
|
|
1491
|
-
return (node) => {
|
|
1492
|
-
const size = nodeSizeFunc(node);
|
|
1493
|
-
const spacing = nodeSpacingFunc(node);
|
|
1494
|
-
return Math.max(...parseSize(size)) + spacing;
|
|
1495
|
-
};
|
|
1496
|
-
};
|
|
1497
|
-
|
|
1498
1498
|
/**
|
|
1499
1499
|
* <zh/> 内部图数据结构,用于 antv-dagre 布局算法
|
|
1500
1500
|
*
|
|
@@ -4934,9 +4934,9 @@ class AntVDagreLayout extends BaseLayout {
|
|
|
4934
4934
|
return __awaiter(this, void 0, void 0, function* () {
|
|
4935
4935
|
const { nodeSize, align, rankdir = 'TB', ranksep, nodesep, edgeLabelSpace, ranker = 'tight-tree', nodeOrder, begin, controlPoints, radial, sortByCombo,
|
|
4936
4936
|
// focusNode,
|
|
4937
|
-
preset, } = options;
|
|
4938
|
-
const ranksepfunc = formatNumberFn(
|
|
4939
|
-
const nodesepfunc = formatNumberFn(
|
|
4937
|
+
preset, ranksepFunc, nodesepFunc, } = options;
|
|
4938
|
+
const ranksepfunc = formatNumberFn(ranksepFunc, ranksep !== null && ranksep !== void 0 ? ranksep : 50);
|
|
4939
|
+
const nodesepfunc = formatNumberFn(nodesepFunc, nodesep !== null && nodesep !== void 0 ? nodesep : 50);
|
|
4940
4940
|
let horisep = nodesepfunc;
|
|
4941
4941
|
let vertisep = ranksepfunc;
|
|
4942
4942
|
if (rankdir === 'LR' || rankdir === 'RL') {
|
|
@@ -4951,9 +4951,10 @@ class AntVDagreLayout extends BaseLayout {
|
|
|
4951
4951
|
const edges = this.model.edges();
|
|
4952
4952
|
nodes.forEach((node) => {
|
|
4953
4953
|
var _a;
|
|
4954
|
-
const
|
|
4955
|
-
const
|
|
4956
|
-
const
|
|
4954
|
+
const raw = node._original;
|
|
4955
|
+
const size = parseSize(nodeSizeFunc(raw));
|
|
4956
|
+
const verti = vertisep(raw);
|
|
4957
|
+
const hori = horisep(raw);
|
|
4957
4958
|
const width = size[0] + 2 * hori;
|
|
4958
4959
|
const height = size[1] + 2 * verti;
|
|
4959
4960
|
const layer = (_a = node.data) === null || _a === void 0 ? void 0 : _a.layer;
|
|
@@ -5019,6 +5020,7 @@ class AntVDagreLayout extends BaseLayout {
|
|
|
5019
5020
|
acyclicer: 'greedy',
|
|
5020
5021
|
ranker,
|
|
5021
5022
|
rankdir,
|
|
5023
|
+
nodesep,
|
|
5022
5024
|
align,
|
|
5023
5025
|
});
|
|
5024
5026
|
const layoutTopLeft = [0, 0];
|
|
@@ -7123,25 +7125,17 @@ function forceInABox() {
|
|
|
7123
7125
|
}
|
|
7124
7126
|
|
|
7125
7127
|
const DEFAULTS_LAYOUT_OPTIONS$6 = {
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
|
|
7130
|
-
|
|
7128
|
+
link: {
|
|
7129
|
+
id: (d) => String(d.id),
|
|
7130
|
+
},
|
|
7131
|
+
manyBody: {
|
|
7132
|
+
strength: -30,
|
|
7133
|
+
},
|
|
7131
7134
|
preventOverlap: false,
|
|
7132
7135
|
nodeSize: 10,
|
|
7133
7136
|
nodeSpacing: 0,
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
nodeStrength: -30,
|
|
7137
|
-
distanceMin: undefined,
|
|
7138
|
-
distanceMax: undefined,
|
|
7139
|
-
theta: undefined,
|
|
7140
|
-
alpha: 1,
|
|
7141
|
-
alphaMin: 0.001,
|
|
7142
|
-
alphaDecay: 1 - Math.pow(0.001, 1 / 300),
|
|
7143
|
-
alphaTarget: 0,
|
|
7144
|
-
velocityDecay: 0.4,
|
|
7137
|
+
x: false,
|
|
7138
|
+
y: false,
|
|
7145
7139
|
clustering: false,
|
|
7146
7140
|
clusterNodeStrength: -1,
|
|
7147
7141
|
clusterEdgeStrength: 0.1,
|
|
@@ -7358,20 +7352,17 @@ class D3ForceLayout extends BaseLayoutWithIterations {
|
|
|
7358
7352
|
this.setupClusterForce(simulation, options);
|
|
7359
7353
|
}
|
|
7360
7354
|
getCenterOptions(options) {
|
|
7361
|
-
if (
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
|
|
7372
|
-
});
|
|
7373
|
-
}
|
|
7374
|
-
return undefined;
|
|
7355
|
+
if (options.center === false)
|
|
7356
|
+
return undefined;
|
|
7357
|
+
const viewport = normalizeViewport({
|
|
7358
|
+
width: options.width,
|
|
7359
|
+
height: options.height,
|
|
7360
|
+
});
|
|
7361
|
+
return assignDefined({}, options.center || {}, {
|
|
7362
|
+
x: viewport.width / 2,
|
|
7363
|
+
y: viewport.height / 2,
|
|
7364
|
+
strength: options.centerStrength,
|
|
7365
|
+
});
|
|
7375
7366
|
}
|
|
7376
7367
|
setupCenterForce(simulation, options) {
|
|
7377
7368
|
const center = this.getCenterOptions(options);
|
|
@@ -7395,19 +7386,14 @@ class D3ForceLayout extends BaseLayoutWithIterations {
|
|
|
7395
7386
|
}
|
|
7396
7387
|
}
|
|
7397
7388
|
getManyBodyOptions(options) {
|
|
7398
|
-
if (options.manyBody
|
|
7399
|
-
|
|
7400
|
-
|
|
7401
|
-
options.
|
|
7402
|
-
options.
|
|
7403
|
-
|
|
7404
|
-
|
|
7405
|
-
|
|
7406
|
-
distanceMax: options.distanceMax,
|
|
7407
|
-
theta: options.theta,
|
|
7408
|
-
});
|
|
7409
|
-
}
|
|
7410
|
-
return undefined;
|
|
7389
|
+
if (options.manyBody === false)
|
|
7390
|
+
return undefined;
|
|
7391
|
+
return assignDefined({}, options.manyBody || {}, {
|
|
7392
|
+
strength: options.nodeStrength,
|
|
7393
|
+
distanceMin: options.distanceMin,
|
|
7394
|
+
distanceMax: options.distanceMax,
|
|
7395
|
+
theta: options.theta,
|
|
7396
|
+
});
|
|
7411
7397
|
}
|
|
7412
7398
|
setupManyBodyForce(simulation, options) {
|
|
7413
7399
|
const manyBody = this.getManyBodyOptions(options);
|
|
@@ -7433,19 +7419,14 @@ class D3ForceLayout extends BaseLayoutWithIterations {
|
|
|
7433
7419
|
}
|
|
7434
7420
|
}
|
|
7435
7421
|
getLinkOptions(options) {
|
|
7436
|
-
if (options.link
|
|
7437
|
-
|
|
7438
|
-
|
|
7439
|
-
options.
|
|
7440
|
-
options.
|
|
7441
|
-
|
|
7442
|
-
|
|
7443
|
-
|
|
7444
|
-
strength: options.edgeStrength,
|
|
7445
|
-
iterations: options.edgeIterations,
|
|
7446
|
-
});
|
|
7447
|
-
}
|
|
7448
|
-
return undefined;
|
|
7422
|
+
if (options.link === false)
|
|
7423
|
+
return undefined;
|
|
7424
|
+
return assignDefined({}, options.link || {}, {
|
|
7425
|
+
id: options.edgeId,
|
|
7426
|
+
distance: options.linkDistance,
|
|
7427
|
+
strength: options.edgeStrength,
|
|
7428
|
+
iterations: options.edgeIterations,
|
|
7429
|
+
});
|
|
7449
7430
|
}
|
|
7450
7431
|
setupLinkForce(simulation, options) {
|
|
7451
7432
|
const edges = this.model.edges();
|
|
@@ -7472,23 +7453,16 @@ class D3ForceLayout extends BaseLayoutWithIterations {
|
|
|
7472
7453
|
}
|
|
7473
7454
|
}
|
|
7474
7455
|
getCollisionOptions(options) {
|
|
7475
|
-
if (
|
|
7456
|
+
if (options.preventOverlap === false || options.collide === false)
|
|
7476
7457
|
return undefined;
|
|
7477
|
-
|
|
7478
|
-
options.nodeSize
|
|
7479
|
-
|
|
7480
|
-
|
|
7481
|
-
options.
|
|
7482
|
-
|
|
7483
|
-
|
|
7484
|
-
|
|
7485
|
-
return assignDefined({}, options.collide || {}, {
|
|
7486
|
-
radius,
|
|
7487
|
-
strength: options.collideStrength,
|
|
7488
|
-
iterations: options.collideIterations,
|
|
7489
|
-
});
|
|
7490
|
-
}
|
|
7491
|
-
return undefined;
|
|
7458
|
+
const radius = options.nodeSize || options.nodeSpacing
|
|
7459
|
+
? (d) => formatNodeSizeFn(options.nodeSize, options.nodeSpacing)(d._original) / 2
|
|
7460
|
+
: undefined;
|
|
7461
|
+
return assignDefined({}, options.collide || {}, {
|
|
7462
|
+
radius: options.collide || radius,
|
|
7463
|
+
strength: options.collideStrength,
|
|
7464
|
+
iterations: options.collideIterations,
|
|
7465
|
+
});
|
|
7492
7466
|
}
|
|
7493
7467
|
setupCollisionForce(simulation, options) {
|
|
7494
7468
|
const collide = this.getCollisionOptions(options);
|
|
@@ -7513,18 +7487,13 @@ class D3ForceLayout extends BaseLayoutWithIterations {
|
|
|
7513
7487
|
}
|
|
7514
7488
|
getXForceOptions(options) {
|
|
7515
7489
|
var _a;
|
|
7516
|
-
if (options.x
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
options.
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
x: (_a = options.forceXPosition) !== null && _a !== void 0 ? _a : (center && center.x),
|
|
7524
|
-
strength: options.forceXStrength,
|
|
7525
|
-
});
|
|
7526
|
-
}
|
|
7527
|
-
return undefined;
|
|
7490
|
+
if (options.x === false)
|
|
7491
|
+
return undefined;
|
|
7492
|
+
const center = this.getCenterOptions(options);
|
|
7493
|
+
return assignDefined({}, options.x || {}, {
|
|
7494
|
+
x: (_a = options.forceXPosition) !== null && _a !== void 0 ? _a : (center && center.x),
|
|
7495
|
+
strength: options.forceXStrength,
|
|
7496
|
+
});
|
|
7528
7497
|
}
|
|
7529
7498
|
setupXForce(simulation, options) {
|
|
7530
7499
|
const x = this.getXForceOptions(options);
|
|
@@ -7547,18 +7516,13 @@ class D3ForceLayout extends BaseLayoutWithIterations {
|
|
|
7547
7516
|
}
|
|
7548
7517
|
getYForceOptions(options) {
|
|
7549
7518
|
var _a;
|
|
7550
|
-
if (options.y
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
options.
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
y: (_a = options.forceYPosition) !== null && _a !== void 0 ? _a : (center && center.y),
|
|
7558
|
-
strength: options.forceYStrength,
|
|
7559
|
-
});
|
|
7560
|
-
}
|
|
7561
|
-
return undefined;
|
|
7519
|
+
if (options.y === false)
|
|
7520
|
+
return undefined;
|
|
7521
|
+
const center = this.getCenterOptions(options);
|
|
7522
|
+
return assignDefined({}, options.y || {}, {
|
|
7523
|
+
y: (_a = options.forceYPosition) !== null && _a !== void 0 ? _a : (center && center.y),
|
|
7524
|
+
strength: options.forceYStrength,
|
|
7525
|
+
});
|
|
7562
7526
|
}
|
|
7563
7527
|
setupYForce(simulation, options) {
|
|
7564
7528
|
const y = this.getYForceOptions(options);
|