UNPKG

8.16 kBJavaScriptView Raw
1"use strict";
2/**
3 * @fileOverview concentric layout
4 * @author shiwu.wyy@antfin.com
5 * this algorithm refers to <cytoscape.js> - https://github.com/cytoscape/cytoscape.js/
6 */
7var __extends = (this && this.__extends) || (function () {
8 var extendStatics = function (d, b) {
9 extendStatics = Object.setPrototypeOf ||
10 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
11 function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
12 return extendStatics(d, b);
13 };
14 return function (d, b) {
15 extendStatics(d, b);
16 function __() { this.constructor = d; }
17 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
18 };
19})();
20Object.defineProperty(exports, "__esModule", { value: true });
21exports.ConcentricLayout = void 0;
22var util_1 = require("../util");
23var base_1 = require("./base");
24/**
25 * 同心圆布局
26 */
27var ConcentricLayout = /** @class */ (function (_super) {
28 __extends(ConcentricLayout, _super);
29 function ConcentricLayout(options) {
30 var _this = _super.call(this) || this;
31 _this.nodeSize = 30;
32 /** min spacing between outside of nodes (used for radius adjustment) */
33 _this.minNodeSpacing = 10;
34 /** prevents node overlap, may overflow boundingBox if not enough space */
35 _this.preventOverlap = false;
36 /** whether levels have an equal radial distance betwen them, may cause bounding box overflow */
37 _this.equidistant = false;
38 /** where nodes start in radians */
39 _this.startAngle = (3 / 2) * Math.PI;
40 /** whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false) */
41 _this.clockwise = true;
42 /** 根据 sortBy 指定的属性进行排布,数值高的放在中心,如果是 sortBy 则会计算节点度数,度数最高的放在中心 */
43 _this.sortBy = 'degree';
44 _this.nodes = [];
45 _this.edges = [];
46 _this.width = 300;
47 _this.height = 300;
48 _this.updateCfg(options);
49 return _this;
50 }
51 ConcentricLayout.prototype.getDefaultCfg = function () {
52 return {
53 nodeSize: 30,
54 minNodeSpacing: 10,
55 preventOverlap: false,
56 sweep: undefined,
57 equidistant: false,
58 startAngle: (3 / 2) * Math.PI,
59 clockwise: true,
60 maxLevelDiff: undefined,
61 sortBy: 'degree',
62 };
63 };
64 /**
65 * 执行布局
66 */
67 ConcentricLayout.prototype.execute = function () {
68 var self = this;
69 var nodes = self.nodes;
70 var edges = self.edges;
71 var n = nodes.length;
72 if (n === 0) {
73 return;
74 }
75 if (!self.width && typeof window !== 'undefined') {
76 self.width = window.innerWidth;
77 }
78 if (!self.height && typeof window !== 'undefined') {
79 self.height = window.innerHeight;
80 }
81 if (!self.center) {
82 self.center = [self.width / 2, self.height / 2];
83 }
84 var center = self.center;
85 if (n === 1) {
86 nodes[0].x = center[0];
87 nodes[0].y = center[1];
88 return;
89 }
90 var layoutNodes = [];
91 var maxNodeSize;
92 if (util_1.isArray(self.nodeSize)) {
93 maxNodeSize = Math.max(self.nodeSize[0], self.nodeSize[1]);
94 }
95 else {
96 maxNodeSize = self.nodeSize;
97 }
98 nodes.forEach(function (node) {
99 layoutNodes.push(node);
100 var nodeSize = maxNodeSize;
101 if (util_1.isArray(node.size)) {
102 nodeSize = Math.max(node.size[0], node.size[1]);
103 }
104 else if (util_1.isNumber(node.size)) {
105 nodeSize = node.size;
106 }
107 maxNodeSize = Math.max(maxNodeSize, nodeSize);
108 });
109 self.clockwise = self.counterclockwise !== undefined ? !self.counterclockwise : self.clockwise;
110 // layout
111 var nodeMap = {};
112 var indexMap = {};
113 layoutNodes.forEach(function (node, i) {
114 nodeMap[node.id] = node;
115 indexMap[node.id] = i;
116 });
117 // get the node degrees
118 if (self.sortBy === 'degree' ||
119 !util_1.isString(self.sortBy) ||
120 layoutNodes[0][self.sortBy] === undefined) {
121 self.sortBy = 'degree';
122 if (!util_1.isNumber(nodes[0].degree)) {
123 var values_1 = util_1.getDegree(nodes.length, indexMap, edges);
124 layoutNodes.forEach(function (node, i) {
125 node.degree = values_1[i];
126 });
127 }
128 }
129 // sort nodes by value
130 layoutNodes.sort(function (n1, n2) { return n2[self.sortBy] - n1[self.sortBy]; });
131 self.maxValueNode = layoutNodes[0];
132 self.maxLevelDiff = self.maxLevelDiff || self.maxValueNode[self.sortBy] / 4;
133 // put the values into levels
134 var levels = [[]];
135 var currentLevel = levels[0];
136 layoutNodes.forEach(function (node) {
137 if (currentLevel.length > 0) {
138 var diff = Math.abs(currentLevel[0][self.sortBy] - node[self.sortBy]);
139 if (self.maxLevelDiff && diff >= self.maxLevelDiff) {
140 currentLevel = [];
141 levels.push(currentLevel);
142 }
143 }
144 currentLevel.push(node);
145 });
146 // create positions for levels
147 var minDist = maxNodeSize + self.minNodeSpacing; // min dist between nodes
148 if (!self.preventOverlap) {
149 // then strictly constrain to bb
150 var firstLvlHasMulti = levels.length > 0 && levels[0].length > 1;
151 var maxR = Math.min(self.width, self.height) / 2 - minDist;
152 var rStep = maxR / (levels.length + (firstLvlHasMulti ? 1 : 0));
153 minDist = Math.min(minDist, rStep);
154 }
155 // find the metrics for each level
156 var r = 0;
157 levels.forEach(function (level) {
158 var sweep = self.sweep;
159 if (sweep === undefined) {
160 sweep = 2 * Math.PI - (2 * Math.PI) / level.length;
161 }
162 var dTheta = (level.dTheta = sweep / Math.max(1, level.length - 1));
163 // calculate the radius
164 if (level.length > 1 && self.preventOverlap) {
165 // but only if more than one node (can't overlap)
166 var dcos = Math.cos(dTheta) - Math.cos(0);
167 var dsin = Math.sin(dTheta) - Math.sin(0);
168 var rMin = Math.sqrt((minDist * minDist) / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
169 r = Math.max(rMin, r);
170 }
171 level.r = r;
172 r += minDist;
173 });
174 if (self.equidistant) {
175 var rDeltaMax_1 = 0;
176 var rr_1 = 0;
177 for (var i = 0; i < levels.length; i++) {
178 var level = levels[i];
179 var rDelta = level.r - rr_1;
180 rDeltaMax_1 = Math.max(rDeltaMax_1, rDelta);
181 }
182 rr_1 = 0;
183 levels.forEach(function (level, i) {
184 if (i === 0) {
185 rr_1 = level.r;
186 }
187 level.r = rr_1;
188 rr_1 += rDeltaMax_1;
189 });
190 }
191 // calculate the node positions
192 levels.forEach(function (level) {
193 var dTheta = level.dTheta;
194 var rr = level.r;
195 level.forEach(function (node, j) {
196 var theta = self.startAngle + (self.clockwise ? 1 : -1) * dTheta * j;
197 node.x = center[0] + rr * Math.cos(theta);
198 node.y = center[1] + rr * Math.sin(theta);
199 });
200 });
201 return {
202 nodes: nodes,
203 edges: edges,
204 };
205 };
206 ConcentricLayout.prototype.getType = function () {
207 return 'concentric';
208 };
209 return ConcentricLayout;
210}(base_1.Base));
211exports.ConcentricLayout = ConcentricLayout;
212//# sourceMappingURL=concentric.js.map
\No newline at end of file