UNPKG

33.1 kBJavaScriptView Raw
1"use strict";
2/**
3 * @fileOverview Combo force layout
4 * @author shiwu.wyy@antfin.com
5 */
6var __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})();
19Object.defineProperty(exports, "__esModule", { value: true });
20exports.ComboForceLayout = void 0;
21var base_1 = require("./base");
22var util_1 = require("../util");
23/**
24 * force layout for graph with combos
25 */
26var ComboForceLayout = /** @class */ (function (_super) {
27 __extends(ComboForceLayout, _super);
28 function ComboForceLayout(options) {
29 var _this = _super.call(this) || this;
30 /** 布局中心 */
31 _this.center = [0, 0];
32 /** 停止迭代的最大迭代数 */
33 _this.maxIteration = 100;
34 /** 重力大小,影响图的紧凑程度 */
35 _this.gravity = 10;
36 /** 群组中心力大小 */
37 _this.comboGravity = 10;
38 /** 默认边长度 */
39 _this.linkDistance = 10;
40 /** 每次迭代位移的衰减相关参数 */
41 _this.alpha = 1;
42 _this.alphaMin = 0.001;
43 _this.alphaDecay = 1 - Math.pow(_this.alphaMin, (1 / 300));
44 _this.alphaTarget = 0;
45 /** 节点运动速度衰减参数 */
46 _this.velocityDecay = 0.6;
47 /** 边引力大小 */
48 _this.edgeStrength = 0.6;
49 /** 节点引力大小 */
50 _this.nodeStrength = 30;
51 /** 是否开启防止重叠 */
52 _this.preventOverlap = false;
53 /** 是否开启节点之间的防止重叠 */
54 _this.preventNodeOverlap = false;
55 /** 是否开启 Combo 之间的防止重叠 */
56 _this.preventComboOverlap = false;
57 /** 防止重叠的碰撞力大小 */
58 _this.collideStrength = undefined;
59 /** 防止重叠的碰撞力大小 */
60 _this.nodeCollideStrength = 0.5;
61 /** 防止重叠的碰撞力大小 */
62 _this.comboCollideStrength = 0.5;
63 /** Combo 最小间距,防止重叠时的间隙 */
64 _this.comboSpacing = 20;
65 /** Combo 内部的 padding */
66 _this.comboPadding = 10;
67 /** 优化计算斥力的速度,两节点间距超过 optimizeRangeFactor * width 则不再计算斥力和重叠斥力 */
68 _this.optimizeRangeFactor = 1;
69 /** 每次迭代的回调函数 */
70 _this.onTick = function () { };
71 /** 每次迭代的回调函数 */
72 _this.onLayoutEnd = function () { };
73 /** 根据边两端节点层级差距的调整引力系数的因子,取值范围 [0, 1]。层级差距越大,引力越小 */
74 _this.depthAttractiveForceScale = 1;
75 /** 根据边两端节点层级差距的调整斥力系数的因子,取值范围 [1, Infinity]。层级差距越大,斥力越大 */
76 _this.depthRepulsiveForceScale = 2;
77 /** 内部计算参数 */
78 _this.nodes = [];
79 _this.edges = [];
80 _this.combos = [];
81 _this.comboTrees = [];
82 _this.width = 300;
83 _this.height = 300;
84 _this.bias = [];
85 _this.nodeMap = {};
86 _this.oriComboMap = {};
87 _this.indexMap = {};
88 _this.comboMap = {};
89 _this.previousLayouted = false;
90 _this.updateCfg(options);
91 return _this;
92 }
93 ComboForceLayout.prototype.getDefaultCfg = function () {
94 return {
95 maxIteration: 100,
96 center: [0, 0],
97 gravity: 10,
98 speed: 1,
99 comboGravity: 30,
100 preventOverlap: false,
101 preventComboOverlap: true,
102 preventNodeOverlap: true,
103 nodeSpacing: undefined,
104 collideStrength: undefined,
105 nodeCollideStrength: 0.5,
106 comboCollideStrength: 0.5,
107 comboSpacing: 20,
108 comboPadding: 10,
109 edgeStrength: 0.6,
110 nodeStrength: 30,
111 linkDistance: 10,
112 };
113 };
114 /**
115 * 执行布局
116 */
117 ComboForceLayout.prototype.execute = function () {
118 var self = this;
119 var nodes = self.nodes;
120 var center = self.center;
121 self.comboTree = {
122 id: 'comboTreeRoot',
123 depth: -1,
124 children: self.comboTrees,
125 };
126 if (!nodes || nodes.length === 0) {
127 return;
128 }
129 if (nodes.length === 1) {
130 nodes[0].x = center[0];
131 nodes[0].y = center[1];
132 return;
133 }
134 self.initVals();
135 // layout
136 self.run();
137 self.onLayoutEnd();
138 };
139 ComboForceLayout.prototype.run = function () {
140 var self = this;
141 var nodes = self.nodes;
142 var maxIteration = self.previousLayouted ? self.maxIteration / 5 : self.maxIteration;
143 if (!self.width && typeof window !== 'undefined') {
144 self.width = window.innerWidth;
145 }
146 if (!self.height && typeof window !== 'undefined') {
147 self.height = window.innerHeight;
148 }
149 var center = self.center;
150 var velocityDecay = self.velocityDecay;
151 // init the positions to make the nodes with same combo gather around the combo
152 var comboMap = self.comboMap;
153 if (!self.previousLayouted)
154 self.initPos(comboMap);
155 var _loop_1 = function (i) {
156 var displacements = [];
157 nodes.forEach(function (_, j) {
158 displacements[j] = { x: 0, y: 0 };
159 });
160 self.applyCalculate(displacements);
161 // gravity for combos
162 self.applyComboCenterForce(displacements);
163 // move
164 nodes.forEach(function (n, j) {
165 if (!util_1.isNumber(n.x) || !util_1.isNumber(n.y))
166 return;
167 n.x += displacements[j].x * velocityDecay;
168 n.y += displacements[j].y * velocityDecay;
169 });
170 self.alpha += (self.alphaTarget - self.alpha) * self.alphaDecay;
171 self.onTick();
172 };
173 // iterate
174 for (var i = 0; i < maxIteration; i++) {
175 _loop_1(i);
176 }
177 // move to center
178 var meanCenter = [0, 0];
179 nodes.forEach(function (n) {
180 if (!util_1.isNumber(n.x) || !util_1.isNumber(n.y))
181 return;
182 meanCenter[0] += n.x;
183 meanCenter[1] += n.y;
184 });
185 meanCenter[0] /= nodes.length;
186 meanCenter[1] /= nodes.length;
187 var centerOffset = [center[0] - meanCenter[0], center[1] - meanCenter[1]];
188 nodes.forEach(function (n, j) {
189 if (!util_1.isNumber(n.x) || !util_1.isNumber(n.y))
190 return;
191 n.x += centerOffset[0];
192 n.y += centerOffset[1];
193 });
194 // arrange the empty combo
195 self.combos.forEach(function (combo) {
196 var mapped = comboMap[combo.id];
197 if (mapped && mapped.empty) {
198 combo.x = mapped.cx || combo.x;
199 combo.y = mapped.cy || combo.y;
200 }
201 });
202 self.previousLayouted = true;
203 };
204 ComboForceLayout.prototype.initVals = function () {
205 var self = this;
206 var edges = self.edges;
207 var nodes = self.nodes;
208 var combos = self.combos;
209 var count = {};
210 var nodeMap = {};
211 var indexMap = {};
212 nodes.forEach(function (node, i) {
213 nodeMap[node.id] = node;
214 indexMap[node.id] = i;
215 });
216 self.nodeMap = nodeMap;
217 self.indexMap = indexMap;
218 var oriComboMap = {};
219 combos.forEach(function (combo) {
220 oriComboMap[combo.id] = combo;
221 });
222 self.oriComboMap = oriComboMap;
223 self.comboMap = self.getComboMap();
224 var preventOverlap = self.preventOverlap;
225 self.preventComboOverlap = self.preventComboOverlap || preventOverlap;
226 self.preventNodeOverlap = self.preventNodeOverlap || preventOverlap;
227 var collideStrength = self.collideStrength;
228 if (collideStrength) {
229 self.comboCollideStrength = collideStrength;
230 self.nodeCollideStrength = collideStrength;
231 }
232 self.comboCollideStrength = self.comboCollideStrength ? self.comboCollideStrength : 0;
233 self.nodeCollideStrength = self.nodeCollideStrength ? self.nodeCollideStrength : 0;
234 // get edge bias
235 for (var i = 0; i < edges.length; ++i) {
236 if (count[edges[i].source])
237 count[edges[i].source]++;
238 else
239 count[edges[i].source] = 1;
240 if (count[edges[i].target])
241 count[edges[i].target]++;
242 else
243 count[edges[i].target] = 1;
244 }
245 var bias = [];
246 for (var i = 0; i < edges.length; ++i) {
247 bias[i] = count[edges[i].source] / (count[edges[i].source] + count[edges[i].target]);
248 }
249 this.bias = bias;
250 var nodeSize = self.nodeSize;
251 var nodeSpacing = self.nodeSpacing;
252 var nodeSizeFunc;
253 var nodeSpacingFunc;
254 // nodeSpacing to function
255 if (util_1.isNumber(nodeSpacing)) {
256 nodeSpacingFunc = function () { return nodeSpacing; };
257 }
258 else if (util_1.isFunction(nodeSpacing)) {
259 nodeSpacingFunc = nodeSpacing;
260 }
261 else {
262 nodeSpacingFunc = function () { return 0; };
263 }
264 this.nodeSpacing = nodeSpacingFunc;
265 // nodeSize to function
266 if (!nodeSize) {
267 nodeSizeFunc = function (d) {
268 if (d.size) {
269 if (util_1.isArray(d.size)) {
270 var res = d.size[0] > d.size[1] ? d.size[0] : d.size[1];
271 return res / 2;
272 }
273 return d.size / 2;
274 }
275 return 10;
276 };
277 }
278 else if (util_1.isFunction(nodeSize)) {
279 nodeSizeFunc = function (d) {
280 return nodeSize(d);
281 };
282 }
283 else if (util_1.isArray(nodeSize)) {
284 var larger = nodeSize[0] > nodeSize[1] ? nodeSize[0] : nodeSize[1];
285 var radius_1 = larger / 2;
286 nodeSizeFunc = function (d) { return radius_1; };
287 }
288 else {
289 // number type
290 var radius_2 = nodeSize / 2;
291 nodeSizeFunc = function (d) { return radius_2; };
292 }
293 this.nodeSize = nodeSizeFunc;
294 // comboSpacing to function
295 var comboSpacing = self.comboSpacing;
296 var comboSpacingFunc;
297 if (util_1.isNumber(comboSpacing)) {
298 comboSpacingFunc = function () { return comboSpacing; };
299 }
300 else if (util_1.isFunction(comboSpacing)) {
301 comboSpacingFunc = comboSpacing;
302 }
303 else {
304 // null type
305 comboSpacingFunc = function () { return 0; };
306 }
307 this.comboSpacing = comboSpacingFunc;
308 // comboPadding to function
309 var comboPadding = self.comboPadding;
310 var comboPaddingFunc;
311 if (util_1.isNumber(comboPadding)) {
312 comboPaddingFunc = function () { return comboPadding; };
313 }
314 else if (util_1.isArray(comboPadding)) {
315 comboPaddingFunc = function () { return Math.max.apply(null, comboPadding); };
316 }
317 else if (util_1.isFunction(comboPadding)) {
318 comboPaddingFunc = comboPadding;
319 }
320 else {
321 // null type
322 comboPaddingFunc = function () { return 0; };
323 }
324 this.comboPadding = comboPaddingFunc;
325 // linkDistance to function
326 var linkDistance = this.linkDistance;
327 var linkDistanceFunc;
328 if (!linkDistance) {
329 linkDistance = 10;
330 }
331 if (util_1.isNumber(linkDistance)) {
332 linkDistanceFunc = function (d) {
333 return linkDistance;
334 };
335 }
336 else {
337 linkDistanceFunc = linkDistance;
338 }
339 this.linkDistance = linkDistanceFunc;
340 // linkStrength to function
341 var edgeStrength = this.edgeStrength;
342 var edgeStrengthFunc;
343 if (!edgeStrength) {
344 edgeStrength = 1;
345 }
346 if (util_1.isNumber(edgeStrength)) {
347 edgeStrengthFunc = function (d) {
348 return edgeStrength;
349 };
350 }
351 else {
352 edgeStrengthFunc = edgeStrength;
353 }
354 this.edgeStrength = edgeStrengthFunc;
355 // nodeStrength to function
356 var nodeStrength = this.nodeStrength;
357 var nodeStrengthFunc;
358 if (!nodeStrength) {
359 nodeStrength = 30;
360 }
361 if (util_1.isNumber(nodeStrength)) {
362 nodeStrengthFunc = function (d) {
363 return nodeStrength;
364 };
365 }
366 else {
367 nodeStrengthFunc = nodeStrength;
368 }
369 this.nodeStrength = nodeStrengthFunc;
370 };
371 ComboForceLayout.prototype.initPos = function (comboMap) {
372 var self = this;
373 var nodes = self.nodes;
374 nodes.forEach(function (node, i) {
375 var comboId = node.comboId;
376 var combo = comboMap[comboId];
377 if (comboId && combo) {
378 node.x = combo.cx + 100 / (i + 1);
379 node.y = combo.cy + 100 / (i + 1);
380 }
381 else {
382 node.x = 100 / (i + 1);
383 node.y = 100 / (i + 1);
384 }
385 });
386 };
387 ComboForceLayout.prototype.getComboMap = function () {
388 var self = this;
389 var nodeMap = self.nodeMap;
390 var indexMap = self.indexMap;
391 var comboTrees = self.comboTrees;
392 var oriComboMap = self.oriComboMap;
393 var comboMap = {};
394 (comboTrees || []).forEach(function (ctree) {
395 var treeChildren = [];
396 util_1.traverseTreeUp(ctree, function (treeNode) {
397 if (treeNode.itemType === 'node')
398 return true; // skip it
399 if (!oriComboMap[treeNode.id])
400 return true; // means it is hidden, skip it
401 if (comboMap[treeNode.id] === undefined) {
402 var combo = {
403 id: treeNode.id,
404 name: treeNode.id,
405 cx: 0,
406 cy: 0,
407 count: 0,
408 depth: self.oriComboMap[treeNode.id].depth,
409 children: [],
410 };
411 comboMap[treeNode.id] = combo;
412 }
413 var children = treeNode.children;
414 if (children) {
415 children.forEach(function (child) {
416 if (!comboMap[child.id] && !nodeMap[child.id])
417 return true; // means it is hidden
418 treeChildren.push(child);
419 });
420 }
421 var c = comboMap[treeNode.id];
422 c.cx = 0;
423 c.cy = 0;
424 // In order to layout the empty combo, add a virtual node to it
425 if (treeChildren.length === 0) {
426 c.empty = true;
427 var oriCombo = oriComboMap[treeNode.id];
428 var idx = Object.keys(nodeMap).length;
429 var virtualNodeId = treeNode.id + "-visual-child-" + idx;
430 var vnode = {
431 id: virtualNodeId,
432 x: oriCombo.x,
433 y: oriCombo.y,
434 depth: c.depth + 1,
435 itemType: 'node',
436 };
437 self.nodes.push(vnode);
438 nodeMap[virtualNodeId] = vnode;
439 indexMap[virtualNodeId] = idx;
440 c.cx = oriCombo.x;
441 c.cy = oriCombo.y;
442 treeChildren.push(vnode);
443 }
444 treeChildren.forEach(function (child) {
445 c.count++;
446 if (child.itemType !== 'node') {
447 var childCombo = comboMap[child.id];
448 if (util_1.isNumber(childCombo.cx))
449 c.cx += childCombo.cx;
450 if (util_1.isNumber(childCombo.cy))
451 c.cy += childCombo.cy;
452 return;
453 }
454 var node = nodeMap[child.id];
455 // means the node is hidden, skip it
456 if (!node)
457 return;
458 if (util_1.isNumber(node.x)) {
459 c.cx += node.x;
460 }
461 if (util_1.isNumber(node.y)) {
462 c.cy += node.y;
463 }
464 });
465 c.cx /= c.count;
466 c.cy /= c.count;
467 c.children = treeChildren;
468 return true;
469 });
470 });
471 return comboMap;
472 };
473 ComboForceLayout.prototype.applyComboCenterForce = function (displacements) {
474 var self = this;
475 var gravity = self.gravity;
476 var comboGravity = self.comboGravity || gravity;
477 var alpha = this.alpha;
478 var comboTrees = self.comboTrees;
479 var indexMap = self.indexMap;
480 var nodeMap = self.nodeMap;
481 var comboMap = self.comboMap;
482 (comboTrees || []).forEach(function (ctree) {
483 util_1.traverseTreeUp(ctree, function (treeNode) {
484 if (treeNode.itemType === 'node')
485 return true; // skip it
486 var combo = comboMap[treeNode.id];
487 // means the combo is hidden, skip it
488 if (!combo)
489 return true;
490 var c = comboMap[treeNode.id];
491 // higher depth the combo, larger the gravity
492 var gravityScale = (c.depth + 1) / 10 * 0.5;
493 // apply combo center force for all the descend nodes in this combo
494 // and update the center position and count for this combo
495 var comboX = c.cx;
496 var comboY = c.cy;
497 c.cx = 0;
498 c.cy = 0;
499 c.children.forEach(function (child) {
500 if (child.itemType !== 'node') {
501 var childCombo = comboMap[child.id];
502 if (childCombo && util_1.isNumber(childCombo.cx))
503 c.cx += childCombo.cx;
504 if (childCombo && util_1.isNumber(childCombo.cy))
505 c.cy += childCombo.cy;
506 return;
507 }
508 var node = nodeMap[child.id];
509 var vecX = node.x - comboX || 0.005;
510 var vecY = node.y - comboY || 0.005;
511 var l = Math.sqrt(vecX * vecX + vecY * vecY);
512 var childIdx = indexMap[node.id];
513 var params = ((comboGravity * alpha) / l) * gravityScale;
514 displacements[childIdx].x -= vecX * params;
515 displacements[childIdx].y -= vecY * params;
516 if (util_1.isNumber(node.x))
517 c.cx += node.x;
518 if (util_1.isNumber(node.y))
519 c.cy += node.y;
520 });
521 c.cx /= c.count;
522 c.cy /= c.count;
523 return true;
524 });
525 });
526 };
527 ComboForceLayout.prototype.applyCalculate = function (displacements) {
528 var self = this;
529 var comboMap = self.comboMap;
530 var nodes = self.nodes;
531 // store the vx, vy, and distance to reduce dulplicate calculation
532 var vecMap = {};
533 nodes.forEach(function (v, i) {
534 nodes.forEach(function (u, j) {
535 if (i < j)
536 return;
537 var vx = v.x - u.x || 0.005;
538 var vy = v.y - u.y || 0.005;
539 var vl2 = vx * vx + vy * vy;
540 var vl = Math.sqrt(vl2);
541 if (vl2 < 1)
542 vl2 = vl;
543 vecMap[v.id + "-" + u.id] = { vx: vx, vy: vy, vl2: vl2, vl: vl };
544 vecMap[u.id + "-" + v.id] = { vl2: vl2, vl: vl, vx: -vx, vy: -vy };
545 });
546 });
547 // get the sizes of the combos
548 self.updateComboSizes(comboMap);
549 self.calRepulsive(displacements, vecMap);
550 self.calAttractive(displacements, vecMap);
551 var preventComboOverlap = self.preventComboOverlap;
552 if (preventComboOverlap)
553 self.comboNonOverlapping(displacements, comboMap);
554 };
555 /**
556 * Update the sizes of the combos according to their children
557 * Used for combos nonoverlap, but not re-render the combo shapes
558 */
559 ComboForceLayout.prototype.updateComboSizes = function (comboMap) {
560 var self = this;
561 var comboTrees = self.comboTrees;
562 var nodeMap = self.nodeMap;
563 var nodeSize = self.nodeSize;
564 var comboSpacing = self.comboSpacing;
565 var comboPadding = self.comboPadding;
566 (comboTrees || []).forEach(function (ctree) {
567 var treeChildren = [];
568 util_1.traverseTreeUp(ctree, function (treeNode) {
569 if (treeNode.itemType === 'node')
570 return true; // skip it
571 var c = comboMap[treeNode.id];
572 // means the combo is hidden, skip it
573 if (!c)
574 return false;
575 var children = treeNode.children;
576 if (children) {
577 children.forEach(function (child) {
578 // means the combo is hidden.
579 if (!comboMap[child.id] && !nodeMap[child.id])
580 return;
581 treeChildren.push(child);
582 });
583 }
584 c.minX = Infinity;
585 c.minY = Infinity;
586 c.maxX = -Infinity;
587 c.maxY = -Infinity;
588 treeChildren.forEach(function (child) {
589 if (child.itemType !== 'node')
590 return true; // skip it
591 var node = nodeMap[child.id];
592 if (!node)
593 return true; // means it is hidden
594 var r = nodeSize(node);
595 var nodeMinX = node.x - r;
596 var nodeMinY = node.y - r;
597 var nodeMaxX = node.x + r;
598 var nodeMaxY = node.y + r;
599 if (c.minX > nodeMinX)
600 c.minX = nodeMinX;
601 if (c.minY > nodeMinY)
602 c.minY = nodeMinY;
603 if (c.maxX < nodeMaxX)
604 c.maxX = nodeMaxX;
605 if (c.maxY < nodeMaxY)
606 c.maxY = nodeMaxY;
607 });
608 var minSize = self.oriComboMap[treeNode.id].size || 10;
609 if (util_1.isArray(minSize))
610 minSize = minSize[0];
611 var maxLength = Math.max(c.maxX - c.minX, c.maxY - c.minY, minSize);
612 c.r = maxLength / 2 + comboSpacing(c) / 2 + comboPadding(c);
613 return true;
614 });
615 });
616 };
617 /**
618 * prevent the overlappings among combos
619 */
620 ComboForceLayout.prototype.comboNonOverlapping = function (displacements, comboMap) {
621 var self = this;
622 var comboTree = self.comboTree;
623 var comboCollideStrength = self.comboCollideStrength;
624 var indexMap = self.indexMap;
625 var nodeMap = self.nodeMap;
626 util_1.traverseTreeUp(comboTree, function (treeNode) {
627 if (!comboMap[treeNode.id] && !nodeMap[treeNode.id] && treeNode.id !== 'comboTreeRoot') {
628 return false;
629 } // means it is hidden
630 var children = treeNode.children;
631 // 同个子树下的子 combo 间两两对比
632 if (children && children.length > 1) {
633 children.forEach(function (v, i) {
634 if (v.itemType === 'node')
635 return false; // skip it
636 var cv = comboMap[v.id];
637 if (!cv)
638 return; // means it is hidden, skip it
639 children.forEach(function (u, j) {
640 if (i <= j)
641 return false;
642 if (u.itemType === 'node')
643 return false; // skip it
644 var cu = comboMap[u.id];
645 if (!cu)
646 return false; // means it is hidden, skip it
647 var vx = cv.cx - cu.cx || 0.005;
648 var vy = cv.cy - cu.cy || 0.005;
649 var l = vx * vx + vy * vy;
650 var rv = cv.r;
651 var ru = cu.r;
652 var r = rv + ru;
653 var ru2 = ru * ru;
654 var rv2 = rv * rv;
655 // overlapping
656 if (l < r * r) {
657 var vnodes = v.children;
658 if (!vnodes || vnodes.length === 0)
659 return false; // skip it
660 var unodes_1 = u.children;
661 if (!unodes_1 || unodes_1.length === 0)
662 return false; // skip it
663 var sqrtl = Math.sqrt(l);
664 var ll = ((r - sqrtl) / sqrtl) * comboCollideStrength;
665 var xl_1 = vx * ll;
666 var yl_1 = vy * ll;
667 var rratio_1 = ru2 / (rv2 + ru2);
668 var irratio_1 = 1 - rratio_1;
669 // 两兄弟 combo 的子节点上施加斥力
670 vnodes.forEach(function (vn) {
671 if (vn.itemType !== 'node')
672 return false; // skip it
673 if (!nodeMap[vn.id])
674 return; // means it is hidden, skip it
675 var vindex = indexMap[vn.id];
676 unodes_1.forEach(function (un) {
677 if (un.itemType !== 'node')
678 return false;
679 if (!nodeMap[un.id])
680 return false; // means it is hidden, skip it
681 var uindex = indexMap[un.id];
682 displacements[vindex].x += xl_1 * rratio_1;
683 displacements[vindex].y += yl_1 * rratio_1;
684 displacements[uindex].x -= xl_1 * irratio_1;
685 displacements[uindex].y -= yl_1 * irratio_1;
686 });
687 });
688 }
689 });
690 });
691 }
692 return true;
693 });
694 };
695 /**
696 * Calculate the repulsive force between each node pair
697 * @param displacements The array stores the displacements for nodes
698 * @param vecMap The map stores vector between each node pair
699 */
700 ComboForceLayout.prototype.calRepulsive = function (displacements, vecMap) {
701 var self = this;
702 var nodes = self.nodes;
703 var max = self.width * self.optimizeRangeFactor;
704 var nodeStrength = self.nodeStrength;
705 var alpha = self.alpha;
706 var nodeCollideStrength = self.nodeCollideStrength;
707 var preventNodeOverlap = self.preventNodeOverlap;
708 var nodeSizeFunc = self.nodeSize;
709 var nodeSpacingFunc = self.nodeSpacing;
710 var scale = self.depthRepulsiveForceScale;
711 var center = self.center;
712 nodes.forEach(function (v, i) {
713 if (!v.x || !v.y)
714 return;
715 // center gravity
716 if (center) {
717 var gravity = self.gravity;
718 var vecX = v.x - center[0] || 0.005;
719 var vecY = v.y - center[1] || 0.005;
720 var l = Math.sqrt(vecX * vecX + vecY * vecY);
721 displacements[i].x -= (vecX * gravity * alpha) / l;
722 displacements[i].y -= (vecY * gravity * alpha) / l;
723 }
724 nodes.forEach(function (u, j) {
725 if (i === j) {
726 return;
727 }
728 if (!u.x || !u.y)
729 return;
730 var _a = vecMap[v.id + "-" + u.id], vl2 = _a.vl2, vl = _a.vl;
731 if (vl > max)
732 return;
733 var _b = vecMap[v.id + "-" + u.id], vx = _b.vx, vy = _b.vy;
734 var depthDiff = (Math.log(Math.abs(u.depth - v.depth) / 10) + 1 || 1);
735 depthDiff = depthDiff < 1 ? 1 : depthDiff;
736 if (u.comboId !== v.comboId)
737 depthDiff += 1;
738 var depthParam = depthDiff ? Math.pow(scale, depthDiff) : 1;
739 var params = ((nodeStrength(u) * alpha) / vl2) * depthParam;
740 displacements[i].x += vx * params;
741 displacements[i].y += vy * params;
742 // prevent node overlappings
743 if (i < j && preventNodeOverlap) {
744 var ri = nodeSizeFunc(v) + nodeSpacingFunc(v);
745 var rj = nodeSizeFunc(u) + nodeSpacingFunc(u);
746 var r = ri + rj;
747 if (vl2 < r * r) {
748 var ll = ((r - vl) / vl) * nodeCollideStrength;
749 var rj2 = rj * rj;
750 var rratio = rj2 / (ri * ri + rj2);
751 var xl = vx * ll;
752 var yl = vy * ll;
753 displacements[i].x += xl * rratio;
754 displacements[i].y += yl * rratio;
755 rratio = 1 - rratio;
756 displacements[j].x -= xl * rratio;
757 displacements[j].y -= yl * rratio;
758 }
759 }
760 });
761 });
762 };
763 /**
764 * Calculate the attractive force between the node pair with edge
765 * @param displacements The array stores the displacements for nodes
766 * @param vecMap The map stores vector between each node pair
767 */
768 ComboForceLayout.prototype.calAttractive = function (displacements, vecMap) {
769 var self = this;
770 var edges = self.edges;
771 var linkDistance = self.linkDistance;
772 var alpha = self.alpha;
773 var edgeStrength = self.edgeStrength;
774 var bias = self.bias;
775 var scale = self.depthAttractiveForceScale;
776 edges.forEach(function (e, i) {
777 if (!e.source || !e.target || e.source === e.target)
778 return;
779 var uIndex = self.indexMap[e.source];
780 var vIndex = self.indexMap[e.target];
781 var u = self.nodeMap[e.source];
782 var v = self.nodeMap[e.target];
783 if (!u || !v)
784 return;
785 var depthDiff = Math.log(Math.abs(u.depth - v.depth) / 10);
786 if (u.comboId === v.comboId) {
787 depthDiff = depthDiff / 2;
788 }
789 var depthParam = depthDiff ? Math.pow(scale, depthDiff) : 1;
790 if (u.comboId !== v.comboId && depthParam === 1) {
791 depthParam = scale / 2;
792 }
793 else if (u.comboId === v.comboId) {
794 depthParam = 2;
795 }
796 if (!util_1.isNumber(v.x) || !util_1.isNumber(u.x) || !util_1.isNumber(v.y) || !util_1.isNumber(u.y))
797 return;
798 var _a = vecMap[e.target + "-" + e.source], vl = _a.vl, vx = _a.vx, vy = _a.vy;
799 var l = ((vl - linkDistance(e)) / vl) * alpha * edgeStrength(e) * depthParam;
800 var vecX = vx * l;
801 var vecY = vy * l;
802 var b = bias[i];
803 displacements[vIndex].x -= vecX * b;
804 displacements[vIndex].y -= vecY * b;
805 displacements[uIndex].x += vecX * (1 - b);
806 displacements[uIndex].y += vecY * (1 - b);
807 });
808 };
809 ComboForceLayout.prototype.getType = function () {
810 return 'comboForce';
811 };
812 return ComboForceLayout;
813}(base_1.Base));
814exports.ComboForceLayout = ComboForceLayout;
815//# sourceMappingURL=comboForce.js.map
\No newline at end of file