@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.
Files changed (110) hide show
  1. package/dist/index.js +296 -209
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.min.js +2 -2
  4. package/dist/index.min.js.map +1 -1
  5. package/dist/worker.js +1 -1
  6. package/dist/worker.js.map +1 -1
  7. package/lib/algorithm/antv-dagre/index.js +8 -7
  8. package/lib/algorithm/antv-dagre/index.js.map +1 -1
  9. package/lib/algorithm/antv-dagre/types.d.ts +3 -14
  10. package/lib/algorithm/circular/index.js +8 -7
  11. package/lib/algorithm/circular/index.js.map +1 -1
  12. package/lib/algorithm/circular/types.d.ts +0 -16
  13. package/lib/algorithm/combo-combined/index.js +9 -7
  14. package/lib/algorithm/combo-combined/index.js.map +1 -1
  15. package/lib/algorithm/combo-combined/types.d.ts +6 -18
  16. package/lib/algorithm/concentric/index.js +10 -16
  17. package/lib/algorithm/concentric/index.js.map +1 -1
  18. package/lib/algorithm/concentric/types.d.ts +3 -17
  19. package/lib/algorithm/d3-force/index.js +16 -12
  20. package/lib/algorithm/d3-force/index.js.map +1 -1
  21. package/lib/algorithm/d3-force/types.d.ts +10 -24
  22. package/lib/algorithm/d3-force-3d/index.js +1 -1
  23. package/lib/algorithm/d3-force-3d/index.js.map +1 -1
  24. package/lib/algorithm/d3-force-3d/types.d.ts +5 -4
  25. package/lib/algorithm/dagre/index.js +10 -7
  26. package/lib/algorithm/dagre/index.js.map +1 -1
  27. package/lib/algorithm/dagre/types.d.ts +8 -14
  28. package/lib/algorithm/force/index.d.ts +1 -1
  29. package/lib/algorithm/force/index.js +56 -44
  30. package/lib/algorithm/force/index.js.map +1 -1
  31. package/lib/algorithm/force/types.d.ts +39 -36
  32. package/lib/algorithm/force-atlas2/index.d.ts +1 -1
  33. package/lib/algorithm/force-atlas2/index.js +8 -7
  34. package/lib/algorithm/force-atlas2/index.js.map +1 -1
  35. package/lib/algorithm/force-atlas2/types.d.ts +0 -14
  36. package/lib/algorithm/fruchterman/index.d.ts +1 -1
  37. package/lib/algorithm/fruchterman/index.js +8 -10
  38. package/lib/algorithm/fruchterman/index.js.map +1 -1
  39. package/lib/algorithm/fruchterman/simulation.js +2 -1
  40. package/lib/algorithm/fruchterman/simulation.js.map +1 -1
  41. package/lib/algorithm/fruchterman/types.d.ts +2 -1
  42. package/lib/algorithm/grid/index.d.ts +1 -1
  43. package/lib/algorithm/grid/index.js +29 -34
  44. package/lib/algorithm/grid/index.js.map +1 -1
  45. package/lib/algorithm/grid/types.d.ts +5 -24
  46. package/lib/algorithm/mds/index.js +1 -0
  47. package/lib/algorithm/mds/index.js.map +1 -1
  48. package/lib/algorithm/radial/index.js +8 -5
  49. package/lib/algorithm/radial/index.js.map +1 -1
  50. package/lib/algorithm/radial/radial-nonoverlap-force.js +2 -4
  51. package/lib/algorithm/radial/radial-nonoverlap-force.js.map +1 -1
  52. package/lib/algorithm/radial/types.d.ts +3 -16
  53. package/lib/algorithm/random/index.js +1 -0
  54. package/lib/algorithm/random/index.js.map +1 -1
  55. package/lib/algorithm/types.d.ts +16 -0
  56. package/lib/index.d.ts +5 -3
  57. package/lib/index.js +3 -1
  58. package/lib/index.js.map +1 -1
  59. package/lib/node_modules/@antv/expr/dist/index.esm.js +4 -0
  60. package/lib/node_modules/@antv/expr/dist/index.esm.js.map +1 -0
  61. package/lib/types/common.d.ts +17 -1
  62. package/lib/types/data.d.ts +1 -1
  63. package/lib/util/expr.d.ts +12 -0
  64. package/lib/util/expr.js +26 -0
  65. package/lib/util/expr.js.map +1 -0
  66. package/lib/util/format.d.ts +37 -0
  67. package/lib/util/format.js +61 -17
  68. package/lib/util/format.js.map +1 -1
  69. package/lib/util/order.d.ts +3 -4
  70. package/lib/util/order.js.map +1 -1
  71. package/lib/util/size.d.ts +2 -1
  72. package/lib/util/size.js +9 -1
  73. package/lib/util/size.js.map +1 -1
  74. package/lib/worker.js +283 -227
  75. package/lib/worker.js.map +1 -1
  76. package/package.json +2 -1
  77. package/src/algorithm/antv-dagre/index.ts +15 -12
  78. package/src/algorithm/antv-dagre/types.ts +3 -14
  79. package/src/algorithm/circular/index.ts +5 -4
  80. package/src/algorithm/circular/types.ts +0 -15
  81. package/src/algorithm/combo-combined/index.ts +21 -17
  82. package/src/algorithm/combo-combined/types.ts +6 -21
  83. package/src/algorithm/concentric/index.ts +18 -16
  84. package/src/algorithm/concentric/types.ts +2 -16
  85. package/src/algorithm/d3-force/index.ts +25 -18
  86. package/src/algorithm/d3-force/types.ts +9 -22
  87. package/src/algorithm/d3-force-3d/index.ts +1 -1
  88. package/src/algorithm/d3-force-3d/types.ts +5 -0
  89. package/src/algorithm/dagre/index.ts +9 -7
  90. package/src/algorithm/dagre/types.ts +7 -15
  91. package/src/algorithm/force/index.ts +64 -40
  92. package/src/algorithm/force/types.ts +76 -45
  93. package/src/algorithm/force-atlas2/index.ts +13 -15
  94. package/src/algorithm/force-atlas2/types.ts +0 -12
  95. package/src/algorithm/fruchterman/index.ts +7 -15
  96. package/src/algorithm/fruchterman/simulation.ts +2 -2
  97. package/src/algorithm/fruchterman/types.ts +5 -2
  98. package/src/algorithm/grid/index.ts +45 -46
  99. package/src/algorithm/grid/types.ts +6 -35
  100. package/src/algorithm/radial/index.ts +14 -6
  101. package/src/algorithm/radial/radial-nonoverlap-force.ts +11 -6
  102. package/src/algorithm/radial/types.ts +2 -15
  103. package/src/algorithm/types.ts +18 -0
  104. package/src/types/common.ts +22 -0
  105. package/src/types/data.ts +1 -1
  106. package/src/util/expr.ts +26 -0
  107. package/src/util/format.ts +71 -27
  108. package/src/util/index.ts +2 -0
  109. package/src/util/order.ts +3 -9
  110. 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 === 'function' ? edgeLabelPos : () => edgeLabelPos;
