@antv/layout 0.2.2 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/dist/layout.min.js +1 -1
  2. package/dist/layout.min.js.map +1 -1
  3. package/es/layout/comboCombined.js +6 -21
  4. package/es/layout/comboCombined.js.map +1 -1
  5. package/es/layout/grid.js +2 -0
  6. package/es/layout/grid.js.map +1 -1
  7. package/es/layout/types.d.ts +7 -3
  8. package/lib/layout/comboCombined.js +6 -21
  9. package/lib/layout/comboCombined.js.map +1 -1
  10. package/lib/layout/grid.js +2 -0
  11. package/lib/layout/grid.js.map +1 -1
  12. package/lib/layout/types.d.ts +7 -3
  13. package/package.json +2 -1
  14. package/src/index.ts +7 -0
  15. package/src/layout/base.ts +54 -0
  16. package/src/layout/circular.ts +369 -0
  17. package/src/layout/comboCombined.ts +391 -0
  18. package/src/layout/comboForce.ts +873 -0
  19. package/src/layout/concentric.ts +289 -0
  20. package/src/layout/constants.ts +21 -0
  21. package/src/layout/dagre/graph.ts +104 -0
  22. package/src/layout/dagre/index.ts +31 -0
  23. package/src/layout/dagre/src/acyclic.ts +58 -0
  24. package/src/layout/dagre/src/add-border-segments.ts +47 -0
  25. package/src/layout/dagre/src/coordinate-system.ts +77 -0
  26. package/src/layout/dagre/src/data/list.ts +60 -0
  27. package/src/layout/dagre/src/debug.ts +30 -0
  28. package/src/layout/dagre/src/greedy-fas.ts +144 -0
  29. package/src/layout/dagre/src/layout.ts +580 -0
  30. package/src/layout/dagre/src/nesting-graph.ts +143 -0
  31. package/src/layout/dagre/src/normalize.ts +96 -0
  32. package/src/layout/dagre/src/order/add-subgraph-constraints.ts +29 -0
  33. package/src/layout/dagre/src/order/barycenter.ts +26 -0
  34. package/src/layout/dagre/src/order/build-layer-graph.ts +82 -0
  35. package/src/layout/dagre/src/order/cross-count.ts +77 -0
  36. package/src/layout/dagre/src/order/index.ts +105 -0
  37. package/src/layout/dagre/src/order/init-data-order.ts +27 -0
  38. package/src/layout/dagre/src/order/init-order.ts +56 -0
  39. package/src/layout/dagre/src/order/resolve-conflicts.ts +152 -0
  40. package/src/layout/dagre/src/order/sort-subgraph.ts +105 -0
  41. package/src/layout/dagre/src/order/sort.ts +76 -0
  42. package/src/layout/dagre/src/parent-dummy-chains.ts +102 -0
  43. package/src/layout/dagre/src/position/bk.ts +494 -0
  44. package/src/layout/dagre/src/position/index.ts +82 -0
  45. package/src/layout/dagre/src/rank/feasible-tree.ts +165 -0
  46. package/src/layout/dagre/src/rank/index.ts +54 -0
  47. package/src/layout/dagre/src/rank/network-simplex.ts +225 -0
  48. package/src/layout/dagre/src/rank/util.ts +157 -0
  49. package/src/layout/dagre/src/util.ts +308 -0
  50. package/src/layout/dagre.ts +423 -0
  51. package/src/layout/dagreCompound.ts +518 -0
  52. package/src/layout/er/core.ts +117 -0
  53. package/src/layout/er/forceGrid.ts +95 -0
  54. package/src/layout/er/grid.ts +185 -0
  55. package/src/layout/er/index.ts +68 -0
  56. package/src/layout/er/mysqlWorkbench.ts +345 -0
  57. package/src/layout/er/type.ts +39 -0
  58. package/src/layout/force/force-in-a-box.ts +400 -0
  59. package/src/layout/force/force.ts +391 -0
  60. package/src/layout/force/index.ts +1 -0
  61. package/src/layout/forceAtlas2/body.ts +115 -0
  62. package/src/layout/forceAtlas2/index.ts +556 -0
  63. package/src/layout/forceAtlas2/quad.ts +115 -0
  64. package/src/layout/forceAtlas2/quadTree.ts +107 -0
  65. package/src/layout/fruchterman.ts +361 -0
  66. package/src/layout/gForce.ts +487 -0
  67. package/src/layout/gpu/fruchterman.ts +314 -0
  68. package/src/layout/gpu/fruchtermanShader.ts +204 -0
  69. package/src/layout/gpu/gForce.ts +406 -0
  70. package/src/layout/gpu/gForceShader.ts +221 -0
  71. package/src/layout/grid.ts +393 -0
  72. package/src/layout/index.ts +45 -0
  73. package/src/layout/layout.ts +75 -0
  74. package/src/layout/mds.ts +140 -0
  75. package/src/layout/radial/index.ts +1 -0
  76. package/src/layout/radial/mds.ts +51 -0
  77. package/src/layout/radial/radial.ts +500 -0
  78. package/src/layout/radial/radialNonoverlapForce.ts +189 -0
  79. package/src/layout/random.ts +75 -0
  80. package/src/layout/types.ts +425 -0
  81. package/src/registy/index.ts +43 -0
  82. package/src/util/array.ts +1 -0
  83. package/src/util/function.ts +64 -0
  84. package/src/util/gpu.ts +254 -0
  85. package/src/util/index.ts +6 -0
  86. package/src/util/math.ts +158 -0
  87. package/src/util/number.ts +8 -0
  88. package/src/util/object.ts +28 -0
  89. package/src/util/string.ts +18 -0
  90. package/CHANGELOG.md +0 -88
