@antv/layout 1.2.10 → 1.2.11

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