@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.
- package/dist/0229766b7896aada2515.worker.js +2 -0
- package/dist/0229766b7896aada2515.worker.js.map +1 -0
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/lib/bundle-entry.d.ts +18 -0
- package/lib/bundle-entry.js +19 -0
- package/lib/bundle-entry.js.map +1 -0
- package/lib/bundle-supervisor.d.ts +51 -0
- package/lib/bundle-supervisor.js +93 -0
- package/lib/bundle-supervisor.js.map +1 -0
- package/lib/bundle-worker.d.ts +3 -0
- package/lib/bundle-worker.js +53 -0
- package/lib/bundle-worker.js.map +1 -0
- package/lib/circular.js +258 -0
- package/lib/circular.js.map +1 -0
- package/lib/concentric.js +221 -0
- package/lib/concentric.js.map +1 -0
- package/lib/d3Force/forceInBox.js +331 -0
- package/lib/d3Force/forceInBox.js.map +1 -0
- package/lib/d3Force/index.js +339 -0
- package/lib/d3Force/index.js.map +1 -0
- package/lib/dagre/acyclic.js +62 -0
- package/lib/dagre/acyclic.js.map +1 -0
- package/lib/dagre/add-border-segments.js +37 -0
- package/lib/dagre/add-border-segments.js.map +1 -0
- package/lib/dagre/coordinate-system.js +65 -0
- package/lib/dagre/coordinate-system.js.map +1 -0
- package/lib/dagre/data/list.js +50 -0
- package/lib/dagre/data/list.js.map +1 -0
- package/lib/dagre/greedy-fas.js +147 -0
- package/lib/dagre/greedy-fas.js.map +1 -0
- package/lib/dagre/layout.js +468 -0
- package/lib/dagre/layout.js.map +1 -0
- package/lib/dagre/nesting-graph.js +153 -0
- package/lib/dagre/nesting-graph.js.map +1 -0
- package/lib/dagre/normalize.js +98 -0
- package/lib/dagre/normalize.js.map +1 -0
- package/lib/dagre/order/add-subgraph-constraints.js +41 -0
- package/lib/dagre/order/add-subgraph-constraints.js.map +1 -0
- package/lib/dagre/order/barycenter.js +23 -0
- package/lib/dagre/order/barycenter.js.map +1 -0
- package/lib/dagre/order/build-layer-graph.js +96 -0
- package/lib/dagre/order/build-layer-graph.js.map +1 -0
- package/lib/dagre/order/cross-count.js +63 -0
- package/lib/dagre/order/cross-count.js.map +1 -0
- package/lib/dagre/order/index.js +91 -0
- package/lib/dagre/order/index.js.map +1 -0
- package/lib/dagre/order/init-data-order.js +28 -0
- package/lib/dagre/order/init-data-order.js.map +1 -0
- package/lib/dagre/order/init-order.js +50 -0
- package/lib/dagre/order/init-order.js.map +1 -0
- package/lib/dagre/order/resolve-conflicts.js +121 -0
- package/lib/dagre/order/resolve-conflicts.js.map +1 -0
- package/lib/dagre/order/sort-subgraph.js +84 -0
- package/lib/dagre/order/sort-subgraph.js.map +1 -0
- package/lib/dagre/order/sort.js +74 -0
- package/lib/dagre/order/sort.js.map +1 -0
- package/lib/dagre/parent-dummy-chains.js +80 -0
- package/lib/dagre/parent-dummy-chains.js.map +1 -0
- package/lib/dagre/position/bk.js +398 -0
- package/lib/dagre/position/bk.js.map +1 -0
- package/lib/dagre/position/index.js +54 -0
- package/lib/dagre/position/index.js.map +1 -0
- package/lib/dagre/rank/feasible-tree.js +164 -0
- package/lib/dagre/rank/feasible-tree.js.map +1 -0
- package/lib/dagre/rank/index.js +48 -0
- package/lib/dagre/rank/index.js.map +1 -0
- package/lib/dagre/rank/network-simplex.js +232 -0
- package/lib/dagre/rank/network-simplex.js.map +1 -0
- package/lib/dagre/rank/util.js +138 -0
- package/lib/dagre/rank/util.js.map +1 -0
- package/lib/dagre/util.js +289 -0
- package/lib/dagre/util.js.map +1 -0
- package/lib/dagre.d.ts +1 -2
- package/lib/dagre.js +543 -0
- package/lib/dagre.js.map +1 -0
- package/lib/force/forceNBody.js +103 -0
- package/lib/force/forceNBody.js.map +1 -0
- package/lib/force/index.js +797 -0
- package/lib/force/index.js.map +1 -0
- package/lib/force/types.js +2 -0
- package/lib/force/types.js.map +1 -0
- package/lib/forceAtlas2/body.js +91 -0
- package/lib/forceAtlas2/body.js.map +1 -0
- package/lib/forceAtlas2/index.js +562 -0
- package/lib/forceAtlas2/index.js.map +1 -0
- package/lib/forceAtlas2/quad.js +98 -0
- package/lib/forceAtlas2/quad.js.map +1 -0
- package/lib/forceAtlas2/quadTree.js +105 -0
- package/lib/forceAtlas2/quadTree.js.map +1 -0
- package/lib/fruchterman.js +376 -0
- package/lib/fruchterman.js.map +1 -0
- package/lib/grid.js +308 -0
- package/lib/grid.js.map +1 -0
- package/lib/index.js +16 -0
- package/lib/index.js.map +1 -0
- package/lib/mds.js +137 -0
- package/lib/mds.js.map +1 -0
- package/lib/radial/index.js +355 -0
- package/lib/radial/index.js.map +1 -0
- package/lib/radial/mds.js +29 -0
- package/lib/radial/mds.js.map +1 -0
- package/lib/radial/radial-nonoverlap-force.js +100 -0
- package/lib/radial/radial-nonoverlap-force.js.map +1 -0
- package/lib/random.js +98 -0
- package/lib/random.js.map +1 -0
- package/lib/registry.d.ts +0 -1
- package/lib/registry.js +25 -0
- package/lib/registry.js.map +1 -0
- package/lib/supervisor.js +86 -0
- package/lib/supervisor.js.map +1 -0
- package/lib/types.js +4 -0
- package/lib/types.js.map +1 -0
- package/lib/util/array.js +2 -0
- package/lib/util/array.js.map +1 -0
- package/lib/util/common.d.ts +12 -0
- package/lib/util/common.js +32 -0
- package/lib/util/common.js.map +1 -0
- package/lib/util/function.js +124 -0
- package/lib/util/function.js.map +1 -0
- package/lib/util/gpu.js +214 -0
- package/lib/util/gpu.js.map +1 -0
- package/lib/util/index.js +7 -0
- package/lib/util/index.js.map +1 -0
- package/lib/util/math.js +228 -0
- package/lib/util/math.js.map +1 -0
- package/lib/util/number.js +5 -0
- package/lib/util/number.js.map +1 -0
- package/lib/util/object.js +42 -0
- package/lib/util/object.js.map +1 -0
- package/lib/util/string.js +15 -0
- package/lib/util/string.js.map +1 -0
- package/lib/worker.d.ts +1 -3
- package/lib/worker.js +45 -0
- package/lib/worker.js.map +1 -0
- package/package.json +6 -5
- package/dist/5e37927bd3e57252658c.worker.js +0 -2
- package/dist/5e37927bd3e57252658c.worker.js.map +0 -1
- package/esm/5e37927bd3e57252658c.worker.js +0 -2
- package/esm/5e37927bd3e57252658c.worker.js.map +0 -1
- package/esm/index.esm.js +0 -2
- 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
|