@@ -0,0 +1,369 @@
1
+ /**
2
+ * @fileOverview random layout
3
+ * @author shiwu.wyy@antfin.com
4
+ */
5
+
6
+ import {
7
+ OutNode,
8
+ Edge,
9
+ PointTuple,
10
+ IndexMap,
11
+ CircularLayoutOptions
12
+ } from "./types";
13
+ import { Base } from "./base";
14
+ import { getDegree, clone, getEdgeTerminal, getFuncByUnknownType } from "../util";
15
+
16
+ type INode = OutNode & {
17
+ degree: number;
18
+ size: number | PointTuple;
19
+ weight: number;
20
+ children: string[];
21
+ parent: string[];
22
+ };
23
+
24
+ function initHierarchy(
25
+ nodes: INode[],
26
+ edges: Edge[],
27
+ nodeMap: IndexMap,
28
+ directed: boolean
29
+ ) {
30
+ nodes.forEach((_, i: number) => {
31
+ nodes[i].children = [];
32
+ nodes[i].parent = [];
33
+ });
34
+ if (directed) {
35
+ edges.forEach((e) => {
36
+ const source = getEdgeTerminal(e, 'source');
37
+ const target = getEdgeTerminal(e, 'target');
38
+ let sourceIdx = 0;
39
+ if (source) {
40
+ sourceIdx = nodeMap[source];
41
+ }
42
+ let targetIdx = 0;
43
+ if (target) {
44
+ targetIdx = nodeMap[target];
45
+ }
46
+ const child = nodes[sourceIdx].children!;
47
+ const parent = nodes[targetIdx].parent!;
48
+ child.push(nodes[targetIdx].id);
49
+ parent.push(nodes[sourceIdx].id);
50
+ });
51
+ } else {
52
+ edges.forEach((e) => {
53
+ const source = getEdgeTerminal(e, 'source');
54
+ const target = getEdgeTerminal(e, 'target');
55
+ let sourceIdx = 0;
56
+ if (source) {
57
+ sourceIdx = nodeMap[source];
58
+ }
59
+ let targetIdx = 0;
60
+ if (target) {
61
+ targetIdx = nodeMap[target];
62
+ }
63
+ const sourceChildren = nodes[sourceIdx].children!;
64
+ const targetChildren = nodes[targetIdx].children!;
65
+ sourceChildren.push(nodes[targetIdx].id);
66
+ targetChildren.push(nodes[sourceIdx].id);
67
+ });
68
+ }
69
+ }
70
+
71
+ function connect(a: INode, b: INode, edges: Edge[]) {
72
+ const m = edges.length;
73
+ for (let i = 0; i < m; i++) {
74
+ const source = getEdgeTerminal(edges[i], 'source');
75
+ const target = getEdgeTerminal(edges[i], 'target');
76
+ if (
77
+ (a.id === source && b.id === target) ||
78
+ (b.id === source && a.id === target)
79
+ ) {
80
+ return true;
81
+ }
82
+ }
83
+ return false;
84
+ }
85
+
86
+ function compareDegree(a: INode, b: INode) {
87
+ const aDegree = a.degree!;
88
+ const bDegree = b.degree!;
89
+ if (aDegree < bDegree) {
90
+ return -1;
91
+ }
92
+ if (aDegree > bDegree) {
93
+ return 1;
94
+ }
95
+ return 0;
96
+ }
97
+
98
+ /**
99
+ * 圆形布局
100
+ */
101
+ export class CircularLayout extends Base {
102
+ /** 布局中心 */
103
+ public center: PointTuple;
104
+
105
+ /** 固定半径,若设置了 radius,则 startRadius 与 endRadius 不起效 */
106
+ public radius: number | null = null;
107
+
108
+ /** 节点间距,若设置 nodeSpacing,则 radius 将被自动计算,即设置 radius 不生效 */
109
+ public nodeSpacing: ((d?: unknown) => number) | number | undefined;
110
+
111
+ /** 节点大小,配合 nodeSpacing,一起用于计算 radius。若不配置,节点大小默认为 30 */
112
+ public nodeSize: number | undefined = undefined;
113
+
114
+ /** 起始半径 */
115
+ public startRadius: number | null = null;
116
+
117
+ /** 终止半径 */
118
+ public endRadius: number | null = null;
119
+
120
+ /** 起始角度 */
121
+ public startAngle: number = 0;
122
+
123
+ /** 终止角度 */
124
+ public endAngle: number = 2 * Math.PI;
125
+
126
+ /** 是否顺时针 */
127
+ public clockwise: boolean = true;
128
+
129
+ /** 节点在环上分成段数(几个段将均匀分布),在 endRadius - startRadius != 0 时生效 */
130
+ public divisions: number = 1;
131
+
132
+ /** 节点在环上排序的依据,可选: 'topology', 'degree', 'null' */
133
+ public ordering: "topology" | "topology-directed" | "degree" | null = null;
134
+
135
+ /** how many 2*pi from first to last nodes */
136
+ public angleRatio = 1;
137
+
138
+ public nodes: INode[] = [];
139
+
140
+ public edges: Edge[] = [];
141
+
142
+ private nodeMap: IndexMap = {};
143
+
144
+ private degrees: number[] = [];
145
+
146
+ public width: number = 300;
147
+
148
+ public height: number = 300;
149
+
150
+ public onLayoutEnd: () => void;
151
+
152
+ constructor(options?: CircularLayoutOptions) {
153
+ super();
154
+ this.updateCfg(options);
155
+ }
156
+
157
+ public getDefaultCfg() {
158
+ return {
159
+ radius: null,
160
+ startRadius: null,
161
+ endRadius: null,
162
+ startAngle: 0,
163
+ endAngle: 2 * Math.PI,
164
+ clockwise: true,
165
+ divisions: 1,
166
+ ordering: null,
167
+ angleRatio: 1
168
+ };
169
+ }
170
+
171
+ /**
172
+ * 执行布局
173
+ */
174
+ public execute() {
175
+ const self = this;
176
+ const nodes = self.nodes;
177
+ const edges = self.edges;
178
+ const n = nodes.length;
179
+ if (n === 0) {
180
+ if (self.onLayoutEnd) self.onLayoutEnd();
181
+ return;
182
+ }
183
+
184
+ if (!self.width && typeof window !== "undefined") {
185
+ self.width = window.innerWidth;
186
+ }
187
+ if (!self.height && typeof window !== "undefined") {
188
+ self.height = window.innerHeight;
189
+ }
190
+ if (!self.center) {
191
+ self.center = [self.width / 2, self.height / 2];
192
+ }
193
+ const center = self.center;
194
+
195
+ if (n === 1) {
196
+ nodes[0].x = center[0];
197
+ nodes[0].y = center[1];
198
+ if (self.onLayoutEnd) self.onLayoutEnd();
199
+ return;
200
+ }
201
+
202
+ let { radius, startRadius, endRadius } = self;
203
+ const { divisions, startAngle, endAngle, angleRatio, ordering, clockwise, nodeSpacing: paramNodeSpacing, nodeSize: paramNodeSize } = self;
204
+ const angleStep = (endAngle - startAngle) / n;
205
+ // layout
206
+ const nodeMap: IndexMap = {};
207
+ nodes.forEach((node, i) => {
208
+ nodeMap[node.id] = i;
209
+ });
210
+ self.nodeMap = nodeMap;
211
+ const degrees = getDegree(nodes.length, nodeMap, edges);
212
+ self.degrees = degrees;
213
+ if (paramNodeSpacing) {
214
+ const nodeSpacing: Function = getFuncByUnknownType(10, paramNodeSpacing);
215
+ const nodeSize: Function = getFuncByUnknownType(10, paramNodeSize);
216
+ let maxNodeSize = -Infinity;
217
+ nodes.forEach((node) => {
218
+ const nSize = nodeSize(node);
219
+ if (maxNodeSize < nSize) maxNodeSize = nSize;
220
+ });
221
+ let length = 0;
222
+ nodes.forEach((node, i) => {
223
+ if (i === 0) length += (maxNodeSize || 10);
224
+ else length += (nodeSpacing(node) || 0) + (maxNodeSize || 10);
225
+ });
226
+ radius = length / (2 * Math.PI);
227
+ } else if (!radius && !startRadius && !endRadius) {
228
+ radius = self.height > self.width ? self.width / 2 : self.height / 2;
229
+ } else if (!startRadius && endRadius) {
230
+ startRadius = endRadius;
231
+ } else if (startRadius && !endRadius) {
232
+ endRadius = startRadius;
233
+ }
234
+ const astep = angleStep * angleRatio;
235
+
236
+ let layoutNodes = [];
237
+ if (ordering === "topology") {
238
+ // layout according to the topology
239
+ layoutNodes = self.topologyOrdering();
240
+ } else if (ordering === "topology-directed") {
241
+ // layout according to the topology
242
+ layoutNodes = self.topologyOrdering(true);
243
+ } else if (ordering === "degree") {
244
+ // layout according to the descent order of degrees
245
+ layoutNodes = self.degreeOrdering();
246
+ } else {
247
+ // layout according to the original order in the data.nodes
248
+ layoutNodes = nodes;
249
+ }
250
+
251
+ const divN = Math.ceil(n / divisions); // node number in each division
252
+ for (let i = 0; i < n; ++i) {
253
+ let r = radius;
254
+ if (!r && startRadius !== null && endRadius !== null) {
255
+ r = startRadius + (i * (endRadius - startRadius)) / (n - 1);
256
+ }
257
+ if (!r) {
258
+ r = 10 + (i * 100) / (n - 1);
259
+ }
260
+ let angle =
261
+ startAngle +
262
+ (i % divN) * astep +
263
+ ((2 * Math.PI) / divisions) * Math.floor(i / divN);
264
+ if (!clockwise) {
265
+ angle =
266
+ endAngle -
267
+ (i % divN) * astep -
268
+ ((2 * Math.PI) / divisions) * Math.floor(i / divN);
269
+ }
270
+ layoutNodes[i].x = center[0] + Math.cos(angle) * r;
271
+ layoutNodes[i].y = center[1] + Math.sin(angle) * r;
272
+ layoutNodes[i].weight = degrees[i];
273
+ }
274
+
275
+ self.onLayoutEnd?.();
276
+
277
+ return {
278
+ nodes: layoutNodes,
279
+ edges: this.edges
280
+ };
281
+ }
282
+
283
+ /**
284
+ * 根据节点的拓扑结构排序
285
+ * @return {array} orderedNodes 排序后的结果
286
+ */
287
+ public topologyOrdering(directed: boolean = false) {
288
+ const self = this;
289
+ const degrees = self.degrees;
290
+ const edges = self.edges;
291
+ const nodes = self.nodes;
292
+ const cnodes = clone(nodes);
293
+ const nodeMap = self.nodeMap;
294
+ const orderedCNodes = [cnodes[0]];
295
+ const resNodes = [nodes[0]];
296
+ const pickFlags: boolean[] = [];
297
+ const n = nodes.length;
298
+ pickFlags[0] = true;
299
+ initHierarchy(cnodes, edges, nodeMap, directed);
300
+ let k = 0;
301
+ cnodes.forEach((cnode, i) => {
302
+ if (i !== 0) {
303
+ if (
304
+ (i === n - 1 ||
305
+ degrees[i] !== degrees[i + 1] ||
306
+ connect(
307
+ orderedCNodes[k],
308
+ cnode,
309
+ edges
310
+ )) &&
311
+ !pickFlags[i]
312
+ ) {
313
+ orderedCNodes.push(cnode);
314
+ resNodes.push(nodes[nodeMap[cnode.id]]);
315
+ pickFlags[i] = true;
316
+ k++;
317
+ } else {
318
+ const children = orderedCNodes[k].children!;
319
+ let foundChild = false;
320
+ for (let j = 0; j < children.length; j++) {
321
+ const childIdx = nodeMap[children[j]];
322
+ if (degrees[childIdx] === degrees[i] && !pickFlags[childIdx]) {
323
+ orderedCNodes.push(cnodes[childIdx]);
324
+ resNodes.push(nodes[nodeMap[cnodes[childIdx].id]]);
325
+ pickFlags[childIdx] = true;
326
+ foundChild = true;
327
+ break;
328
+ }
329
+ }
330
+ let ii = 0;
331
+ while (!foundChild) {
332
+ if (!pickFlags[ii]) {
333
+ orderedCNodes.push(cnodes[ii]);
334
+ resNodes.push(nodes[nodeMap[cnodes[ii].id]]);
335
+ pickFlags[ii] = true;
336
+ foundChild = true;
337
+ }
338
+ ii++;
339
+ if (ii === n) {
340
+ break;
341
+ }
342
+ }
343
+ }
344
+ }
345
+ });
346
+ return resNodes;
347
+ }
348
+
349
+ /**
350
+ * 根据节点度数大小排序
351
+ * @return {array} orderedNodes 排序后的结果
352
+ */
353
+ public degreeOrdering(): INode[] {
354
+ const self = this;
355
+ const nodes = self.nodes;
356
+ const orderedNodes: INode[] = [];
357
+ const degrees = self.degrees;
358
+ nodes.forEach((node, i) => {
359
+ node.degree = degrees[i];
360
+ orderedNodes.push(node);
361
+ });
362
+ orderedNodes.sort(compareDegree);
363
+ return orderedNodes;
364
+ }
365
+
366
+ public getType() {
367
+ return "circular";
368
+ }
369
+ }