@antv/layout 1.2.0 → 1.2.2

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.
Files changed (142) hide show
  1. package/dist/0229766b7896aada2515.worker.js +2 -0
  2. package/dist/0229766b7896aada2515.worker.js.map +1 -0
  3. package/dist/index.min.js +1 -1
  4. package/dist/index.min.js.map +1 -1
  5. package/lib/bundle-entry.d.ts +18 -0
  6. package/lib/bundle-entry.js +19 -0
  7. package/lib/bundle-entry.js.map +1 -0
  8. package/lib/bundle-supervisor.d.ts +51 -0
  9. package/lib/bundle-supervisor.js +93 -0
  10. package/lib/bundle-supervisor.js.map +1 -0
  11. package/lib/bundle-worker.d.ts +3 -0
  12. package/lib/bundle-worker.js +53 -0
  13. package/lib/bundle-worker.js.map +1 -0
  14. package/lib/circular.js +258 -0
  15. package/lib/circular.js.map +1 -0
  16. package/lib/concentric.js +221 -0
  17. package/lib/concentric.js.map +1 -0
  18. package/lib/d3Force/forceInBox.js +331 -0
  19. package/lib/d3Force/forceInBox.js.map +1 -0
  20. package/lib/d3Force/index.js +339 -0
  21. package/lib/d3Force/index.js.map +1 -0
  22. package/lib/dagre/acyclic.js +62 -0
  23. package/lib/dagre/acyclic.js.map +1 -0
  24. package/lib/dagre/add-border-segments.js +37 -0
  25. package/lib/dagre/add-border-segments.js.map +1 -0
  26. package/lib/dagre/coordinate-system.js +65 -0
  27. package/lib/dagre/coordinate-system.js.map +1 -0
  28. package/lib/dagre/data/list.js +50 -0
  29. package/lib/dagre/data/list.js.map +1 -0
  30. package/lib/dagre/greedy-fas.js +147 -0
  31. package/lib/dagre/greedy-fas.js.map +1 -0
  32. package/lib/dagre/layout.js +468 -0
  33. package/lib/dagre/layout.js.map +1 -0
  34. package/lib/dagre/nesting-graph.js +153 -0
  35. package/lib/dagre/nesting-graph.js.map +1 -0
  36. package/lib/dagre/normalize.js +98 -0
  37. package/lib/dagre/normalize.js.map +1 -0
  38. package/lib/dagre/order/add-subgraph-constraints.js +41 -0
  39. package/lib/dagre/order/add-subgraph-constraints.js.map +1 -0
  40. package/lib/dagre/order/barycenter.js +23 -0
  41. package/lib/dagre/order/barycenter.js.map +1 -0
  42. package/lib/dagre/order/build-layer-graph.js +96 -0
  43. package/lib/dagre/order/build-layer-graph.js.map +1 -0
  44. package/lib/dagre/order/cross-count.js +63 -0
  45. package/lib/dagre/order/cross-count.js.map +1 -0
  46. package/lib/dagre/order/index.js +91 -0
  47. package/lib/dagre/order/index.js.map +1 -0
  48. package/lib/dagre/order/init-data-order.js +28 -0
  49. package/lib/dagre/order/init-data-order.js.map +1 -0
  50. package/lib/dagre/order/init-order.js +50 -0
  51. package/lib/dagre/order/init-order.js.map +1 -0
  52. package/lib/dagre/order/resolve-conflicts.js +121 -0
  53. package/lib/dagre/order/resolve-conflicts.js.map +1 -0
  54. package/lib/dagre/order/sort-subgraph.js +84 -0
  55. package/lib/dagre/order/sort-subgraph.js.map +1 -0
  56. package/lib/dagre/order/sort.js +74 -0
  57. package/lib/dagre/order/sort.js.map +1 -0
  58. package/lib/dagre/parent-dummy-chains.js +80 -0
  59. package/lib/dagre/parent-dummy-chains.js.map +1 -0
  60. package/lib/dagre/position/bk.js +398 -0
  61. package/lib/dagre/position/bk.js.map +1 -0
  62. package/lib/dagre/position/index.js +54 -0
  63. package/lib/dagre/position/index.js.map +1 -0
  64. package/lib/dagre/rank/feasible-tree.js +164 -0
  65. package/lib/dagre/rank/feasible-tree.js.map +1 -0
  66. package/lib/dagre/rank/index.js +48 -0
  67. package/lib/dagre/rank/index.js.map +1 -0
  68. package/lib/dagre/rank/network-simplex.js +232 -0
  69. package/lib/dagre/rank/network-simplex.js.map +1 -0
  70. package/lib/dagre/rank/util.js +138 -0
  71. package/lib/dagre/rank/util.js.map +1 -0
  72. package/lib/dagre/util.js +289 -0
  73. package/lib/dagre/util.js.map +1 -0
  74. package/lib/dagre.d.ts +1 -2
  75. package/lib/dagre.js +543 -0
  76. package/lib/dagre.js.map +1 -0
  77. package/lib/force/forceNBody.js +103 -0
  78. package/lib/force/forceNBody.js.map +1 -0
  79. package/lib/force/index.js +797 -0
  80. package/lib/force/index.js.map +1 -0
  81. package/lib/force/types.js +2 -0
  82. package/lib/force/types.js.map +1 -0
  83. package/lib/forceAtlas2/body.js +91 -0
  84. package/lib/forceAtlas2/body.js.map +1 -0
  85. package/lib/forceAtlas2/index.js +562 -0
  86. package/lib/forceAtlas2/index.js.map +1 -0
  87. package/lib/forceAtlas2/quad.js +98 -0
  88. package/lib/forceAtlas2/quad.js.map +1 -0
  89. package/lib/forceAtlas2/quadTree.js +105 -0
  90. package/lib/forceAtlas2/quadTree.js.map +1 -0
  91. package/lib/fruchterman.js +376 -0
  92. package/lib/fruchterman.js.map +1 -0
  93. package/lib/grid.js +308 -0
  94. package/lib/grid.js.map +1 -0
  95. package/lib/index.js +16 -0
  96. package/lib/index.js.map +1 -0
  97. package/lib/mds.js +137 -0
  98. package/lib/mds.js.map +1 -0
  99. package/lib/radial/index.js +355 -0
  100. package/lib/radial/index.js.map +1 -0
  101. package/lib/radial/mds.js +29 -0
  102. package/lib/radial/mds.js.map +1 -0
  103. package/lib/radial/radial-nonoverlap-force.js +100 -0
  104. package/lib/radial/radial-nonoverlap-force.js.map +1 -0
  105. package/lib/random.js +98 -0
  106. package/lib/random.js.map +1 -0
  107. package/lib/registry.d.ts +0 -1
  108. package/lib/registry.js +25 -0
  109. package/lib/registry.js.map +1 -0
  110. package/lib/supervisor.js +86 -0
  111. package/lib/supervisor.js.map +1 -0
  112. package/lib/types.js +4 -0
  113. package/lib/types.js.map +1 -0
  114. package/lib/util/array.js +2 -0
  115. package/lib/util/array.js.map +1 -0
  116. package/lib/util/common.d.ts +12 -0
  117. package/lib/util/common.js +32 -0
  118. package/lib/util/common.js.map +1 -0
  119. package/lib/util/function.js +124 -0
  120. package/lib/util/function.js.map +1 -0
  121. package/lib/util/gpu.js +214 -0
  122. package/lib/util/gpu.js.map +1 -0
  123. package/lib/util/index.js +7 -0
  124. package/lib/util/index.js.map +1 -0
  125. package/lib/util/math.js +228 -0
  126. package/lib/util/math.js.map +1 -0
  127. package/lib/util/number.js +5 -0
  128. package/lib/util/number.js.map +1 -0
  129. package/lib/util/object.js +42 -0
  130. package/lib/util/object.js.map +1 -0
  131. package/lib/util/string.js +15 -0
  132. package/lib/util/string.js.map +1 -0
  133. package/lib/worker.d.ts +1 -3
  134. package/lib/worker.js +45 -0
  135. package/lib/worker.js.map +1 -0
  136. package/package.json +6 -5
  137. package/dist/5e37927bd3e57252658c.worker.js +0 -2
  138. package/dist/5e37927bd3e57252658c.worker.js.map +0 -1
  139. package/esm/5e37927bd3e57252658c.worker.js +0 -2
  140. package/esm/5e37927bd3e57252658c.worker.js.map +0 -1
  141. package/esm/index.esm.js +0 -2
  142. package/esm/index.esm.js.map +0 -1
