@antv/layout 2.0.0-beta.0 → 2.0.0-beta.1
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/index.js +296 -209
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +2 -2
- package/dist/index.min.js.map +1 -1
- package/dist/worker.js +1 -1
- package/dist/worker.js.map +1 -1
- package/lib/algorithm/antv-dagre/index.js +8 -7
- package/lib/algorithm/antv-dagre/index.js.map +1 -1
- package/lib/algorithm/antv-dagre/types.d.ts +3 -14
- package/lib/algorithm/circular/index.js +8 -7
- package/lib/algorithm/circular/index.js.map +1 -1
- package/lib/algorithm/circular/types.d.ts +0 -16
- package/lib/algorithm/combo-combined/index.js +9 -7
- package/lib/algorithm/combo-combined/index.js.map +1 -1
- package/lib/algorithm/combo-combined/types.d.ts +6 -18
- package/lib/algorithm/concentric/index.js +10 -16
- package/lib/algorithm/concentric/index.js.map +1 -1
- package/lib/algorithm/concentric/types.d.ts +3 -17
- package/lib/algorithm/d3-force/index.js +16 -12
- package/lib/algorithm/d3-force/index.js.map +1 -1
- package/lib/algorithm/d3-force/types.d.ts +10 -24
- package/lib/algorithm/d3-force-3d/index.js +1 -1
- package/lib/algorithm/d3-force-3d/index.js.map +1 -1
- package/lib/algorithm/d3-force-3d/types.d.ts +5 -4
- package/lib/algorithm/dagre/index.js +10 -7
- package/lib/algorithm/dagre/index.js.map +1 -1
- package/lib/algorithm/dagre/types.d.ts +8 -14
- package/lib/algorithm/force/index.d.ts +1 -1
- package/lib/algorithm/force/index.js +56 -44
- package/lib/algorithm/force/index.js.map +1 -1
- package/lib/algorithm/force/types.d.ts +39 -36
- package/lib/algorithm/force-atlas2/index.d.ts +1 -1
- package/lib/algorithm/force-atlas2/index.js +8 -7
- package/lib/algorithm/force-atlas2/index.js.map +1 -1
- package/lib/algorithm/force-atlas2/types.d.ts +0 -14
- package/lib/algorithm/fruchterman/index.d.ts +1 -1
- package/lib/algorithm/fruchterman/index.js +8 -10
- package/lib/algorithm/fruchterman/index.js.map +1 -1
- package/lib/algorithm/fruchterman/simulation.js +2 -1
- package/lib/algorithm/fruchterman/simulation.js.map +1 -1
- package/lib/algorithm/fruchterman/types.d.ts +2 -1
- package/lib/algorithm/grid/index.d.ts +1 -1
- package/lib/algorithm/grid/index.js +29 -34
- package/lib/algorithm/grid/index.js.map +1 -1
- package/lib/algorithm/grid/types.d.ts +5 -24
- package/lib/algorithm/mds/index.js +1 -0
- package/lib/algorithm/mds/index.js.map +1 -1
- package/lib/algorithm/radial/index.js +8 -5
- package/lib/algorithm/radial/index.js.map +1 -1
- package/lib/algorithm/radial/radial-nonoverlap-force.js +2 -4
- package/lib/algorithm/radial/radial-nonoverlap-force.js.map +1 -1
- package/lib/algorithm/radial/types.d.ts +3 -16
- package/lib/algorithm/random/index.js +1 -0
- package/lib/algorithm/random/index.js.map +1 -1
- package/lib/algorithm/types.d.ts +16 -0
- package/lib/index.d.ts +5 -3
- package/lib/index.js +3 -1
- package/lib/index.js.map +1 -1
- package/lib/node_modules/@antv/expr/dist/index.esm.js +4 -0
- package/lib/node_modules/@antv/expr/dist/index.esm.js.map +1 -0
- package/lib/types/common.d.ts +17 -1
- package/lib/types/data.d.ts +1 -1
- package/lib/util/expr.d.ts +12 -0
- package/lib/util/expr.js +26 -0
- package/lib/util/expr.js.map +1 -0
- package/lib/util/format.d.ts +37 -0
- package/lib/util/format.js +61 -17
- package/lib/util/format.js.map +1 -1
- package/lib/util/order.d.ts +3 -4
- package/lib/util/order.js.map +1 -1
- package/lib/util/size.d.ts +2 -1
- package/lib/util/size.js +9 -1
- package/lib/util/size.js.map +1 -1
- package/lib/worker.js +283 -227
- package/lib/worker.js.map +1 -1
- package/package.json +2 -1
- package/src/algorithm/antv-dagre/index.ts +15 -12
- package/src/algorithm/antv-dagre/types.ts +3 -14
- package/src/algorithm/circular/index.ts +5 -4
- package/src/algorithm/circular/types.ts +0 -15
- package/src/algorithm/combo-combined/index.ts +21 -17
- package/src/algorithm/combo-combined/types.ts +6 -21
- package/src/algorithm/concentric/index.ts +18 -16
- package/src/algorithm/concentric/types.ts +2 -16
- package/src/algorithm/d3-force/index.ts +25 -18
- package/src/algorithm/d3-force/types.ts +9 -22
- package/src/algorithm/d3-force-3d/index.ts +1 -1
- package/src/algorithm/d3-force-3d/types.ts +5 -0
- package/src/algorithm/dagre/index.ts +9 -7
- package/src/algorithm/dagre/types.ts +7 -15
- package/src/algorithm/force/index.ts +64 -40
- package/src/algorithm/force/types.ts +76 -45
- package/src/algorithm/force-atlas2/index.ts +13 -15
- package/src/algorithm/force-atlas2/types.ts +0 -12
- package/src/algorithm/fruchterman/index.ts +7 -15
- package/src/algorithm/fruchterman/simulation.ts +2 -2
- package/src/algorithm/fruchterman/types.ts +5 -2
- package/src/algorithm/grid/index.ts +45 -46
- package/src/algorithm/grid/types.ts +6 -35
- package/src/algorithm/radial/index.ts +14 -6
- package/src/algorithm/radial/radial-nonoverlap-force.ts +11 -6
- package/src/algorithm/radial/types.ts +2 -15
- package/src/algorithm/types.ts +18 -0
- package/src/types/common.ts +22 -0
- package/src/types/data.ts +1 -1
- package/src/util/expr.ts +26 -0
- package/src/util/format.ts +71 -27
- package/src/util/index.ts +2 -0
- package/src/util/order.ts +3 -9
- package/src/util/size.ts +8 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { isBoolean, isNil, pick } from '@antv/util';
|
|
2
2
|
import dagre, { graphlib } from 'dagre';
|
|
3
|
-
import { BaseLayout } from '../base-layout';
|
|
4
3
|
import type { LayoutNode } from '../../types';
|
|
5
4
|
import { parsePoint, parseSize } from '../../util';
|
|
6
|
-
import { formatNumberFn, formatSizeFn } from '../../util/format';
|
|
5
|
+
import { formatFn, formatNumberFn, formatSizeFn } from '../../util/format';
|
|
6
|
+
import { BaseLayout } from '../base-layout';
|
|
7
7
|
import type { DagreLayoutOptions } from './types';
|
|
8
8
|
|
|
9
9
|
export type { DagreLayoutOptions };
|
|
@@ -98,12 +98,14 @@ export class DagreLayout extends BaseLayout<DagreLayoutOptions> {
|
|
|
98
98
|
edgeWeight,
|
|
99
99
|
} = this.options;
|
|
100
100
|
|
|
101
|
-
const edgeLabelSizeFn = formatSizeFn(edgeLabelSize, 0);
|
|
102
|
-
const edgeLabelOffsetFn = formatNumberFn(edgeLabelOffset, 10);
|
|
101
|
+
const edgeLabelSizeFn = formatSizeFn(edgeLabelSize, 0, 'edge');
|
|
102
|
+
const edgeLabelOffsetFn = formatNumberFn(edgeLabelOffset, 10, 'edge');
|
|
103
103
|
const edgeLabelPosFn =
|
|
104
|
-
typeof edgeLabelPos === '
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
typeof edgeLabelPos === 'string'
|
|
105
|
+
? () => edgeLabelPos
|
|
106
|
+
: formatFn(edgeLabelPos, ['edge']);
|
|
107
|
+
const edgeMinLenFn = formatNumberFn(edgeMinLen, 1, 'edge');
|
|
108
|
+
const edgeWeightFn = formatNumberFn(edgeWeight, 1, 'edge');
|
|
107
109
|
|
|
108
110
|
this.model.forEachEdge((edge) => {
|
|
109
111
|
const raw = edge._original;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { GraphLabel } from 'dagre';
|
|
2
|
-
import type {
|
|
2
|
+
import type { EdgeData, Expr, Size } from '../../types';
|
|
3
3
|
import type { EdgeLabelPos } from '../../types/edge-label';
|
|
4
|
-
import type {
|
|
4
|
+
import type { BaseLayoutOptions } from '../types';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* <zh/> 边标签位置:'l' 左侧,'c' 中心,'r' 右侧
|
|
@@ -38,47 +38,39 @@ export interface DagreLayoutOptions extends BaseLayoutOptions, GraphLabel {
|
|
|
38
38
|
*/
|
|
39
39
|
multigraph?: boolean;
|
|
40
40
|
|
|
41
|
-
/**
|
|
42
|
-
* <zh/> 定义节点占用的空间大小,影响节点间距和整体布局疏密
|
|
43
|
-
*
|
|
44
|
-
* <en/> Defines space occupied by nodes, affecting inter-node spacing and overall layout density
|
|
45
|
-
* @defaultValue [0, 0]
|
|
46
|
-
*/
|
|
47
|
-
nodeSize?: Size | ((d?: NodeData) => Size);
|
|
48
|
-
|
|
49
41
|
/**
|
|
50
42
|
* <zh/> 设置边跨越的最小层数,值越大节点间距越远,用于控制布局紧凑度
|
|
51
43
|
*
|
|
52
44
|
* <en/> Sets minimum number of layers an edge spans; larger values create more distance between nodes, controlling layout compactness
|
|
53
45
|
* @defaultValue 1
|
|
54
46
|
*/
|
|
55
|
-
edgeMinLen?: number | ((
|
|
47
|
+
edgeMinLen?: number | Expr | ((edge: EdgeData) => number);
|
|
56
48
|
|
|
57
49
|
/**
|
|
58
50
|
* <zh/> 边的权重,影响边的长度优化优先级,权重大的边倾向于更短
|
|
59
51
|
*
|
|
60
52
|
* <en/> Edge weight affecting length optimization priority; higher weight edges tend to be shorter
|
|
61
53
|
*/
|
|
62
|
-
edgeWeight?: number | ((
|
|
54
|
+
edgeWeight?: number | Expr | ((edge: EdgeData) => number);
|
|
63
55
|
|
|
64
56
|
/**
|
|
65
57
|
* <zh/> 边标签的尺寸,用于为标签预留空间,避免与节点重叠
|
|
66
58
|
*
|
|
67
59
|
* <en/> Size of edge labels for reserving space to prevent overlap with nodes
|
|
68
60
|
*/
|
|
69
|
-
edgeLabelSize?: Size | ((
|
|
61
|
+
edgeLabelSize?: Size | Expr | ((edge: EdgeData) => Size);
|
|
70
62
|
|
|
71
63
|
/**
|
|
72
64
|
* <zh/> 标签在边上的位置,控制标签相对于边的对齐方式
|
|
73
65
|
*
|
|
74
66
|
* <en/> Label position on edge, controlling label alignment relative to the edge
|
|
75
67
|
*/
|
|
76
|
-
edgeLabelPos?: EdgeLabelPos | ((
|
|
68
|
+
edgeLabelPos?: EdgeLabelPos | Expr | ((edge: EdgeData) => EdgeLabelPos);
|
|
77
69
|
|
|
78
70
|
/**
|
|
79
71
|
* <zh/> 标签与边的偏移距离,用于微调标签位置避免视觉重叠
|
|
80
72
|
*
|
|
81
73
|
* <en/> Offset distance between label and edge for fine-tuning label position to avoid visual overlap
|
|
82
74
|
*/
|
|
83
|
-
edgeLabelOffset?: number | ((
|
|
75
|
+
edgeLabelOffset?: number | Expr | ((edge: EdgeData) => number);
|
|
84
76
|
}
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import { isEmpty } from '@antv/util';
|
|
2
|
-
import {
|
|
2
|
+
import type { GraphLib } from '../../model/data';
|
|
3
|
+
import { initNodePosition } from '../../model/data';
|
|
3
4
|
import type { EdgeData, NodeData, Point, PointObject } from '../../types';
|
|
4
5
|
import { normalizeViewport } from '../../util';
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import { formatNodeSizeFn, formatNumberFn } from '../../util/format';
|
|
6
|
+
import { formatFn, formatNodeSizeFn, formatNumberFn } from '../../util/format';
|
|
7
|
+
import { BaseLayoutWithIterations } from '../base-layout';
|
|
8
8
|
import { forceAttractive } from './attractive';
|
|
9
9
|
import { forceCentripetal } from './centripetal';
|
|
10
10
|
import { forceCollide } from './collide';
|
|
11
11
|
import { forceGravity } from './gravity';
|
|
12
12
|
import { forceRepulsive } from './repulsive';
|
|
13
13
|
import { ForceSimulation } from './simulation';
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
ForceLayoutOptions,
|
|
16
|
+
GetCenterFn,
|
|
17
|
+
NodeClusterByFn,
|
|
18
|
+
ParsedForceLayoutOptions,
|
|
19
|
+
} from './types';
|
|
15
20
|
|
|
16
21
|
export type { ForceLayoutOptions };
|
|
17
22
|
|
|
@@ -251,31 +256,45 @@ export class ForceLayout extends BaseLayoutWithIterations<ForceLayoutOptions> {
|
|
|
251
256
|
...normalizeViewport(options),
|
|
252
257
|
} as ParsedForceLayoutOptions;
|
|
253
258
|
|
|
259
|
+
// Format nodeClusterBy (for clustering / leafCluster)
|
|
260
|
+
if (_.nodeClusterBy) {
|
|
261
|
+
_.nodeClusterBy = formatFn(_.nodeClusterBy, ['node']) as NodeClusterByFn;
|
|
262
|
+
}
|
|
263
|
+
|
|
254
264
|
// Format node mass
|
|
255
265
|
if (!options.getMass) {
|
|
256
|
-
_.getMass = (
|
|
257
|
-
if (!
|
|
266
|
+
_.getMass = (node: NodeData) => {
|
|
267
|
+
if (!node) return 1;
|
|
258
268
|
const massWeight = 1;
|
|
259
|
-
const degree = this.model.degree(
|
|
269
|
+
const degree = this.model.degree(node.id, 'both');
|
|
260
270
|
return !degree || degree < 5 ? massWeight : degree * 5 * massWeight;
|
|
261
271
|
};
|
|
272
|
+
} else {
|
|
273
|
+
_.getMass = formatNumberFn(options.getMass, 1);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Format per-node center force callback
|
|
277
|
+
if (options.getCenter) {
|
|
278
|
+
const params = ['node', 'degree'];
|
|
279
|
+
_.getCenter = formatFn(options.getCenter, params) as GetCenterFn;
|
|
262
280
|
}
|
|
263
281
|
|
|
264
282
|
// Format node size
|
|
265
|
-
|
|
283
|
+
const nodeSizeVec = formatNodeSizeFn(options.nodeSize, options.nodeSpacing);
|
|
284
|
+
_.nodeSize = (node: NodeData) => {
|
|
285
|
+
if (!node) return 0;
|
|
286
|
+
const [w, h, z] = nodeSizeVec(node);
|
|
287
|
+
return Math.max(w, h, z);
|
|
288
|
+
};
|
|
266
289
|
|
|
267
290
|
// Format node / edge strengths
|
|
268
291
|
_.linkDistance = options.linkDistance
|
|
269
|
-
?
|
|
270
|
-
: (
|
|
271
|
-
|
|
272
|
-
1 +
|
|
273
|
-
_.nodeSize(this.model.node(edge!.source)!._original) +
|
|
274
|
-
_.nodeSize(this.model.node(edge!.target)!._original)
|
|
275
|
-
);
|
|
276
|
-
};
|
|
292
|
+
? (formatFn(options.linkDistance, ['edge', 'source', 'target']) as any)
|
|
293
|
+
: (_: EdgeData, source: NodeData, target: NodeData) =>
|
|
294
|
+
1 + _.nodeSize(source) + _.nodeSize(target);
|
|
277
295
|
_.nodeStrength = formatNumberFn(options.nodeStrength, 1);
|
|
278
|
-
_.edgeStrength = formatNumberFn(options.edgeStrength, 1);
|
|
296
|
+
_.edgeStrength = formatNumberFn(options.edgeStrength, 1, 'edge');
|
|
297
|
+
_.clusterNodeStrength = formatNumberFn(options.clusterNodeStrength, 1);
|
|
279
298
|
|
|
280
299
|
// Format centripetal options
|
|
281
300
|
this.formatCentripetal(_);
|
|
@@ -291,28 +310,45 @@ export class ForceLayout extends BaseLayoutWithIterations<ForceLayoutOptions> {
|
|
|
291
310
|
dimensions,
|
|
292
311
|
centripetalOptions,
|
|
293
312
|
center,
|
|
294
|
-
clusterNodeStrength,
|
|
295
313
|
leafCluster,
|
|
296
314
|
clustering,
|
|
297
315
|
nodeClusterBy,
|
|
298
316
|
} = options;
|
|
299
317
|
|
|
300
|
-
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
318
|
+
const leafParams = ['node', 'nodes', 'edges'];
|
|
319
|
+
const leafFn = formatFn(centripetalOptions?.leaf, leafParams);
|
|
320
|
+
const singleFn = formatNumberFn(centripetalOptions?.single, 2);
|
|
321
|
+
const othersFn = formatNumberFn(centripetalOptions?.others, 1);
|
|
322
|
+
|
|
323
|
+
const centerRaw =
|
|
324
|
+
centripetalOptions?.center ??
|
|
325
|
+
((_: NodeData) => {
|
|
306
326
|
return {
|
|
307
327
|
x: center[0],
|
|
308
328
|
y: center[1],
|
|
309
329
|
z: dimensions === 3 ? center[2] : undefined,
|
|
310
330
|
};
|
|
311
|
-
}
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
const centerFn = formatFn(centerRaw, [
|
|
334
|
+
'node',
|
|
335
|
+
'nodes',
|
|
336
|
+
'edges',
|
|
337
|
+
'width',
|
|
338
|
+
'height',
|
|
339
|
+
]) as any;
|
|
340
|
+
|
|
341
|
+
const basicCentripetal = {
|
|
342
|
+
...centripetalOptions,
|
|
343
|
+
leaf: leafFn,
|
|
344
|
+
single: singleFn,
|
|
345
|
+
others: othersFn,
|
|
346
|
+
center: centerFn,
|
|
312
347
|
};
|
|
313
348
|
|
|
314
|
-
|
|
315
|
-
|
|
349
|
+
// If user provided centripetalOptions, normalize them even without clustering modes.
|
|
350
|
+
if (centripetalOptions) {
|
|
351
|
+
options.centripetalOptions = basicCentripetal as any;
|
|
316
352
|
}
|
|
317
353
|
|
|
318
354
|
let sameTypeLeafMap: any;
|
|
@@ -406,18 +442,6 @@ export class ForceLayout extends BaseLayoutWithIterations<ForceLayoutOptions> {
|
|
|
406
442
|
},
|
|
407
443
|
});
|
|
408
444
|
}
|
|
409
|
-
|
|
410
|
-
// Normalize functions
|
|
411
|
-
const { leaf, single, others } = options.centripetalOptions || {};
|
|
412
|
-
if (leaf && typeof leaf !== 'function') {
|
|
413
|
-
options.centripetalOptions.leaf = () => leaf;
|
|
414
|
-
}
|
|
415
|
-
if (single && typeof single !== 'function') {
|
|
416
|
-
options.centripetalOptions.single = () => single;
|
|
417
|
-
}
|
|
418
|
-
if (others && typeof others !== 'function') {
|
|
419
|
-
options.centripetalOptions.others = () => others;
|
|
420
|
-
}
|
|
421
445
|
}
|
|
422
446
|
|
|
423
447
|
/**
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
CommonForceLayoutOptions,
|
|
3
3
|
EdgeData,
|
|
4
|
+
Expr,
|
|
4
5
|
NodeData,
|
|
5
6
|
Point,
|
|
6
|
-
Size,
|
|
7
7
|
} from '../../types';
|
|
8
8
|
|
|
9
9
|
export type AccMap = { [id: string]: { x: number; y: number; z: number } };
|
|
@@ -25,6 +25,7 @@ export interface CentripetalOptions {
|
|
|
25
25
|
*/
|
|
26
26
|
leaf?:
|
|
27
27
|
| number
|
|
28
|
+
| Expr
|
|
28
29
|
| ((node: NodeData, nodes: NodeData[], edges: EdgeData[]) => number);
|
|
29
30
|
/**
|
|
30
31
|
* <zh/> 离散节点(即度数为 0 的节点)受到的向心力大小
|
|
@@ -35,7 +36,7 @@ export interface CentripetalOptions {
|
|
|
35
36
|
* - ((node: NodeData) => number): return different values according to the node situation
|
|
36
37
|
* @defaultValue 2
|
|
37
38
|
*/
|
|
38
|
-
single?: number | ((node: NodeData) => number);
|
|
39
|
+
single?: number | Expr | ((node: NodeData) => number);
|
|
39
40
|
/**
|
|
40
41
|
* <zh/> 除离散节点、叶子节点以外的其他节点(即度数 > 1 的节点)受到的向心力大小
|
|
41
42
|
* - number: 固定向心力大小
|
|
@@ -45,13 +46,35 @@ export interface CentripetalOptions {
|
|
|
45
46
|
* - ((node: NodeData) => number): return different values according to the node situation
|
|
46
47
|
* @defaultValue 1
|
|
47
48
|
*/
|
|
48
|
-
others?: number | ((node: NodeData) => number);
|
|
49
|
+
others?: number | Expr | ((node: NodeData) => number);
|
|
49
50
|
/**
|
|
50
51
|
* <zh/> 向心力发出的位置,可根据节点、边的情况返回不同的值、默认为图的中心
|
|
51
52
|
*
|
|
52
53
|
* <en/> The position where the centripetal force is emitted, which can return different values according to the node, edge, and situation. The default is the center of the graph
|
|
53
54
|
*/
|
|
54
|
-
center?:
|
|
55
|
+
center?:
|
|
56
|
+
| Expr
|
|
57
|
+
| ((
|
|
58
|
+
node: NodeData,
|
|
59
|
+
nodes: NodeData[],
|
|
60
|
+
edges: EdgeData[],
|
|
61
|
+
width: number,
|
|
62
|
+
height: number,
|
|
63
|
+
) => {
|
|
64
|
+
x: number;
|
|
65
|
+
y: number;
|
|
66
|
+
z?: number;
|
|
67
|
+
centerStrength?: number;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
interface FormatCentripetalOptions extends CentripetalOptions {
|
|
72
|
+
leaf: (node: NodeData, nodes: NodeData[], edges: EdgeData[]) => number;
|
|
73
|
+
/** Force strength for single nodes. */
|
|
74
|
+
single: (node: NodeData) => number;
|
|
75
|
+
/** Force strength for other nodes. */
|
|
76
|
+
others: (node: NodeData) => number;
|
|
77
|
+
center: (
|
|
55
78
|
node: NodeData,
|
|
56
79
|
nodes: NodeData[],
|
|
57
80
|
edges: EdgeData[],
|
|
@@ -65,41 +88,34 @@ export interface CentripetalOptions {
|
|
|
65
88
|
};
|
|
66
89
|
}
|
|
67
90
|
|
|
68
|
-
interface FormatCentripetalOptions extends CentripetalOptions {
|
|
69
|
-
leaf: (node: NodeData, nodes: NodeData[], edges: EdgeData[]) => number;
|
|
70
|
-
/** Force strength for single nodes. */
|
|
71
|
-
single: (node: NodeData) => number;
|
|
72
|
-
/** Force strength for other nodes. */
|
|
73
|
-
others: (node: NodeData) => number;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
91
|
export interface ForceLayoutOptions extends CommonForceLayoutOptions {
|
|
77
92
|
/**
|
|
78
93
|
* <zh/> 边的长度
|
|
79
94
|
* - number: 固定长度
|
|
80
|
-
* - ((edge
|
|
95
|
+
* - ((edge: EdgeData, source: NodeData, target: NodeData) => number): 根据边的信息返回长度
|
|
81
96
|
* <en/> The length of the edge
|
|
82
97
|
* - number: fixed length
|
|
83
|
-
* - ((edge
|
|
98
|
+
* - ((edge: EdgeData, source: NodeData, target: NodeData) => number): return length according to the edge information
|
|
84
99
|
* @defaultValue 200
|
|
85
100
|
*/
|
|
86
101
|
linkDistance?:
|
|
87
102
|
| number
|
|
88
|
-
|
|
|
103
|
+
| Expr
|
|
104
|
+
| ((edge: EdgeData, source: NodeData, target: NodeData) => number);
|
|
89
105
|
/**
|
|
90
106
|
* <zh/> 节点作用力,正数代表节点之间的引力作用,负数代表节点之间的斥力作用
|
|
91
107
|
*
|
|
92
108
|
* <en/> The force of the node, positive numbers represent the attraction force between nodes, and negative numbers represent the repulsion force between nodes
|
|
93
109
|
* @defaultValue 1000
|
|
94
110
|
*/
|
|
95
|
-
nodeStrength?: number | ((
|
|
111
|
+
nodeStrength?: number | Expr | ((node: NodeData) => number);
|
|
96
112
|
/**
|
|
97
113
|
* <zh/> 边的作用力(引力)大小
|
|
98
114
|
*
|
|
99
115
|
* <en/> The size of the force of the edge (attraction)
|
|
100
116
|
* @defaultValue 50
|
|
101
117
|
*/
|
|
102
|
-
edgeStrength?: number | ((
|
|
118
|
+
edgeStrength?: number | Expr | ((edge: EdgeData) => number);
|
|
103
119
|
/**
|
|
104
120
|
* <zh/> 是否防止重叠,必须配合下面属性 nodeSize 或节点数据中的 data.size 属性,只有在数据中设置了 data.size 或在该布局中配置了与当前图节点大小相同的 nodeSize 值,才能够进行节点重叠的碰撞检测
|
|
105
121
|
*
|
|
@@ -107,18 +123,6 @@ export interface ForceLayoutOptions extends CommonForceLayoutOptions {
|
|
|
107
123
|
* @defaultValue true
|
|
108
124
|
*/
|
|
109
125
|
preventOverlap?: boolean;
|
|
110
|
-
/**
|
|
111
|
-
* <zh/> 节点大小(直径)。用于防止节点重叠时的碰撞检测
|
|
112
|
-
*
|
|
113
|
-
* <en/> The size of the node (diameter). Used for collision detection when preventing node overlap
|
|
114
|
-
*/
|
|
115
|
-
nodeSize?: Size | ((d?: NodeData) => Size);
|
|
116
|
-
/**
|
|
117
|
-
* <zh/> preventOverlap 为 true 时生效, 防止重叠时节点边缘间距的最小值。可以是回调函数, 为不同节点设置不同的最小间距
|
|
118
|
-
*
|
|
119
|
-
* <en/> It is effective when preventOverlap is true. The minimum spacing of the node edge when preventing overlap. It can be a callback function to set different minimum spacing for different nodes
|
|
120
|
-
*/
|
|
121
|
-
nodeSpacing?: number | ((d?: NodeData) => number);
|
|
122
126
|
/**
|
|
123
127
|
* <zh/> 阻尼系数,取值范围 [0, 1]。数字越大,速度降低得越慢
|
|
124
128
|
*
|
|
@@ -194,14 +198,14 @@ export interface ForceLayoutOptions extends CommonForceLayoutOptions {
|
|
|
194
198
|
*
|
|
195
199
|
* <en/> Specify the field name of the node data as the clustering basis for the node, and it takes effect when clustering is true. You can combine it with clusterNodeStrength to use it
|
|
196
200
|
*/
|
|
197
|
-
nodeClusterBy?: (node: NodeData) => string;
|
|
201
|
+
nodeClusterBy?: Expr | ((node: NodeData) => string);
|
|
198
202
|
/**
|
|
199
203
|
* <zh/> 配合 clustering 和 nodeClusterBy 使用,指定聚类向心力的大小
|
|
200
204
|
*
|
|
201
205
|
* <en/> Use it with clustering and nodeClusterBy to specify the size of the centripetal force of the cluster
|
|
202
206
|
* @defaultValue 20
|
|
203
207
|
*/
|
|
204
|
-
clusterNodeStrength?: number | ((node: NodeData) => number);
|
|
208
|
+
clusterNodeStrength?: number | Expr | ((node: NodeData) => number);
|
|
205
209
|
/**
|
|
206
210
|
* <zh/> 防止重叠的力强度,范围 [0, 1]
|
|
207
211
|
*
|
|
@@ -216,7 +220,7 @@ export interface ForceLayoutOptions extends CommonForceLayoutOptions {
|
|
|
216
220
|
* @param node - <zh/> 节点数据 | <en/> NodeData data
|
|
217
221
|
* @returns <zh/> 节点质量大小 | <en/> Mass size of the node
|
|
218
222
|
*/
|
|
219
|
-
getMass?: (node
|
|
223
|
+
getMass?: Expr | ((node: NodeData) => number);
|
|
220
224
|
/**
|
|
221
225
|
* <zh/> 每个节点中心力的 x、y、强度的回调函数,若不指定,则没有额外中心力
|
|
222
226
|
*
|
|
@@ -225,7 +229,7 @@ export interface ForceLayoutOptions extends CommonForceLayoutOptions {
|
|
|
225
229
|
* @param degree - <zh/> 节点度数 | <en/> NodeData degree
|
|
226
230
|
* @returns <zh/> 中心力 x、y、强度 | <en/> Center force x、y、strength
|
|
227
231
|
*/
|
|
228
|
-
getCenter?: (node
|
|
232
|
+
getCenter?: Expr | ((node: NodeData, degree: number) => number[]);
|
|
229
233
|
/**
|
|
230
234
|
* <zh/> 每个迭代的监控信息回调,energy 表示布局的收敛能量。若配置可能带来额外的计算能量性能消耗,不配置则不计算
|
|
231
235
|
*
|
|
@@ -240,7 +244,18 @@ export interface ForceLayoutOptions extends CommonForceLayoutOptions {
|
|
|
240
244
|
}) => void;
|
|
241
245
|
}
|
|
242
246
|
|
|
243
|
-
export interface ParsedForceLayoutOptions
|
|
247
|
+
export interface ParsedForceLayoutOptions
|
|
248
|
+
extends Omit<
|
|
249
|
+
ForceLayoutOptions,
|
|
250
|
+
| 'centripetalOptions'
|
|
251
|
+
| 'nodeClusterBy'
|
|
252
|
+
| 'clusterNodeStrength'
|
|
253
|
+
| 'getMass'
|
|
254
|
+
| 'getCenter'
|
|
255
|
+
| 'nodeStrength'
|
|
256
|
+
| 'edgeStrength'
|
|
257
|
+
| 'linkDistance'
|
|
258
|
+
> {
|
|
244
259
|
width: number;
|
|
245
260
|
height: number;
|
|
246
261
|
center: Point;
|
|
@@ -251,15 +266,31 @@ export interface ParsedForceLayoutOptions extends ForceLayoutOptions {
|
|
|
251
266
|
damping: number;
|
|
252
267
|
maxSpeed: number;
|
|
253
268
|
coulombDisScale: number;
|
|
254
|
-
centripetalOptions
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
) => number;
|
|
264
|
-
clusterNodeStrength: (node?: NodeData) => number;
|
|
269
|
+
centripetalOptions?: FormatCentripetalOptions;
|
|
270
|
+
nodeClusterBy?: NodeClusterByFn;
|
|
271
|
+
getCenter?: GetCenterFn;
|
|
272
|
+
nodeSize: NodeSizeFn;
|
|
273
|
+
getMass: GetMassFn;
|
|
274
|
+
nodeStrength: NodeStrengthFn;
|
|
275
|
+
edgeStrength: EdgeStrengthFn;
|
|
276
|
+
linkDistance: LinkDistanceFn;
|
|
277
|
+
clusterNodeStrength: NodeStrengthFn;
|
|
265
278
|
}
|
|
279
|
+
|
|
280
|
+
export type NodeClusterByFn = (node: NodeData) => string;
|
|
281
|
+
|
|
282
|
+
export type GetCenterFn = (node: NodeData, degree: number) => number[];
|
|
283
|
+
|
|
284
|
+
export type NodeSizeFn = (node: NodeData) => number;
|
|
285
|
+
|
|
286
|
+
export type GetMassFn = (node: NodeData) => number;
|
|
287
|
+
|
|
288
|
+
export type NodeStrengthFn = (node: NodeData) => number;
|
|
289
|
+
|
|
290
|
+
export type EdgeStrengthFn = (edge: EdgeData) => number;
|
|
291
|
+
|
|
292
|
+
export type LinkDistanceFn = (
|
|
293
|
+
edge: EdgeData,
|
|
294
|
+
source: NodeData,
|
|
295
|
+
target: NodeData,
|
|
296
|
+
) => number;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { initNodePosition } from '../../model/data';
|
|
2
2
|
import type { ID, NullablePosition } from '../../types';
|
|
3
3
|
import { normalizeViewport } from '../../util';
|
|
4
|
-
import { initNodePosition } from '../../model/data';
|
|
5
4
|
import { applySingleNodeLayout } from '../../util/common';
|
|
6
|
-
import { formatNodeSizeFn
|
|
5
|
+
import { formatNodeSizeFn } from '../../util/format';
|
|
6
|
+
import { BaseLayoutWithIterations } from '../base-layout';
|
|
7
7
|
import { Simulation } from './simulation';
|
|
8
8
|
import type {
|
|
9
9
|
ForceAtlas2LayoutOptions,
|
|
@@ -97,9 +97,16 @@ export class ForceAtlas2Layout extends BaseLayoutWithIterations<ForceAtlas2Layou
|
|
|
97
97
|
nodeSpacing?: ForceAtlas2LayoutOptions['nodeSpacing'],
|
|
98
98
|
): SizeMap {
|
|
99
99
|
const result: SizeMap = {};
|
|
100
|
+
|
|
101
|
+
const nodeSizeFn = formatNodeSizeFn(
|
|
102
|
+
nodeSize,
|
|
103
|
+
nodeSpacing,
|
|
104
|
+
DEFAULTS_LAYOUT_OPTIONS.nodeSize as number,
|
|
105
|
+
DEFAULTS_LAYOUT_OPTIONS.nodeSpacing as number,
|
|
106
|
+
);
|
|
107
|
+
|
|
100
108
|
this.model.forEachNode((node) => {
|
|
101
|
-
|
|
102
|
-
result[node.id] = nodeSizeFn(node._original);
|
|
109
|
+
result[node.id] = Math.max(...nodeSizeFn(node._original!));
|
|
103
110
|
});
|
|
104
111
|
return result;
|
|
105
112
|
}
|
|
@@ -119,8 +126,7 @@ export class ForceAtlas2Layout extends BaseLayoutWithIterations<ForceAtlas2Layou
|
|
|
119
126
|
private parseOptions(
|
|
120
127
|
options: ForceAtlas2LayoutOptions = {},
|
|
121
128
|
): ParsedForceAtlas2LayoutOptions {
|
|
122
|
-
const { barnesHut, prune, maxIteration, kr, kg
|
|
123
|
-
options;
|
|
129
|
+
const { barnesHut, prune, maxIteration, kr, kg } = options;
|
|
124
130
|
const auto: Partial<ForceAtlas2LayoutOptions> = {};
|
|
125
131
|
|
|
126
132
|
const n = this.model.nodeCount();
|
|
@@ -153,14 +159,6 @@ export class ForceAtlas2Layout extends BaseLayoutWithIterations<ForceAtlas2Layou
|
|
|
153
159
|
...options,
|
|
154
160
|
...auto,
|
|
155
161
|
...normalizeViewport(options),
|
|
156
|
-
nodeSize: formatSizeFn(
|
|
157
|
-
nodeSize,
|
|
158
|
-
DEFAULTS_LAYOUT_OPTIONS.nodeSize as number,
|
|
159
|
-
),
|
|
160
|
-
nodeSpacing: formatNumberFn(
|
|
161
|
-
nodeSpacing,
|
|
162
|
-
DEFAULTS_LAYOUT_OPTIONS.nodeSpacing as number,
|
|
163
|
-
),
|
|
164
162
|
} as ParsedForceAtlas2LayoutOptions;
|
|
165
163
|
}
|
|
166
164
|
|
|
@@ -89,18 +89,6 @@ export interface ForceAtlas2LayoutOptions extends CommonForceLayoutOptions {
|
|
|
89
89
|
* <en/> By default, it will be activated when the number of nodes is greater than 100. Note that pruning can improve the convergence speed, but it may reduce the layout quality of the graph. Setting it to false will not be activated automatically
|
|
90
90
|
*/
|
|
91
91
|
prune?: boolean;
|
|
92
|
-
/**
|
|
93
|
-
* <zh/> 节点大小(直径)。用于防止节点重叠时的碰撞检测
|
|
94
|
-
*
|
|
95
|
-
* <en/> Node size (diameter). Used for collision detection when preventing node overlap
|
|
96
|
-
*/
|
|
97
|
-
nodeSize?: Size | ((d?: NodeData) => Size);
|
|
98
|
-
/**
|
|
99
|
-
* <zh/> 节点间距。用于防止节点重叠时的碰撞检测
|
|
100
|
-
*
|
|
101
|
-
* <en/> Node spacing. Used for collision detection when preventing node overlap
|
|
102
|
-
*/
|
|
103
|
-
nodeSpacing?: number | ((d?: NodeData) => number);
|
|
104
92
|
}
|
|
105
93
|
|
|
106
94
|
export type ParsedForceAtlas2LayoutOptions = Required<ForceAtlas2LayoutOptions>;
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import { BaseLayoutWithIterations } from '../base-layout';
|
|
2
|
-
import type { ID, NodeData, NullablePosition } from '../../types';
|
|
3
|
-
import {
|
|
4
|
-
applySingleNodeLayout,
|
|
5
|
-
getNestedValue,
|
|
6
|
-
normalizeViewport,
|
|
7
|
-
} from '../../util';
|
|
8
1
|
import { initNodePosition } from '../../model/data';
|
|
2
|
+
import type { ID, NullablePosition } from '../../types';
|
|
3
|
+
import { applySingleNodeLayout, formatFn, normalizeViewport } from '../../util';
|
|
4
|
+
import { BaseLayoutWithIterations } from '../base-layout';
|
|
9
5
|
import { Simulation } from './simulation';
|
|
10
6
|
import type {
|
|
11
7
|
FruchtermanLayoutOptions,
|
|
@@ -22,7 +18,7 @@ const DEFAULTS_LAYOUT_OPTIONS: Partial<FruchtermanLayoutOptions> = {
|
|
|
22
18
|
clusterGravity: 10,
|
|
23
19
|
width: 300,
|
|
24
20
|
height: 300,
|
|
25
|
-
nodeClusterBy: '
|
|
21
|
+
nodeClusterBy: 'node.cluster',
|
|
26
22
|
dimensions: 2,
|
|
27
23
|
};
|
|
28
24
|
|
|
@@ -36,18 +32,14 @@ export class FruchtermanLayout extends BaseLayoutWithIterations<FruchtermanLayou
|
|
|
36
32
|
}
|
|
37
33
|
|
|
38
34
|
protected parseOptions(
|
|
39
|
-
options
|
|
35
|
+
options: Partial<FruchtermanLayoutOptions> = {},
|
|
40
36
|
): ParsedFruchtermanLayoutOptions {
|
|
41
37
|
const { clustering, nodeClusterBy } = this.options;
|
|
42
38
|
const clusteringEnabled = clustering && !!nodeClusterBy;
|
|
43
|
-
const nodeClusterByFunc =
|
|
44
|
-
typeof nodeClusterBy === 'string'
|
|
45
|
-
? (node: NodeData) => getNestedValue(node, nodeClusterBy)
|
|
46
|
-
: nodeClusterBy!;
|
|
47
39
|
|
|
48
|
-
Object.assign(
|
|
40
|
+
Object.assign(options, normalizeViewport(options), {
|
|
49
41
|
clustering: clusteringEnabled,
|
|
50
|
-
nodeClusterBy:
|
|
42
|
+
nodeClusterBy: formatFn(nodeClusterBy, ['node']),
|
|
51
43
|
});
|
|
52
44
|
|
|
53
45
|
return options as ParsedFruchtermanLayoutOptions;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isNil } from '@antv/util';
|
|
2
|
-
import {
|
|
2
|
+
import type { GraphLib } from '../../model/data';
|
|
3
3
|
import type {
|
|
4
4
|
DisplacementMap,
|
|
5
5
|
ID,
|
|
@@ -8,7 +8,7 @@ import type {
|
|
|
8
8
|
NullablePosition,
|
|
9
9
|
} from '../../types';
|
|
10
10
|
import { normalizeViewport } from '../../util';
|
|
11
|
-
import
|
|
11
|
+
import { BaseSimulation } from '../base-simulation';
|
|
12
12
|
import type {
|
|
13
13
|
FruchtermanLayoutOptions,
|
|
14
14
|
ParsedFruchtermanLayoutOptions,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CommonForceLayoutOptions, NodeData } from '../../types';
|
|
1
|
+
import type { CommonForceLayoutOptions, Expr, NodeData } from '../../types';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* <zh/> Fruchterman 力导布局配置项
|
|
@@ -13,6 +13,7 @@ export interface FruchtermanLayoutOptions extends CommonForceLayoutOptions {
|
|
|
13
13
|
* @defaultValue 10
|
|
14
14
|
*/
|
|
15
15
|
gravity?: number;
|
|
16
|
+
|
|
16
17
|
/**
|
|
17
18
|
* <zh/> 每次迭代节点移动的速度。速度太快可能会导致强烈震荡
|
|
18
19
|
*
|
|
@@ -20,6 +21,7 @@ export interface FruchtermanLayoutOptions extends CommonForceLayoutOptions {
|
|
|
20
21
|
* @defaultValue 5
|
|
21
22
|
*/
|
|
22
23
|
speed?: number;
|
|
24
|
+
|
|
23
25
|
/**
|
|
24
26
|
* <zh/> 是否按照聚类布局
|
|
25
27
|
*
|
|
@@ -27,6 +29,7 @@ export interface FruchtermanLayoutOptions extends CommonForceLayoutOptions {
|
|
|
27
29
|
* @defaultValue false
|
|
28
30
|
*/
|
|
29
31
|
clustering?: boolean;
|
|
32
|
+
|
|
30
33
|
/**
|
|
31
34
|
* <zh/> 聚类内部的重力大小,影响聚类的紧凑程度,在 clustering 为 true 时生效
|
|
32
35
|
*
|
|
@@ -41,7 +44,7 @@ export interface FruchtermanLayoutOptions extends CommonForceLayoutOptions {
|
|
|
41
44
|
* <en/> The field name of the node data in the data, which is used when cluster is true
|
|
42
45
|
* @defaultValue 'cluster'
|
|
43
46
|
*/
|
|
44
|
-
nodeClusterBy?:
|
|
47
|
+
nodeClusterBy?: Expr | ((node: NodeData) => string);
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
export type ParsedFruchtermanLayoutOptions = Required<FruchtermanLayoutOptions>;
|