@antv/layout 2.0.0-alpha.2 → 2.0.0-alpha.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antv/layout",
3
- "version": "2.0.0-alpha.2",
3
+ "version": "2.0.0-alpha.3",
4
4
  "description": "graph layout algorithm",
5
5
  "main": "dist/index.min.js",
6
6
  "module": "lib/index.js",
@@ -1,9 +1,9 @@
1
1
  import { isNumber } from '@antv/util';
2
- import { BaseLayout } from '../base-layout';
3
2
  import type { NodeData, PointObject } from '../../types';
4
3
  import { parsePoint } from '../../util';
5
4
  import { formatNumberFn, formatSizeFn } from '../../util/format';
6
5
  import { parseSize } from '../../util/size';
6
+ import { BaseLayout } from '../base-layout';
7
7
  import { DagreGraph, GraphNode } from './graph';
8
8
  import { layout } from './layout';
9
9
  import { AntVDagreLayoutOptions } from './types';
@@ -50,18 +50,15 @@ export class AntVDagreLayout extends BaseLayout<AntVDagreLayoutOptions> {
50
50
  sortByCombo,
51
51
  // focusNode,
52
52
  preset,
53
+ ranksepFunc,
54
+ nodesepFunc,
53
55
  } = options;
54
56
 
55
- const ranksepfunc = formatNumberFn(
56
- ranksep,
57
- DEFAULTS_LAYOUT_OPTIONS.ranksep as number,
58
- );
59
- const nodesepfunc = formatNumberFn(
60
- nodesep,
61
- DEFAULTS_LAYOUT_OPTIONS.nodesep as number,
62
- );
57
+ const ranksepfunc = formatNumberFn(ranksepFunc, ranksep ?? 50);
58
+ const nodesepfunc = formatNumberFn(nodesepFunc, nodesep ?? 50);
63
59
  let horisep: (d?: NodeData | undefined) => number = nodesepfunc;
64
60
  let vertisep: (d?: NodeData | undefined) => number = ranksepfunc;
