@deck.gl-community/graph-layers 9.0.1 → 9.1.0-beta.2
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/LICENSE +1 -1
- package/dist/core/cache.d.ts +1 -0
- package/dist/core/cache.d.ts.map +1 -0
- package/dist/core/constants.d.ts +1 -0
- package/dist/core/constants.d.ts.map +1 -0
- package/dist/core/constants.js +1 -1
- package/dist/core/graph-engine.d.ts +17 -7
- package/dist/core/graph-engine.d.ts.map +1 -0
- package/dist/core/graph-engine.js +14 -5
- package/dist/core/graph-layout.d.ts +69 -0
- package/dist/core/graph-layout.d.ts.map +1 -0
- package/dist/core/{base-layout.js → graph-layout.js} +63 -80
- package/dist/core/interaction-manager.d.ts +3 -2
- package/dist/core/interaction-manager.d.ts.map +1 -0
- package/dist/core/interaction-manager.js +1 -1
- package/dist/{core → graph}/edge.d.ts +20 -18
- package/dist/graph/edge.d.ts.map +1 -0
- package/dist/{core → graph}/edge.js +12 -15
- package/dist/{core → graph}/graph.d.ts +37 -33
- package/dist/graph/graph.d.ts.map +1 -0
- package/dist/{core → graph}/graph.js +44 -37
- package/dist/{core → graph}/node.d.ts +22 -21
- package/dist/graph/node.d.ts.map +1 -0
- package/dist/{core → graph}/node.js +16 -18
- package/dist/index.cjs +1181 -434
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +22 -19
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -23
- package/dist/layers/common-layers/flow-path-layer/flow-path-layer-fragment.glsl.d.ts +1 -0
- package/dist/layers/common-layers/flow-path-layer/flow-path-layer-fragment.glsl.d.ts.map +1 -0
- package/dist/layers/common-layers/flow-path-layer/flow-path-layer-vertex-tf.glsl.d.ts +1 -0
- package/dist/layers/common-layers/flow-path-layer/flow-path-layer-vertex-tf.glsl.d.ts.map +1 -0
- package/dist/layers/common-layers/flow-path-layer/flow-path-layer-vertex.glsl.d.ts +1 -0
- package/dist/layers/common-layers/flow-path-layer/flow-path-layer-vertex.glsl.d.ts.map +1 -0
- package/dist/layers/common-layers/flow-path-layer/flow-path-layer.d.ts +1 -0
- package/dist/layers/common-layers/flow-path-layer/flow-path-layer.d.ts.map +1 -0
- package/dist/layers/common-layers/marker-layer/atlas-data-url.d.ts +1 -0
- package/dist/layers/common-layers/marker-layer/atlas-data-url.d.ts.map +1 -0
- package/dist/layers/common-layers/marker-layer/marker-layer.d.ts +1 -0
- package/dist/layers/common-layers/marker-layer/marker-layer.d.ts.map +1 -0
- package/dist/layers/common-layers/marker-layer/marker-layer.js +2 -2
- package/dist/layers/common-layers/marker-layer/marker-list.d.ts +1 -0
- package/dist/layers/common-layers/marker-layer/marker-list.d.ts.map +1 -0
- package/dist/layers/common-layers/marker-layer/marker-mapping.d.ts +1 -0
- package/dist/layers/common-layers/marker-layer/marker-mapping.d.ts.map +1 -0
- package/dist/layers/common-layers/spline-layer/spline-layer.d.ts +1 -0
- package/dist/layers/common-layers/spline-layer/spline-layer.d.ts.map +1 -0
- package/dist/layers/common-layers/zoomable-text-layer/zoomable-text-layer.d.ts +1 -0
- package/dist/layers/common-layers/zoomable-text-layer/zoomable-text-layer.d.ts.map +1 -0
- package/dist/layers/edge-layer.d.ts +4 -3
- package/dist/layers/edge-layer.d.ts.map +1 -0
- package/dist/layers/edge-layer.js +4 -4
- package/dist/layers/edge-layers/curved-edge-layer.d.ts +2 -1
- package/dist/layers/edge-layers/curved-edge-layer.d.ts.map +1 -0
- package/dist/layers/edge-layers/curved-edge-layer.js +1 -1
- package/dist/layers/edge-layers/edge-label-layer.d.ts +2 -1
- package/dist/layers/edge-layers/edge-label-layer.d.ts.map +1 -0
- package/dist/layers/edge-layers/edge-label-layer.js +1 -1
- package/dist/layers/edge-layers/flow-layer.d.ts +2 -1
- package/dist/layers/edge-layers/flow-layer.d.ts.map +1 -0
- package/dist/layers/edge-layers/flow-layer.js +1 -1
- package/dist/layers/edge-layers/path-edge-layer.d.ts +1 -0
- package/dist/layers/edge-layers/path-edge-layer.d.ts.map +1 -0
- package/dist/layers/edge-layers/straight-line-edge-layer.d.ts +1 -0
- package/dist/layers/edge-layers/straight-line-edge-layer.d.ts.map +1 -0
- package/dist/layers/graph-layer.d.ts +54 -13
- package/dist/layers/graph-layer.d.ts.map +1 -0
- package/dist/layers/graph-layer.js +93 -51
- package/dist/layers/node-layers/circle-layer.d.ts +1 -0
- package/dist/layers/node-layers/circle-layer.d.ts.map +1 -0
- package/dist/layers/node-layers/image-layer.d.ts +1 -0
- package/dist/layers/node-layers/image-layer.d.ts.map +1 -0
- package/dist/layers/node-layers/label-layer.d.ts +2 -1
- package/dist/layers/node-layers/label-layer.d.ts.map +1 -0
- package/dist/layers/node-layers/label-layer.js +1 -1
- package/dist/layers/node-layers/{path-rounded-rectange-layer.d.ts → path-rounded-rectangle-layer.d.ts} +1 -0
- package/dist/layers/node-layers/path-rounded-rectangle-layer.d.ts.map +1 -0
- package/dist/layers/node-layers/{path-rounded-rectange-layer.js → path-rounded-rectangle-layer.js} +1 -1
- package/dist/layers/node-layers/rectangle-layer.d.ts +1 -0
- package/dist/layers/node-layers/rectangle-layer.d.ts.map +1 -0
- package/dist/layers/node-layers/rounded-rectangle-layer-fragment.d.ts +2 -1
- package/dist/layers/node-layers/rounded-rectangle-layer-fragment.d.ts.map +1 -0
- package/dist/layers/node-layers/rounded-rectangle-layer-fragment.js +1 -3
- package/dist/layers/node-layers/rounded-rectangle-layer.d.ts +14 -4
- package/dist/layers/node-layers/rounded-rectangle-layer.d.ts.map +1 -0
- package/dist/layers/node-layers/rounded-rectangle-layer.js +27 -13
- package/dist/layers/node-layers/zoomable-marker-layer.d.ts +2 -1
- package/dist/layers/node-layers/zoomable-marker-layer.d.ts.map +1 -0
- package/dist/layers/node-layers/zoomable-marker-layer.js +1 -1
- package/dist/layouts/d3-force/d3-force-layout.d.ts +13 -3
- package/dist/layouts/d3-force/d3-force-layout.d.ts.map +1 -0
- package/dist/layouts/d3-force/d3-force-layout.js +12 -12
- package/dist/layouts/d3-force/worker.d.ts +1 -0
- package/dist/layouts/d3-force/worker.d.ts.map +1 -0
- package/dist/layouts/experimental/force-multi-graph-layout.d.ts +43 -0
- package/dist/layouts/experimental/force-multi-graph-layout.d.ts.map +1 -0
- package/dist/layouts/experimental/force-multi-graph-layout.js +226 -0
- package/dist/layouts/experimental/hive-plot-layout.d.ts +34 -0
- package/dist/layouts/experimental/hive-plot-layout.d.ts.map +1 -0
- package/dist/layouts/experimental/hive-plot-layout.js +142 -0
- package/dist/layouts/experimental/radial-layout.d.ts +28 -0
- package/dist/layouts/experimental/radial-layout.d.ts.map +1 -0
- package/dist/layouts/experimental/radial-layout.js +164 -0
- package/dist/layouts/gpu-force/gpu-force-layout.d.ts +16 -3
- package/dist/layouts/gpu-force/gpu-force-layout.d.ts.map +1 -0
- package/dist/layouts/gpu-force/gpu-force-layout.js +21 -19
- package/dist/layouts/gpu-force/worker.d.ts +1 -0
- package/dist/layouts/gpu-force/worker.d.ts.map +1 -0
- package/dist/layouts/simple-layout.d.ts +42 -0
- package/dist/layouts/simple-layout.d.ts.map +1 -0
- package/dist/layouts/{simple-layout/simple-layout.js → simple-layout.js} +8 -7
- package/dist/loaders/create-graph.d.ts +13 -0
- package/dist/loaders/create-graph.d.ts.map +1 -0
- package/dist/{utils → loaders}/create-graph.js +9 -4
- package/dist/loaders/edge-parsers.d.ts +3 -6
- package/dist/loaders/edge-parsers.d.ts.map +1 -0
- package/dist/loaders/edge-parsers.js +1 -1
- package/dist/loaders/json-loader.d.ts +3 -2
- package/dist/loaders/json-loader.d.ts.map +1 -0
- package/dist/loaders/json-loader.js +4 -4
- package/dist/loaders/node-parsers.d.ts +3 -3
- package/dist/loaders/node-parsers.d.ts.map +1 -0
- package/dist/loaders/node-parsers.js +1 -1
- package/dist/loaders/simple-json-graph-loader.d.ts +12 -0
- package/dist/loaders/simple-json-graph-loader.d.ts.map +1 -0
- package/dist/loaders/simple-json-graph-loader.js +20 -0
- package/dist/loaders/table-graph-loader.d.ts +17 -0
- package/dist/loaders/table-graph-loader.d.ts.map +1 -0
- package/dist/loaders/table-graph-loader.js +91 -0
- package/dist/style/style-property.d.ts +1 -0
- package/dist/style/style-property.d.ts.map +1 -0
- package/dist/style/style-property.js +1 -1
- package/dist/style/style-sheet.d.ts +1 -0
- package/dist/style/style-sheet.d.ts.map +1 -0
- package/dist/style/style-sheet.js +3 -3
- package/dist/utils/layer-utils.d.ts +1 -0
- package/dist/utils/layer-utils.d.ts.map +1 -0
- package/dist/utils/log.d.ts +2 -1
- package/dist/utils/log.d.ts.map +1 -0
- package/dist/utils/log.js +3 -3
- package/dist/utils/polygon-calculations.d.ts +1 -0
- package/dist/utils/polygon-calculations.d.ts.map +1 -0
- package/dist/widgets/long-press-button.d.ts +13 -0
- package/dist/widgets/long-press-button.d.ts.map +1 -0
- package/dist/widgets/long-press-button.js +31 -0
- package/dist/widgets/view-control-widget.d.ts +78 -0
- package/dist/widgets/view-control-widget.d.ts.map +1 -0
- package/dist/widgets/view-control-widget.js +194 -0
- package/package.json +8 -7
- package/src/core/graph-engine.ts +30 -10
- package/src/core/graph-layout.ts +146 -0
- package/src/core/interaction-manager.ts +2 -2
- package/src/{core → graph}/edge.ts +19 -17
- package/src/{core → graph}/graph.ts +51 -36
- package/src/{core → graph}/node.ts +21 -20
- package/src/index.ts +28 -28
- package/src/layers/graph-layer.ts +133 -46
- package/src/layers/node-layers/rounded-rectangle-layer-fragment.ts +1 -3
- package/src/layers/node-layers/rounded-rectangle-layer.ts +34 -10
- package/src/layouts/d3-force/d3-force-layout.ts +21 -11
- package/src/layouts/experimental/force-multi-graph-layout.ts +268 -0
- package/src/layouts/experimental/hive-plot-layout.ts +182 -0
- package/src/layouts/experimental/radial-layout.ts +210 -0
- package/src/layouts/gpu-force/gpu-force-layout.ts +32 -17
- package/src/layouts/{simple-layout/simple-layout.ts → simple-layout.ts} +34 -19
- package/src/{utils → loaders}/create-graph.ts +9 -4
- package/src/loaders/edge-parsers.ts +2 -1
- package/src/loaders/json-loader.ts +1 -1
- package/src/loaders/node-parsers.ts +2 -1
- package/src/loaders/simple-json-graph-loader.ts +28 -0
- package/src/loaders/table-graph-loader.ts +124 -0
- package/src/utils/log.ts +3 -3
- package/src/widgets/long-press-button.tsx +50 -0
- package/src/widgets/view-control-widget.tsx +337 -0
- package/dist/core/base-layout.d.ts +0 -71
- package/dist/layouts/simple-layout/simple-layout.d.ts +0 -22
- package/dist/utils/create-graph.d.ts +0 -8
- package/src/core/base-layout.ts +0 -154
- /package/src/layers/node-layers/{path-rounded-rectange-layer.ts → path-rounded-rectangle-layer.ts} +0 -0
- /package/src/layouts/d3-force/{worker.ts → worker.js} +0 -0
- /package/src/layouts/gpu-force/{worker.ts → worker.js} +0 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// deck.gl-community
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {GraphLayout, GraphLayoutOptions} from '../../core/graph-layout';
|
|
6
|
+
import {Node} from '../../graph/node';
|
|
7
|
+
import {EDGE_TYPE} from '../../core/constants';
|
|
8
|
+
import {Graph} from '../../graph/graph';
|
|
9
|
+
|
|
10
|
+
export type HivePlotLayoutOptions = GraphLayoutOptions & {
|
|
11
|
+
innerRadius?: number;
|
|
12
|
+
outerRadius?: number;
|
|
13
|
+
getNodeAxis?: (node: Node) => any;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export class HivePlotLayout extends GraphLayout<HivePlotLayoutOptions> {
|
|
17
|
+
static defaultOptions = {
|
|
18
|
+
innerRadius: 100,
|
|
19
|
+
outerRadius: 500,
|
|
20
|
+
getNodeAxis: (node: Node) => node.getPropertyValue('group')
|
|
21
|
+
} as const satisfies Readonly<Required<HivePlotLayoutOptions>>;
|
|
22
|
+
|
|
23
|
+
_name = 'HivePlot';
|
|
24
|
+
_graph: Graph;
|
|
25
|
+
_totalAxis: number;
|
|
26
|
+
_axis: Record<string, any>;
|
|
27
|
+
_nodeMap = {};
|
|
28
|
+
_nodePositionMap = {};
|
|
29
|
+
|
|
30
|
+
constructor(options: HivePlotLayoutOptions = {}) {
|
|
31
|
+
super(options);
|
|
32
|
+
this._options = {
|
|
33
|
+
...HivePlotLayout.defaultOptions,
|
|
34
|
+
...options
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
initializeGraph(graph: Graph) {
|
|
39
|
+
this.updateGraph(graph);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
updateGraph(graph) {
|
|
43
|
+
const {getNodeAxis, innerRadius, outerRadius} = this._options;
|
|
44
|
+
this._graph = graph;
|
|
45
|
+
this._nodeMap = graph.getNodes().reduce((res, node) => {
|
|
46
|
+
res[node.getId()] = node;
|
|
47
|
+
return res;
|
|
48
|
+
}, {});
|
|
49
|
+
|
|
50
|
+
// bucket nodes into few axis
|
|
51
|
+
|
|
52
|
+
this._axis = graph.getNodes().reduce((res, node) => {
|
|
53
|
+
const axis = getNodeAxis(node);
|
|
54
|
+
if (!res[axis]) {
|
|
55
|
+
res[axis] = [];
|
|
56
|
+
}
|
|
57
|
+
res[axis].push(node);
|
|
58
|
+
return res;
|
|
59
|
+
}, {});
|
|
60
|
+
|
|
61
|
+
// sort nodes along the same axis by degree
|
|
62
|
+
this._axis = Object.keys(this._axis).reduce((res, axis) => {
|
|
63
|
+
const bucketedNodes = this._axis[axis];
|
|
64
|
+
const sortedNodes = bucketedNodes.sort((a, b) => {
|
|
65
|
+
if (a.getDegree() > b.getDegree()) {
|
|
66
|
+
return 1;
|
|
67
|
+
}
|
|
68
|
+
if (a.getDegree() === b.getDegree()) {
|
|
69
|
+
return 0;
|
|
70
|
+
}
|
|
71
|
+
return -1;
|
|
72
|
+
});
|
|
73
|
+
res[axis] = sortedNodes;
|
|
74
|
+
return res;
|
|
75
|
+
}, {});
|
|
76
|
+
this._totalAxis = Object.keys(this._axis).length;
|
|
77
|
+
const center = [0, 0];
|
|
78
|
+
const angleInterval = 360 / Object.keys(this._axis).length;
|
|
79
|
+
|
|
80
|
+
// calculate positions
|
|
81
|
+
this._nodePositionMap = Object.keys(this._axis).reduce((res, axis, axisIdx) => {
|
|
82
|
+
const axisAngle = angleInterval * axisIdx;
|
|
83
|
+
const bucketedNodes = this._axis[axis];
|
|
84
|
+
const interval = (outerRadius - innerRadius) / bucketedNodes.length;
|
|
85
|
+
|
|
86
|
+
bucketedNodes.forEach((node, idx) => {
|
|
87
|
+
const radius = innerRadius + idx * interval;
|
|
88
|
+
const x = Math.cos((axisAngle / 180) * Math.PI) * radius + center[0];
|
|
89
|
+
const y = Math.sin((axisAngle / 180) * Math.PI) * radius + center[1];
|
|
90
|
+
res[node.getId()] = [x, y];
|
|
91
|
+
});
|
|
92
|
+
return res;
|
|
93
|
+
}, {});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
start() {
|
|
97
|
+
this._onLayoutChange();
|
|
98
|
+
this._onLayoutDone();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
getNodePosition = (node) => this._nodePositionMap[node.getId()];
|
|
102
|
+
|
|
103
|
+
getEdgePosition = (edge) => {
|
|
104
|
+
const {getNodeAxis} = this._options;
|
|
105
|
+
const sourceNodeId = edge.getSourceNodeId();
|
|
106
|
+
const targetNodeId = edge.getTargetNodeId();
|
|
107
|
+
|
|
108
|
+
const sourcePosition = this._nodePositionMap[sourceNodeId];
|
|
109
|
+
const targetPosition = this._nodePositionMap[targetNodeId];
|
|
110
|
+
|
|
111
|
+
const sourceNode = this._nodeMap[sourceNodeId];
|
|
112
|
+
const targetNode = this._nodeMap[targetNodeId];
|
|
113
|
+
|
|
114
|
+
const sourceNodeAxis = getNodeAxis(sourceNode);
|
|
115
|
+
const targetNodeAxis = getNodeAxis(targetNode);
|
|
116
|
+
|
|
117
|
+
if (sourceNodeAxis === targetNodeAxis) {
|
|
118
|
+
return {
|
|
119
|
+
type: EDGE_TYPE.LINE,
|
|
120
|
+
sourcePosition,
|
|
121
|
+
targetPosition,
|
|
122
|
+
controlPoints: []
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const controlPoint = computeControlPoint({
|
|
127
|
+
sourcePosition,
|
|
128
|
+
sourceNodeAxis,
|
|
129
|
+
targetPosition,
|
|
130
|
+
targetNodeAxis,
|
|
131
|
+
totalAxis: this._totalAxis
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
type: EDGE_TYPE.SPLINE_CURVE,
|
|
136
|
+
sourcePosition,
|
|
137
|
+
targetPosition,
|
|
138
|
+
controlPoints: [controlPoint]
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
lockNodePosition = (node, x, y) => {
|
|
143
|
+
this._nodePositionMap[node.id] = [x, y];
|
|
144
|
+
this._onLayoutChange();
|
|
145
|
+
this._onLayoutDone();
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function computeControlPoint({
|
|
150
|
+
sourcePosition,
|
|
151
|
+
sourceNodeAxis,
|
|
152
|
+
targetPosition,
|
|
153
|
+
targetNodeAxis,
|
|
154
|
+
totalAxis
|
|
155
|
+
}): [number, number] {
|
|
156
|
+
const halfAxis = (totalAxis - 1) / 2;
|
|
157
|
+
// check whether the source/target are at the same side.
|
|
158
|
+
const sameSide =
|
|
159
|
+
(sourceNodeAxis <= halfAxis && targetNodeAxis <= halfAxis) ||
|
|
160
|
+
(sourceNodeAxis > halfAxis && targetNodeAxis > halfAxis);
|
|
161
|
+
// curve direction
|
|
162
|
+
const direction = sameSide && sourceNodeAxis <= halfAxis && targetNodeAxis <= halfAxis ? 1 : -1;
|
|
163
|
+
|
|
164
|
+
// flip the source/target to follow the clockwise diretion
|
|
165
|
+
const source = sourceNodeAxis < targetNodeAxis && sameSide ? sourcePosition : targetPosition;
|
|
166
|
+
const target = sourceNodeAxis < targetNodeAxis && sameSide ? targetPosition : sourcePosition;
|
|
167
|
+
|
|
168
|
+
// calculate offset
|
|
169
|
+
const distance = Math.hypot(source[0] - target[0], source[1] - target[1]);
|
|
170
|
+
const offset = distance * 0.2;
|
|
171
|
+
|
|
172
|
+
const midPoint = [(source[0] + target[0]) / 2, (source[1] + target[1]) / 2];
|
|
173
|
+
const dx = target[0] - source[0];
|
|
174
|
+
const dy = target[1] - source[1];
|
|
175
|
+
const normal = [dy, -dx];
|
|
176
|
+
const length = Math.hypot(dy, -dx);
|
|
177
|
+
const normalized = [normal[0] / length, normal[1] / length];
|
|
178
|
+
return [
|
|
179
|
+
midPoint[0] + normalized[0] * offset * direction,
|
|
180
|
+
midPoint[1] + normalized[1] * offset * direction
|
|
181
|
+
];
|
|
182
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// deck.gl-community
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {GraphLayout, GraphLayoutOptions} from '../../core/graph-layout';
|
|
6
|
+
import {Node} from '../../graph/node';
|
|
7
|
+
import {EDGE_TYPE} from '../../core/constants';
|
|
8
|
+
import {Graph} from '../../graph/graph';
|
|
9
|
+
|
|
10
|
+
export type RadialLayoutOptions = GraphLayoutOptions & {
|
|
11
|
+
radius?: number;
|
|
12
|
+
tree?: any;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const traverseTree = (nodeId, nodeMap) => {
|
|
16
|
+
const node = nodeMap[nodeId];
|
|
17
|
+
if (node.isLeaf) {
|
|
18
|
+
return node;
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
...node,
|
|
22
|
+
children: node.children.map((nid) => traverseTree(nid, nodeMap))
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const getLeafNodeCount = (node, count) => {
|
|
27
|
+
if (!node.children || node.children.length === 0) {
|
|
28
|
+
return count + 1;
|
|
29
|
+
}
|
|
30
|
+
const sum = node.children.reduce((res, c) => {
|
|
31
|
+
return res + getLeafNodeCount(c, 0);
|
|
32
|
+
}, 0);
|
|
33
|
+
return count + sum;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const getTreeDepth = (node, depth = 0) => {
|
|
37
|
+
if (node.isLeaf) {
|
|
38
|
+
return depth;
|
|
39
|
+
}
|
|
40
|
+
return getTreeDepth(node.children[0], depth + 1);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const getPath = (node, targetId, path) => {
|
|
44
|
+
if (node.id === targetId) {
|
|
45
|
+
path.push(node.id);
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
const inChildren = node.children && node.children.some((c) => getPath(c, targetId, path));
|
|
49
|
+
if (inChildren) {
|
|
50
|
+
path.push(node.id);
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export class RadialLayout extends GraphLayout<RadialLayoutOptions> {
|
|
57
|
+
static defaultOptions = {
|
|
58
|
+
radius: 500
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
_name = 'RadialLayout';
|
|
62
|
+
_graph: Graph = null;
|
|
63
|
+
// custom layout data structure
|
|
64
|
+
_hierarchicalPoints = {};
|
|
65
|
+
nestedTree;
|
|
66
|
+
|
|
67
|
+
constructor(options: RadialLayoutOptions = {}) {
|
|
68
|
+
super(options);
|
|
69
|
+
this._options = {
|
|
70
|
+
...RadialLayout.defaultOptions,
|
|
71
|
+
...options
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
initializeGraph(graph: Graph): void {
|
|
76
|
+
this.updateGraph(graph);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
updateGraph(graph: Graph): void {
|
|
80
|
+
this._graph = graph;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
start(): void {
|
|
84
|
+
const nodeCount = this._graph.getNodes().length;
|
|
85
|
+
if (nodeCount === 0) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const {tree} = this._options;
|
|
90
|
+
|
|
91
|
+
if (!tree || tree.length === 0) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const {radius} = this._options;
|
|
96
|
+
const unitAngle = 360 / nodeCount;
|
|
97
|
+
|
|
98
|
+
// hierarchical positions
|
|
99
|
+
const rootNode = tree[0];
|
|
100
|
+
|
|
101
|
+
const nodeMap = tree.reduce((res, node) => {
|
|
102
|
+
res[node.id] = {
|
|
103
|
+
...node,
|
|
104
|
+
isLeaf: !node.children || node.children.length === 0
|
|
105
|
+
};
|
|
106
|
+
return res;
|
|
107
|
+
}, {});
|
|
108
|
+
// nested structure
|
|
109
|
+
this.nestedTree = traverseTree(rootNode.id, nodeMap);
|
|
110
|
+
|
|
111
|
+
const totalLevels = getTreeDepth(this.nestedTree, 0);
|
|
112
|
+
const distanceBetweenLevels = radius / (totalLevels - 1);
|
|
113
|
+
|
|
114
|
+
const calculatePosition = (node, level, startAngle, positionMap) => {
|
|
115
|
+
const isRoot = node.id === rootNode.id;
|
|
116
|
+
|
|
117
|
+
if (node.children && node.children.length !== 0) {
|
|
118
|
+
const groupSize = getLeafNodeCount(node, 0);
|
|
119
|
+
// center the pos
|
|
120
|
+
positionMap[node.id] = isRoot
|
|
121
|
+
? [0, 0]
|
|
122
|
+
: rotate(
|
|
123
|
+
0,
|
|
124
|
+
0,
|
|
125
|
+
0,
|
|
126
|
+
distanceBetweenLevels * (level + 1),
|
|
127
|
+
startAngle + unitAngle * (groupSize / 2)
|
|
128
|
+
);
|
|
129
|
+
// calculate children position
|
|
130
|
+
let tempAngle = startAngle;
|
|
131
|
+
node.children.forEach((n) => {
|
|
132
|
+
calculatePosition(n, level + 1, tempAngle, positionMap);
|
|
133
|
+
tempAngle += getLeafNodeCount(n, 0) * unitAngle;
|
|
134
|
+
});
|
|
135
|
+
} else {
|
|
136
|
+
positionMap[node.id] = rotate(
|
|
137
|
+
0,
|
|
138
|
+
0,
|
|
139
|
+
0,
|
|
140
|
+
distanceBetweenLevels * (level + 1),
|
|
141
|
+
startAngle + unitAngle
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
this._hierarchicalPoints = {};
|
|
147
|
+
calculatePosition(this.nestedTree, 0, 0, this._hierarchicalPoints);
|
|
148
|
+
// layout completes: notifiy component to re-render
|
|
149
|
+
this._onLayoutChange();
|
|
150
|
+
this._onLayoutDone();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
getNodePosition = (node) => {
|
|
154
|
+
return this._hierarchicalPoints[node.id];
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// spline curve version
|
|
158
|
+
getEdgePosition = (edge) => {
|
|
159
|
+
const sourceNodeId = edge.getSourceNodeId();
|
|
160
|
+
const targetNodeId = edge.getTargetNodeId();
|
|
161
|
+
const sourceNodePos = this._hierarchicalPoints[sourceNodeId];
|
|
162
|
+
const targetNodePos = this._hierarchicalPoints[targetNodeId];
|
|
163
|
+
|
|
164
|
+
const sourcePath = [];
|
|
165
|
+
getPath(this.nestedTree, sourceNodeId, sourcePath);
|
|
166
|
+
const targetPath = [];
|
|
167
|
+
getPath(this.nestedTree, targetNodeId, targetPath);
|
|
168
|
+
|
|
169
|
+
const totalLevels = sourcePath.length;
|
|
170
|
+
let commonAncestorLevel = totalLevels - 1; // root
|
|
171
|
+
for (let i = 0; i < totalLevels; i++) {
|
|
172
|
+
if (sourcePath[i] === targetPath[i]) {
|
|
173
|
+
commonAncestorLevel = i;
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const wayPoints = [];
|
|
179
|
+
for (let i = 1; i <= commonAncestorLevel; i++) {
|
|
180
|
+
const nodeId = sourcePath[i];
|
|
181
|
+
wayPoints.push(this._hierarchicalPoints[nodeId]);
|
|
182
|
+
}
|
|
183
|
+
for (let i = commonAncestorLevel - 1; i > 0; i--) {
|
|
184
|
+
const nodeId = targetPath[i];
|
|
185
|
+
wayPoints.push(this._hierarchicalPoints[nodeId]);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
type: EDGE_TYPE.SPLINE_CURVE,
|
|
190
|
+
sourcePosition: sourceNodePos,
|
|
191
|
+
targetPosition: targetNodePos,
|
|
192
|
+
controlPoints: wayPoints
|
|
193
|
+
};
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
lockNodePosition = (node, x, y) => {
|
|
197
|
+
this._hierarchicalPoints[node.id] = [x, y];
|
|
198
|
+
this._onLayoutChange();
|
|
199
|
+
this._onLayoutDone();
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function rotate(cx, cy, x, y, angle) {
|
|
204
|
+
const radians = (Math.PI / 180) * angle;
|
|
205
|
+
const cos = Math.cos(radians);
|
|
206
|
+
const sin = Math.sin(radians);
|
|
207
|
+
const nx = cos * (x - cx) + sin * (y - cy) + cx;
|
|
208
|
+
const ny = cos * (y - cy) - sin * (x - cx) + cy;
|
|
209
|
+
return [nx, ny];
|
|
210
|
+
}
|
|
@@ -2,21 +2,32 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {GraphLayout, GraphLayoutOptions} from '../../core/graph-layout';
|
|
6
6
|
|
|
7
7
|
import {EDGE_TYPE} from '../../core/constants';
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
alpha
|
|
11
|
-
resumeAlpha
|
|
12
|
-
nBodyStrength
|
|
13
|
-
nBodyDistanceMin
|
|
14
|
-
nBodyDistanceMax
|
|
15
|
-
getCollisionRadius
|
|
9
|
+
export type GPUForceLayoutOptions = GraphLayoutOptions & {
|
|
10
|
+
alpha?: number;
|
|
11
|
+
resumeAlpha?: number;
|
|
12
|
+
nBodyStrength?: number;
|
|
13
|
+
nBodyDistanceMin?: number;
|
|
14
|
+
nBodyDistanceMax?: number;
|
|
15
|
+
getCollisionRadius?: number;
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
/**
|
|
19
|
+
* @todo this layout should be updated with the organizational and logic improvements made in d3-force
|
|
20
|
+
*/
|
|
21
|
+
export class GPUForceLayout extends GraphLayout<GPUForceLayoutOptions> {
|
|
22
|
+
static defaultOptions: Required<GPUForceLayoutOptions> = {
|
|
23
|
+
alpha: 0.3,
|
|
24
|
+
resumeAlpha: 0.1,
|
|
25
|
+
nBodyStrength: -900,
|
|
26
|
+
nBodyDistanceMin: 100,
|
|
27
|
+
nBodyDistanceMax: 400,
|
|
28
|
+
getCollisionRadius: 0
|
|
29
|
+
};
|
|
30
|
+
|
|
20
31
|
protected readonly _name: string = 'GPU';
|
|
21
32
|
private _d3Graph: any;
|
|
22
33
|
private _nodeMap: any;
|
|
@@ -24,13 +35,17 @@ export class GPUForceLayout extends BaseLayout {
|
|
|
24
35
|
private _graph: any;
|
|
25
36
|
private _worker: Worker;
|
|
26
37
|
private _callbacks: any;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
...defaultOptions,
|
|
38
|
+
|
|
39
|
+
constructor(options: GPUForceLayoutOptions = {}) {
|
|
40
|
+
const _options = {
|
|
41
|
+
...GPUForceLayout.defaultOptions,
|
|
32
42
|
...options
|
|
33
43
|
};
|
|
44
|
+
|
|
45
|
+
super(_options);
|
|
46
|
+
|
|
47
|
+
this._name = 'GPU';
|
|
48
|
+
this._options = _options;
|
|
34
49
|
// store graph and prepare internal data
|
|
35
50
|
this._d3Graph = {nodes: [], edges: []};
|
|
36
51
|
this._nodeMap = {};
|
|
@@ -91,8 +106,8 @@ export class GPUForceLayout extends BaseLayout {
|
|
|
91
106
|
}
|
|
92
107
|
|
|
93
108
|
this._worker = new Worker(new URL('./worker.js', import.meta.url).href);
|
|
94
|
-
const {alpha, nBodyStrength, nBodyDistanceMin, nBodyDistanceMax, getCollisionRadius} =
|
|
95
|
-
._options
|
|
109
|
+
const {alpha, nBodyStrength, nBodyDistanceMin, nBodyDistanceMax, getCollisionRadius} =
|
|
110
|
+
this._options;
|
|
96
111
|
this._worker.postMessage({
|
|
97
112
|
nodes: this._d3Graph.nodes,
|
|
98
113
|
edges: this._d3Graph.edges,
|
|
@@ -2,30 +2,45 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import {Node} from '
|
|
7
|
-
import {EDGE_TYPE} from '
|
|
8
|
-
import {Graph} from '
|
|
9
|
-
|
|
10
|
-
type
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
5
|
+
import {GraphLayout, GraphLayoutOptions} from '../core/graph-layout';
|
|
6
|
+
import {Node} from '../graph/node';
|
|
7
|
+
import {EDGE_TYPE} from '../core/constants';
|
|
8
|
+
import {Graph} from '../graph/graph';
|
|
9
|
+
|
|
10
|
+
export type SimpleLayoutOptions = GraphLayoutOptions & {
|
|
11
|
+
/** The accessor lets the application supply the position ([x, y]) of each node.
|
|
12
|
+
* @example
|
|
13
|
+
```js
|
|
14
|
+
<GraphGL
|
|
15
|
+
{...otherProps}
|
|
16
|
+
layout={
|
|
17
|
+
new SimpleLayout({
|
|
18
|
+
nodePositionAccessor: node => [
|
|
19
|
+
node.getPropertyValue('x'),
|
|
20
|
+
node.getPropertyValue('y'),
|
|
21
|
+
]
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
/>
|
|
25
|
+
```
|
|
26
|
+
*/
|
|
27
|
+
nodePositionAccessor?: (node: Node) => [number, number];
|
|
19
28
|
};
|
|
20
29
|
|
|
21
|
-
|
|
30
|
+
/** A basic layout where the application controls positions of each node */
|
|
31
|
+
export class SimpleLayout extends GraphLayout<SimpleLayoutOptions> {
|
|
32
|
+
static defaultOptions: Required<SimpleLayoutOptions> = {
|
|
33
|
+
nodePositionAccessor: (node) =>
|
|
34
|
+
[node.getPropertyValue('x'), node.getPropertyValue('y')] as [number, number]
|
|
35
|
+
};
|
|
36
|
+
|
|
22
37
|
protected readonly _name = 'SimpleLayout';
|
|
23
38
|
protected _graph: Graph | null = null;
|
|
24
39
|
protected _nodeMap: Record<string, Node> = {};
|
|
25
|
-
protected _nodePositionMap: Record<string,
|
|
40
|
+
protected _nodePositionMap: Record<string, (node: Node) => [number, number]> = {};
|
|
26
41
|
|
|
27
|
-
constructor(options = {}) {
|
|
28
|
-
super({...defaultOptions, ...options});
|
|
42
|
+
constructor(options: SimpleLayoutOptions = {}) {
|
|
43
|
+
super({...SimpleLayout.defaultOptions, ...options});
|
|
29
44
|
}
|
|
30
45
|
|
|
31
46
|
initializeGraph(graph: Graph): void {
|
|
@@ -57,7 +72,7 @@ export class SimpleLayout extends BaseLayout {
|
|
|
57
72
|
return res;
|
|
58
73
|
}, {});
|
|
59
74
|
this._nodePositionMap = graph.getNodes().reduce((res, node) => {
|
|
60
|
-
res[node.getId()] =
|
|
75
|
+
res[node.getId()] = this._options.nodePositionAccessor(node);
|
|
61
76
|
return res;
|
|
62
77
|
}, {});
|
|
63
78
|
}
|
|
@@ -2,11 +2,16 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import {Edge} from '../
|
|
6
|
-
import {Node} from '../
|
|
7
|
-
import {Graph} from '../
|
|
5
|
+
import {Edge} from '../graph/edge';
|
|
6
|
+
import {Node} from '../graph/node';
|
|
7
|
+
import {Graph} from '../graph/graph';
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* @deprecated Use `new Graph(name, nodes, edges)`
|
|
11
|
+
* Create a graph from a list of Nodes and edges
|
|
12
|
+
*/
|
|
13
|
+
export function createGraph(props: {name; nodes; edges; nodeParser; edgeParser}) {
|
|
14
|
+
const {name, nodes, edges, nodeParser, edgeParser} = props;
|
|
10
15
|
// create a new empty graph
|
|
11
16
|
const graph = new Graph();
|
|
12
17
|
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
+
import type {EdgeOptions} from '../graph/edge';
|
|
5
6
|
import {log} from '../utils/log';
|
|
6
7
|
|
|
7
|
-
export function basicEdgeParser(edge) {
|
|
8
|
+
export function basicEdgeParser(edge: any): Omit<EdgeOptions, 'data'> {
|
|
8
9
|
const {id, directed, sourceId, targetId} = edge;
|
|
9
10
|
|
|
10
11
|
if (sourceId === undefined || targetId === undefined) {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import {createGraph} from '
|
|
5
|
+
import {createGraph} from './create-graph';
|
|
6
6
|
import {basicNodeParser} from './node-parsers';
|
|
7
7
|
import {basicEdgeParser} from './edge-parsers';
|
|
8
8
|
import {log} from '../utils/log';
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
+
import type {NodeOptions} from '../graph/node';
|
|
5
6
|
import {log} from '../utils/log';
|
|
6
7
|
|
|
7
|
-
export function basicNodeParser(node) {
|
|
8
|
+
export function basicNodeParser(node: any): Pick<NodeOptions, 'id'> {
|
|
8
9
|
if (node.id === undefined) {
|
|
9
10
|
log.error('Invalid node: id is missing.')();
|
|
10
11
|
return null;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// deck.gl-community
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {createGraph} from './create-graph';
|
|
6
|
+
import {log} from '../utils/log';
|
|
7
|
+
import {basicNodeParser} from './node-parsers';
|
|
8
|
+
import {basicEdgeParser} from './edge-parsers';
|
|
9
|
+
|
|
10
|
+
/** @deprecated Use loadSimpleJSONGraph */
|
|
11
|
+
export const JSONLoader = ({json, nodeParser, edgeParser}) =>
|
|
12
|
+
loadSimpleJSONGraph(json, {nodeParser, edgeParser});
|
|
13
|
+
|
|
14
|
+
/** A loader for a simple graph format */
|
|
15
|
+
export function loadSimpleJSONGraph(
|
|
16
|
+
json: Record<string, unknown>,
|
|
17
|
+
options?: {nodeParser; edgeParser}
|
|
18
|
+
) {
|
|
19
|
+
const {nodeParser = basicNodeParser, edgeParser = basicEdgeParser} = options;
|
|
20
|
+
const {name = 'default', nodes, edges} = json;
|
|
21
|
+
if (!nodes) {
|
|
22
|
+
log.error('Invalid graph: nodes is missing.')();
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const graph = createGraph({name, nodes, edges, nodeParser, edgeParser});
|
|
27
|
+
return graph;
|
|
28
|
+
}
|