1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | var __extends = (this && this.__extends) || (function () {
|
7 | var extendStatics = function (d, b) {
|
8 | extendStatics = Object.setPrototypeOf ||
|
9 | ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
10 | function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
11 | return extendStatics(d, b);
|
12 | };
|
13 | return function (d, b) {
|
14 | extendStatics(d, b);
|
15 | function __() { this.constructor = d; }
|
16 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
17 | };
|
18 | })();
|
19 | Object.defineProperty(exports, "__esModule", { value: true });
|
20 | exports.FruchtermanLayout = void 0;
|
21 | var base_1 = require("./base");
|
22 | var util_1 = require("../util");
|
23 | var SPEED_DIVISOR = 800;
|
24 |
|
25 |
|
26 |
|
27 | var FruchtermanLayout = (function (_super) {
|
28 | __extends(FruchtermanLayout, _super);
|
29 | function FruchtermanLayout(options) {
|
30 | var _this = _super.call(this) || this;
|
31 |
|
32 | _this.maxIteration = 1000;
|
33 |
|
34 | _this.gravity = 10;
|
35 |
|
36 | _this.speed = 1;
|
37 |
|
38 | _this.clustering = false;
|
39 |
|
40 | _this.clusterGravity = 10;
|
41 | _this.nodes = [];
|
42 | _this.edges = [];
|
43 | _this.width = 300;
|
44 | _this.height = 300;
|
45 | _this.nodeMap = {};
|
46 | _this.nodeIdxMap = {};
|
47 | _this.updateCfg(options);
|
48 | return _this;
|
49 | }
|
50 | FruchtermanLayout.prototype.getDefaultCfg = function () {
|
51 | return {
|
52 | maxIteration: 1000,
|
53 | gravity: 10,
|
54 | speed: 1,
|
55 | clustering: false,
|
56 | clusterGravity: 10,
|
57 | };
|
58 | };
|
59 | |
60 |
|
61 |
|
62 | FruchtermanLayout.prototype.execute = function () {
|
63 | var _this = this;
|
64 | var self = this;
|
65 | var nodes = self.nodes;
|
66 | if (!nodes || nodes.length === 0) {
|
67 | return;
|
68 | }
|
69 | if (!self.width && typeof window !== 'undefined') {
|
70 | self.width = window.innerWidth;
|
71 | }
|
72 | if (!self.height && typeof window !== 'undefined') {
|
73 | self.height = window.innerHeight;
|
74 | }
|
75 | if (!self.center) {
|
76 | self.center = [self.width / 2, self.height / 2];
|
77 | }
|
78 | var center = self.center;
|
79 | if (nodes.length === 1) {
|
80 | nodes[0].x = center[0];
|
81 | nodes[0].y = center[1];
|
82 | return;
|
83 | }
|
84 | var nodeMap = {};
|
85 | var nodeIdxMap = {};
|
86 | nodes.forEach(function (node, i) {
|
87 | if (!util_1.isNumber(node.x))
|
88 | node.x = Math.random() * _this.width;
|
89 | if (!util_1.isNumber(node.y))
|
90 | node.y = Math.random() * _this.height;
|
91 | nodeMap[node.id] = node;
|
92 | nodeIdxMap[node.id] = i;
|
93 | });
|
94 | self.nodeMap = nodeMap;
|
95 | self.nodeIdxMap = nodeIdxMap;
|
96 |
|
97 | return self.run();
|
98 | };
|
99 | FruchtermanLayout.prototype.run = function () {
|
100 | var self = this;
|
101 | var nodes = self.nodes;
|
102 | var edges = self.edges;
|
103 | var maxIteration = self.maxIteration;
|
104 | var center = self.center;
|
105 | var area = self.height * self.width;
|
106 | var maxDisplace = Math.sqrt(area) / 10;
|
107 | var k2 = area / (nodes.length + 1);
|
108 | var k = Math.sqrt(k2);
|
109 | var gravity = self.gravity;
|
110 | var speed = self.speed;
|
111 | var clustering = self.clustering;
|
112 | var clusterMap = {};
|
113 | if (clustering) {
|
114 | nodes.forEach(function (n) {
|
115 | if (clusterMap[n.cluster] === undefined) {
|
116 | var cluster = {
|
117 | name: n.cluster,
|
118 | cx: 0,
|
119 | cy: 0,
|
120 | count: 0,
|
121 | };
|
122 | clusterMap[n.cluster] = cluster;
|
123 | }
|
124 | var c = clusterMap[n.cluster];
|
125 | if (util_1.isNumber(n.x)) {
|
126 | c.cx += n.x;
|
127 | }
|
128 | if (util_1.isNumber(n.y)) {
|
129 | c.cy += n.y;
|
130 | }
|
131 | c.count++;
|
132 | });
|
133 | for (var key in clusterMap) {
|
134 | clusterMap[key].cx /= clusterMap[key].count;
|
135 | clusterMap[key].cy /= clusterMap[key].count;
|
136 | }
|
137 | }
|
138 | var _loop_1 = function (i) {
|
139 | var displacements = [];
|
140 | nodes.forEach(function (_, j) {
|
141 | displacements[j] = { x: 0, y: 0 };
|
142 | });
|
143 | self.applyCalculate(nodes, edges, displacements, k, k2);
|
144 |
|
145 | if (clustering) {
|
146 | var clusterGravity_1 = self.clusterGravity || gravity;
|
147 | nodes.forEach(function (n, j) {
|
148 | if (!util_1.isNumber(n.x) || !util_1.isNumber(n.y))
|
149 | return;
|
150 | var c = clusterMap[n.cluster];
|
151 | var distLength = Math.sqrt((n.x - c.cx) * (n.x - c.cx) + (n.y - c.cy) * (n.y - c.cy));
|
152 | var gravityForce = k * clusterGravity_1;
|
153 | displacements[j].x -= (gravityForce * (n.x - c.cx)) / distLength;
|
154 | displacements[j].y -= (gravityForce * (n.y - c.cy)) / distLength;
|
155 | });
|
156 | for (var key in clusterMap) {
|
157 | clusterMap[key].cx = 0;
|
158 | clusterMap[key].cy = 0;
|
159 | clusterMap[key].count = 0;
|
160 | }
|
161 | nodes.forEach(function (n) {
|
162 | var c = clusterMap[n.cluster];
|
163 | if (util_1.isNumber(n.x)) {
|
164 | c.cx += n.x;
|
165 | }
|
166 | if (util_1.isNumber(n.y)) {
|
167 | c.cy += n.y;
|
168 | }
|
169 | c.count++;
|
170 | });
|
171 | for (var key in clusterMap) {
|
172 | clusterMap[key].cx /= clusterMap[key].count;
|
173 | clusterMap[key].cy /= clusterMap[key].count;
|
174 | }
|
175 | }
|
176 |
|
177 | nodes.forEach(function (n, j) {
|
178 | if (!util_1.isNumber(n.x) || !util_1.isNumber(n.y))
|
179 | return;
|
180 | var gravityForce = 0.01 * k * gravity;
|
181 | displacements[j].x -= gravityForce * (n.x - center[0]);
|
182 | displacements[j].y -= gravityForce * (n.y - center[1]);
|
183 | });
|
184 |
|
185 | nodes.forEach(function (n, j) {
|
186 | if (!util_1.isNumber(n.x) || !util_1.isNumber(n.y))
|
187 | return;
|
188 | var distLength = Math.sqrt(displacements[j].x * displacements[j].x + displacements[j].y * displacements[j].y);
|
189 | if (distLength > 0) {
|
190 |
|
191 | var limitedDist = Math.min(maxDisplace * (speed / SPEED_DIVISOR), distLength);
|
192 | n.x += (displacements[j].x / distLength) * limitedDist;
|
193 | n.y += (displacements[j].y / distLength) * limitedDist;
|
194 | }
|
195 | });
|
196 | };
|
197 | for (var i = 0; i < maxIteration; i++) {
|
198 | _loop_1(i);
|
199 | }
|
200 | return {
|
201 | nodes: nodes,
|
202 | edges: edges,
|
203 | };
|
204 | };
|
205 | FruchtermanLayout.prototype.applyCalculate = function (nodes, edges, displacements, k, k2) {
|
206 | var self = this;
|
207 | self.calRepulsive(nodes, displacements, k2);
|
208 | self.calAttractive(edges, displacements, k);
|
209 | };
|
210 | FruchtermanLayout.prototype.calRepulsive = function (nodes, displacements, k2) {
|
211 | nodes.forEach(function (v, i) {
|
212 | displacements[i] = { x: 0, y: 0 };
|
213 | nodes.forEach(function (u, j) {
|
214 | if (i === j) {
|
215 | return;
|
216 | }
|
217 | if (!util_1.isNumber(v.x) || !util_1.isNumber(u.x) || !util_1.isNumber(v.y) || !util_1.isNumber(u.y))
|
218 | return;
|
219 | var vecX = v.x - u.x;
|
220 | var vecY = v.y - u.y;
|
221 | var vecLengthSqr = vecX * vecX + vecY * vecY;
|
222 | if (vecLengthSqr === 0) {
|
223 | vecLengthSqr = 1;
|
224 | var sign = i > j ? 1 : -1;
|
225 | vecX = 0.01 * sign;
|
226 | vecY = 0.01 * sign;
|
227 | }
|
228 | var common = (k2) / vecLengthSqr;
|
229 | displacements[i].x += vecX * common;
|
230 | displacements[i].y += vecY * common;
|
231 | });
|
232 | });
|
233 | };
|
234 | FruchtermanLayout.prototype.calAttractive = function (edges, displacements, k) {
|
235 | var _this = this;
|
236 | edges.forEach(function (e) {
|
237 | if (!e.source || !e.target)
|
238 | return;
|
239 | var uIndex = _this.nodeIdxMap[e.source];
|
240 | var vIndex = _this.nodeIdxMap[e.target];
|
241 | if (uIndex === vIndex) {
|
242 | return;
|
243 | }
|
244 | var u = _this.nodeMap[e.source];
|
245 | var v = _this.nodeMap[e.target];
|
246 | if (!util_1.isNumber(v.x) || !util_1.isNumber(u.x) || !util_1.isNumber(v.y) || !util_1.isNumber(u.y))
|
247 | return;
|
248 | var vecX = v.x - u.x;
|
249 | var vecY = v.y - u.y;
|
250 | var vecLength = Math.sqrt(vecX * vecX + vecY * vecY);
|
251 | var common = (vecLength * vecLength) / k;
|
252 | displacements[vIndex].x -= (vecX / vecLength) * common;
|
253 | displacements[vIndex].y -= (vecY / vecLength) * common;
|
254 | displacements[uIndex].x += (vecX / vecLength) * common;
|
255 | displacements[uIndex].y += (vecY / vecLength) * common;
|
256 | });
|
257 | };
|
258 | FruchtermanLayout.prototype.getType = function () {
|
259 | return 'fruchterman';
|
260 | };
|
261 | return FruchtermanLayout;
|
262 | }(base_1.Base));
|
263 | exports.FruchtermanLayout = FruchtermanLayout;
|
264 |
|
\ | No newline at end of file |