105
- const edgeMinLenFn = formatNumberFn(edgeMinLen, 1);
106
- const edgeWeightFn = formatNumberFn(edgeWeight, 1);
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 { BaseLayoutOptions } from '../types';
2
+ import type { EdgeData, Expr, Size } from '../../types';
3
3
  import type { EdgeLabelPos } from '../../types/edge-label';
4
- import type { NodeData, Size } from '../../types';
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 | ((d?: NodeData) => 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 | ((d?: NodeData) => 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 | ((d?: NodeData) => 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 | ((d?: NodeData) => 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 | ((d?: NodeData) => number);
75
+ edgeLabelOffset?: number | Expr | ((edge: EdgeData) => number);
84
76
  }
@@ -1,17 +1,22 @@
1
1
  import { isEmpty } from '@antv/util';
2
- import { BaseLayoutWithIterations } from '../base-layout';
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 { initNodePosition } from '../../model/data';
6
- import type { GraphLib } from '../../model/data';
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 { ForceLayoutOptions, ParsedForceLayoutOptions } from './types';
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 = (d?: NodeData) => {
257
- if (!d) return 1;
266
+ _.getMass = (node: NodeData) => {
267
+ if (!node) return 1;
258
268
  const massWeight = 1;
259
- const degree = this.model.degree(d.id, 'both');
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
- _.nodeSize = formatNodeSizeFn(options.nodeSize, options.nodeSpacing);
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
- ? formatNumberFn(options.linkDistance, 1)
270
- : (edge?: EdgeData) => {
271
- return (
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
- // Basic centripetal settings
301
- const basicCentripetal = centripetalOptions || {
302
- leaf: 2,
303
- single: 2,
304
- others: 1,
305
- center: (_: NodeData) => {
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
- if (typeof clusterNodeStrength !== 'function') {
315
- options.clusterNodeStrength = () => clusterNodeStrength as number;
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?: EdgeData, source?: any, target?: any) => number): 根据边的信息返回长度
95
+ * - ((edge: EdgeData, source: NodeData, target: NodeData) => number): 根据边的信息返回长度
81
96
  * <en/> The length of the edge
82
97
  * - number: fixed length
83
- * - ((edge?: EdgeData, source?: any, target?: any) => number): return length according to the edge information
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
- | ((edge?: EdgeData, source?: any, target?: any) => number);
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 | ((d?: NodeData) => 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 | ((d?: EdgeData) => 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?: NodeData) => number;
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?: NodeData, degree?: number) => number[];
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 extends ForceLayoutOptions {
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: FormatCentripetalOptions;
255
- nodeSize: (d?: NodeData) => number;
256
- getMass: (d?: NodeData) => number;
257
- nodeStrength: (d?: NodeData) => number;
258
- edgeStrength: (d?: EdgeData) => number;
259
- linkDistance: (
260
- edge?: EdgeData,
261
- source?: NodeData,
262
- target?: NodeData,
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 { BaseLayoutWithIterations } from '../base-layout';
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, formatNumberFn, formatSizeFn } from '../../util/format';
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
- const nodeSizeFn = formatNodeSizeFn(nodeSize, nodeSpacing);
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, nodeSize, nodeSpacing } =
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: 'data.cluster',
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?: Partial<FruchtermanLayoutOptions>,
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((options ||= {}), normalizeViewport(options), {
40
+ Object.assign(options, normalizeViewport(options), {
49
41
  clustering: clusteringEnabled,
50
- nodeClusterBy: nodeClusterByFunc,
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 { BaseSimulation } from '../base-simulation';
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 type { GraphLib } from '../../model/data';
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?: string | ((node: NodeData) => string);
47
+ nodeClusterBy?: Expr | ((node: NodeData) => string);
45
48
  }
46
49
 
47
50
  export type ParsedFruchtermanLayoutOptions = Required<FruchtermanLayoutOptions>;