@antv/layout 2.0.0-alpha.2 → 2.0.0-alpha.4
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 +1207 -1247
- 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 +10 -8
- package/lib/algorithm/antv-dagre/index.js.map +1 -1
- package/lib/algorithm/antv-dagre/types.d.ts +25 -3
- package/lib/algorithm/base-layout.d.ts +1 -0
- package/lib/algorithm/base-layout.js +5 -3
- package/lib/algorithm/base-layout.js.map +1 -1
- package/lib/algorithm/circular/index.js +2 -2
- package/lib/algorithm/circular/index.js.map +1 -1
- package/lib/algorithm/combo-combined/index.js +4 -4
- package/lib/algorithm/combo-combined/index.js.map +1 -1
- package/lib/algorithm/d3-force/index.d.ts +2 -2
- package/lib/algorithm/d3-force/index.js +60 -104
- package/lib/algorithm/d3-force/index.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/util/object.d.ts +1 -2
- package/lib/util/object.js +1 -4
- package/lib/util/object.js.map +1 -1
- package/lib/util/order.d.ts +1 -1
- package/lib/util/order.js +4 -1
- package/lib/util/order.js.map +1 -1
- package/lib/worker.js +768 -807
- package/lib/worker.js.map +1 -1
- package/package.json +1 -1
- package/src/algorithm/antv-dagre/index.ts +11 -12
- package/src/algorithm/antv-dagre/types.ts +25 -3
- package/src/algorithm/base-layout.ts +13 -8
- package/src/algorithm/circular/index.ts +2 -2
- package/src/algorithm/combo-combined/index.ts +5 -5
- package/src/algorithm/d3-force/index.ts +69 -122
- package/src/util/object.ts +0 -4
- package/src/util/order.ts +4 -0
package/package.json
CHANGED
|
@@ -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
|
-
|
|
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
|
|
84
|
-
const
|
|
85
|
-
const
|
|
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
|
|
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
|
|
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 {
|
|
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>(
|
|
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>(
|
|
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 });
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { isNil } from '@antv/util';
|
|
2
|
-
import { BaseLayout } from '../base-layout';
|
|
3
2
|
import { normalizeViewport, orderByDegree, orderByTopology } from '../../util';
|
|
4
3
|
import { applySingleNodeLayout } from '../../util/common';
|
|
5
4
|
import { formatNodeSizeFn } from '../../util/format';
|
|
5
|
+
import { BaseLayout } from '../base-layout';
|
|
6
6
|
import type { CircularLayoutOptions } from './types';
|
|
7
7
|
|
|
8
8
|
export type { CircularLayoutOptions };
|
|
@@ -61,7 +61,7 @@ export class CircularLayout extends BaseLayout<CircularLayoutOptions> {
|
|
|
61
61
|
orderByTopology(this.model, true);
|
|
62
62
|
} else if (ordering === 'degree') {
|
|
63
63
|
// layout according to the descent order of degrees
|
|
64
|
-
orderByDegree(this.model);
|
|
64
|
+
orderByDegree(this.model, 'asc');
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
let { radius, startRadius, endRadius } = this.options;
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { BaseLayout, isLayoutWithIterations } from '../base-layout';
|
|
2
|
-
import type { Layout } from '../types';
|
|
3
1
|
import { registry } from '../../registry';
|
|
4
2
|
import type {
|
|
5
3
|
GraphData,
|
|
@@ -11,6 +9,8 @@ import type {
|
|
|
11
9
|
} from '../../types';
|
|
12
10
|
import { normalizeViewport, parseSize } from '../../util';
|
|
13
11
|
import { formatNodeSizeFn, formatNumberFn } from '../../util/format';
|
|
12
|
+
import { BaseLayout, isLayoutWithIterations } from '../base-layout';
|
|
13
|
+
import type { Layout } from '../types';
|
|
14
14
|
import type {
|
|
15
15
|
ComboCombinedLayoutConfig,
|
|
16
16
|
ComboCombinedLayoutOptions,
|
|
@@ -34,9 +34,9 @@ const DEFAULT_OPTIONS: ComboCombinedLayoutOptions = {
|
|
|
34
34
|
? { type: 'force', preventOverlap: true }
|
|
35
35
|
: { type: 'concentric', preventOverlap: true },
|
|
36
36
|
nodeSize: 20,
|
|
37
|
-
nodeSpacing:
|
|
38
|
-
comboPadding:
|
|
39
|
-
comboSpacing:
|
|
37
|
+
nodeSpacing: 0,
|
|
38
|
+
comboPadding: 10,
|
|
39
|
+
comboSpacing: 0,
|
|
40
40
|
};
|
|
41
41
|
|
|
42
42
|
const ROOT_ID = 'root';
|
|
@@ -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
|
-
|
|
28
|
+
link: {
|
|
29
|
+
id: (d) => String(d.id),
|
|
30
|
+
},
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
47
|
-
|
|
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
|
|
|
@@ -191,12 +182,6 @@ export class D3ForceLayout<
|
|
|
191
182
|
|
|
192
183
|
protected parseOptions(options: Partial<T>): T {
|
|
193
184
|
const _ = options;
|
|
194
|
-
// process nodeSize
|
|
195
|
-
if (_.collide && _.collide?.radius === undefined) {
|
|
196
|
-
_.collide = _.collide || {};
|
|
197
|
-
// @ts-ignore
|
|
198
|
-
_.collide.radius = _.nodeSize ?? 10;
|
|
199
|
-
}
|
|
200
185
|
// process iterations
|
|
201
186
|
if (_.iterations === undefined) {
|
|
202
187
|
if (_.link && _.link.iterations === undefined) {
|
|
@@ -301,22 +286,17 @@ export class D3ForceLayout<
|
|
|
301
286
|
}
|
|
302
287
|
|
|
303
288
|
private getCenterOptions(options: T): T['center'] | undefined {
|
|
304
|
-
if (
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
options.
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
y: viewport.height / 2,
|
|
316
|
-
strength: options.centerStrength,
|
|
317
|
-
}) as T['center'];
|
|
318
|
-
}
|
|
319
|
-
return undefined;
|
|
289
|
+
if (options.center === false) return undefined;
|
|
290
|
+
|
|
291
|
+
const viewport = normalizeViewport({
|
|
292
|
+
width: options.width,
|
|
293
|
+
height: options.height,
|
|
294
|
+
});
|
|
295
|
+
return assignDefined({}, options.center || {}, {
|
|
296
|
+
x: viewport.width / 2,
|
|
297
|
+
y: viewport.height / 2,
|
|
298
|
+
strength: options.centerStrength,
|
|
299
|
+
}) as T['center'];
|
|
320
300
|
}
|
|
321
301
|
|
|
322
302
|
protected setupCenterForce(simulation: Simulation<N, E>, options: T) {
|
|
@@ -342,21 +322,14 @@ export class D3ForceLayout<
|
|
|
342
322
|
}
|
|
343
323
|
|
|
344
324
|
private getManyBodyOptions(options: T): D3ForceLayoutOptions['manyBody'] {
|
|
345
|
-
if (
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
options.
|
|
349
|
-
options.
|
|
350
|
-
options.
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
strength: options.nodeStrength,
|
|
354
|
-
distanceMin: options.distanceMin,
|
|
355
|
-
distanceMax: options.distanceMax,
|
|
356
|
-
theta: options.theta,
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
return undefined;
|
|
325
|
+
if (options.manyBody === false) return undefined;
|
|
326
|
+
|
|
327
|
+
return assignDefined({}, options.manyBody || {}, {
|
|
328
|
+
strength: options.nodeStrength,
|
|
329
|
+
distanceMin: options.distanceMin,
|
|
330
|
+
distanceMax: options.distanceMax,
|
|
331
|
+
theta: options.theta,
|
|
332
|
+
});
|
|
360
333
|
}
|
|
361
334
|
|
|
362
335
|
protected setupManyBodyForce(simulation: Simulation<N, E>, options: T) {
|
|
@@ -386,21 +359,14 @@ export class D3ForceLayout<
|
|
|
386
359
|
}
|
|
387
360
|
|
|
388
361
|
private getLinkOptions(options: T): D3ForceLayoutOptions['link'] {
|
|
389
|
-
if (
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
options.
|
|
393
|
-
options.
|
|
394
|
-
options.
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
id: options.edgeId,
|
|
398
|
-
distance: options.linkDistance,
|
|
399
|
-
strength: options.edgeStrength,
|
|
400
|
-
iterations: options.edgeIterations,
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
return undefined;
|
|
362
|
+
if (options.link === false) return undefined;
|
|
363
|
+
|
|
364
|
+
return assignDefined({}, options.link || {}, {
|
|
365
|
+
id: options.edgeId,
|
|
366
|
+
distance: options.linkDistance,
|
|
367
|
+
strength: options.edgeStrength,
|
|
368
|
+
iterations: options.edgeIterations,
|
|
369
|
+
});
|
|
404
370
|
}
|
|
405
371
|
|
|
406
372
|
protected setupLinkForce(simulation: Simulation<N, E>, options: T) {
|
|
@@ -429,30 +395,26 @@ export class D3ForceLayout<
|
|
|
429
395
|
}
|
|
430
396
|
|
|
431
397
|
private getCollisionOptions(options: T): D3ForceLayoutOptions['collide'] {
|
|
432
|
-
if (!options.preventOverlap) return undefined;
|
|
433
398
|
if (
|
|
434
|
-
options.
|
|
435
|
-
options.
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
});
|
|
454
|
-
}
|
|
455
|
-
return undefined;
|
|
399
|
+
options.preventOverlap === false &&
|
|
400
|
+
(options.collide === false || options.collide === undefined)
|
|
401
|
+
)
|
|
402
|
+
return undefined;
|
|
403
|
+
|
|
404
|
+
const radius =
|
|
405
|
+
options.nodeSize || options.nodeSpacing
|
|
406
|
+
? (d: NodeDatum) =>
|
|
407
|
+
formatNodeSizeFn(
|
|
408
|
+
options.nodeSize,
|
|
409
|
+
options.nodeSpacing,
|
|
410
|
+
)(d._original) / 2
|
|
411
|
+
: undefined;
|
|
412
|
+
|
|
413
|
+
return assignDefined({}, options.collide || {}, {
|
|
414
|
+
radius: (options.collide && options.collide.radius) || radius,
|
|
415
|
+
strength: options.collideStrength,
|
|
416
|
+
iterations: options.collideIterations,
|
|
417
|
+
});
|
|
456
418
|
}
|
|
457
419
|
|
|
458
420
|
protected setupCollisionForce(simulation: Simulation<N, E>, options: T) {
|
|
@@ -479,20 +441,13 @@ export class D3ForceLayout<
|
|
|
479
441
|
}
|
|
480
442
|
|
|
481
443
|
private getXForceOptions(options: T): D3ForceLayoutOptions['x'] {
|
|
482
|
-
if (
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
options.
|
|
487
|
-
options.
|
|
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;
|
|
444
|
+
if (options.x === false) return undefined;
|
|
445
|
+
|
|
446
|
+
const center = this.getCenterOptions(options);
|
|
447
|
+
return assignDefined({}, options.x || {}, {
|
|
448
|
+
x: options.forceXPosition ?? (center && center.x),
|
|
449
|
+
strength: options.forceXStrength,
|
|
450
|
+
});
|
|
496
451
|
}
|
|
497
452
|
|
|
498
453
|
protected setupXForce(simulation: Simulation<N, E>, options: T) {
|
|
@@ -516,21 +471,13 @@ export class D3ForceLayout<
|
|
|
516
471
|
}
|
|
517
472
|
|
|
518
473
|
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
|
-
}
|
|
474
|
+
if (options.y === false) return undefined;
|
|
532
475
|
|
|
533
|
-
|
|
476
|
+
const center = this.getCenterOptions(options);
|
|
477
|
+
return assignDefined({}, options.y || {}, {
|
|
478
|
+
y: options.forceYPosition ?? (center && center.y),
|
|
479
|
+
strength: options.forceYStrength,
|
|
480
|
+
});
|
|
534
481
|
}
|
|
535
482
|
|
|
536
483
|
protected setupYForce(simulation: Simulation<N, E>, options: T) {
|
package/src/util/object.ts
CHANGED
package/src/util/order.ts
CHANGED
|
@@ -24,10 +24,14 @@ function sort<N extends NodeData = NodeData>(
|
|
|
24
24
|
|
|
25
25
|
export function orderByDegree<N extends NodeData = NodeData>(
|
|
26
26
|
model: GraphLib<N>,
|
|
27
|
+
order: 'asc' | 'desc' = 'desc',
|
|
27
28
|
): GraphLib<N> {
|
|
28
29
|
return sort(model, (nodeA, nodeB) => {
|
|
29
30
|
const degreeA = model.degree(nodeA.id);
|
|
30
31
|
const degreeB = model.degree(nodeB.id);
|
|
32
|
+
if(order === 'asc') {
|
|
33
|
+
return degreeA - degreeB; // ascending order
|
|
34
|
+
}
|
|
31
35
|
return degreeB - degreeA; // descending order
|
|
32
36
|
});
|
|
33
37
|
}
|