@antv/layout 2.0.0-alpha.4 → 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/README.md +85 -23
- 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 +6 -3
- 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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antv/layout",
|
|
3
|
-
"version": "2.0.0-
|
|
3
|
+
"version": "2.0.0-beta.1",
|
|
4
4
|
"description": "graph layout algorithm",
|
|
5
5
|
"main": "dist/index.min.js",
|
|
6
6
|
"module": "lib/index.js",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@antv/event-emitter": "^0.1.3",
|
|
38
|
+
"@antv/expr": "^1.0.2",
|
|
38
39
|
"@antv/graphlib": "^2.0.0",
|
|
39
40
|
"@antv/util": "^3.3.2",
|
|
40
41
|
"comlink": "^4.4.1",
|
|
@@ -66,6 +67,7 @@
|
|
|
66
67
|
"eslint-config-airbnb-base": "^14.2.1",
|
|
67
68
|
"eslint-config-prettier": "^6.15.0",
|
|
68
69
|
"eslint-plugin-import": "^2.29.1",
|
|
70
|
+
"gh-pages": "^6.3.0",
|
|
69
71
|
"husky": "^7.0.4",
|
|
70
72
|
"interactjs": "^1.10.27",
|
|
71
73
|
"jest": "^30.2.0",
|
|
@@ -101,11 +103,12 @@
|
|
|
101
103
|
"size": "limit-size",
|
|
102
104
|
"test:coverage:open": "open-cli coverage/lcov-report/index.html",
|
|
103
105
|
"test:coverage": "jest --coverage",
|
|
104
|
-
"test": "jest"
|
|
106
|
+
"test": "jest",
|
|
107
|
+
"deploy": "npm --prefix site run build && gh-pages -d site/doc_build"
|
|
105
108
|
},
|
|
106
109
|
"publishConfig": {
|
|
107
110
|
"access": "public",
|
|
108
|
-
"tag": "
|
|
111
|
+
"tag": "beta",
|
|
109
112
|
"registry": "https://registry.npmjs.org/"
|
|
110
113
|
},
|
|
111
114
|
"limit-size": [
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { isNumber } from '@antv/util';
|
|
2
2
|
import type { NodeData, PointObject } from '../../types';
|
|
3
3
|
import { parsePoint } from '../../util';
|
|
4
|
-
import {
|
|
5
|
-
import { parseSize } from '../../util/size';
|
|
4
|
+
import { formatNodeSizeFn, formatNumberFn } from '../../util/format';
|
|
6
5
|
import { BaseLayout } from '../base-layout';
|
|
7
6
|
import { DagreGraph, GraphNode } from './graph';
|
|
8
7
|
import { layout } from './layout';
|
|
@@ -12,6 +11,7 @@ export type { AntVDagreLayoutOptions };
|
|
|
12
11
|
|
|
13
12
|
const DEFAULTS_LAYOUT_OPTIONS: Partial<AntVDagreLayoutOptions> = {
|
|
14
13
|
nodeSize: 10,
|
|
14
|
+
nodeSpacing: 0,
|
|
15
15
|
rankdir: 'TB',
|
|
16
16
|
nodesep: 50, // 节点水平间距(px)
|
|
17
17
|
ranksep: 50, // 每一层节点之间间距
|
|
@@ -37,6 +37,7 @@ export class AntVDagreLayout extends BaseLayout<AntVDagreLayoutOptions> {
|
|
|
37
37
|
protected async layout(options: AntVDagreLayoutOptions): Promise<void> {
|
|
38
38
|
const {
|
|
39
39
|
nodeSize,
|
|
40
|
+
nodeSpacing,
|
|
40
41
|
align,
|
|
41
42
|
rankdir = 'TB',
|
|
42
43
|
ranksep,
|
|
@@ -54,21 +55,16 @@ export class AntVDagreLayout extends BaseLayout<AntVDagreLayoutOptions> {
|
|
|
54
55
|
nodesepFunc,
|
|
55
56
|
} = options;
|
|
56
57
|
|
|
57
|
-
const ranksepfunc = formatNumberFn(ranksepFunc, ranksep ?? 50);
|
|
58
|
-
const nodesepfunc = formatNumberFn(nodesepFunc, nodesep ?? 50);
|
|
59
|
-
let horisep: (
|
|
60
|
-
let vertisep: (
|
|
58
|
+
const ranksepfunc = formatNumberFn(ranksepFunc, ranksep ?? 50, 'node');
|
|
59
|
+
const nodesepfunc = formatNumberFn(nodesepFunc, nodesep ?? 50, 'node');
|
|
60
|
+
let horisep: (node: NodeData) => number = nodesepfunc;
|
|
61
|
+
let vertisep: (node: NodeData) => number = ranksepfunc;
|
|
61
62
|
|
|
62
63
|
if (rankdir === 'LR' || rankdir === 'RL') {
|
|
63
64
|
horisep = ranksepfunc;
|
|
64
65
|
vertisep = nodesepfunc;
|
|
65
66
|
}
|
|
66
67
|
|
|
67
|
-
const nodeSizeFunc = formatSizeFn(
|
|
68
|
-
nodeSize,
|
|
69
|
-
DEFAULTS_LAYOUT_OPTIONS.nodeSize as number,
|
|
70
|
-
);
|
|
71
|
-
|
|
72
68
|
// Create internal graph
|
|
73
69
|
const g = new DagreGraph<NodeData, any>({ tree: [] });
|
|
74
70
|
|
|
@@ -76,9 +72,16 @@ export class AntVDagreLayout extends BaseLayout<AntVDagreLayoutOptions> {
|
|
|
76
72
|
const nodes = this.model.nodes();
|
|
77
73
|
const edges = this.model.edges();
|
|
78
74
|
|
|
75
|
+
const sizeFn = formatNodeSizeFn(
|
|
76
|
+
nodeSize,
|
|
77
|
+
nodeSpacing,
|
|
78
|
+
DEFAULTS_LAYOUT_OPTIONS.nodeSize as number,
|
|
79
|
+
DEFAULTS_LAYOUT_OPTIONS.nodeSpacing as number,
|
|
80
|
+
);
|
|
81
|
+
|
|
79
82
|
nodes.forEach((node) => {
|
|
80
83
|
const raw = node._original;
|
|
81
|
-
const size =
|
|
84
|
+
const size = sizeFn(raw);
|
|
82
85
|
const verti = vertisep(raw);
|
|
83
86
|
const hori = horisep(raw);
|
|
84
87
|
const width = size[0] + 2 * hori;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ID, NodeData, Point
|
|
1
|
+
import type { Expr, ID, NodeData, Point } from '../../types';
|
|
2
2
|
import { BaseLayoutOptions } from '../base-layout';
|
|
3
3
|
|
|
4
4
|
export type DagreRankdir =
|
|
@@ -62,17 +62,6 @@ export interface AntVDagreLayoutOptions extends BaseLayoutOptions {
|
|
|
62
62
|
* @defaultValue undefined
|
|
63
63
|
*/
|
|
64
64
|
begin?: Point;
|
|
65
|
-
/**
|
|
66
|
-
* <zh/> 节点大小(直径)。
|
|
67
|
-
*
|
|
68
|
-
* <en/> The diameter of the node
|
|
69
|
-
* @remarks
|
|
70
|
-
* <zh/> 用于防止节点重叠时的碰撞检测
|
|
71
|
-
*
|
|
72
|
-
* <en/> Used for collision detection when nodes overlap
|
|
73
|
-
* @defaultValue undefined
|
|
74
|
-
*/
|
|
75
|
-
nodeSize?: Size | ((d?: NodeData) => Size);
|
|
76
65
|
/**
|
|
77
66
|
* <zh/> 节点间距(px)
|
|
78
67
|
*
|
|
@@ -105,7 +94,7 @@ export interface AntVDagreLayoutOptions extends BaseLayoutOptions {
|
|
|
105
94
|
* <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
95
|
* @param d - <zh/> 节点实例 | <en/> Node instance
|
|
107
96
|
*/
|
|
108
|
-
nodesepFunc?: (
|
|
97
|
+
nodesepFunc?: Expr | ((node: NodeData) => number);
|
|
109
98
|
/**
|
|
110
99
|
* <zh/> 层间距(px)的回调函数
|
|
111
100
|
*
|
|
@@ -116,7 +105,7 @@ export interface AntVDagreLayoutOptions extends BaseLayoutOptions {
|
|
|
116
105
|
* <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
106
|
* @param d - <zh/> 节点实例 | <en/> Node instance
|
|
118
107
|
*/
|
|
119
|
-
ranksepFunc?: (
|
|
108
|
+
ranksepFunc?: Expr | ((node: NodeData) => number);
|
|
120
109
|
/**
|
|
121
110
|
* <zh/> 是否同时计算边上的的控制点位置
|
|
122
111
|
*
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { isNil } from '@antv/util';
|
|
2
1
|
import { normalizeViewport, orderByDegree, orderByTopology } from '../../util';
|
|
3
2
|
import { applySingleNodeLayout } from '../../util/common';
|
|
4
3
|
import { formatNodeSizeFn } from '../../util/format';
|
|
@@ -18,6 +17,7 @@ const DEFAULT_LAYOUT_OPTIONS: CircularLayoutOptions = {
|
|
|
18
17
|
ordering: null,
|
|
19
18
|
angleRatio: 1,
|
|
20
19
|
nodeSize: 10,
|
|
20
|
+
nodeSpacing: 0,
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
/**
|
|
@@ -67,16 +67,17 @@ export class CircularLayout extends BaseLayout<CircularLayoutOptions> {
|
|
|
67
67
|
let { radius, startRadius, endRadius } = this.options;
|
|
68
68
|
|
|
69
69
|
const nodes = this.model.nodes();
|
|
70
|
-
const
|
|
70
|
+
const sizeFn = formatNodeSizeFn(
|
|
71
71
|
nodeSize,
|
|
72
72
|
nodeSpacing,
|
|
73
73
|
DEFAULT_LAYOUT_OPTIONS.nodeSize as number,
|
|
74
|
+
DEFAULT_LAYOUT_OPTIONS.nodeSpacing as number,
|
|
74
75
|
);
|
|
75
76
|
|
|
76
|
-
if (
|
|
77
|
+
if (nodeSpacing) {
|
|
77
78
|
let perimeter = 0;
|
|
78
79
|
for (const node of nodes) {
|
|
79
|
-
perimeter +=
|
|
80
|
+
perimeter += Math.max(...sizeFn(node._original));
|
|
80
81
|
}
|
|
81
82
|
radius = perimeter / (2 * Math.PI);
|
|
82
83
|
} else if (!radius && !startRadius && !endRadius) {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { BaseLayoutOptions } from '../types';
|
|
2
|
-
import type { NodeData, Size } from '../../types';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* <zh/> 环形 Circular 布局配置
|
|
@@ -85,20 +84,6 @@ export interface CircularLayoutOptions extends BaseLayoutOptions {
|
|
|
85
84
|
* @defaultValue 2 * Math.PI
|
|
86
85
|
*/
|
|
87
86
|
endAngle?: number;
|
|
88
|
-
/**
|
|
89
|
-
* <zh/> 环与环之间最小间距,用于调整半径
|
|
90
|
-
*
|
|
91
|
-
* <en/> Minimum spacing between rings, used to adjust the radius
|
|
92
|
-
* @defaultValue 0
|
|
93
|
-
*/
|
|
94
|
-
nodeSpacing?: number | ((d?: NodeData) => number);
|
|
95
|
-
/**
|
|
96
|
-
* <zh/> 节点大小(直径)。用于防止节点重叠时的碰撞检测
|
|
97
|
-
*
|
|
98
|
-
* <en/> Node size (diameter). Used for collision detection when nodes overlap
|
|
99
|
-
* @defaultValue 10
|
|
100
|
-
*/
|
|
101
|
-
nodeSize?: Size | ((d?: NodeData) => Size);
|
|
102
87
|
}
|
|
103
88
|
|
|
104
89
|
export interface ParsedCircularLayoutOptions
|
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
import { registry } from '../../registry';
|
|
2
|
-
import type {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
LayoutNode,
|
|
6
|
-
NodeData,
|
|
7
|
-
Point,
|
|
8
|
-
STDSize,
|
|
9
|
-
} from '../../types';
|
|
10
|
-
import { normalizeViewport, parseSize } from '../../util';
|
|
11
|
-
import { formatNodeSizeFn, formatNumberFn } from '../../util/format';
|
|
2
|
+
import type { GraphData, ID, LayoutNode, Point, STDSize } from '../../types';
|
|
3
|
+
import { normalizeViewport } from '../../util';
|
|
4
|
+
import { formatFn, formatNodeSizeFn, formatNumberFn } from '../../util/format';
|
|
12
5
|
import { BaseLayout, isLayoutWithIterations } from '../base-layout';
|
|
13
6
|
import type { Layout } from '../types';
|
|
14
7
|
import type {
|
|
@@ -205,7 +198,10 @@ export class ComboCombinedLayout extends BaseLayout<ComboCombinedLayoutOptions>
|
|
|
205
198
|
}
|
|
206
199
|
|
|
207
200
|
private getLayoutConfig(combo: HierarchyNode) {
|
|
208
|
-
const
|
|
201
|
+
const layout =
|
|
202
|
+
typeof this.options.layout === 'object'
|
|
203
|
+
? this.options.layout
|
|
204
|
+
: formatFn(this.options.layout, ['comboId']);
|
|
209
205
|
|
|
210
206
|
if (typeof layout === 'function') {
|
|
211
207
|
const comboId = combo.id === ROOT_ID ? null : combo.id!;
|
|
@@ -219,7 +215,7 @@ export class ComboCombinedLayout extends BaseLayout<ComboCombinedLayoutOptions>
|
|
|
219
215
|
const base = {
|
|
220
216
|
type: 'concentric',
|
|
221
217
|
...normalizeViewport(this.options),
|
|
222
|
-
nodeSize:
|
|
218
|
+
nodeSize: 'node.size',
|
|
223
219
|
nodeSpacing: 0,
|
|
224
220
|
};
|
|
225
221
|
|
|
@@ -334,8 +330,12 @@ export class ComboCombinedLayout extends BaseLayout<ComboCombinedLayoutOptions>
|
|
|
334
330
|
return { center: [0, 0], width: 0, height: 0 };
|
|
335
331
|
}
|
|
336
332
|
|
|
337
|
-
const comboPaddingFn = formatNumberFn(
|
|
338
|
-
|
|
333
|
+
const comboPaddingFn = formatNumberFn(
|
|
334
|
+
this.options.comboPadding,
|
|
335
|
+
20,
|
|
336
|
+
'combo',
|
|
337
|
+
);
|
|
338
|
+
const padding = comboPaddingFn(combo._original!);
|
|
339
339
|
|
|
340
340
|
return {
|
|
341
341
|
center: [(minX + maxX) / 2, (minY + maxY) / 2],
|
|
@@ -359,15 +359,19 @@ export class ComboCombinedLayout extends BaseLayout<ComboCombinedLayoutOptions>
|
|
|
359
359
|
): STDSize {
|
|
360
360
|
const { nodeSize, nodeSpacing } = this.options;
|
|
361
361
|
const sizeFn = formatNodeSizeFn(nodeSize, includeSpacing ? nodeSpacing : 0);
|
|
362
|
-
return
|
|
362
|
+
return sizeFn(node._original!);
|
|
363
363
|
}
|
|
364
364
|
|
|
365
365
|
private getComboSize(
|
|
366
366
|
combo: HierarchyNode,
|
|
367
367
|
includeSpacing: boolean = true,
|
|
368
368
|
): STDSize {
|
|
369
|
-
const comboSpacingFn = formatNumberFn(
|
|
370
|
-
|
|
369
|
+
const comboSpacingFn = formatNumberFn(
|
|
370
|
+
this.options.comboSpacing,
|
|
371
|
+
0,
|
|
372
|
+
'combo',
|
|
373
|
+
);
|
|
374
|
+
const spacing = includeSpacing ? comboSpacingFn(combo._original!) : 0;
|
|
371
375
|
const [width, height] = combo.size as STDSize;
|
|
372
376
|
return [width + spacing / 2, height + spacing / 2, 0];
|
|
373
377
|
}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
+
import type { Expr, ID, NodeData } from '../../types';
|
|
1
2
|
import type { BaseLayoutOptions } from '../types';
|
|
2
|
-
import type { ID, NodeData, Size } from '../../types';
|
|
3
3
|
|
|
4
|
-
export type ComboCombinedLayoutConfig =
|
|
5
|
-
| string
|
|
6
|
-
| { type: string; [key: string]: any };
|
|
4
|
+
export type ComboCombinedLayoutConfig = { type: string; [key: string]: any };
|
|
7
5
|
|
|
8
6
|
export interface ComboCombinedLayoutOptions extends BaseLayoutOptions {
|
|
9
7
|
/**
|
|
@@ -11,29 +9,16 @@ export interface ComboCombinedLayoutOptions extends BaseLayoutOptions {
|
|
|
11
9
|
*/
|
|
12
10
|
layout?:
|
|
13
11
|
| ComboCombinedLayoutConfig
|
|
14
|
-
| ((comboId: ID | null) => ComboCombinedLayoutConfig)
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* <zh/> 节点尺寸
|
|
18
|
-
*
|
|
19
|
-
* <en/> Node size
|
|
20
|
-
*/
|
|
21
|
-
nodeSize?: Size | ((node?: NodeData) => Size);
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* <zh/> 节点间距
|
|
25
|
-
*
|
|
26
|
-
* <en/> Node spacing
|
|
27
|
-
*/
|
|
28
|
-
nodeSpacing?: number | ((node?: NodeData) => number);
|
|
12
|
+
| ((comboId: ID | null) => ComboCombinedLayoutConfig)
|
|
13
|
+
| Expr;
|
|
29
14
|
|
|
30
15
|
/**
|
|
31
16
|
* Combo 之间的间距
|
|
32
17
|
*/
|
|
33
|
-
comboSpacing?: number | ((combo
|
|
18
|
+
comboSpacing?: number | ((combo: NodeData) => number) | Expr;
|
|
34
19
|
|
|
35
20
|
/**
|
|
36
21
|
* Combo 内部的边距
|
|
37
22
|
*/
|
|
38
|
-
comboPadding?: number | ((combo
|
|
23
|
+
comboPadding?: number | ((combo: NodeData) => number) | Expr;
|
|
39
24
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { BaseLayout } from '../base-layout';
|
|
2
1
|
import type { LayoutNode, NodeData } from '../../types';
|
|
3
2
|
import {
|
|
4
3
|
applySingleNodeLayout,
|
|
@@ -6,7 +5,8 @@ import {
|
|
|
6
5
|
orderByDegree,
|
|
7
6
|
orderBySorter,
|
|
8
7
|
} from '../../util';
|
|
9
|
-
import { formatNodeSizeFn } from '../../util/format';
|
|
8
|
+
import { formatFn, formatNodeSizeFn } from '../../util/format';
|
|
9
|
+
import { BaseLayout } from '../base-layout';
|
|
10
10
|
import type { ConcentricLayoutOptions } from './types';
|
|
11
11
|
|
|
12
12
|
export type { ConcentricLayoutOptions };
|
|
@@ -52,25 +52,21 @@ export class ConcentricLayout extends BaseLayout<ConcentricLayoutOptions> {
|
|
|
52
52
|
equidistant,
|
|
53
53
|
preventOverlap,
|
|
54
54
|
startAngle = DEFAULTS_LAYOUT_OPTIONS.startAngle,
|
|
55
|
-
nodeSize
|
|
55
|
+
nodeSize,
|
|
56
56
|
nodeSpacing,
|
|
57
57
|
} = this.options;
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (typeof testValue !== 'number') sortBy = 'degree';
|
|
64
|
-
} else {
|
|
65
|
-
sortBy = 'degree';
|
|
66
|
-
}
|
|
59
|
+
const sortBy =
|
|
60
|
+
!propsSortBy || propsSortBy === 'degree'
|
|
61
|
+
? ('degree' as const)
|
|
62
|
+
: (formatFn(propsSortBy, ['node']) as (node: NodeData) => number);
|
|
67
63
|
|
|
68
64
|
if (sortBy === 'degree') {
|
|
69
65
|
orderByDegree(this.model);
|
|
70
66
|
} else {
|
|
71
67
|
const sorter = (nodeA: NodeData, nodeB: NodeData) => {
|
|
72
|
-
const a =
|
|
73
|
-
const b =
|
|
68
|
+
const a = sortBy(nodeA);
|
|
69
|
+
const b = sortBy(nodeB);
|
|
74
70
|
return a === b ? 0 : a > b ? -1 : 1;
|
|
75
71
|
};
|
|
76
72
|
orderBySorter(this.model, sorter);
|
|
@@ -83,17 +79,23 @@ export class ConcentricLayout extends BaseLayout<ConcentricLayoutOptions> {
|
|
|
83
79
|
const v =
|
|
84
80
|
sortBy === 'degree'
|
|
85
81
|
? this.model.degree(node.id)
|
|
86
|
-
: sortBy
|
|
82
|
+
: sortBy(node._original);
|
|
87
83
|
sortKeys.set(node.id, v);
|
|
88
84
|
}
|
|
89
85
|
|
|
90
86
|
const maxValueNode = this.model.firstNode()!;
|
|
91
87
|
const maxLevelDiff = propsMaxLevelDiff || sortKeys.get(maxValueNode.id) / 4;
|
|
92
88
|
|
|
93
|
-
const
|
|
89
|
+
const sizeFn = formatNodeSizeFn(
|
|
90
|
+
nodeSize,
|
|
91
|
+
nodeSpacing,
|
|
92
|
+
DEFAULTS_LAYOUT_OPTIONS.nodeSize as number,
|
|
93
|
+
DEFAULTS_LAYOUT_OPTIONS.nodeSpacing as number,
|
|
94
|
+
);
|
|
95
|
+
|
|
94
96
|
const nodeDistances = new Map<LayoutNode['id'], number>();
|
|
95
97
|
for (const node of nodes) {
|
|
96
|
-
nodeDistances.set(node.id,
|
|
98
|
+
nodeDistances.set(node.id, Math.max(...sizeFn(node._original)));
|
|
97
99
|
}
|
|
98
100
|
|
|
99
101
|
// put the values into levels
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import type { Expr, NodeData } from '../../types';
|
|
1
2
|
import type { BaseLayoutOptions } from '../types';
|
|
2
|
-
import type { NodeData, Size } from '../../types';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* <zh/> Concentric 同心圆布局配置
|
|
@@ -18,20 +18,6 @@ export interface ConcentricLayoutOptions extends BaseLayoutOptions {
|
|
|
18
18
|
* @defaultValue false
|
|
19
19
|
*/
|
|
20
20
|
preventOverlap?: boolean;
|
|
21
|
-
/**
|
|
22
|
-
* <zh/> 节点大小(直径)。用于防止节点重叠时的碰撞检测
|
|
23
|
-
*
|
|
24
|
-
* <en/> Node size (diameter). Used for collision detection when preventing node overlap
|
|
25
|
-
* @defaultValue 30
|
|
26
|
-
*/
|
|
27
|
-
nodeSize?: Size | ((d?: NodeData) => Size);
|
|
28
|
-
/**
|
|
29
|
-
* <zh/> 环与环之间最小间距,用于调整半径
|
|
30
|
-
*
|
|
31
|
-
* <en/> Minimum spacing between rings, used to adjust the radius
|
|
32
|
-
* @defaultValue 10
|
|
33
|
-
*/
|
|
34
|
-
nodeSpacing?: number | ((d?: NodeData) => number);
|
|
35
21
|
/**
|
|
36
22
|
* <zh/> 第一个节点与最后一个节点之间的弧度差
|
|
37
23
|
*
|
|
@@ -85,5 +71,5 @@ export interface ConcentricLayoutOptions extends BaseLayoutOptions {
|
|
|
85
71
|
* - ((node) => ...): Custom sorting function, returns a number, the higher the value, the more the node will be placed in the center
|
|
86
72
|
* @defaultValue degree
|
|
87
73
|
*/
|
|
88
|
-
sortBy?: 'degree' | ((
|
|
74
|
+
sortBy?: 'degree' | Expr | ((node: NodeData) => number);
|
|
89
75
|
}
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from 'd3-force';
|
|
13
13
|
import type { ID, Position } from '../../types';
|
|
14
14
|
import { assignDefined, normalizeViewport } from '../../util';
|
|
15
|
-
import { formatNodeSizeFn } from '../../util/format';
|
|
15
|
+
import { formatFn, formatNodeSizeFn } from '../../util/format';
|
|
16
16
|
import { BaseLayoutWithIterations } from '../base-layout';
|
|
17
17
|
import forceInABox from './force-in-a-box';
|
|
18
18
|
import type {
|
|
@@ -25,9 +25,7 @@ import type {
|
|
|
25
25
|
export type { D3ForceLayoutOptions };
|
|
26
26
|
|
|
27
27
|
const DEFAULTS_LAYOUT_OPTIONS: Partial<D3ForceLayoutOptions> = {
|
|
28
|
-
|
|
29
|
-
id: (d) => String(d.id),
|
|
30
|
-
},
|
|
28
|
+
edgeId: 'edge.id',
|
|
31
29
|
|
|
32
30
|
manyBody: {
|
|
33
31
|
strength: -30,
|
|
@@ -325,7 +323,9 @@ export class D3ForceLayout<
|
|
|
325
323
|
if (options.manyBody === false) return undefined;
|
|
326
324
|
|
|
327
325
|
return assignDefined({}, options.manyBody || {}, {
|
|
328
|
-
strength: options.nodeStrength
|
|
326
|
+
strength: options.nodeStrength
|
|
327
|
+
? formatFn(options.nodeStrength, ['node'])
|
|
328
|
+
: undefined,
|
|
329
329
|
distanceMin: options.distanceMin,
|
|
330
330
|
distanceMax: options.distanceMax,
|
|
331
331
|
theta: options.theta,
|
|
@@ -362,9 +362,13 @@ export class D3ForceLayout<
|
|
|
362
362
|
if (options.link === false) return undefined;
|
|
363
363
|
|
|
364
364
|
return assignDefined({}, options.link || {}, {
|
|
365
|
-
id: options.edgeId,
|
|
366
|
-
distance: options.linkDistance
|
|
367
|
-
|
|
365
|
+
id: options.edgeId ? formatFn(options.edgeId, ['edge']) : undefined,
|
|
366
|
+
distance: options.linkDistance
|
|
367
|
+
? formatFn(options.linkDistance, ['edge'])
|
|
368
|
+
: undefined,
|
|
369
|
+
strength: options.edgeStrength
|
|
370
|
+
? formatFn(options.edgeStrength, ['edge'])
|
|
371
|
+
: undefined,
|
|
368
372
|
iterations: options.edgeIterations,
|
|
369
373
|
});
|
|
370
374
|
}
|
|
@@ -401,14 +405,13 @@ export class D3ForceLayout<
|
|
|
401
405
|
)
|
|
402
406
|
return undefined;
|
|
403
407
|
|
|
404
|
-
const
|
|
405
|
-
options.nodeSize
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
: undefined;
|
|
408
|
+
const sizeFn = formatNodeSizeFn(
|
|
409
|
+
options.nodeSize,
|
|
410
|
+
options.nodeSpacing,
|
|
411
|
+
DEFAULTS_LAYOUT_OPTIONS.nodeSize as number,
|
|
412
|
+
DEFAULTS_LAYOUT_OPTIONS.nodeSpacing as number,
|
|
413
|
+
);
|
|
414
|
+
const radius = (d: NodeDatum) => Math.max(...sizeFn(d._original)) / 2;
|
|
412
415
|
|
|
413
416
|
return assignDefined({}, options.collide || {}, {
|
|
414
417
|
radius: (options.collide && options.collide.radius) || radius,
|
|
@@ -524,7 +527,11 @@ export class D3ForceLayout<
|
|
|
524
527
|
if (radial) {
|
|
525
528
|
let force = simulation.force('radial');
|
|
526
529
|
if (!force) {
|
|
527
|
-
force = forceRadial(
|
|
530
|
+
force = forceRadial(
|
|
531
|
+
(radial.radius as () => number) || 100,
|
|
532
|
+
radial.x,
|
|
533
|
+
radial.y,
|
|
534
|
+
);
|
|
528
535
|
simulation.force('radial', force as any);
|
|
529
536
|
}
|
|
530
537
|
|
|
@@ -567,7 +574,7 @@ export class D3ForceLayout<
|
|
|
567
574
|
['centerY', center && center.y],
|
|
568
575
|
['template', 'force'],
|
|
569
576
|
['strength', clusterFociStrength],
|
|
570
|
-
['groupBy', clusterBy],
|
|
577
|
+
['groupBy', clusterBy ? formatFn(clusterBy, ['node']) : undefined],
|
|
571
578
|
['nodes', this.model.nodes()],
|
|
572
579
|
['links', this.model.edges()],
|
|
573
580
|
['forceLinkDistance', clusterEdgeDistance],
|
|
@@ -3,8 +3,8 @@ import type {
|
|
|
3
3
|
SimulationLinkDatum,
|
|
4
4
|
SimulationNodeDatum,
|
|
5
5
|
} from 'd3-force';
|
|
6
|
+
import type { Expr, LayoutEdge, LayoutNode } from '../../types';
|
|
6
7
|
import type { BaseLayoutOptions, Layout } from '../types';
|
|
7
|
-
import type { LayoutEdge, LayoutNode } from '../../types';
|
|
8
8
|
|
|
9
9
|
export interface D3ForceCommonOptions
|
|
10
10
|
extends Omit<BaseLayoutOptions, 'center'> {
|
|
@@ -42,21 +42,21 @@ export interface D3ForceCommonOptions
|
|
|
42
42
|
* <en/> Unique identifier field or function for edges
|
|
43
43
|
* @defaultValue (edge) => String(edge.id)
|
|
44
44
|
*/
|
|
45
|
-
edgeId?: (edge: EdgeDatum) => string;
|
|
45
|
+
edgeId?: Expr | ((edge: EdgeDatum) => string);
|
|
46
46
|
/**
|
|
47
47
|
* <zh/> 边的理想长度,可以是数值或根据边数据返回长度的函数
|
|
48
48
|
*
|
|
49
49
|
* <en/> Ideal length of edges, can be a number or a function that returns length based on edge data
|
|
50
50
|
* @defaultValue 50
|
|
51
51
|
*/
|
|
52
|
-
linkDistance?: number | ((edge: EdgeDatum) => number);
|
|
52
|
+
linkDistance?: number | Expr | ((edge: EdgeDatum) => number);
|
|
53
53
|
/**
|
|
54
54
|
* <zh/> 边的强度,可以是数值或根据边数据返回强度的函数。值范围为 [0, 1]
|
|
55
55
|
*
|
|
56
56
|
* <en/> Strength of edges, can be a number or a function that returns strength based on edge data. Value range is [0, 1]
|
|
57
57
|
* @defaultValue null
|
|
58
58
|
*/
|
|
59
|
-
edgeStrength?: number | ((edge: EdgeDatum) => number) | null;
|
|
59
|
+
edgeStrength?: number | Expr | ((edge: EdgeDatum) => number) | null;
|
|
60
60
|
/**
|
|
61
61
|
* <zh/> 链接力的迭代次数
|
|
62
62
|
*
|
|
@@ -70,7 +70,7 @@ export interface D3ForceCommonOptions
|
|
|
70
70
|
* <en/> Strength of node force, negative for repulsion, positive for attraction
|
|
71
71
|
* @defaultValue -30
|
|
72
72
|
*/
|
|
73
|
-
nodeStrength?: number | ((node: NodeDatum) => number);
|
|
73
|
+
nodeStrength?: number | Expr | ((node: NodeDatum) => number);
|
|
74
74
|
/**
|
|
75
75
|
* <zh/> 多体力的近似参数,值范围为 (0, 1]
|
|
76
76
|
*
|
|
@@ -113,21 +113,6 @@ export interface D3ForceCommonOptions
|
|
|
113
113
|
* @defaultValue 1
|
|
114
114
|
*/
|
|
115
115
|
collideIterations?: number;
|
|
116
|
-
/**
|
|
117
|
-
* <zh/> 节点大小(直径)。用于防止节点重叠时的碰撞检测
|
|
118
|
-
*
|
|
119
|
-
* <en/> Node size (diameter). Used for collision detection when nodes overlap
|
|
120
|
-
*
|
|
121
|
-
* @defaultValue 10
|
|
122
|
-
*/
|
|
123
|
-
nodeSize?: number | ((d?: NodeDatum) => number);
|
|
124
|
-
/**
|
|
125
|
-
* <zh/> 节点之间的最小间距
|
|
126
|
-
*
|
|
127
|
-
* <en/> Minimum spacing between nodes
|
|
128
|
-
* @defaultValue 0
|
|
129
|
-
*/
|
|
130
|
-
nodeSpacing?: number | ((d?: NodeDatum) => number);
|
|
131
116
|
/**
|
|
132
117
|
* <zh/> 径向力的理想半径,可以是数值或根据节点数据返回半径的函数
|
|
133
118
|
*
|
|
@@ -169,7 +154,7 @@ export interface D3ForceCommonOptions
|
|
|
169
154
|
* <en/> Field or function used for clustering
|
|
170
155
|
* @defaultValue (d) => d.cluster
|
|
171
156
|
*/
|
|
172
|
-
clusterBy?: (
|
|
157
|
+
clusterBy?: Expr | ((node: NodeDatum) => string | number);
|
|
173
158
|
/**
|
|
174
159
|
* <zh/> 聚类内节点之间的作用力强度
|
|
175
160
|
*
|
|
@@ -257,7 +242,7 @@ export interface D3ForceCommonOptions
|
|
|
257
242
|
* <en/> Set the function for generating random numbers
|
|
258
243
|
* @returns <zh/> 随机数 | <en/> Random number
|
|
259
244
|
*/
|
|
260
|
-
randomSource?: () => number;
|
|
245
|
+
randomSource?: Expr | (() => number);
|
|
261
246
|
/**
|
|
262
247
|
* <zh/> 碰撞力
|
|
263
248
|
*
|
|
@@ -360,9 +345,11 @@ export interface D3ForceLayoutOptions extends D3ForceCommonOptions {
|
|
|
360
345
|
| {
|
|
361
346
|
strength?:
|
|
362
347
|
| number
|
|
348
|
+
| Expr
|
|
363
349
|
| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number);
|
|
364
350
|
radius?:
|
|
365
351
|
| number
|
|
352
|
+
| Expr
|
|
366
353
|
| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number);
|
|
367
354
|
x?: number;
|
|
368
355
|
y?: number;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Expr } from '../../types';
|
|
1
2
|
import type {
|
|
2
3
|
D3ForceCommonOptions,
|
|
3
4
|
EdgeDatum as _EdgeDatum,
|
|
@@ -32,9 +33,11 @@ export interface D3Force3DLayoutOptions extends D3ForceCommonOptions {
|
|
|
32
33
|
| {
|
|
33
34
|
strength?:
|
|
34
35
|
| number
|
|
36
|
+
| Expr
|
|
35
37
|
| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number);
|
|
36
38
|
radius?:
|
|
37
39
|
| number
|
|
40
|
+
| Expr
|
|
38
41
|
| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number);
|
|
39
42
|
x?: number;
|
|
40
43
|
y?: number;
|
|
@@ -50,9 +53,11 @@ export interface D3Force3DLayoutOptions extends D3ForceCommonOptions {
|
|
|
50
53
|
| {
|
|
51
54
|
strength?:
|
|
52
55
|
| number
|
|
56
|
+
| Expr
|
|
53
57
|
| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number);
|
|
54
58
|
z?:
|
|
55
59
|
| number
|
|
60
|
+
| Expr
|
|
56
61
|
| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number);
|
|
57
62
|
};
|
|
58
63
|
}
|