@@ -0,0 +1,797 @@
1
+ import { __assign, __awaiter, __generator, __read } from "tslib";
2
+ import { Graph as IGraph } from "@antv/graphlib";
3
+ import { isNumber, isObject } from "@antv/util";
4
+ import { formatNumberFn, isArray } from "../util";
5
+ import { forceNBody } from "./forceNBody";
6
+ var DEFAULTS_LAYOUT_OPTIONS = {
7
+ dimensions: 2,
8
+ maxIteration: 500,
9
+ gravity: 10,
10
+ factor: 1,
11
+ edgeStrength: 200,
12
+ nodeStrength: 1000,
13
+ coulombDisScale: 0.005,
14
+ damping: 0.9,
15
+ maxSpeed: 500,
16
+ minMovement: 0.4,
17
+ interval: 0.02,
18
+ linkDistance: 200,
19
+ clusterNodeStrength: 20,
20
+ preventOverlap: true,
21
+ distanceThresholdMode: "mean",
22
+ };
23
+ /**
24
+ * Layout with faster force
25
+ *
26
+ * @example
27
+ * // Assign layout options when initialization.
28
+ * const layout = new ForceLayout({ center: [100, 100] });
29
+ * const positions = await layout.execute(graph); // { nodes: [], edges: [] }
30
+ *
31
+ * // Or use different options later.
32
+ * const layout = new ForceLayout({ center: [100, 100] });
33
+ * const positions = await layout.execute(graph, { center: [100, 100] }); // { nodes: [], edges: [] }
34
+ *
35
+ * // If you want to assign the positions directly to the nodes, use assign method.
36
+ * await layout.assign(graph, { center: [100, 100] });
37
+ */
38
+ var ForceLayout = /** @class */ (function () {
39
+ function ForceLayout(options) {
40
+ if (options === void 0) { options = {}; }
41
+ this.options = options;
42
+ this.id = "force";
43
+ /**
44
+ * time interval for layout force animations
45
+ */
46
+ this.timeInterval = 0;
47
+ /**
48
+ * compare with minMovement to end the nodes' movement
49
+ */
50
+ this.judgingDistance = 0;
51
+ this.running = false;
52
+ this.options = __assign(__assign({}, DEFAULTS_LAYOUT_OPTIONS), options);
53
+ }
54
+ /**
55
+ * Return the positions of nodes and edges(if needed).
56
+ */
57
+ ForceLayout.prototype.execute = function (graph, options) {
58
+ return __awaiter(this, void 0, void 0, function () {
59
+ return __generator(this, function (_a) {
60
+ return [2 /*return*/, this.genericForceLayout(false, graph, options)];
61
+ });
62
+ });
63
+ };
64
+ /**
65
+ * To directly assign the positions to the nodes.
66
+ */
67
+ ForceLayout.prototype.assign = function (graph, options) {
68
+ return __awaiter(this, void 0, void 0, function () {
69
+ return __generator(this, function (_a) {
70
+ this.genericForceLayout(true, graph, options);
71
+ return [2 /*return*/];
72
+ });
73
+ });
74
+ };
75
+ /**
76
+ * Stop simulation immediately.
77
+ */
78
+ ForceLayout.prototype.stop = function () {
79
+ if (this.timeInterval && typeof window !== "undefined") {
80
+ window.clearInterval(this.timeInterval);
81
+ }
82
+ this.running = false;
83
+ };
84
+ /**
85
+ * Manually steps the simulation by the specified number of iterations.
86
+ * @see https://github.com/d3/d3-force#simulation_tick
87
+ */
88
+ ForceLayout.prototype.tick = function (iterations) {
89
+ var _this = this;
90
+ if (iterations === void 0) { iterations = this.options.maxIteration || 1; }
91
+ if (this.lastResult) {
92
+ return this.lastResult;
93
+ }
94
+ for (var i = 0; (this.judgingDistance > this.lastOptions.minMovement || i < 1) &&
95
+ i < iterations; i++) {
96
+ this.runOneStep(this.lastCalcGraph, this.lastGraph, i, this.lastVelMap, this.lastOptions);
97
+ this.updatePosition(this.lastGraph, this.lastCalcGraph, this.lastVelMap, this.lastOptions);
98
+ }
99
+ var result = {
100
+ nodes: this.lastLayoutNodes,
101
+ edges: this.lastLayoutEdges,
102
+ };
103
+ if (this.lastAssign) {
104
+ result.nodes.forEach(function (node) {
105
+ return _this.lastGraph.mergeNodeData(node.id, {
106
+ x: node.data.x,
107
+ y: node.data.y,
108
+ z: _this.options.dimensions === 3 ? node.data.z : undefined
109
+ });
110
+ });
111
+ }
112
+ return result;
113
+ };
114
+ ForceLayout.prototype.genericForceLayout = function (assign, graph, options) {
115
+ return __awaiter(this, void 0, void 0, function () {
116
+ var mergedOptions, nodes, edges, formattedOptions, dimensions, width, height, nodeSize, getMass, nodeStrength, edgeStrength, linkDistance, layoutNodes, layoutEdges, velMap, calcGraph, maxIteration, minMovement, onTick, iter;
117
+ var _this = this;
118
+ return __generator(this, function (_a) {
119
+ mergedOptions = __assign(__assign({}, this.options), options);
120
+ nodes = graph.getAllNodes();
121
+ edges = graph.getAllEdges();
122
+ formattedOptions = this.formatOptions(mergedOptions, graph);
123
+ dimensions = formattedOptions.dimensions, width = formattedOptions.width, height = formattedOptions.height, nodeSize = formattedOptions.nodeSize, getMass = formattedOptions.getMass, nodeStrength = formattedOptions.nodeStrength, edgeStrength = formattedOptions.edgeStrength, linkDistance = formattedOptions.linkDistance;
124
+ layoutNodes = nodes.map(function (node, i) {
125
+ return (__assign(__assign({}, node), { data: __assign(__assign({}, node.data), {
126
+ // ...randomDistribution(node, dimensions, 30, i),
127
+ x: isNumber(node.data.x) ? node.data.x : Math.random() * width, y: isNumber(node.data.y) ? node.data.y : Math.random() * height, z: isNumber(node.data.z) ? node.data.z : Math.random() * Math.sqrt(width * height), size: nodeSize(node) || 30, mass: getMass(node), nodeStrength: nodeStrength(node) }) }));
128
+ });
129
+ layoutEdges = edges.map(function (edge) { return (__assign(__assign({}, edge), { data: __assign(__assign({}, edge.data), { edgeStrength: edgeStrength(edge), linkDistance: linkDistance(edge, graph.getNode(edge.source), graph.getNode(edge.target)) }) })); });
130
+ if (!(nodes === null || nodes === void 0 ? void 0 : nodes.length)) {
131
+ this.lastResult = { nodes: [], edges: edges };
132
+ return [2 /*return*/, { nodes: [], edges: edges }];
133
+ }
134
+ velMap = {};
135
+ nodes.forEach(function (node, i) {
136
+ velMap[node.id] = {
137
+ x: 0,
138
+ y: 0,
139
+ z: 0,
140
+ };
141
+ });
142
+ calcGraph = new IGraph({
143
+ nodes: layoutNodes,
144
+ edges: layoutEdges,
145
+ });
146
+ this.formatCentripetal(formattedOptions, calcGraph);
147
+ maxIteration = formattedOptions.maxIteration, minMovement = formattedOptions.minMovement, onTick = formattedOptions.onTick;
148
+ // Use them later in `tick`.
149
+ this.lastLayoutNodes = layoutNodes;
150
+ this.lastLayoutEdges = layoutEdges;
151
+ this.lastAssign = assign;
152
+ this.lastGraph = graph;
153
+ this.lastCalcGraph = calcGraph;
154
+ this.lastOptions = formattedOptions;
155
+ this.lastVelMap = velMap;
156
+ if (typeof window === "undefined")
157
+ return [2 /*return*/];
158
+ iter = 0;
159
+ return [2 /*return*/, new Promise(function (resolve) {
160
+ // interval for render the result after each iteration
161
+ _this.timeInterval = window.setInterval(function () {
162
+ if (!nodes || !_this.running) {
163
+ resolve({
164
+ nodes: formatOutNodes(graph, layoutNodes),
165
+ edges: edges,
166
+ });
167
+ }
168
+ _this.runOneStep(calcGraph, graph, iter, velMap, formattedOptions);
169
+ _this.updatePosition(graph, calcGraph, velMap, formattedOptions);
170
+ if (assign) {
171
+ layoutNodes.forEach(function (node) {
172
+ return graph.mergeNodeData(node.id, {
173
+ x: node.data.x,
174
+ y: node.data.y,
175
+ z: dimensions === 3 ? node.data.z : undefined
176
+ });
177
+ });
178
+ }
179
+ onTick === null || onTick === void 0 ? void 0 : onTick({
180
+ nodes: formatOutNodes(graph, layoutNodes),
181
+ edges: edges,
182
+ });
183
+ iter++;
184
+ if (iter >= maxIteration || _this.judgingDistance < minMovement) {
185
+ window.clearInterval(_this.timeInterval);
186
+ resolve({
187
+ nodes: formatOutNodes(graph, layoutNodes),
188
+ edges: edges,
189
+ });
190
+ }
191
+ }, 0);
192
+ _this.running = true;
193
+ })];
194
+ });
195
+ });
196
+ };
197
+ /**
198
+ * Format merged layout options.
199
+ * @param options merged layout options
200
+ * @param graph original graph
201
+ * @returns
202
+ */
203
+ ForceLayout.prototype.formatOptions = function (options, graph) {
204
+ var formattedOptions = options;
205
+ var propsWidth = options.width, propsHeight = options.height, getMass = options.getMass;
206
+ // === formating width, height, and center =====
207
+ formattedOptions.width =
208
+ !propsWidth && typeof window !== "undefined"
209
+ ? window.innerWidth
210
+ : propsWidth;
211
+ formattedOptions.height =
212
+ !propsHeight && typeof window !== "undefined"
213
+ ? window.innerHeight
214
+ : propsHeight;
215
+ if (!options.center) {
216
+ formattedOptions.center = [
217
+ formattedOptions.width / 2,
218
+ formattedOptions.height / 2,
219
+ ];
220
+ }
221
+ // === formating node mass =====
222
+ if (!getMass) {
223
+ formattedOptions.getMass = function (d) {
224
+ var massWeight = 1;
225
+ if (isNumber(d === null || d === void 0 ? void 0 : d.data.mass))
226
+ massWeight = d === null || d === void 0 ? void 0 : d.data.mass;
227
+ var degree = graph.getDegree(d.id, "both");
228
+ return !degree || degree < 5 ? massWeight : degree * 5 * massWeight;
229
+ };
230
+ }
231
+ // === formating node size =====
232
+ if (options.preventOverlap) {
233
+ var nodeSpacingFunc_1 = formatNumberFn(0, options.nodeSpacing);
234
+ if (!options.nodeSize) {
235
+ formattedOptions.nodeSize = function (d) {
236
+ var size = ((d === null || d === void 0 ? void 0 : d.data) || {}).size;
237
+ if (size) {
238
+ if (isArray(size)) {
239
+ return Math.max(size[0], size[1]) + nodeSpacingFunc_1(d);
240
+ }
241
+ if (isObject(size)) {
242
+ return Math.max(size.width, size.height) + nodeSpacingFunc_1(d);
243
+ }
244
+ return size + nodeSpacingFunc_1(d);
245
+ }
246
+ return 10 + nodeSpacingFunc_1(d);
247
+ };
248
+ }
249
+ else if (isArray(options.nodeSize)) {
250
+ formattedOptions.nodeSize = function (d) {
251
+ var nodeSize = options.nodeSize;
252
+ return Math.max(nodeSize[0], nodeSize[1]) + nodeSpacingFunc_1(d);
253
+ };
254
+ }
255
+ else {
256
+ formattedOptions.nodeSize = function (d) {
257
+ return options.nodeSize + nodeSpacingFunc_1(d);
258
+ };
259
+ }
260
+ }
261
+ // === formating node / edge strengths =====
262
+ formattedOptions.linkDistance = options.linkDistance
263
+ ? formatNumberFn(1, options.linkDistance)
264
+ : function (edge) {
265
+ return 1 +
266
+ formattedOptions.nodeSize(graph.getNode(edge.source)) +
267
+ formattedOptions.nodeSize(graph.getNode(edge.target));
268
+ };
269
+ formattedOptions.nodeStrength = formatNumberFn(1, options.nodeStrength);
270
+ formattedOptions.edgeStrength = formatNumberFn(1, options.edgeStrength);
271
+ return options;
272
+ };
273
+ /**
274
+ * Format centripetalOption in the option.
275
+ * @param options merged layout options
276
+ * @param calcGraph calculation graph
277
+ */
278
+ ForceLayout.prototype.formatCentripetal = function (options, calcGraph) {
279
+ var dimensions = options.dimensions, centripetalOptions = options.centripetalOptions, center = options.center, clusterNodeStrength = options.clusterNodeStrength, leafCluster = options.leafCluster, clustering = options.clustering, nodeClusterBy = options.nodeClusterBy;
280
+ var calcNodes = calcGraph.getAllNodes();
281
+ // === formating centripetalOptions =====
282
+ var basicCentripetal = centripetalOptions || {
283
+ leaf: 2,
284
+ single: 2,
285
+ others: 1,
286
+ // eslint-disable-next-line
287
+ center: function (n) {
288
+ return {
289
+ x: center[0],
290
+ y: center[1],
291
+ z: dimensions === 3 ? center[2] : undefined
292
+ };
293
+ },
294
+ };
295
+ if (typeof clusterNodeStrength !== "function") {
296
+ options.clusterNodeStrength = function (node) {
297
+ return clusterNodeStrength;
298
+ };
299
+ }
300
+ var sameTypeLeafMap;
301
+ var clusters = [];
302
+ if (leafCluster && nodeClusterBy) {
303
+ sameTypeLeafMap = getSameTypeLeafMap(calcGraph, nodeClusterBy);
304
+ clusters =
305
+ Array.from(new Set(calcNodes === null || calcNodes === void 0 ? void 0 : calcNodes.map(function (node) { return node.data[nodeClusterBy]; }))) || [];
306
+ // @ts-ignore
307
+ options.centripetalOptions = Object.assign(basicCentripetal, {
308
+ single: 100,
309
+ leaf: function (node) {
310
+ // 找出与它关联的边的起点或终点出发的所有一度节点中同类型的叶子节点
311
+ var _a = sameTypeLeafMap[node.id] || {}, siblingLeaves = _a.siblingLeaves, sameTypeLeaves = _a.sameTypeLeaves;
312
+ // 如果都是同一类型或者每种类型只有1个,则施加默认向心力
313
+ if ((sameTypeLeaves === null || sameTypeLeaves === void 0 ? void 0 : sameTypeLeaves.length) === (siblingLeaves === null || siblingLeaves === void 0 ? void 0 : siblingLeaves.length) ||
314
+ (clusters === null || clusters === void 0 ? void 0 : clusters.length) === 1) {
315
+ return 1;
316
+ }
317
+ return options.clusterNodeStrength(node);
318
+ },
319
+ others: 1,
320
+ center: function (node) {
321
+ var degree = calcGraph.getDegree(node.id, "both");
322
+ // 孤点默认给1个远离的中心点
323
+ if (!degree) {
324
+ return {
325
+ x: 100,
326
+ y: 100,
327
+ z: 0,
328
+ };
329
+ }
330
+ var centerPos;
331
+ if (degree === 1) {
332
+ // 如果为叶子节点
333
+ // 找出与它关联的边的起点出发的所有一度节点中同类型的叶子节点
334
+ var _a = (sameTypeLeafMap[node.id] || {}).sameTypeLeaves, sameTypeLeaves = _a === void 0 ? [] : _a;
335
+ if (sameTypeLeaves.length === 1) {
336
+ // 如果同类型的叶子节点只有1个,中心位置为undefined
337
+ centerPos = undefined;
338
+ }
339
+ else if (sameTypeLeaves.length > 1) {
340
+ // 找出同类型节点平均位置作为中心
341
+ centerPos = getAvgNodePosition(sameTypeLeaves);
342
+ }
343
+ }
344
+ else {
345
+ centerPos = undefined;
346
+ }
347
+ return {
348
+ x: centerPos === null || centerPos === void 0 ? void 0 : centerPos.x,
349
+ y: centerPos === null || centerPos === void 0 ? void 0 : centerPos.y,
350
+ z: centerPos === null || centerPos === void 0 ? void 0 : centerPos.z,
351
+ };
352
+ },
353
+ });
354
+ }
355
+ if (clustering && nodeClusterBy) {
356
+ if (!sameTypeLeafMap) {
357
+ sameTypeLeafMap = getSameTypeLeafMap(calcGraph, nodeClusterBy);
358
+ }
359
+ if (!clusters) {
360
+ clusters = Array.from(new Set(calcNodes.map(function (node) { return node.data[nodeClusterBy]; })));
361
+ }
362
+ clusters = clusters.filter(function (item) { return item !== undefined; });
363
+ var centerInfo_1 = {};
364
+ clusters.forEach(function (cluster) {
365
+ var sameTypeNodes = calcNodes
366
+ .filter(function (node) { return node.data[nodeClusterBy] === cluster; })
367
+ .map(function (node) { return calcGraph.getNode(node.id); });
368
+ // 找出同类型节点平均位置节点的距离最近的节点作为中心节点
369
+ centerInfo_1[cluster] = getAvgNodePosition(sameTypeNodes);
370
+ });
371
+ options.centripetalOptions = Object.assign(basicCentripetal, {
372
+ single: function (node) { return options.clusterNodeStrength(node); },
373
+ leaf: function (node) { return options.clusterNodeStrength(node); },
374
+ others: function (node) { return options.clusterNodeStrength(node); },
375
+ center: function (node) {
376
+ // 找出同类型节点平均位置节点的距离最近的节点作为中心节点
377
+ var centerPos = centerInfo_1[node.data[nodeClusterBy]];
378
+ return {
379
+ x: centerPos === null || centerPos === void 0 ? void 0 : centerPos.x,
380
+ y: centerPos === null || centerPos === void 0 ? void 0 : centerPos.y,
381
+ z: centerPos === null || centerPos === void 0 ? void 0 : centerPos.z,
382
+ };
383
+ },
384
+ });
385
+ }
386
+ var _a = options.centripetalOptions || {}, leaf = _a.leaf, single = _a.single, others = _a.others;
387
+ if (leaf && typeof leaf !== "function") {
388
+ options.centripetalOptions.leaf = function () { return leaf; };
389
+ }
390
+ if (single && typeof single !== "function") {
391
+ options.centripetalOptions.single = function () { return single; };
392
+ }
393
+ if (others && typeof others !== "function") {
394
+ options.centripetalOptions.others = function () { return others; };
395
+ }
396
+ };
397
+ /**
398
+ * One iteration.
399
+ * @param calcGraph calculation graph
400
+ * @param graph origin graph
401
+ * @param iter current iteration index
402
+ * @param velMap nodes' velocity map
403
+ * @param options formatted layout options
404
+ * @returns
405
+ */
406
+ ForceLayout.prototype.runOneStep = function (calcGraph, graph, iter, velMap, options) {
407
+ var accMap = {};
408
+ var calcNodes = calcGraph.getAllNodes();
409
+ var calcEdges = calcGraph.getAllEdges();
410
+ if (!(calcNodes === null || calcNodes === void 0 ? void 0 : calcNodes.length))
411
+ return;
412
+ var monitor = options.monitor;
413
+ this.calRepulsive(calcGraph, accMap, options);
414
+ if (calcEdges)
415
+ this.calAttractive(calcGraph, accMap, options);
416
+ this.calGravity(calcGraph, graph, accMap, options);
417
+ this.updateVelocity(calcGraph, accMap, velMap, options);
418
+ /** 如果需要监控信息,则提供给用户 */
419
+ if (monitor) {
420
+ var energy = this.calTotalEnergy(accMap, calcNodes);
421
+ monitor({
422
+ energy: energy,
423
+ nodes: graph.getAllNodes(),
424
+ edges: graph.getAllEdges(),
425
+ iterations: iter,
426
+ });
427
+ }
428
+ };
429
+ /**
430
+ * Calculate graph energy for monitoring convergence.
431
+ * @param accMap acceleration map
432
+ * @param nodes calculation nodes
433
+ * @returns energy
434
+ */
435
+ ForceLayout.prototype.calTotalEnergy = function (accMap, nodes) {
436
+ var _this = this;
437
+ if (!(nodes === null || nodes === void 0 ? void 0 : nodes.length))
438
+ return 0;
439
+ var energy = 0.0;
440
+ nodes.forEach(function (node, i) {
441
+ var vx = accMap[node.id].x;
442
+ var vy = accMap[node.id].y;
443
+ var vz = _this.options.dimensions === 3 ? accMap[node.id].z : 0;
444
+ var speed2 = vx * vx + vy * vy + vz * vz;
445
+ var _a = node.data.mass, mass = _a === void 0 ? 1 : _a;
446
+ energy += mass * speed2 * 0.5; // p = 1/2*(mv^2)
447
+ });
448
+ return energy;
449
+ };
450
+ /**
451
+ * Calculate the repulsive forces according to coulombs law.
452
+ * @param calcGraph calculation graph
453
+ * @param accMap acceleration map
454
+ * @param options formatted layout options
455
+ */
456
+ ForceLayout.prototype.calRepulsive = function (calcGraph, accMap, options) {
457
+ var dimensions = options.dimensions, factor = options.factor, coulombDisScale = options.coulombDisScale;
458
+ forceNBody(calcGraph, factor, coulombDisScale * coulombDisScale, accMap, dimensions);
459
+ };
460
+ /**
461
+ * Calculate the attractive forces according to hooks law.
462
+ * @param calcGraph calculation graph
463
+ * @param accMap acceleration map
464
+ */
465
+ ForceLayout.prototype.calAttractive = function (calcGraph, accMap, options) {
466
+ var dimensions = options.dimensions;
467
+ calcGraph.getAllEdges().forEach(function (edge, i) {
468
+ var source = edge.source, target = edge.target;
469
+ var sourceNode = calcGraph.getNode(source);
470
+ var targetNode = calcGraph.getNode(target);
471
+ if (!sourceNode || !targetNode)
472
+ return;
473
+ var vecX = targetNode.data.x - sourceNode.data.x;
474
+ var vecY = targetNode.data.y - sourceNode.data.y;
475
+ var vecZ = dimensions === 3 ? targetNode.data.z - sourceNode.data.z : 0;
476
+ if (!vecX && !vecY) {
477
+ vecX = Math.random() * 0.01;
478
+ vecY = Math.random() * 0.01;
479
+ if (dimensions === 3 && !vecZ) {
480
+ vecZ = Math.random() * 0.01;
481
+ }
482
+ }
483
+ var vecLength = Math.sqrt(vecX * vecX + vecY * vecY + vecZ * vecZ);
484
+ var direX = vecX / vecLength;
485
+ var direY = vecY / vecLength;
486
+ var direZ = vecZ / vecLength;
487
+ var _a = edge.data || {}, _b = _a.linkDistance, linkDistance = _b === void 0 ? 200 : _b, _c = _a.edgeStrength, edgeStrength = _c === void 0 ? 200 : _c;
488
+ var diff = linkDistance - vecLength;
489
+ var param = diff * edgeStrength;
490
+ var massSource = sourceNode.data.mass || 1;
491
+ var massTarget = targetNode.data.mass || 1;
492
+ // 质量占比越大,对另一端影响程度越大
493
+ var sourceMassRatio = 1 / massSource;
494
+ var targetMassRatio = 1 / massTarget;
495
+ var disX = direX * param;
496
+ var disY = direY * param;
497
+ var disZ = direZ * param;
498
+ accMap[source].x -= disX * sourceMassRatio;
499
+ accMap[source].y -= disY * sourceMassRatio;
500
+ accMap[source].z -= disZ * sourceMassRatio;
501
+ accMap[target].x += disX * targetMassRatio;
502
+ accMap[target].y += disY * targetMassRatio;
503
+ accMap[target].z += disZ * targetMassRatio;
504
+ });
505
+ };
506
+ /**
507
+ * Calculate the gravity forces toward center.
508
+ * @param calcGraph calculation graph
509
+ * @param graph origin graph
510
+ * @param accMap acceleration map
511
+ * @param options formatted layout options
512
+ */
513
+ ForceLayout.prototype.calGravity = function (calcGraph, graph, accMap, options) {
514
+ var getCenter = options.getCenter;
515
+ var calcNodes = calcGraph.getAllNodes();
516
+ var nodes = graph.getAllNodes();
517
+ var edges = graph.getAllEdges();
518
+ var width = options.width, height = options.height, center = options.center, defaultGravity = options.gravity, centripetalOptions = options.centripetalOptions;
519
+ if (!calcNodes)
520
+ return;
521
+ calcNodes.forEach(function (calcNode) {
522
+ var id = calcNode.id, data = calcNode.data;
523
+ var mass = data.mass, x = data.x, y = data.y, z = data.z;
524
+ var node = graph.getNode(id);
525
+ var vecX = 0;
526
+ var vecY = 0;
527
+ var vecZ = 0;
528
+ var gravity = defaultGravity;
529
+ var inDegree = calcGraph.getDegree(id, "in");
530
+ var outDegree = calcGraph.getDegree(id, "out");
531
+ var degree = calcGraph.getDegree(id, "both");
532
+ var forceCenter = getCenter === null || getCenter === void 0 ? void 0 : getCenter(node, degree);
533
+ if (forceCenter) {
534
+ var _a = __read(forceCenter, 3), centerX = _a[0], centerY = _a[1], strength = _a[2];
535
+ vecX = x - centerX;
536
+ vecY = y - centerY;
537
+ gravity = strength;
538
+ }
539
+ else {
540
+ vecX = x - center[0];
541
+ vecY = y - center[1];
542
+ vecZ = z - center[2];
543
+ }
544
+ if (gravity) {
545
+ accMap[id].x -= (gravity * vecX) / mass;
546
+ accMap[id].y -= (gravity * vecY) / mass;
547
+ accMap[id].z -= (gravity * vecZ) / mass;
548
+ }
549
+ if (centripetalOptions) {
550
+ var leaf = centripetalOptions.leaf, single = centripetalOptions.single, others = centripetalOptions.others, centriCenter = centripetalOptions.center;
551
+ var _b = (centriCenter === null || centriCenter === void 0 ? void 0 : centriCenter(node, nodes, edges, width, height)) || {
552
+ x: 0,
553
+ y: 0,
554
+ z: 0,
555
+ centerStrength: 0,
556
+ }, centriX = _b.x, centriY = _b.y, centriZ = _b.z, centerStrength = _b.centerStrength;
557
+ if (!isNumber(centriX) || !isNumber(centriY))
558
+ return;
559
+ var vx = (x - centriX) / mass;
560
+ var vy = (y - centriY) / mass;
561
+ var vz = (z - centriZ) / mass;
562
+ if (centerStrength) {
563
+ accMap[id].x -= centerStrength * vx;
564
+ accMap[id].y -= centerStrength * vy;
565
+ accMap[id].z -= centerStrength * vz;
566
+ }
567
+ // 孤点
568
+ if (degree === 0) {
569
+ var singleStrength = single(node);
570
+ if (!singleStrength)
571
+ return;
572
+ accMap[id].x -= singleStrength * vx;
573
+ accMap[id].y -= singleStrength * vy;
574
+ accMap[id].z -= singleStrength * vz;
575
+ return;
576
+ }
577
+ // 没有出度或没有入度,都认为是叶子节点
578
+ if (inDegree === 0 || outDegree === 0) {
579
+ var leafStrength = leaf(node, nodes, edges);
580
+ if (!leafStrength)
581
+ return;
582
+ accMap[id].x -= leafStrength * vx;
583
+ accMap[id].y -= leafStrength * vy;
584
+ accMap[id].z -= leafStrength * vz;
585
+ return;
586
+ }
587
+ /** others */
588
+ var othersStrength = others(node);
589
+ if (!othersStrength)
590
+ return;
591
+ accMap[id].x -= othersStrength * vx;
592
+ accMap[id].y -= othersStrength * vy;
593
+ accMap[id].z -= othersStrength * vz;
594
+ }
595
+ });
596
+ };
597
+ /**
598
+ * Update the velocities for nodes.
599
+ * @param calcGraph calculation graph
600
+ * @param accMap acceleration map
601
+ * @param velMap velocity map
602
+ * @param options formatted layout options
603
+ * @returns
604
+ */
605
+ ForceLayout.prototype.updateVelocity = function (calcGraph, accMap, velMap, options) {
606
+ var damping = options.damping, maxSpeed = options.maxSpeed, interval = options.interval, dimensions = options.dimensions;
607
+ var calcNodes = calcGraph.getAllNodes();
608
+ if (!(calcNodes === null || calcNodes === void 0 ? void 0 : calcNodes.length))
609
+ return;
610
+ calcNodes.forEach(function (calcNode) {
611
+ var id = calcNode.id;
612
+ var vx = (velMap[id].x + accMap[id].x * interval) * damping || 0.01;
613
+ var vy = (velMap[id].y + accMap[id].y * interval) * damping || 0.01;
614
+ var vz = dimensions === 3 ? ((velMap[id].z + accMap[id].z * interval) * damping || 0.01) : 0.0;
615
+ var vLength = Math.sqrt(vx * vx + vy * vy + vz * vz);
616
+ if (vLength > maxSpeed) {
617
+ var param2 = maxSpeed / vLength;
618
+ vx = param2 * vx;
619
+ vy = param2 * vy;
620
+ vz = param2 * vz;
621
+ }
622
+ velMap[id] = {
623
+ x: vx,
624
+ y: vy,
625
+ z: vz
626
+ };
627
+ });
628
+ };
629
+ /**
630
+ * Update nodes' positions.
631
+ * @param graph origin graph
632
+ * @param calcGraph calculatition graph
633
+ * @param velMap velocity map
634
+ * @param options formatted layou options
635
+ * @returns
636
+ */
637
+ ForceLayout.prototype.updatePosition = function (graph, calcGraph, velMap, options) {
638
+ var _this = this;
639
+ var distanceThresholdMode = options.distanceThresholdMode, interval = options.interval, dimensions = options.dimensions;
640
+ var calcNodes = calcGraph.getAllNodes();
641
+ if (!(calcNodes === null || calcNodes === void 0 ? void 0 : calcNodes.length)) {
642
+ this.judgingDistance = 0;
643
+ return;
644
+ }
645
+ var sum = 0;
646
+ if (distanceThresholdMode === "max")
647
+ this.judgingDistance = -Infinity;
648
+ else if (distanceThresholdMode === "min")
649
+ this.judgingDistance = Infinity;
650
+ calcNodes.forEach(function (calcNode) {
651
+ var id = calcNode.id;
652
+ var node = graph.getNode(id);
653
+ if (isNumber(node.data.fx) && isNumber(node.data.fy)) {
654
+ calcGraph.mergeNodeData(id, {
655
+ x: node.data.fx,
656
+ y: node.data.fy,
657
+ z: dimensions === 3 ? node.data.fz : undefined,
658
+ });
659
+ return;
660
+ }
661
+ var distX = velMap[id].x * interval;
662
+ var distY = velMap[id].y * interval;
663
+ var distZ = dimensions === 3 ? velMap[id].z * interval : 0.0;
664
+ calcGraph.mergeNodeData(id, {
665
+ x: calcNode.data.x + distX,
666
+ y: calcNode.data.y + distY,
667
+ z: calcNode.data.z + distZ,
668
+ });
669
+ var distanceMagnitude = Math.sqrt(distX * distX + distY * distY + distZ * distZ);
670
+ switch (distanceThresholdMode) {
671
+ case "max":
672
+ if (_this.judgingDistance < distanceMagnitude) {
673
+ _this.judgingDistance = distanceMagnitude;
674
+ }
675
+ break;
676
+ case "min":
677
+ if (_this.judgingDistance > distanceMagnitude) {
678
+ _this.judgingDistance = distanceMagnitude;
679
+ }
680
+ break;
681
+ default:
682
+ sum = sum + distanceMagnitude;
683
+ break;
684
+ }
685
+ });
686
+ if (!distanceThresholdMode || distanceThresholdMode === "mean") {
687
+ this.judgingDistance = sum / calcNodes.length;
688
+ }
689
+ };
690
+ return ForceLayout;
691
+ }());
692
+ export { ForceLayout };
693
+ /**
694
+ * Group the leaf nodes according to nodeClusterBy field.
695
+ * @param calcGraph calculation graph
696
+ * @param nodeClusterBy the field name in node.data to ditinguish different node clusters
697
+ * @returns related same group leaf nodes for each leaf node
698
+ */
699
+ var getSameTypeLeafMap = function (calcGraph, nodeClusterBy) {
700
+ var calcNodes = calcGraph.getAllNodes();
701
+ if (!(calcNodes === null || calcNodes === void 0 ? void 0 : calcNodes.length))
702
+ return {};
703
+ var sameTypeLeafMap = {};
704
+ calcNodes.forEach(function (node, i) {
705
+ var degree = calcGraph.getDegree(node.id, "both");
706
+ if (degree === 1) {
707
+ sameTypeLeafMap[node.id] = getCoreNodeAndSiblingLeaves(calcGraph, "leaf", node, nodeClusterBy);
708
+ }
709
+ });
710
+ return sameTypeLeafMap;
711
+ };
712
+ /**
713
+ * Find the successor or predecessor of node as coreNode, the sibling leaf nodes
714
+ * @param calcGraph calculation graph
715
+ * @param type ('all') filter out the not-same-cluster nodes, ('leaf') or filter out the not-leaf nodes in the same time
716
+ * @param node the target node
717
+ * @param nodeClusterBy the field name in node.data to ditinguish different node clusters
718
+ * @returns coreNode, sibling leaf nodes, and grouped sibling leaf nodes
719
+ */
720
+ var getCoreNodeAndSiblingLeaves = function (calcGraph, type, node, nodeClusterBy) {
721
+ var inDegree = calcGraph.getDegree(node.id, "in");
722
+ var outDegree = calcGraph.getDegree(node.id, "out");
723
+ // node is not a leaf, coreNode is itself, siblingLeaves is empty
724
+ var coreNode = node;
725
+ var siblingLeaves = [];
726
+ if (inDegree === 0) {
727
+ // node is a leaf node without out edges, its related(successor) node is coreNode, siblingLeaves is the neighbors of its related node
728
+ coreNode = calcGraph.getSuccessors(node.id)[0];
729
+ siblingLeaves = calcGraph.getNeighbors(coreNode.id);
730
+ }
731
+ else if (outDegree === 0) {
732
+ // node is a leaf node without in edges, its related(predecessor) node is coreNode, siblingLeaves is the neighbors of its related node
733
+ coreNode = calcGraph.getPredecessors(node.id)[0];
734
+ siblingLeaves = calcGraph.getNeighbors(coreNode.id);
735
+ }
736
+ // siblingLeaves are leaf nodes
737
+ siblingLeaves = siblingLeaves.filter(function (node) {
738
+ return calcGraph.getDegree(node.id, "in") === 0 ||
739
+ calcGraph.getDegree(node.id, "out") === 0;
740
+ });
741
+ var sameTypeLeaves = getSameTypeNodes(calcGraph, type, nodeClusterBy, node, siblingLeaves);
742
+ return { coreNode: coreNode, siblingLeaves: siblingLeaves, sameTypeLeaves: sameTypeLeaves };
743
+ };
744
+ /**
745
+ * Find the same type (according to nodeClusterBy field) of node in relativeNodes.
746
+ * @param calcGraph calculation graph
747
+ * @param type ('all') filter out the not-same-cluster nodes, ('leaf') or filter out the not-leaf nodes in the same time
748
+ * @param nodeClusterBy the field name in node.data to ditinguish different node clusters
749
+ * @param node the target node
750
+ * @param relativeNodes node's related ndoes to be filtered
751
+ * @returns related nodes that meet the filtering conditions
752
+ */
753
+ var getSameTypeNodes = function (calcGraph, type, nodeClusterBy, node, relativeNodes) {
754
+ var typeName = node.data[nodeClusterBy] || "";
755
+ var sameTypeNodes = (relativeNodes === null || relativeNodes === void 0 ? void 0 : relativeNodes.filter(function (item) { return item.data[nodeClusterBy] === typeName; })) ||
756
+ [];
757
+ if (type === "leaf") {
758
+ sameTypeNodes = sameTypeNodes.filter(function (item) {
759
+ return calcGraph.getDegree(item.id, "in") === 0 ||
760
+ calcGraph.getDegree(item.id, "out") === 0;
761
+ });
762
+ }
763
+ return sameTypeNodes;
764
+ };
765
+ /**
766
+ * Get the average position of nodes.
767
+ * @param nodes nodes set
768
+ * @returns average ppsition
769
+ */
770
+ var getAvgNodePosition = function (nodes) {
771
+ var totalNodes = { x: 0, y: 0 };
772
+ nodes.forEach(function (node) {
773
+ var _a = node.data, x = _a.x, y = _a.y;
774
+ totalNodes.x += x || 0;
775
+ totalNodes.y += y || 0;
776
+ });
777
+ // 获取均值向量
778
+ var length = nodes.length || 1;
779
+ return {
780
+ x: totalNodes.x / length,
781
+ y: totalNodes.y / length,
782
+ };
783
+ };
784
+ /**
785
+ * Format the output nodes from CalcNode[].
786
+ * @param graph origin graph
787
+ * @param layoutNodes calculation nodes
788
+ * @returns output nodes
789
+ */
790
+ var formatOutNodes = function (graph, layoutNodes) {
791
+ return layoutNodes.map(function (calcNode) {
792
+ var id = calcNode.id, data = calcNode.data;
793
+ var node = graph.getNode(id);
794
+ return __assign(__assign({}, node), { data: __assign(__assign({}, node.data), { x: data.x, y: data.y, z: data.z }) });
795
+ });
796
+ };
797
+ //# sourceMappingURL=index.js.map