@antv/layout 0.3.22 → 0.3.24
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.
- package/dist/layout.min.js +1 -1
- package/dist/layout.min.js.map +1 -1
- package/es/layout/comboCombined.d.ts +3 -3
- package/es/layout/comboCombined.js +39 -26
- package/es/layout/comboCombined.js.map +1 -1
- package/es/layout/radial/radial.d.ts +4 -2
- package/es/layout/radial/radial.js +40 -29
- package/es/layout/radial/radial.js.map +1 -1
- package/es/layout/types.d.ts +27 -26
- package/lib/layout/comboCombined.d.ts +3 -3
- package/lib/layout/comboCombined.js +38 -23
- package/lib/layout/comboCombined.js.map +1 -1
- package/lib/layout/radial/radial.d.ts +4 -2
- package/lib/layout/radial/radial.js +36 -25
- package/lib/layout/radial/radial.js.map +1 -1
- package/lib/layout/types.d.ts +27 -26
- package/package.json +1 -1
- package/src/layout/comboCombined.ts +86 -38
- package/src/layout/radial/radial.ts +50 -36
- package/src/layout/types.ts +62 -50
package/lib/layout/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Base } from
|
|
1
|
+
import { Base } from './base';
|
|
2
2
|
export interface Node {
|
|
3
3
|
id: string;
|
|
4
4
|
}
|
|
@@ -77,7 +77,7 @@ export interface ComboTree {
|
|
|
77
77
|
children?: ComboTree[];
|
|
78
78
|
depth?: number;
|
|
79
79
|
parentId?: string;
|
|
80
|
-
itemType?:
|
|
80
|
+
itemType?: 'node' | 'combo';
|
|
81
81
|
[key: string]: unknown;
|
|
82
82
|
}
|
|
83
83
|
export interface ComboConfig {
|
|
@@ -87,7 +87,7 @@ export interface ComboConfig {
|
|
|
87
87
|
depth?: number;
|
|
88
88
|
}
|
|
89
89
|
export interface CircularLayoutOptions {
|
|
90
|
-
type:
|
|
90
|
+
type: 'circular';
|
|
91
91
|
center?: PointTuple;
|
|
92
92
|
width?: number;
|
|
93
93
|
height?: number;
|
|
@@ -96,7 +96,7 @@ export interface CircularLayoutOptions {
|
|
|
96
96
|
endRadius?: number | null;
|
|
97
97
|
clockwise?: boolean;
|
|
98
98
|
divisions?: number;
|
|
99
|
-
ordering?:
|
|
99
|
+
ordering?: 'topology' | 'topology-directed' | 'degree' | null;
|
|
100
100
|
angleRatio?: number;
|
|
101
101
|
workerEnabled?: boolean;
|
|
102
102
|
startAngle?: number;
|
|
@@ -104,7 +104,7 @@ export interface CircularLayoutOptions {
|
|
|
104
104
|
onLayoutEnd?: () => void;
|
|
105
105
|
}
|
|
106
106
|
export interface ComboForceLayoutOptions {
|
|
107
|
-
type:
|
|
107
|
+
type: 'comboForce';
|
|
108
108
|
center?: PointTuple;
|
|
109
109
|
maxIteration?: number;
|
|
110
110
|
linkDistance?: number | ((d?: unknown) => number);
|
|
@@ -134,7 +134,7 @@ export interface ComboForceLayoutOptions {
|
|
|
134
134
|
workerEnabled?: boolean;
|
|
135
135
|
}
|
|
136
136
|
export interface ComboCombinedLayoutOptions {
|
|
137
|
-
type:
|
|
137
|
+
type: 'comboConcentricForce';
|
|
138
138
|
center?: PointTuple;
|
|
139
139
|
nodeSize?: number | number[] | ((d?: any) => number) | undefined;
|
|
140
140
|
spacing?: number | number[] | ((d?: any) => number) | undefined;
|
|
@@ -144,7 +144,7 @@ export interface ComboCombinedLayoutOptions {
|
|
|
144
144
|
innerLayout?: Base;
|
|
145
145
|
}
|
|
146
146
|
export interface ConcentricLayoutOptions {
|
|
147
|
-
type:
|
|
147
|
+
type: 'concentric';
|
|
148
148
|
center?: PointTuple;
|
|
149
149
|
preventOverlap?: boolean;
|
|
150
150
|
nodeSize?: number | PointTuple;
|
|
@@ -161,9 +161,9 @@ export interface ConcentricLayoutOptions {
|
|
|
161
161
|
onLayoutEnd?: () => void;
|
|
162
162
|
}
|
|
163
163
|
export interface DagreLayoutOptions {
|
|
164
|
-
type:
|
|
165
|
-
rankdir?:
|
|
166
|
-
align?:
|
|
164
|
+
type: 'dagre';
|
|
165
|
+
rankdir?: 'TB' | 'BT' | 'LR' | 'RL';
|
|
166
|
+
align?: 'UL' | 'UR' | 'DL' | 'DR';
|
|
167
167
|
begin?: PointTuple;
|
|
168
168
|
nodeSize?: number | number[] | undefined;
|
|
169
169
|
nodesep?: number;
|
|
@@ -182,9 +182,9 @@ export interface DagreLayoutOptions {
|
|
|
182
182
|
ranksepFunc?: ((d?: any) => number) | undefined;
|
|
183
183
|
}
|
|
184
184
|
export interface DagreCompoundLayoutOptions {
|
|
185
|
-
type?:
|
|
186
|
-
rankdir?:
|
|
187
|
-
align?:
|
|
185
|
+
type?: 'dagreCompound';
|
|
186
|
+
rankdir?: 'TB' | 'BT' | 'LR' | 'RL';
|
|
187
|
+
align?: 'UL' | 'UR' | 'DL' | 'DR';
|
|
188
188
|
begin?: PointTuple;
|
|
189
189
|
nodeSize?: number | number[] | undefined;
|
|
190
190
|
nodesep?: number;
|
|
@@ -195,7 +195,7 @@ export interface DagreCompoundLayoutOptions {
|
|
|
195
195
|
onLayoutEnd?: () => void;
|
|
196
196
|
}
|
|
197
197
|
export interface FruchtermanLayoutOptions {
|
|
198
|
-
type:
|
|
198
|
+
type: 'fruchterman';
|
|
199
199
|
center?: PointTuple;
|
|
200
200
|
maxIteration?: number;
|
|
201
201
|
width?: number;
|
|
@@ -223,7 +223,7 @@ export interface CentripetalOptions {
|
|
|
223
223
|
};
|
|
224
224
|
}
|
|
225
225
|
export interface Force2LayoutOptions {
|
|
226
|
-
type?:
|
|
226
|
+
type?: 'force2';
|
|
227
227
|
center?: PointTuple;
|
|
228
228
|
width?: number;
|
|
229
229
|
height?: number;
|
|
@@ -262,7 +262,7 @@ export interface Force2LayoutOptions {
|
|
|
262
262
|
}) => void;
|
|
263
263
|
}
|
|
264
264
|
export interface GForceLayoutOptions {
|
|
265
|
-
type?:
|
|
265
|
+
type?: 'gForce';
|
|
266
266
|
center?: PointTuple;
|
|
267
267
|
width?: number;
|
|
268
268
|
height?: number;
|
|
@@ -291,7 +291,7 @@ type INode = OutNode & {
|
|
|
291
291
|
size: number | PointTuple;
|
|
292
292
|
};
|
|
293
293
|
export interface GridLayoutOptions {
|
|
294
|
-
type:
|
|
294
|
+
type: 'grid';
|
|
295
295
|
width?: number;
|
|
296
296
|
height?: number;
|
|
297
297
|
begin?: PointTuple;
|
|
@@ -310,14 +310,14 @@ export interface GridLayoutOptions {
|
|
|
310
310
|
onLayoutEnd?: () => void;
|
|
311
311
|
}
|
|
312
312
|
export interface MDSLayoutOptions {
|
|
313
|
-
type:
|
|
313
|
+
type: 'mds';
|
|
314
314
|
center?: PointTuple;
|
|
315
315
|
linkDistance?: number;
|
|
316
316
|
workerEnabled?: boolean;
|
|
317
317
|
onLayoutEnd?: () => void;
|
|
318
318
|
}
|
|
319
319
|
export interface RandomLayoutOptions {
|
|
320
|
-
type:
|
|
320
|
+
type: 'random';
|
|
321
321
|
center?: PointTuple;
|
|
322
322
|
width?: number;
|
|
323
323
|
height?: number;
|
|
@@ -325,7 +325,7 @@ export interface RandomLayoutOptions {
|
|
|
325
325
|
onLayoutEnd?: () => void;
|
|
326
326
|
}
|
|
327
327
|
export interface ForceLayoutOptions {
|
|
328
|
-
type:
|
|
328
|
+
type: 'force';
|
|
329
329
|
center?: PointTuple;
|
|
330
330
|
linkDistance?: number | ((d?: any) => number) | undefined;
|
|
331
331
|
edgeStrength?: number | ((d?: any) => number) | undefined;
|
|
@@ -349,7 +349,7 @@ export interface ForceLayoutOptions {
|
|
|
349
349
|
workerEnabled?: boolean;
|
|
350
350
|
}
|
|
351
351
|
export interface RadialLayoutOptions {
|
|
352
|
-
type:
|
|
352
|
+
type: 'radial';
|
|
353
353
|
center?: PointTuple;
|
|
354
354
|
width?: number;
|
|
355
355
|
height?: number;
|
|
@@ -365,10 +365,11 @@ export interface RadialLayoutOptions {
|
|
|
365
365
|
sortBy?: string | undefined;
|
|
366
366
|
sortStrength?: number;
|
|
367
367
|
workerEnabled?: boolean;
|
|
368
|
+
initWithMDS?: boolean;
|
|
368
369
|
onLayoutEnd?: () => void;
|
|
369
370
|
}
|
|
370
371
|
export interface FruchtermanGPULayoutOptions {
|
|
371
|
-
type:
|
|
372
|
+
type: 'fruchterman-gpu';
|
|
372
373
|
center?: PointTuple;
|
|
373
374
|
width?: number;
|
|
374
375
|
height?: number;
|
|
@@ -382,7 +383,7 @@ export interface FruchtermanGPULayoutOptions {
|
|
|
382
383
|
onLayoutEnd?: () => void;
|
|
383
384
|
}
|
|
384
385
|
export interface GForceGPULayoutOptions {
|
|
385
|
-
type:
|
|
386
|
+
type: 'gForce-gpu';
|
|
386
387
|
center?: PointTuple;
|
|
387
388
|
linkDistance?: number | ((d?: any) => number) | undefined;
|
|
388
389
|
nodeStrength?: number | ((d?: any) => number) | undefined;
|
|
@@ -400,7 +401,7 @@ export interface GForceGPULayoutOptions {
|
|
|
400
401
|
gpuEnabled?: boolean;
|
|
401
402
|
}
|
|
402
403
|
export interface ForceAtlas2LayoutOptions {
|
|
403
|
-
type:
|
|
404
|
+
type: 'forceAtlas2';
|
|
404
405
|
center?: PointTuple;
|
|
405
406
|
width?: number;
|
|
406
407
|
height?: number;
|
|
@@ -420,13 +421,13 @@ export interface ForceAtlas2LayoutOptions {
|
|
|
420
421
|
prune?: boolean;
|
|
421
422
|
}
|
|
422
423
|
export interface ERLayoutOptions {
|
|
423
|
-
type:
|
|
424
|
+
type: 'er';
|
|
424
425
|
width?: number;
|
|
425
426
|
height?: number;
|
|
426
427
|
nodeMinGap?: number;
|
|
427
428
|
}
|
|
428
429
|
export declare namespace ILayout {
|
|
429
|
-
type LayoutTypes =
|
|
430
|
+
type LayoutTypes = 'grid' | 'random' | 'force' | 'circular' | 'dagre' | 'radial' | 'concentric' | 'mds' | 'fruchterman' | 'fruchterman-gpu' | 'gForce' | 'gForce-gpu' | 'comboForce' | 'forceAtlas2' | 'er';
|
|
430
431
|
type LayoutOptions = GridLayoutOptions | RandomLayoutOptions | ForceLayoutOptions | CircularLayoutOptions | DagreLayoutOptions | RadialLayoutOptions | ConcentricLayoutOptions | MDSLayoutOptions | FruchtermanLayoutOptions | FruchtermanGPULayoutOptions | GForceLayoutOptions | GForceGPULayoutOptions | ComboForceLayoutOptions | ComboCombinedLayoutOptions | ForceAtlas2LayoutOptions | ERLayoutOptions;
|
|
431
432
|
}
|
|
432
433
|
export {};
|
package/package.json
CHANGED
|
@@ -3,18 +3,32 @@
|
|
|
3
3
|
* @author shiwu.wyy@antfin.com
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
import {
|
|
7
7
|
Edge,
|
|
8
8
|
Combo,
|
|
9
9
|
OutNode,
|
|
10
10
|
PointTuple,
|
|
11
11
|
ComboTree,
|
|
12
|
-
ComboCombinedLayoutOptions
|
|
13
|
-
} from
|
|
12
|
+
ComboCombinedLayoutOptions,
|
|
13
|
+
} from './types';
|
|
14
14
|
import { FORCE_LAYOUT_TYPE_MAP } from './constants';
|
|
15
|
-
import { Base } from
|
|
16
|
-
import {
|
|
17
|
-
|
|
15
|
+
import { Base } from './base';
|
|
16
|
+
import {
|
|
17
|
+
isArray,
|
|
18
|
+
isNumber,
|
|
19
|
+
isFunction,
|
|
20
|
+
traverseTreeUp,
|
|
21
|
+
isObject,
|
|
22
|
+
getLayoutBBox,
|
|
23
|
+
} from '../util';
|
|
24
|
+
import {
|
|
25
|
+
CircularLayout,
|
|
26
|
+
ConcentricLayout,
|
|
27
|
+
GridLayout,
|
|
28
|
+
RadialLayout,
|
|
29
|
+
GForceLayout,
|
|
30
|
+
MDSLayout,
|
|
31
|
+
} from '.';
|
|
18
32
|
|
|
19
33
|
type Node = OutNode & {
|
|
20
34
|
depth?: number;
|
|
@@ -29,7 +43,6 @@ type Node = OutNode & {
|
|
|
29
43
|
* combined two layouts (inner and outer) for graph with combos
|
|
30
44
|
*/
|
|
31
45
|
export class ComboCombinedLayout extends Base {
|
|
32
|
-
|
|
33
46
|
/** 布局中心 */
|
|
34
47
|
public center: PointTuple = [0, 0];
|
|
35
48
|
|
|
@@ -52,7 +65,11 @@ export class ComboCombinedLayout extends Base {
|
|
|
52
65
|
public outerLayout: any;
|
|
53
66
|
|
|
54
67
|
/** combo 内部的布局算法,默认为 concentric */
|
|
55
|
-
public innerLayout:
|
|
68
|
+
public innerLayout:
|
|
69
|
+
| ConcentricLayout
|
|
70
|
+
| CircularLayout
|
|
71
|
+
| GridLayout
|
|
72
|
+
| RadialLayout;
|
|
56
73
|
|
|
57
74
|
/** Combo 内部的 padding */
|
|
58
75
|
public comboPadding:
|
|
@@ -101,7 +118,7 @@ export class ComboCombinedLayout extends Base {
|
|
|
101
118
|
public run() {
|
|
102
119
|
const self = this;
|
|
103
120
|
const { nodes, edges, combos, comboEdges, center } = self;
|
|
104
|
-
|
|
121
|
+
|
|
105
122
|
const nodeMap: any = {};
|
|
106
123
|
nodes.forEach((node) => {
|
|
107
124
|
nodeMap[node.id] = node;
|
|
@@ -128,10 +145,15 @@ export class ComboCombinedLayout extends Base {
|
|
|
128
145
|
fx: innerNode.fx || comboMap[cTree.id].fx,
|
|
129
146
|
fy: innerNode.fy || comboMap[cTree.id].fy,
|
|
130
147
|
mass: innerNode.mass || comboMap[cTree.id].mass,
|
|
131
|
-
size: innerNode.size
|
|
148
|
+
size: innerNode.size,
|
|
132
149
|
};
|
|
133
150
|
outerNodes.push(oNode);
|
|
134
|
-
if (
|
|
151
|
+
if (
|
|
152
|
+
!isNaN(oNode.x) &&
|
|
153
|
+
oNode.x !== 0 &&
|
|
154
|
+
!isNaN(oNode.y) &&
|
|
155
|
+
oNode.y !== 0
|
|
156
|
+
) {
|
|
135
157
|
allHaveNoPosition = false;
|
|
136
158
|
} else {
|
|
137
159
|
oNode.x = Math.random() * 100;
|
|
@@ -148,7 +170,12 @@ export class ComboCombinedLayout extends Base {
|
|
|
148
170
|
// 代表节点的节点
|
|
149
171
|
const oNode: Node = { ...node };
|
|
150
172
|
outerNodes.push(oNode);
|
|
151
|
-
if (
|
|
173
|
+
if (
|
|
174
|
+
!isNaN(oNode.x) &&
|
|
175
|
+
oNode.x !== 0 &&
|
|
176
|
+
!isNaN(oNode.y) &&
|
|
177
|
+
oNode.y !== 0
|
|
178
|
+
) {
|
|
152
179
|
allHaveNoPosition = false;
|
|
153
180
|
} else {
|
|
154
181
|
oNode.x = Math.random() * 100;
|
|
@@ -161,12 +188,14 @@ export class ComboCombinedLayout extends Base {
|
|
|
161
188
|
const sourceAncestorId = nodeAncestorIdMap[edge.source] || edge.source;
|
|
162
189
|
const targetAncestorId = nodeAncestorIdMap[edge.target] || edge.target;
|
|
163
190
|
// 若两个点的祖先都在力导图的节点中,且是不同的节点,创建一条链接两个祖先的边到力导图的边中
|
|
164
|
-
if (
|
|
191
|
+
if (
|
|
192
|
+
sourceAncestorId !== targetAncestorId &&
|
|
165
193
|
outerNodeIds.includes(sourceAncestorId) &&
|
|
166
|
-
outerNodeIds.includes(targetAncestorId)
|
|
194
|
+
outerNodeIds.includes(targetAncestorId)
|
|
195
|
+
) {
|
|
167
196
|
outerEdges.push({
|
|
168
197
|
source: sourceAncestorId,
|
|
169
|
-
target: targetAncestorId
|
|
198
|
+
target: targetAncestorId,
|
|
170
199
|
});
|
|
171
200
|
}
|
|
172
201
|
});
|
|
@@ -179,19 +208,22 @@ export class ComboCombinedLayout extends Base {
|
|
|
179
208
|
} else {
|
|
180
209
|
const outerData = {
|
|
181
210
|
nodes: outerNodes,
|
|
182
|
-
edges: outerEdges
|
|
211
|
+
edges: outerEdges,
|
|
183
212
|
};
|
|
184
213
|
|
|
185
214
|
// 需要使用一个同步的布局
|
|
186
215
|
// @ts-ignore
|
|
187
|
-
const outerLayout =
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
216
|
+
const outerLayout =
|
|
217
|
+
this.outerLayout ||
|
|
218
|
+
new GForceLayout({
|
|
219
|
+
gravity: 1,
|
|
220
|
+
factor: 4,
|
|
221
|
+
linkDistance: (edge: any, source: any, target: any) => {
|
|
222
|
+
const nodeSize =
|
|
223
|
+
((source.size?.[0] || 30) + (target.size?.[0] || 30)) / 2;
|
|
224
|
+
return Math.min(nodeSize * 1.5, 700);
|
|
225
|
+
},
|
|
226
|
+
});
|
|
195
227
|
const outerLayoutType = outerLayout.getType?.();
|
|
196
228
|
outerLayout.updateCfg({
|
|
197
229
|
center,
|
|
@@ -201,7 +233,8 @@ export class ComboCombinedLayout extends Base {
|
|
|
201
233
|
});
|
|
202
234
|
// 若所有 outerNodes 都没有位置,且 outerLayout 是力导家族的布局,则先执行 preset mds 或 grid
|
|
203
235
|
if (allHaveNoPosition && FORCE_LAYOUT_TYPE_MAP[outerLayoutType]) {
|
|
204
|
-
const outerLayoutPreset =
|
|
236
|
+
const outerLayoutPreset =
|
|
237
|
+
outerNodes.length < 100 ? new MDSLayout() : new GridLayout();
|
|
205
238
|
outerLayoutPreset.layout(outerData);
|
|
206
239
|
}
|
|
207
240
|
outerLayout.layout(outerData);
|
|
@@ -235,8 +268,8 @@ export class ComboCombinedLayout extends Base {
|
|
|
235
268
|
if (!innerGraph) continue;
|
|
236
269
|
innerGraph.nodes.forEach((node: OutNode) => {
|
|
237
270
|
if (!innerGraph.visited) {
|
|
238
|
-
node.x +=
|
|
239
|
-
node.y +=
|
|
271
|
+
node.x += innerGraph.x || 0;
|
|
272
|
+
node.y += innerGraph.y || 0;
|
|
240
273
|
}
|
|
241
274
|
if (nodeMap[node.id]) {
|
|
242
275
|
nodeMap[node.id].x = node.x;
|
|
@@ -257,7 +290,9 @@ export class ComboCombinedLayout extends Base {
|
|
|
257
290
|
const innerGraphs: any = {};
|
|
258
291
|
|
|
259
292
|
// @ts-ignore
|
|
260
|
-
const innerGraphLayout: any =
|
|
293
|
+
const innerGraphLayout: any =
|
|
294
|
+
this.innerLayout ||
|
|
295
|
+
new ConcentricLayout({ type: 'concentric', sortBy: 'id' });
|
|
261
296
|
innerGraphLayout.center = [0, 0];
|
|
262
297
|
innerGraphLayout.preventOverlap = true;
|
|
263
298
|
innerGraphLayout.nodeSpacing = spacing;
|
|
@@ -270,11 +305,13 @@ export class ComboCombinedLayout extends Base {
|
|
|
270
305
|
if (!treeNode.children?.length) {
|
|
271
306
|
// 空 combo
|
|
272
307
|
if (treeNode.itemType === 'combo') {
|
|
273
|
-
const treeNodeSize = padding
|
|
308
|
+
const treeNodeSize = padding
|
|
309
|
+
? [padding * 2, padding * 2]
|
|
310
|
+
: [30, 30];
|
|
274
311
|
innerGraphs[treeNode.id] = {
|
|
275
312
|
id: treeNode.id,
|
|
276
313
|
nodes: [],
|
|
277
|
-
size: treeNodeSize
|
|
314
|
+
size: treeNodeSize,
|
|
278
315
|
};
|
|
279
316
|
}
|
|
280
317
|
} else {
|
|
@@ -287,19 +324,25 @@ export class ComboCombinedLayout extends Base {
|
|
|
287
324
|
const innerGraphNodeIds = innerGraphNodes.map((node) => node.id);
|
|
288
325
|
const innerGraphData = {
|
|
289
326
|
nodes: innerGraphNodes,
|
|
290
|
-
edges: edges.filter(
|
|
327
|
+
edges: edges.filter(
|
|
328
|
+
(edge) =>
|
|
329
|
+
innerGraphNodeIds.includes(edge.source) &&
|
|
330
|
+
innerGraphNodeIds.includes(edge.target)
|
|
331
|
+
),
|
|
291
332
|
};
|
|
292
333
|
let minNodeSize = Infinity;
|
|
293
334
|
innerGraphNodes.forEach((node) => {
|
|
294
335
|
// @ts-ignore
|
|
295
|
-
if (!node.size)
|
|
336
|
+
if (!node.size)
|
|
337
|
+
node.size = innerGraphs[node.id]?.size ||
|
|
338
|
+
(nodeSize as Function)?.(node) || [30, 30];
|
|
296
339
|
if (isNumber(node.size)) node.size = [node.size, node.size];
|
|
297
340
|
if (minNodeSize > node.size[0]) minNodeSize = node.size[0];
|
|
298
341
|
if (minNodeSize > node.size[1]) minNodeSize = node.size[1];
|
|
299
342
|
});
|
|
300
343
|
|
|
301
344
|
// 根据节点数量、spacing,调整布局参数
|
|
302
|
-
|
|
345
|
+
|
|
303
346
|
innerGraphLayout.layout(innerGraphData);
|
|
304
347
|
const { minX, minY, maxX, maxY } = getLayoutBBox(innerGraphNodes);
|
|
305
348
|
// move the innerGraph to [0, 0],for later controled by parent layout
|
|
@@ -308,11 +351,14 @@ export class ComboCombinedLayout extends Base {
|
|
|
308
351
|
node.x -= center.x;
|
|
309
352
|
node.y -= center.y;
|
|
310
353
|
});
|
|
311
|
-
const
|
|
354
|
+
const innerGraphWidth =
|
|
355
|
+
Math.max(maxX - minX, minNodeSize) + padding * 2;
|
|
356
|
+
const innerGraphHeight =
|
|
357
|
+
Math.max(maxY - minY, minNodeSize) + padding * 2;
|
|
312
358
|
innerGraphs[treeNode.id] = {
|
|
313
359
|
id: treeNode.id,
|
|
314
360
|
nodes: innerGraphNodes,
|
|
315
|
-
size: [
|
|
361
|
+
size: [innerGraphWidth, innerGraphHeight],
|
|
316
362
|
};
|
|
317
363
|
}
|
|
318
364
|
return true;
|
|
@@ -347,8 +393,10 @@ export class ComboCombinedLayout extends Base {
|
|
|
347
393
|
if (isArray(d.size)) {
|
|
348
394
|
const res = d.size[0] > d.size[1] ? d.size[0] : d.size[1];
|
|
349
395
|
return (res + spacing) / 2;
|
|
350
|
-
}
|
|
351
|
-
|
|
396
|
+
}
|
|
397
|
+
if (isObject(d.size)) {
|
|
398
|
+
const res =
|
|
399
|
+
d.size.width > d.size.height ? d.size.width : d.size.height;
|
|
352
400
|
return (res + spacing) / 2;
|
|
353
401
|
}
|
|
354
402
|
return (d.size + spacing) / 2;
|
|
@@ -392,6 +440,6 @@ export class ComboCombinedLayout extends Base {
|
|
|
392
440
|
this.comboPadding = comboPaddingFunc;
|
|
393
441
|
}
|
|
394
442
|
public getType() {
|
|
395
|
-
return
|
|
443
|
+
return 'comboCombined';
|
|
396
444
|
}
|
|
397
445
|
}
|
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
OutNode,
|
|
10
10
|
Edge,
|
|
11
11
|
Matrix,
|
|
12
|
-
RadialLayoutOptions
|
|
13
|
-
} from
|
|
12
|
+
RadialLayoutOptions,
|
|
13
|
+
} from '../types';
|
|
14
14
|
import {
|
|
15
15
|
isNaN,
|
|
16
16
|
isArray,
|
|
@@ -19,13 +19,13 @@ import {
|
|
|
19
19
|
isString,
|
|
20
20
|
floydWarshall,
|
|
21
21
|
getAdjMatrix,
|
|
22
|
-
isObject
|
|
23
|
-
} from
|
|
24
|
-
import { Base } from
|
|
25
|
-
import MDS from
|
|
22
|
+
isObject,
|
|
23
|
+
} from '../../util';
|
|
24
|
+
import { Base } from '../base';
|
|
25
|
+
import MDS from './mds';
|
|
26
26
|
import RadialNonoverlapForce, {
|
|
27
|
-
RadialNonoverlapForceParam
|
|
28
|
-
} from
|
|
27
|
+
RadialNonoverlapForceParam,
|
|
28
|
+
} from './radialNonoverlapForce';
|
|
29
29
|
|
|
30
30
|
type INode = OutNode & {
|
|
31
31
|
size?: number | PointTuple;
|
|
@@ -123,6 +123,8 @@ export class RadialLayout extends Base {
|
|
|
123
123
|
|
|
124
124
|
public onLayoutEnd: () => void;
|
|
125
125
|
|
|
126
|
+
public initWithMDS: boolean;
|
|
127
|
+
|
|
126
128
|
constructor(options?: RadialLayoutOptions) {
|
|
127
129
|
super();
|
|
128
130
|
this.updateCfg(options);
|
|
@@ -140,7 +142,8 @@ export class RadialLayout extends Base {
|
|
|
140
142
|
strictRadial: true,
|
|
141
143
|
maxPreventOverlapIteration: 200,
|
|
142
144
|
sortBy: undefined,
|
|
143
|
-
sortStrength: 10
|
|
145
|
+
sortStrength: 10,
|
|
146
|
+
initWithMDS: true,
|
|
144
147
|
};
|
|
145
148
|
}
|
|
146
149
|
|
|
@@ -156,10 +159,10 @@ export class RadialLayout extends Base {
|
|
|
156
159
|
return;
|
|
157
160
|
}
|
|
158
161
|
|
|
159
|
-
if (!self.width && typeof window !==
|
|
162
|
+
if (!self.width && typeof window !== 'undefined') {
|
|
160
163
|
self.width = window.innerWidth;
|
|
161
164
|
}
|
|
162
|
-
if (!self.height && typeof window !==
|
|
165
|
+
if (!self.height && typeof window !== 'undefined') {
|
|
163
166
|
self.height = window.innerHeight;
|
|
164
167
|
}
|
|
165
168
|
if (!self.center) {
|
|
@@ -245,25 +248,34 @@ export class RadialLayout extends Base {
|
|
|
245
248
|
self.weights = W;
|
|
246
249
|
|
|
247
250
|
// the initial positions from mds
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
p[0]
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
p[1]
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
251
|
+
if (self.initWithMDS) {
|
|
252
|
+
const mds = new MDS({ linkDistance, distances: eIdealD });
|
|
253
|
+
let positions = mds.layout();
|
|
254
|
+
positions.forEach((p: PointTuple) => {
|
|
255
|
+
if (isNaN(p[0])) {
|
|
256
|
+
p[0] = Math.random() * linkDistance;
|
|
257
|
+
}
|
|
258
|
+
if (isNaN(p[1])) {
|
|
259
|
+
p[1] = Math.random() * linkDistance;
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
self.positions = positions;
|
|
263
|
+
} else {
|
|
264
|
+
self.positions = nodes.map((node, i) => {
|
|
265
|
+
return [
|
|
266
|
+
(Math.random() - 0.5) * eIdealD[i][focusIndex],
|
|
267
|
+
(Math.random() - 0.5) * eIdealD[i][focusIndex],
|
|
268
|
+
];
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
self.positions.forEach((p: PointTuple, i: number) => {
|
|
260
272
|
nodes[i].x = p[0] + center[0];
|
|
261
273
|
nodes[i].y = p[1] + center[1];
|
|
262
274
|
});
|
|
263
275
|
// move the graph to origin, centered at focusNode
|
|
264
|
-
positions.forEach((p: PointTuple) => {
|
|
265
|
-
p[0] -= positions[focusIndex][0];
|
|
266
|
-
p[1] -= positions[focusIndex][1];
|
|
276
|
+
self.positions.forEach((p: PointTuple) => {
|
|
277
|
+
p[0] -= (self.positions as PointTuple[])[focusIndex][0];
|
|
278
|
+
p[1] -= (self.positions as PointTuple[])[focusIndex][1];
|
|
267
279
|
});
|
|
268
280
|
self.run();
|
|
269
281
|
const preventOverlap = self.preventOverlap;
|
|
@@ -287,9 +299,11 @@ export class RadialLayout extends Base {
|
|
|
287
299
|
if (isArray(d.size)) {
|
|
288
300
|
const res = d.size[0] > d.size[1] ? d.size[0] : d.size[1];
|
|
289
301
|
return res + nodeSpacingFunc(d);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
|
|
302
|
+
}
|
|
303
|
+
if (isObject(d.size)) {
|
|
304
|
+
const res =
|
|
305
|
+
d.size.width > d.size.height ? d.size.width : d.size.height;
|
|
306
|
+
return res + nodeSpacingFunc(d);
|
|
293
307
|
}
|
|
294
308
|
return d.size + nodeSpacingFunc(d);
|
|
295
309
|
}
|
|
@@ -307,20 +321,20 @@ export class RadialLayout extends Base {
|
|
|
307
321
|
nodes,
|
|
308
322
|
nodeSizeFunc,
|
|
309
323
|
adjMatrix,
|
|
310
|
-
positions,
|
|
324
|
+
positions: self.positions,
|
|
311
325
|
radii,
|
|
312
326
|
height,
|
|
313
327
|
width,
|
|
314
328
|
strictRadial,
|
|
315
329
|
focusID: focusIndex,
|
|
316
330
|
iterations: self.maxPreventOverlapIteration || 200,
|
|
317
|
-
k: positions.length / 4.5
|
|
331
|
+
k: self.positions.length / 4.5,
|
|
318
332
|
};
|
|
319
333
|
const nonoverlapForce = new RadialNonoverlapForce(nonoverlapForceParams);
|
|
320
|
-
positions = nonoverlapForce.layout();
|
|
334
|
+
self.positions = nonoverlapForce.layout();
|
|
321
335
|
}
|
|
322
336
|
// move the graph to center
|
|
323
|
-
positions.forEach((p: PointTuple, i: number) => {
|
|
337
|
+
self.positions.forEach((p: PointTuple, i: number) => {
|
|
324
338
|
nodes[i].x = p[0] + center[0];
|
|
325
339
|
nodes[i].y = p[1] + center[1];
|
|
326
340
|
});
|
|
@@ -329,7 +343,7 @@ export class RadialLayout extends Base {
|
|
|
329
343
|
|
|
330
344
|
return {
|
|
331
345
|
nodes,
|
|
332
|
-
edges
|
|
346
|
+
edges,
|
|
333
347
|
};
|
|
334
348
|
}
|
|
335
349
|
|
|
@@ -413,7 +427,7 @@ export class RadialLayout extends Base {
|
|
|
413
427
|
newRow.push(0);
|
|
414
428
|
} else if (radii[i] === radii[j]) {
|
|
415
429
|
// i and j are on the same circle
|
|
416
|
-
if (self.sortBy ===
|
|
430
|
+
if (self.sortBy === 'data') {
|
|
417
431
|
// sort the nodes on the same circle according to the ordering of the data
|
|
418
432
|
newRow.push(
|
|
419
433
|
(v * (Math.abs(i - j) * self.sortStrength)) /
|
|
@@ -495,6 +509,6 @@ export class RadialLayout extends Base {
|
|
|
495
509
|
}
|
|
496
510
|
|
|
497
511
|
public getType() {
|
|
498
|
-
return
|
|
512
|
+
return 'radial';
|
|
499
513
|
}
|
|
500
514
|
}
|