UNPKG

11.7 kBJavaScriptView Raw
1"use strict";
2/**
3 * @fileOverview grid 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.GridLayout = void 0;
22var util_1 = require("../util");
23var base_1 = require("./base");
24/**
25 * 网格布局
26 */
27var GridLayout = /** @class */ (function (_super) {
28 __extends(GridLayout, _super);
29 function GridLayout(options) {
30 var _this = _super.call(this) || this;
31 /** 布局起始点 */
32 _this.begin = [0, 0];
33 /** prevents node overlap, may overflow boundingBox if not enough space */
34 _this.preventOverlap = true;
35 /** extra spacing around nodes when preventOverlap: true */
36 _this.preventOverlapPadding = 10;
37 /** uses all available space on false, uses minimal space on true */
38 _this.condense = false;
39 /** a sorting function to order the nodes; e.g. function(a, b){ return a.datapublic ('weight') - b.data('weight') } */
40 _this.sortBy = 'degree';
41 _this.nodeSize = 30;
42 _this.nodes = [];
43 _this.edges = [];
44 _this.width = 300;
45 _this.height = 300;
46 _this.row = 0;
47 _this.col = 0;
48 _this.cellWidth = 0;
49 _this.cellHeight = 0;
50 _this.cellUsed = {};
51 _this.id2manPos = {};
52 _this.updateCfg(options);
53 return _this;
54 }
55 GridLayout.prototype.getDefaultCfg = function () {
56 return {
57 begin: [0, 0],
58 preventOverlap: true,
59 preventOverlapPadding: 10,
60 condense: false,
61 rows: undefined,
62 cols: undefined,
63 position: undefined,
64 sortBy: 'degree',
65 nodeSize: 30,
66 };
67 };
68 /**
69 * 执行布局
70 */
71 GridLayout.prototype.execute = function () {
72 var self = this;
73 var nodes = self.nodes;
74 var n = nodes.length;
75 var begin = self.begin;
76 if (n === 0) {
77 return;
78 }
79 if (n === 1) {
80 nodes[0].x = begin[0];
81 nodes[0].y = begin[1];
82 return;
83 }
84 var edges = self.edges;
85 var layoutNodes = [];
86 nodes.forEach(function (node) {
87 layoutNodes.push(node);
88 });
89 var nodeIdxMap = {};
90 layoutNodes.forEach(function (node, i) {
91 nodeIdxMap[node.id] = i;
92 });
93 if (self.sortBy === 'degree' ||
94 !util_1.isString(self.sortBy) ||
95 layoutNodes[0][self.sortBy] === undefined) {
96 self.sortBy = 'degree';
97 if (util_1.isNaN(nodes[0].degree)) {
98 var values_1 = util_1.getDegree(layoutNodes.length, nodeIdxMap, edges);
99 layoutNodes.forEach(function (node, i) {
100 node.degree = values_1[i];
101 });
102 }
103 }
104 // sort nodes by value
105 layoutNodes.sort(function (n1, n2) { return n2[self.sortBy] - n1[self.sortBy]; });
106 if (!self.width && typeof window !== 'undefined') {
107 self.width = window.innerWidth;
108 }
109 if (!self.height && typeof window !== 'undefined') {
110 self.height = window.innerHeight;
111 }
112 var oRows = self.rows;
113 var oCols = self.cols != null ? self.cols : self.columns;
114 self.cells = n;
115 // if rows or columns were set in self, use those values
116 if (oRows != null && oCols != null) {
117 self.rows = oRows;
118 self.cols = oCols;
119 }
120 else if (oRows != null && oCols == null) {
121 self.rows = oRows;
122 self.cols = Math.ceil(self.cells / self.rows);
123 }
124 else if (oRows == null && oCols != null) {
125 self.cols = oCols;
126 self.rows = Math.ceil(self.cells / self.cols);
127 }
128 else {
129 // otherwise use the automatic values and adjust accordingly // otherwise use the automatic values and adjust accordingly
130 // width/height * splits^2 = cells where splits is number of times to split width
131 self.splits = Math.sqrt((self.cells * self.height) / self.width);
132 self.rows = Math.round(self.splits);
133 self.cols = Math.round((self.width / self.height) * self.splits);
134 }
135 if (self.cols * self.rows > self.cells) {
136 // otherwise use the automatic values and adjust accordingly
137 // if rounding was up, see if we can reduce rows or columns
138 var sm = self.small();
139 var lg = self.large();
140 // reducing the small side takes away the most cells, so try it first
141 if ((sm - 1) * lg >= self.cells) {
142 self.small(sm - 1);
143 }
144 else if ((lg - 1) * sm >= self.cells) {
145 self.large(lg - 1);
146 }
147 }
148 else {
149 // if rounding was too low, add rows or columns
150 while (self.cols * self.rows < self.cells) {
151 var sm = self.small();
152 var lg = self.large();
153 // try to add to larger side first (adds less in multiplication)
154 if ((lg + 1) * sm >= self.cells) {
155 self.large(lg + 1);
156 }
157 else {
158 self.small(sm + 1);
159 }
160 }
161 }
162 self.cellWidth = self.width / self.cols;
163 self.cellHeight = self.height / self.rows;
164 if (self.condense) {
165 self.cellWidth = 0;
166 self.cellHeight = 0;
167 }
168 if (self.preventOverlap) {
169 layoutNodes.forEach(function (node) {
170 if (!node.x || !node.y) {
171 // for bb
172 node.x = 0;
173 node.y = 0;
174 }
175 var nodew;
176 var nodeh;
177 if (util_1.isArray(node.size)) {
178 nodew = node.size[0];
179 nodeh = node.size[1];
180 }
181 else if (util_1.isNumber(node.size)) {
182 nodew = node.size;
183 nodeh = node.size;
184 }
185 if (nodew === undefined || nodeh === undefined) {
186 if (util_1.isArray(self.nodeSize)) {
187 nodew = self.nodeSize[0];
188 nodeh = self.nodeSize[1];
189 }
190 else if (util_1.isNumber(self.nodeSize)) {
191 nodew = self.nodeSize;
192 nodeh = self.nodeSize;
193 }
194 else {
195 nodew = 30;
196 nodeh = 30;
197 }
198 }
199 var p = self.preventOverlapPadding;
200 var w = nodew + p;
201 var h = nodeh + p;
202 self.cellWidth = Math.max(self.cellWidth, w);
203 self.cellHeight = Math.max(self.cellHeight, h);
204 });
205 }
206 self.cellUsed = {}; // e.g. 'c-0-2' => true
207 // to keep track of current cell position
208 self.row = 0;
209 self.col = 0;
210 // get a cache of all the manual positions
211 self.id2manPos = {};
212 for (var i = 0; i < layoutNodes.length; i++) {
213 var node = layoutNodes[i];
214 var rcPos = void 0;
215 if (self.position) {
216 rcPos = self.position(node);
217 }
218 if (rcPos && (rcPos.row !== undefined || rcPos.col !== undefined)) {
219 // must have at least row or col def'd
220 var pos = {
221 row: rcPos.row,
222 col: rcPos.col,
223 };
224 if (pos.col === undefined) {
225 // find unused col
226 pos.col = 0;
227 while (self.used(pos.row, pos.col)) {
228 pos.col++;
229 }
230 }
231 else if (pos.row === undefined) {
232 // find unused row
233 pos.row = 0;
234 while (self.used(pos.row, pos.col)) {
235 pos.row++;
236 }
237 }
238 self.id2manPos[node.id] = pos;
239 self.use(pos.row, pos.col);
240 }
241 self.getPos(node);
242 }
243 return {
244 edges: edges,
245 nodes: layoutNodes,
246 };
247 };
248 GridLayout.prototype.small = function (val) {
249 var self = this;
250 var res;
251 var rows = self.rows || 5;
252 var cols = self.cols || 5;
253 if (val == null) {
254 res = Math.min(rows, cols);
255 }
256 else {
257 var min = Math.min(rows, cols);
258 if (min === self.rows) {
259 self.rows = val;
260 }
261 else {
262 self.cols = val;
263 }
264 }
265 return res;
266 };
267 GridLayout.prototype.large = function (val) {
268 var self = this;
269 var res;
270 var rows = self.rows || 5;
271 var cols = self.cols || 5;
272 if (val == null) {
273 res = Math.max(rows, cols);
274 }
275 else {
276 var max = Math.max(rows, cols);
277 if (max === self.rows) {
278 self.rows = val;
279 }
280 else {
281 self.cols = val;
282 }
283 }
284 return res;
285 };
286 GridLayout.prototype.used = function (row, col) {
287 var self = this;
288 return self.cellUsed["c-" + row + "-" + col] || false;
289 };
290 GridLayout.prototype.use = function (row, col) {
291 var self = this;
292 self.cellUsed["c-" + row + "-" + col] = true;
293 };
294 GridLayout.prototype.moveToNextCell = function () {
295 var self = this;
296 var cols = self.cols || 5;
297 self.col++;
298 if (self.col >= cols) {
299 self.col = 0;
300 self.row++;
301 }
302 };
303 GridLayout.prototype.getPos = function (node) {
304 var self = this;
305 var begin = self.begin;
306 var cellWidth = self.cellWidth;
307 var cellHeight = self.cellHeight;
308 var x;
309 var y;
310 // see if we have a manual position set
311 var rcPos = self.id2manPos[node.id];
312 if (rcPos) {
313 x = rcPos.col * cellWidth + cellWidth / 2 + begin[0];
314 y = rcPos.row * cellHeight + cellHeight / 2 + begin[1];
315 }
316 else {
317 // otherwise set automatically
318 while (self.used(self.row, self.col)) {
319 self.moveToNextCell();
320 }
321 x = self.col * cellWidth + cellWidth / 2 + begin[0];
322 y = self.row * cellHeight + cellHeight / 2 + begin[1];
323 self.use(self.row, self.col);
324 self.moveToNextCell();
325 }
326 node.x = x;
327 node.y = y;
328 };
329 GridLayout.prototype.getType = function () {
330 return 'grid';
331 };
332 return GridLayout;
333}(base_1.Base));
334exports.GridLayout = GridLayout;
335//# sourceMappingURL=grid.js.map
\No newline at end of file