UNPKG

10.7 kBJavaScriptView Raw
1"use strict";
2/**
3 * @fileOverview random 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.CircularLayout = void 0;
21var base_1 = require("./base");
22var util_1 = require("../util");
23function initHierarchy(nodes, edges, nodeMap, directed) {
24 nodes.forEach(function (_, i) {
25 nodes[i].children = [];
26 nodes[i].parent = [];
27 });
28 if (directed) {
29 edges.forEach(function (e) {
30 var sourceIdx = 0;
31 if (e.source) {
32 sourceIdx = nodeMap[e.source];
33 }
34 var targetIdx = 0;
35 if (e.target) {
36 targetIdx = nodeMap[e.target];
37 }
38 var child = nodes[sourceIdx].children;
39 var parent = nodes[targetIdx].parent;
40 child.push(nodes[targetIdx].id);
41 parent.push(nodes[sourceIdx].id);
42 });
43 }
44 else {
45 edges.forEach(function (e) {
46 var sourceIdx = 0;
47 if (e.source) {
48 sourceIdx = nodeMap[e.source];
49 }
50 var targetIdx = 0;
51 if (e.target) {
52 targetIdx = nodeMap[e.target];
53 }
54 var sourceChildren = nodes[sourceIdx].children;
55 var targetChildren = nodes[targetIdx].children;
56 sourceChildren.push(nodes[targetIdx].id);
57 targetChildren.push(nodes[sourceIdx].id);
58 });
59 }
60}
61function connect(a, b, edges) {
62 var m = edges.length;
63 for (var i = 0; i < m; i++) {
64 if ((a.id === edges[i].source && b.id === edges[i].target) ||
65 (b.id === edges[i].source && a.id === edges[i].target)) {
66 return true;
67 }
68 }
69 return false;
70}
71function compareDegree(a, b) {
72 var aDegree = a.degree;
73 var bDegree = b.degree;
74 if (aDegree < bDegree) {
75 return -1;
76 }
77 if (aDegree > bDegree) {
78 return 1;
79 }
80 return 0;
81}
82/**
83 * 圆形布局
84 */
85var CircularLayout = /** @class */ (function (_super) {
86 __extends(CircularLayout, _super);
87 function CircularLayout(options) {
88 var _this = _super.call(this) || this;
89 /** 固定半径,若设置了 radius,则 startRadius 与 endRadius 不起效 */
90 _this.radius = null;
91 /** 起始半径 */
92 _this.startRadius = null;
93 /** 终止半径 */
94 _this.endRadius = null;
95 /** 起始角度 */
96 _this.startAngle = 0;
97 /** 终止角度 */
98 _this.endAngle = 2 * Math.PI;
99 /** 是否顺时针 */
100 _this.clockwise = true;
101 /** 节点在环上分成段数(几个段将均匀分布),在 endRadius - startRadius != 0 时生效 */
102 _this.divisions = 1;
103 /** 节点在环上排序的依据,可选: 'topology', 'degree', 'null' */
104 _this.ordering = null;
105 /** how many 2*pi from first to last nodes */
106 _this.angleRatio = 1;
107 _this.nodes = [];
108 _this.edges = [];
109 _this.nodeMap = {};
110 _this.degrees = [];
111 _this.width = 300;
112 _this.height = 300;
113 _this.updateCfg(options);
114 return _this;
115 }
116 CircularLayout.prototype.getDefaultCfg = function () {
117 return {
118 radius: null,
119 startRadius: null,
120 endRadius: null,
121 startAngle: 0,
122 endAngle: 2 * Math.PI,
123 clockwise: true,
124 divisions: 1,
125 ordering: null,
126 angleRatio: 1,
127 };
128 };
129 /**
130 * 执行布局
131 */
132 CircularLayout.prototype.execute = function () {
133 var self = this;
134 var nodes = self.nodes;
135 var edges = self.edges;
136 var n = nodes.length;
137 if (n === 0) {
138 return;
139 }
140 if (!self.width && typeof window !== 'undefined') {
141 self.width = window.innerWidth;
142 }
143 if (!self.height && typeof window !== 'undefined') {
144 self.height = window.innerHeight;
145 }
146 if (!self.center) {
147 self.center = [self.width / 2, self.height / 2];
148 }
149 var center = self.center;
150 if (n === 1) {
151 nodes[0].x = center[0];
152 nodes[0].y = center[1];
153 return;
154 }
155 var radius = self.radius;
156 var startRadius = self.startRadius;
157 var endRadius = self.endRadius;
158 var divisions = self.divisions;
159 var startAngle = self.startAngle;
160 var endAngle = self.endAngle;
161 var angleStep = (endAngle - startAngle) / n;
162 // layout
163 var nodeMap = {};
164 nodes.forEach(function (node, i) {
165 nodeMap[node.id] = i;
166 });
167 self.nodeMap = nodeMap;
168 var degrees = util_1.getDegree(nodes.length, nodeMap, edges);
169 self.degrees = degrees;
170 if (!radius && !startRadius && !endRadius) {
171 radius = self.height > self.width ? self.width / 2 : self.height / 2;
172 }
173 else if (!startRadius && endRadius) {
174 startRadius = endRadius;
175 }
176 else if (startRadius && !endRadius) {
177 endRadius = startRadius;
178 }
179 var angleRatio = self.angleRatio;
180 var astep = angleStep * angleRatio;
181 var ordering = self.ordering;
182 var layoutNodes = [];
183 if (ordering === 'topology') {
184 // layout according to the topology
185 layoutNodes = self.topologyOrdering();
186 }
187 else if (ordering === 'topology-directed') {
188 // layout according to the topology
189 layoutNodes = self.topologyOrdering(true);
190 }
191 else if (ordering === 'degree') {
192 // layout according to the descent order of degrees
193 layoutNodes = self.degreeOrdering();
194 }
195 else {
196 // layout according to the original order in the data.nodes
197 layoutNodes = nodes;
198 }
199 var clockwise = self.clockwise;
200 var divN = Math.ceil(n / divisions); // node number in each division
201 for (var i = 0; i < n; ++i) {
202 var r = radius;
203 if (!r && startRadius !== null && endRadius !== null) {
204 r = startRadius + (i * (endRadius - startRadius)) / (n - 1);
205 }
206 if (!r) {
207 r = 10 + (i * 100) / (n - 1);
208 }
209 var angle = startAngle +
210 (i % divN) * astep +
211 ((2 * Math.PI) / divisions) * Math.floor(i / divN);
212 if (!clockwise) {
213 angle =
214 endAngle -
215 (i % divN) * astep -
216 ((2 * Math.PI) / divisions) * Math.floor(i / divN);
217 }
218 layoutNodes[i].x = center[0] + Math.cos(angle) * r;
219 layoutNodes[i].y = center[1] + Math.sin(angle) * r;
220 layoutNodes[i].weight = degrees[i];
221 }
222 return {
223 nodes: layoutNodes,
224 edges: this.edges,
225 };
226 };
227 /**
228 * 根据节点的拓扑结构排序
229 * @return {array} orderedNodes 排序后的结果
230 */
231 CircularLayout.prototype.topologyOrdering = function (directed) {
232 if (directed === void 0) { directed = false; }
233 var self = this;
234 var degrees = self.degrees;
235 var edges = self.edges;
236 var nodes = self.nodes;
237 var cnodes = util_1.clone(nodes);
238 var nodeMap = self.nodeMap;
239 var orderedCNodes = [cnodes[0]];
240 var resNodes = [nodes[0]];
241 var pickFlags = [];
242 var n = nodes.length;
243 pickFlags[0] = true;
244 initHierarchy(cnodes, edges, nodeMap, directed);
245 var k = 0;
246 cnodes.forEach(function (cnode, i) {
247 if (i !== 0) {
248 if ((i === n - 1 ||
249 degrees[i] !== degrees[i + 1] ||
250 connect(orderedCNodes[k], cnode, edges)) &&
251 !pickFlags[i]) {
252 orderedCNodes.push(cnode);
253 resNodes.push(nodes[nodeMap[cnode.id]]);
254 pickFlags[i] = true;
255 k++;
256 }
257 else {
258 var children = orderedCNodes[k].children;
259 var foundChild = false;
260 for (var j = 0; j < children.length; j++) {
261 var childIdx = nodeMap[children[j]];
262 if (degrees[childIdx] === degrees[i] && !pickFlags[childIdx]) {
263 orderedCNodes.push(cnodes[childIdx]);
264 resNodes.push(nodes[nodeMap[cnodes[childIdx].id]]);
265 pickFlags[childIdx] = true;
266 foundChild = true;
267 break;
268 }
269 }
270 var ii = 0;
271 while (!foundChild) {
272 if (!pickFlags[ii]) {
273 orderedCNodes.push(cnodes[ii]);
274 resNodes.push(nodes[nodeMap[cnodes[ii].id]]);
275 pickFlags[ii] = true;
276 foundChild = true;
277 }
278 ii++;
279 if (ii === n) {
280 break;
281 }
282 }
283 }
284 }
285 });
286 return resNodes;
287 };
288 /**
289 * 根据节点度数大小排序
290 * @return {array} orderedNodes 排序后的结果
291 */
292 CircularLayout.prototype.degreeOrdering = function () {
293 var self = this;
294 var nodes = self.nodes;
295 var orderedNodes = [];
296 var degrees = self.degrees;
297 nodes.forEach(function (node, i) {
298 node.degree = degrees[i];
299 orderedNodes.push(node);
300 });
301 orderedNodes.sort(compareDegree);
302 return orderedNodes;
303 };
304 CircularLayout.prototype.getType = function () {
305 return 'circular';
306 };
307 return CircularLayout;
308}(base_1.Base));
309exports.CircularLayout = CircularLayout;
310//# sourceMappingURL=circular.js.map
\No newline at end of file