61
+
65
62
  if (rankdir === 'LR' || rankdir === 'RL') {
66
63
  horisep = ranksepfunc;
67
64
  vertisep = nodesepfunc;
@@ -80,9 +77,10 @@ export class AntVDagreLayout extends BaseLayout<AntVDagreLayoutOptions> {
80
77
  const edges = this.model.edges();
81
78
 
82
79
  nodes.forEach((node) => {
83
- const size = parseSize(nodeSizeFunc(node));
84
- const verti = vertisep(node);
85
- const hori = horisep(node);
80
+ const raw = node._original;
81
+ const size = parseSize(nodeSizeFunc(raw));
82
+ const verti = vertisep(raw);
83
+ const hori = horisep(raw);
86
84
  const width = size[0] + 2 * hori;
87
85
  const height = size[1] + 2 * verti;
88
86
  const layer = node.data?.layer;
@@ -150,6 +148,7 @@ export class AntVDagreLayout extends BaseLayout<AntVDagreLayoutOptions> {
150
148
  acyclicer: 'greedy',
151
149
  ranker,
152
150
  rankdir,
151
+ nodesep,
153
152
  align,
154
153
  });
155
154
 
@@ -1,5 +1,5 @@
1
- import { BaseLayoutOptions } from '../base-layout';
2
1
  import { ID, NodeData, Point, Size } from '../../types';
2
+ import { BaseLayoutOptions } from '../base-layout';
3
3
 
4
4
  export type DagreRankdir =
5
5
  | 'TB'
@@ -83,7 +83,7 @@ export interface AntVDagreLayoutOptions extends BaseLayoutOptions {
83
83
  * <en/> The horizontal gap between nodes (px) in the case of rankdir is 'TB' or 'BT'. The vertical gap between nodes (px) in the case of rankdir is 'LR' or 'RL'. nodesepFunc has a higher priority
84
84
  * @defaultValue 50
85
85
  */
86
- nodesep?: number | ((d?: NodeData) => number);
86
+ nodesep?: number;
87
87
  /**
88
88
  * <zh/> 层间距(px)
89
89
  *
@@ -94,7 +94,29 @@ export interface AntVDagreLayoutOptions extends BaseLayoutOptions {
94
94
  * <en/> The vertical gap between levels (px) in the case of rankdir is 'TB' or 'BT'. The horizontal gap between levels (px) in the case of rankdir is 'LR' or 'RL'. ranksepFunc has a higher priority
95
95
  * @defaultValue 50
96
96
  */
97
- ranksep?: number | ((d?: NodeData) => number);
97
+ ranksep?: number;
98
+ /**
99
+ * <zh/> 节点间距(px)的回调函数,通过该参数可以对不同节点设置不同的节点间距
100
+ *
101
+ * <en/> The callback function of the node spacing (px), which can be used to set different node spacing for different nodes
102
+ * @remarks
103
+ * <zh/> 在 rankdir 为 'TB' 或 'BT' 时是节点的水平间距;在 rankdir 为 'LR' 或 'RL' 时代表节点的竖直方向间距。优先级高于 nodesep,即若设置了 nodesepFunc,则 nodesep 不生效
104
+ *
105
+ * <en/> The horizontal spacing of the node in the case of rankdir is 'TB' or 'BT', and the vertical spacing of the node in the case of rankdir is 'LR' or 'RL'. The priority is higher than nodesep, that is, if nodesepFunc is set, nodesep does not take effect
106
+ * @param d - <zh/> 节点实例 | <en/> Node instance
107
+ */
108
+ nodesepFunc?: (d?: NodeData) => number;
109
+ /**
110
+ * <zh/> 层间距(px)的回调函数
111
+ *
112
+ * <en/> The callback function of the layer spacing (px)
113
+ * @remarks
114
+ * <zh/> 在 rankdir 为 'TB' 或 'BT' 时是竖直方向相邻层间距;在 rankdir 为 'LR' 或 'RL' 时代表水平方向相邻层间距。优先级高于 nodesep,即若设置了 nodesepFunc,则 nodesep 不生效
115
+ *
116
+ * <en/> The vertical spacing of adjacent layers in the case of rankdir is 'TB' or 'BT', and the horizontal spacing of adjacent layers in the case of rankdir is 'LR' or 'RL'. The priority is higher than nodesep, that is, if nodesepFunc is set, nodesep does not take effect
117
+ * @param d - <zh/> 节点实例 | <en/> Node instance
118
+ */
119
+ ranksepFunc?: (d?: NodeData) => number;
98
120
  /**
99
121
  * <zh/> 是否同时计算边上的的控制点位置
100
122
  *
@@ -2,12 +2,7 @@ import type { GraphLib } from '../model/data';
2
2
  import { RuntimeContext } from '../runtime/context';
3
3
  import { Supervisor } from '../runtime/supervisor';
4
4
  import type { GraphData, GraphEdge, GraphNode, Point } from '../types';
5
- import { mergeOptions } from '../util';
6
- import type {
7
- BaseLayoutOptions,
8
- Layout,
9
- LayoutWithIterations,
10
- } from './types';
5
+ import type { BaseLayoutOptions, Layout, LayoutWithIterations } from './types';
11
6
 
12
7
  export type { BaseLayoutOptions };
13
8
 
@@ -35,18 +30,28 @@ export abstract class BaseLayout<
35
30
  protected supervisor: Supervisor | null = null;
36
31
 
37
32
  constructor(options?: Partial<O>) {
38
- this.initialOptions = mergeOptions<O>(this.getDefaultOptions(), options);
33
+ this.initialOptions = this.mergeOptions<O>(
34
+ this.getDefaultOptions(),
35
+ options,
36
+ );
39
37
  }
40
38
 
41
39
  get options(): O {
42
40
  return this.runtimeOptions || this.initialOptions;
43
41
  }
44
42
 
43
+ protected mergeOptions<O>(base: O, patch?: Partial<O>): O {
44
+ return Object.assign({}, base, patch || {});
45
+ }
46
+
45
47
  public async execute(
46
48
  data: GraphData,
47
49
  userOptions?: Partial<O>,
48
50
  ): Promise<void> {
49
- this.runtimeOptions = mergeOptions<O>(this.initialOptions, userOptions);
51
+ this.runtimeOptions = this.mergeOptions<O>(
52
+ this.initialOptions,
53
+ userOptions,
54
+ );
50
55
  const { node, edge, enableWorker } = this.runtimeOptions;
51
56
 
52
57
  this.context = new RuntimeContext(data, { node, edge });
@@ -10,10 +10,10 @@ import {
10
10
  forceX,
11
11
  forceY,
12
12
  } from 'd3-force';
13
- import { BaseLayoutWithIterations } from '../base-layout';
14
13
  import type { ID, Position } from '../../types';
15
14
  import { assignDefined, normalizeViewport } from '../../util';
16
15
  import { formatNodeSizeFn } from '../../util/format';
16
+ import { BaseLayoutWithIterations } from '../base-layout';
17
17
  import forceInABox from './force-in-a-box';
18
18
  import type {
19
19
  D3ForceCommonOptions,
@@ -25,29 +25,20 @@ import type {
25
25
  export type { D3ForceLayoutOptions };
26
26
 
27
27
  const DEFAULTS_LAYOUT_OPTIONS: Partial<D3ForceLayoutOptions> = {
28
- centerStrength: 1,
28
+ link: {
29
+ id: (d) => String(d.id),
30
+ },
29
31
 
30
- edgeId: (d) => String(d.id),
31
- linkDistance: 30,
32
- edgeStrength: undefined,
33
- edgeIterations: 1,
32
+ manyBody: {
33
+ strength: -30,
34
+ },
34
35
 
35
36
  preventOverlap: false,
36
37
  nodeSize: 10,
37
38
  nodeSpacing: 0,
38
- collideStrength: 1,
39
- collideIterations: 1,
40
-
41
- nodeStrength: -30,
42
- distanceMin: undefined,
43
- distanceMax: undefined,
44
- theta: undefined,
45
39
 
46
- alpha: 1,
47
- alphaMin: 0.001,
48
- alphaDecay: 1 - Math.pow(0.001, 1 / 300),
49
- alphaTarget: 0,
50
- velocityDecay: 0.4,
40
+ x: false,
41
+ y: false,
51
42
 
52
43
  clustering: false,
53
44
  clusterNodeStrength: -1,
@@ -84,7 +75,7 @@ export class D3ForceLayout<
84
75
  return DEFAULTS_LAYOUT_OPTIONS as T;
85
76
  }
86
77
 
87
- protected mergeOptions(base: T, patch?: Partial<T>): T {
78
+ protected mergeOptions<T>(base: T, patch?: Partial<T>): T {
88
79
  return deepMix({}, base, patch) as T;
89
80
  }
90
81
 
@@ -301,22 +292,17 @@ export class D3ForceLayout<
301
292
  }
302
293
 
303
294
  private getCenterOptions(options: T): T['center'] | undefined {
304
- if (
305
- !options.width ||
306
- !options.height ||
307
- options.centerStrength !== undefined
308
- ) {
309
- const viewport = normalizeViewport({
310
- width: options.width,
311
- height: options.height,
312
- });
313
- return assignDefined({}, options.center || {}, {
314
- x: viewport.width / 2,
315
- y: viewport.height / 2,
316
- strength: options.centerStrength,
317
- }) as T['center'];
318
- }
319
- return undefined;
295
+ if (options.center === false) return undefined;
296
+
297
+ const viewport = normalizeViewport({
298
+ width: options.width,
299
+ height: options.height,
300
+ });
301
+ return assignDefined({}, options.center || {}, {
302
+ x: viewport.width / 2,
303
+ y: viewport.height / 2,
304
+ strength: options.centerStrength,
305
+ }) as T['center'];
320
306
  }
321
307
 
322
308
  protected setupCenterForce(simulation: Simulation<N, E>, options: T) {
@@ -342,21 +328,14 @@ export class D3ForceLayout<
342
328
  }
343
329
 
344
330
  private getManyBodyOptions(options: T): D3ForceLayoutOptions['manyBody'] {
345
- if (
346
- options.manyBody !== undefined ||
347
- options.nodeStrength !== undefined ||
348
- options.distanceMin !== undefined ||
349
- options.distanceMax !== undefined ||
350
- options.theta !== undefined
351
- ) {
352
- return assignDefined({}, options.manyBody || {}, {
353
- strength: options.nodeStrength,
354
- distanceMin: options.distanceMin,
355
- distanceMax: options.distanceMax,
356
- theta: options.theta,
357
- });
358
- }
359
- return undefined;
331
+ if (options.manyBody === false) return undefined;
332
+
333
+ return assignDefined({}, options.manyBody || {}, {
334
+ strength: options.nodeStrength,
335
+ distanceMin: options.distanceMin,
336
+ distanceMax: options.distanceMax,
337
+ theta: options.theta,
338
+ });
360
339
  }
361
340
 
362
341
  protected setupManyBodyForce(simulation: Simulation<N, E>, options: T) {
@@ -386,21 +365,14 @@ export class D3ForceLayout<
386
365
  }
387
366
 
388
367
  private getLinkOptions(options: T): D3ForceLayoutOptions['link'] {
389
- if (
390
- options.link ||
391
- options.edgeId !== undefined ||
392
- options.linkDistance !== undefined ||
393
- options.edgeStrength !== undefined ||
394
- options.edgeIterations !== undefined
395
- ) {
396
- return assignDefined({}, options.link || {}, {
397
- id: options.edgeId,
398
- distance: options.linkDistance,
399
- strength: options.edgeStrength,
400
- iterations: options.edgeIterations,
401
- });
402
- }
403
- return undefined;
368
+ if (options.link === false) return undefined;
369
+
370
+ return assignDefined({}, options.link || {}, {
371
+ id: options.edgeId,
372
+ distance: options.linkDistance,
373
+ strength: options.edgeStrength,
374
+ iterations: options.edgeIterations,
375
+ });
404
376
  }
405
377
 
406
378
  protected setupLinkForce(simulation: Simulation<N, E>, options: T) {
@@ -429,30 +401,23 @@ export class D3ForceLayout<
429
401
  }
430
402
 
431
403
  private getCollisionOptions(options: T): D3ForceLayoutOptions['collide'] {
432
- if (!options.preventOverlap) return undefined;
433
- if (
434
- options.collide !== undefined ||
435
- options.nodeSize !== undefined ||
436
- options.nodeSpacing !== undefined ||
437
- options.collideStrength !== undefined ||
438
- options.collideIterations !== undefined
439
- ) {
440
- const radius =
441
- options.nodeSize || options.nodeSpacing
442
- ? (d: NodeDatum) =>
443
- formatNodeSizeFn(
444
- options.nodeSize,
445
- options.nodeSpacing,
446
- )(d._original) / 2
447
- : undefined;
448
-
449
- return assignDefined({}, options.collide || {}, {
450
- radius,
451
- strength: options.collideStrength,
452
- iterations: options.collideIterations,
453
- });
454
- }
455
- return undefined;
404
+ if (options.preventOverlap === false || options.collide === false)
405
+ return undefined;
406
+
407
+ const radius =
408
+ options.nodeSize || options.nodeSpacing
409
+ ? (d: NodeDatum) =>
410
+ formatNodeSizeFn(
411
+ options.nodeSize,
412
+ options.nodeSpacing,
413
+ )(d._original) / 2
414
+ : undefined;
415
+
416
+ return assignDefined({}, options.collide || {}, {
417
+ radius: options.collide || radius,
418
+ strength: options.collideStrength,
419
+ iterations: options.collideIterations,
420
+ });
456
421
  }
457
422
 
458
423
  protected setupCollisionForce(simulation: Simulation<N, E>, options: T) {
@@ -479,20 +444,13 @@ export class D3ForceLayout<
479
444
  }
480
445
 
481
446
  private getXForceOptions(options: T): D3ForceLayoutOptions['x'] {
482
- if (
483
- options.x !== undefined ||
484
- options.forceXPosition !== undefined ||
485
- options.forceXStrength !== undefined ||
486
- options.width !== undefined ||
487
- options.height !== undefined
488
- ) {
489
- const center = this.getCenterOptions(options);
490
- return assignDefined({}, options.x || {}, {
491
- x: options.forceXPosition ?? (center && center.x),
492
- strength: options.forceXStrength,
493
- });
494
- }
495
- return undefined;
447
+ if (options.x === false) return undefined;
448
+
449
+ const center = this.getCenterOptions(options);
450
+ return assignDefined({}, options.x || {}, {
451
+ x: options.forceXPosition ?? (center && center.x),
452
+ strength: options.forceXStrength,
453
+ });
496
454
  }
497
455
 
498
456
  protected setupXForce(simulation: Simulation<N, E>, options: T) {
@@ -516,21 +474,13 @@ export class D3ForceLayout<
516
474
  }
517
475
 
518
476
  private getYForceOptions(options: T): D3ForceLayoutOptions['y'] {
519
- if (
520
- options.y !== undefined ||
521
- options.forceYPosition !== undefined ||
522
- options.forceYStrength !== undefined ||
523
- options.width !== undefined ||
524
- options.height !== undefined
525
- ) {
526
- const center = this.getCenterOptions(options);
527
- return assignDefined({}, options.y || {}, {
528
- y: options.forceYPosition ?? (center && center.y),
529
- strength: options.forceYStrength,
530
- });
531
- }
477
+ if (options.y === false) return undefined;
532
478
 
533
- return undefined;
479
+ const center = this.getCenterOptions(options);
480
+ return assignDefined({}, options.y || {}, {
481
+ y: options.forceYPosition ?? (center && center.y),
482
+ strength: options.forceYStrength,
483
+ });
534
484
  }
535
485
 
536
486
  protected setupYForce(simulation: Simulation<N, E>, options: T) {
@@ -48,7 +48,3 @@ export function assignDefined<T extends object>(
48
48
  });
49
49
  return target;
50
50
  }
51
-
52
- export function mergeOptions<T extends object>(base: T, patch?: Partial<T>): T {
53
- return Object.assign({}, base, patch || {});
54
- }