@hpcc-js/graph 2.87.2 → 2.87.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/LICENSE +43 -43
- package/README.md +256 -256
- package/dist/index.es6.js +7 -7
- package/dist/index.es6.js.map +1 -1
- package/dist/index.js +7 -7
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/package.json +8 -8
- package/src/AdjacencyGraph.ts +224 -224
- package/src/Edge.css +23 -23
- package/src/Edge.ts +257 -257
- package/src/Graph.css +18 -18
- package/src/Graph.ts +1075 -1075
- package/src/GraphData.ts +187 -187
- package/src/GraphLayouts.ts +173 -173
- package/src/Sankey.css +46 -46
- package/src/Sankey.ts +291 -291
- package/src/Subgraph.css +10 -10
- package/src/Subgraph.ts +165 -165
- package/src/Vertex.css +3 -3
- package/src/Vertex.ts +282 -282
- package/src/__package__.ts +3 -3
- package/src/__tests__/data.ts +444 -444
- package/src/__tests__/index.ts +1 -1
- package/src/__tests__/test1.ts +18 -18
- package/src/__tests__/test2.ts +80 -80
- package/src/__tests__/test3.ts +46 -46
- package/src/__tests__/test4.ts +66 -66
- package/src/__tests__/test5.ts +85 -85
- package/src/graph2/dataGraph.ts +305 -305
- package/src/graph2/graph.css +34 -34
- package/src/graph2/graph.ts +135 -135
- package/src/graph2/graphReactT.ts +44 -44
- package/src/graph2/graphT.ts +1330 -1330
- package/src/graph2/index.ts +7 -7
- package/src/graph2/layouts/circle.ts +37 -37
- package/src/graph2/layouts/dagre.ts +132 -132
- package/src/graph2/layouts/dagreWorker.ts +35 -35
- package/src/graph2/layouts/forceDirected.ts +117 -117
- package/src/graph2/layouts/forceDirectedWorker.ts +30 -30
- package/src/graph2/layouts/geoForceDirected.ts +112 -112
- package/src/graph2/layouts/graphviz.ts +124 -124
- package/src/graph2/layouts/graphvizWorker.ts +71 -71
- package/src/graph2/layouts/index.ts +7 -7
- package/src/graph2/layouts/layout.ts +105 -105
- package/src/graph2/layouts/null.ts +35 -35
- package/src/graph2/layouts/placeholders.ts +103 -103
- package/src/graph2/layouts/tree.ts +328 -328
- package/src/graph2/liteMap.ts +72 -72
- package/src/graph2/liteSVGZooom.ts +61 -61
- package/src/graph2/sankeyGraph.css +45 -45
- package/src/graph2/sankeyGraph.ts +316 -316
- package/src/graph2/subgraph.tsx +30 -30
- package/src/graph2/vertex.tsx +31 -31
- package/src/index.ts +8 -8
- package/src/test.ts +649 -649
package/src/graph2/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export * from "./graphT";
|
|
2
|
-
export * from "./graphReactT";
|
|
3
|
-
export * from "./graph";
|
|
4
|
-
export * from "./dataGraph";
|
|
5
|
-
export * from "./sankeyGraph";
|
|
6
|
-
export * from "./subgraph";
|
|
7
|
-
export * from "./vertex";
|
|
1
|
+
export * from "./graphT";
|
|
2
|
+
export * from "./graphReactT";
|
|
3
|
+
export * from "./graph";
|
|
4
|
+
export * from "./dataGraph";
|
|
5
|
+
export * from "./sankeyGraph";
|
|
6
|
+
export * from "./subgraph";
|
|
7
|
+
export * from "./vertex";
|
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
import { Layout } from "./layout";
|
|
2
|
-
|
|
3
|
-
const rads = (degrees: number) => degrees * Math.PI / 180;
|
|
4
|
-
const radius = (vertexCount: number, sideLength: number) => sideLength / (2 * Math.sin(rads(180 / vertexCount)));
|
|
5
|
-
|
|
6
|
-
export class Circle extends Layout {
|
|
7
|
-
|
|
8
|
-
constructor(graph, readonly sideLength = 60) {
|
|
9
|
-
super(graph);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
start(): Promise<this> {
|
|
13
|
-
return super.start().then(() => {
|
|
14
|
-
const size = this._graph.size();
|
|
15
|
-
const data = this._graph.graphData();
|
|
16
|
-
const vertices = data.allVertices();
|
|
17
|
-
const edges = data.allEdges();
|
|
18
|
-
edges.forEach(e => delete e.points);
|
|
19
|
-
|
|
20
|
-
const r = radius(vertices.length, this.sideLength);
|
|
21
|
-
const angle = 360 / vertices.length;
|
|
22
|
-
vertices.forEach((v, i) => {
|
|
23
|
-
delete v.fx;
|
|
24
|
-
delete v.fy;
|
|
25
|
-
v.x = size.width / 2 + Math.cos(rads(i * angle)) * r;
|
|
26
|
-
v.y = size.height / 2 + Math.sin(rads(i * angle)) * r;
|
|
27
|
-
});
|
|
28
|
-
this._graph
|
|
29
|
-
.moveVertices(true)
|
|
30
|
-
.moveEdges(true)
|
|
31
|
-
;
|
|
32
|
-
this.stop();
|
|
33
|
-
this._running = false;
|
|
34
|
-
return this;
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
}
|
|
1
|
+
import { Layout } from "./layout";
|
|
2
|
+
|
|
3
|
+
const rads = (degrees: number) => degrees * Math.PI / 180;
|
|
4
|
+
const radius = (vertexCount: number, sideLength: number) => sideLength / (2 * Math.sin(rads(180 / vertexCount)));
|
|
5
|
+
|
|
6
|
+
export class Circle extends Layout {
|
|
7
|
+
|
|
8
|
+
constructor(graph, readonly sideLength = 60) {
|
|
9
|
+
super(graph);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
start(): Promise<this> {
|
|
13
|
+
return super.start().then(() => {
|
|
14
|
+
const size = this._graph.size();
|
|
15
|
+
const data = this._graph.graphData();
|
|
16
|
+
const vertices = data.allVertices();
|
|
17
|
+
const edges = data.allEdges();
|
|
18
|
+
edges.forEach(e => delete e.points);
|
|
19
|
+
|
|
20
|
+
const r = radius(vertices.length, this.sideLength);
|
|
21
|
+
const angle = 360 / vertices.length;
|
|
22
|
+
vertices.forEach((v, i) => {
|
|
23
|
+
delete v.fx;
|
|
24
|
+
delete v.fy;
|
|
25
|
+
v.x = size.width / 2 + Math.cos(rads(i * angle)) * r;
|
|
26
|
+
v.y = size.height / 2 + Math.sin(rads(i * angle)) * r;
|
|
27
|
+
});
|
|
28
|
+
this._graph
|
|
29
|
+
.moveVertices(true)
|
|
30
|
+
.moveEdges(true)
|
|
31
|
+
;
|
|
32
|
+
this.stop();
|
|
33
|
+
this._running = false;
|
|
34
|
+
return this;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -1,132 +1,132 @@
|
|
|
1
|
-
import { curveBasis as d3CurveBasis, line as d3Line } from "d3-shape";
|
|
2
|
-
import { dagre, Options } from "./dagreWorker";
|
|
3
|
-
import { Layout, Point } from "./layout";
|
|
4
|
-
import { EdgePlaceholder } from "./placeholders";
|
|
5
|
-
|
|
6
|
-
const lineBasis = d3Line<Point>()
|
|
7
|
-
.x(d => d[0])
|
|
8
|
-
.y(d => d[1])
|
|
9
|
-
.curve(d3CurveBasis)
|
|
10
|
-
;
|
|
11
|
-
|
|
12
|
-
const clusterID = (id: string | number) => `cluster_${id}`;
|
|
13
|
-
const rClusterID = (id: string) => id.substring(8);
|
|
14
|
-
|
|
15
|
-
function distance(x1, y1, x2, y2) {
|
|
16
|
-
const a = x1 - x2;
|
|
17
|
-
const b = y1 - y2;
|
|
18
|
-
return Math.sqrt(a * a + b * b);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export class Dagre extends Layout {
|
|
22
|
-
|
|
23
|
-
constructor(graph, readonly _options: Options) {
|
|
24
|
-
super(graph);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
start() {
|
|
28
|
-
super.start();
|
|
29
|
-
const size = this._graph.size();
|
|
30
|
-
const data = this._graph.graphData();
|
|
31
|
-
|
|
32
|
-
return dagre({
|
|
33
|
-
subgraphs: data.allSubgraphs().map(s => ({
|
|
34
|
-
...s.props,
|
|
35
|
-
id: clusterID(s.id)
|
|
36
|
-
})),
|
|
37
|
-
nodes: data.allVertices().map(v => {
|
|
38
|
-
delete v.fx;
|
|
39
|
-
delete v.fy;
|
|
40
|
-
const bbox = v.element.node().getBBox();
|
|
41
|
-
return {
|
|
42
|
-
width: bbox.width,
|
|
43
|
-
height: bbox.height,
|
|
44
|
-
...v.props,
|
|
45
|
-
id: String(v.id)
|
|
46
|
-
};
|
|
47
|
-
}),
|
|
48
|
-
links: data.allEdges().map(e => {
|
|
49
|
-
return {
|
|
50
|
-
id: String(e.props.id),
|
|
51
|
-
source: {
|
|
52
|
-
id: String(e.source.props.id),
|
|
53
|
-
text: e.source.props.text,
|
|
54
|
-
},
|
|
55
|
-
target: {
|
|
56
|
-
id: String(e.target.props.id),
|
|
57
|
-
text: e.target.props.text,
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
}),
|
|
61
|
-
hierarchy: [
|
|
62
|
-
...data.allSubgraphs()
|
|
63
|
-
.filter(s => !!data.subgraphParent(s.id))
|
|
64
|
-
.map(s => ({
|
|
65
|
-
parent: clusterID(data.subgraphParent(s.id).props.id),
|
|
66
|
-
child: clusterID(s.id)
|
|
67
|
-
})),
|
|
68
|
-
...data.allVertices()
|
|
69
|
-
.filter(v => data.vertexParent(v.id) !== undefined)
|
|
70
|
-
.map(v => ({
|
|
71
|
-
parent: clusterID(data.vertexParent(v.id).props.id),
|
|
72
|
-
child: String(v.id)
|
|
73
|
-
}))
|
|
74
|
-
]
|
|
75
|
-
}, this._options).response.then((response: any) => {
|
|
76
|
-
if (this.running()) {
|
|
77
|
-
response.subgraphs.forEach(n => {
|
|
78
|
-
const sg = data.subgraph(rClusterID(n.id));
|
|
79
|
-
sg.x = n.x + size.width / 2;
|
|
80
|
-
sg.y = n.y + size.height / 2;
|
|
81
|
-
sg.props.width = n.width;
|
|
82
|
-
sg.props.height = n.height;
|
|
83
|
-
});
|
|
84
|
-
response.nodes.forEach(n => {
|
|
85
|
-
const v = data.vertex(n.id);
|
|
86
|
-
v.x = n.x + size.width / 2;
|
|
87
|
-
v.y = n.y + size.height / 2;
|
|
88
|
-
});
|
|
89
|
-
response.links.forEach(l => {
|
|
90
|
-
const e = data.edge(l.id);
|
|
91
|
-
const sourceDist = distance(e.source.x, e.source.y, l.points[0][0] + size.width / 2, l.points[0][1] + size.height / 2);
|
|
92
|
-
const targetDist = distance(e.target.x, e.target.y, l.points[0][0] + size.width / 2, l.points[0][1] + size.height / 2);
|
|
93
|
-
e.points = [
|
|
94
|
-
sourceDist < targetDist ? [e.source.x, e.source.y] : [e.target.x, e.target.y],
|
|
95
|
-
...l.points.map(p => [p[0] + size.width / 2, p[1] + size.height / 2]),
|
|
96
|
-
sourceDist < targetDist ? [e.target.x, e.target.y] : [e.source.x, e.source.y]
|
|
97
|
-
];
|
|
98
|
-
});
|
|
99
|
-
this._graph
|
|
100
|
-
.moveVertices(true)
|
|
101
|
-
.moveSubgraphs(true)
|
|
102
|
-
.moveEdges(true)
|
|
103
|
-
;
|
|
104
|
-
this.stop();
|
|
105
|
-
}
|
|
106
|
-
return this;
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
edgePath(ep: EdgePlaceholder, curveDepth: number): { path: string, labelPos: Point } {
|
|
111
|
-
let points = [];
|
|
112
|
-
let hasNaN = false;
|
|
113
|
-
if (ep.points) {
|
|
114
|
-
points = ep.points.map(p => {
|
|
115
|
-
const x = this._graph.project(p[0], false);
|
|
116
|
-
const y = this._graph.project(p[1], false);
|
|
117
|
-
if (isNaN(x) || isNaN(y)) {
|
|
118
|
-
hasNaN = true;
|
|
119
|
-
}
|
|
120
|
-
return [x, y];
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
if (hasNaN || points.length < 2) {
|
|
124
|
-
return super.edgePath(ep, curveDepth);
|
|
125
|
-
}
|
|
126
|
-
return {
|
|
127
|
-
path: lineBasis(points),
|
|
128
|
-
labelPos: this.center(points)
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
}
|
|
1
|
+
import { curveBasis as d3CurveBasis, line as d3Line } from "d3-shape";
|
|
2
|
+
import { dagre, Options } from "./dagreWorker";
|
|
3
|
+
import { Layout, Point } from "./layout";
|
|
4
|
+
import { EdgePlaceholder } from "./placeholders";
|
|
5
|
+
|
|
6
|
+
const lineBasis = d3Line<Point>()
|
|
7
|
+
.x(d => d[0])
|
|
8
|
+
.y(d => d[1])
|
|
9
|
+
.curve(d3CurveBasis)
|
|
10
|
+
;
|
|
11
|
+
|
|
12
|
+
const clusterID = (id: string | number) => `cluster_${id}`;
|
|
13
|
+
const rClusterID = (id: string) => id.substring(8);
|
|
14
|
+
|
|
15
|
+
function distance(x1, y1, x2, y2) {
|
|
16
|
+
const a = x1 - x2;
|
|
17
|
+
const b = y1 - y2;
|
|
18
|
+
return Math.sqrt(a * a + b * b);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class Dagre extends Layout {
|
|
22
|
+
|
|
23
|
+
constructor(graph, readonly _options: Options) {
|
|
24
|
+
super(graph);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
start() {
|
|
28
|
+
super.start();
|
|
29
|
+
const size = this._graph.size();
|
|
30
|
+
const data = this._graph.graphData();
|
|
31
|
+
|
|
32
|
+
return dagre({
|
|
33
|
+
subgraphs: data.allSubgraphs().map(s => ({
|
|
34
|
+
...s.props,
|
|
35
|
+
id: clusterID(s.id)
|
|
36
|
+
})),
|
|
37
|
+
nodes: data.allVertices().map(v => {
|
|
38
|
+
delete v.fx;
|
|
39
|
+
delete v.fy;
|
|
40
|
+
const bbox = v.element.node().getBBox();
|
|
41
|
+
return {
|
|
42
|
+
width: bbox.width,
|
|
43
|
+
height: bbox.height,
|
|
44
|
+
...v.props,
|
|
45
|
+
id: String(v.id)
|
|
46
|
+
};
|
|
47
|
+
}),
|
|
48
|
+
links: data.allEdges().map(e => {
|
|
49
|
+
return {
|
|
50
|
+
id: String(e.props.id),
|
|
51
|
+
source: {
|
|
52
|
+
id: String(e.source.props.id),
|
|
53
|
+
text: e.source.props.text,
|
|
54
|
+
},
|
|
55
|
+
target: {
|
|
56
|
+
id: String(e.target.props.id),
|
|
57
|
+
text: e.target.props.text,
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}),
|
|
61
|
+
hierarchy: [
|
|
62
|
+
...data.allSubgraphs()
|
|
63
|
+
.filter(s => !!data.subgraphParent(s.id))
|
|
64
|
+
.map(s => ({
|
|
65
|
+
parent: clusterID(data.subgraphParent(s.id).props.id),
|
|
66
|
+
child: clusterID(s.id)
|
|
67
|
+
})),
|
|
68
|
+
...data.allVertices()
|
|
69
|
+
.filter(v => data.vertexParent(v.id) !== undefined)
|
|
70
|
+
.map(v => ({
|
|
71
|
+
parent: clusterID(data.vertexParent(v.id).props.id),
|
|
72
|
+
child: String(v.id)
|
|
73
|
+
}))
|
|
74
|
+
]
|
|
75
|
+
}, this._options).response.then((response: any) => {
|
|
76
|
+
if (this.running()) {
|
|
77
|
+
response.subgraphs.forEach(n => {
|
|
78
|
+
const sg = data.subgraph(rClusterID(n.id));
|
|
79
|
+
sg.x = n.x + size.width / 2;
|
|
80
|
+
sg.y = n.y + size.height / 2;
|
|
81
|
+
sg.props.width = n.width;
|
|
82
|
+
sg.props.height = n.height;
|
|
83
|
+
});
|
|
84
|
+
response.nodes.forEach(n => {
|
|
85
|
+
const v = data.vertex(n.id);
|
|
86
|
+
v.x = n.x + size.width / 2;
|
|
87
|
+
v.y = n.y + size.height / 2;
|
|
88
|
+
});
|
|
89
|
+
response.links.forEach(l => {
|
|
90
|
+
const e = data.edge(l.id);
|
|
91
|
+
const sourceDist = distance(e.source.x, e.source.y, l.points[0][0] + size.width / 2, l.points[0][1] + size.height / 2);
|
|
92
|
+
const targetDist = distance(e.target.x, e.target.y, l.points[0][0] + size.width / 2, l.points[0][1] + size.height / 2);
|
|
93
|
+
e.points = [
|
|
94
|
+
sourceDist < targetDist ? [e.source.x, e.source.y] : [e.target.x, e.target.y],
|
|
95
|
+
...l.points.map(p => [p[0] + size.width / 2, p[1] + size.height / 2]),
|
|
96
|
+
sourceDist < targetDist ? [e.target.x, e.target.y] : [e.source.x, e.source.y]
|
|
97
|
+
];
|
|
98
|
+
});
|
|
99
|
+
this._graph
|
|
100
|
+
.moveVertices(true)
|
|
101
|
+
.moveSubgraphs(true)
|
|
102
|
+
.moveEdges(true)
|
|
103
|
+
;
|
|
104
|
+
this.stop();
|
|
105
|
+
}
|
|
106
|
+
return this;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
edgePath(ep: EdgePlaceholder, curveDepth: number): { path: string, labelPos: Point } {
|
|
111
|
+
let points = [];
|
|
112
|
+
let hasNaN = false;
|
|
113
|
+
if (ep.points) {
|
|
114
|
+
points = ep.points.map(p => {
|
|
115
|
+
const x = this._graph.project(p[0], false);
|
|
116
|
+
const y = this._graph.project(p[1], false);
|
|
117
|
+
if (isNaN(x) || isNaN(y)) {
|
|
118
|
+
hasNaN = true;
|
|
119
|
+
}
|
|
120
|
+
return [x, y];
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
if (hasNaN || points.length < 2) {
|
|
124
|
+
return super.edgePath(ep, curveDepth);
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
path: lineBasis(points),
|
|
128
|
+
labelPos: this.center(points)
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
}
|
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
export interface Subgraph {
|
|
2
|
-
id: string;
|
|
3
|
-
text: string;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export interface Node {
|
|
7
|
-
id: string;
|
|
8
|
-
text: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface Link {
|
|
12
|
-
id: string;
|
|
13
|
-
source: Node;
|
|
14
|
-
target: Node;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface Hierarchy {
|
|
18
|
-
parent: string;
|
|
19
|
-
child: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface Data {
|
|
23
|
-
subgraphs: Subgraph[];
|
|
24
|
-
nodes: Node[];
|
|
25
|
-
links: Link[];
|
|
26
|
-
hierarchy: Hierarchy[];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface Options {
|
|
30
|
-
rankdir: "TB" | "BT" | "LR" | "RL";
|
|
31
|
-
nodesep: number;
|
|
32
|
-
edgesep: number;
|
|
33
|
-
ranksep: number;
|
|
34
|
-
digraph: boolean;
|
|
35
|
-
}
|
|
1
|
+
export interface Subgraph {
|
|
2
|
+
id: string;
|
|
3
|
+
text: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface Node {
|
|
7
|
+
id: string;
|
|
8
|
+
text: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface Link {
|
|
12
|
+
id: string;
|
|
13
|
+
source: Node;
|
|
14
|
+
target: Node;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface Hierarchy {
|
|
18
|
+
parent: string;
|
|
19
|
+
child: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface Data {
|
|
23
|
+
subgraphs: Subgraph[];
|
|
24
|
+
nodes: Node[];
|
|
25
|
+
links: Link[];
|
|
26
|
+
hierarchy: Hierarchy[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface Options {
|
|
30
|
+
rankdir: "TB" | "BT" | "LR" | "RL";
|
|
31
|
+
nodesep: number;
|
|
32
|
+
edgesep: number;
|
|
33
|
+
ranksep: number;
|
|
34
|
+
digraph: boolean;
|
|
35
|
+
}
|
|
36
36
|
|
|
37
37
|
export function dagre(data: Data, options: Options) {
|
|
38
38
|
// eslint-disable-next-line quotes
|
|
@@ -1,117 +1,117 @@
|
|
|
1
|
-
import { forceCenter as d3ForceCenter, forceLink as d3ForceLink, forceManyBody as d3ForceManyBody, forceSimulation as d3ForceSimulation, forceX as d3ForceX, forceY as d3ForceY } from "d3-force";
|
|
2
|
-
|
|
3
|
-
import { Layout } from "./layout";
|
|
4
|
-
|
|
5
|
-
import { Options } from "./forceDirectedWorker";
|
|
6
|
-
|
|
7
|
-
// Non worker ---
|
|
8
|
-
|
|
9
|
-
export class ForceDirectedBase extends Layout {
|
|
10
|
-
|
|
11
|
-
protected _links;
|
|
12
|
-
protected _charge;
|
|
13
|
-
protected _center;
|
|
14
|
-
protected _simulation;
|
|
15
|
-
|
|
16
|
-
constructor(graph, readonly _options: Options) {
|
|
17
|
-
super(graph);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
start() {
|
|
21
|
-
return super.start().then(() => {
|
|
22
|
-
|
|
23
|
-
const size = this._graph.size();
|
|
24
|
-
const data = this._graph.graphData();
|
|
25
|
-
const vertices = data.allVertices();
|
|
26
|
-
const edges = data.allEdges();
|
|
27
|
-
edges.forEach(e => delete e.points);
|
|
28
|
-
|
|
29
|
-
this._links = d3ForceLink(edges)
|
|
30
|
-
.id(d => d.id)
|
|
31
|
-
.distance(this._options.linkDistance)
|
|
32
|
-
.strength(this._options.linkStrength)
|
|
33
|
-
;
|
|
34
|
-
|
|
35
|
-
this._charge = d3ForceManyBody()
|
|
36
|
-
.strength(this._options.repulsionStrength)
|
|
37
|
-
.distanceMin(this._options.distanceMin)
|
|
38
|
-
.distanceMax(this._options.distanceMax)
|
|
39
|
-
;
|
|
40
|
-
|
|
41
|
-
this._center = d3ForceCenter(size.width / 2, size.height / 2);
|
|
42
|
-
|
|
43
|
-
const fx = d3ForceX()
|
|
44
|
-
.strength(this._options.forceStrength)
|
|
45
|
-
;
|
|
46
|
-
const fy = d3ForceY()
|
|
47
|
-
.strength(this._options.forceStrength)
|
|
48
|
-
;
|
|
49
|
-
|
|
50
|
-
this._simulation = d3ForceSimulation(vertices.map(v => {
|
|
51
|
-
const { width, height } = v.element.node().getBBox();
|
|
52
|
-
v.fx = (this._options.pinCentroid && v.props.centroid) ? size.width / 2 : undefined;
|
|
53
|
-
v.fy = (this._options.pinCentroid && v.props.centroid) ? size.height / 2 : undefined;
|
|
54
|
-
v["width"] = width;
|
|
55
|
-
v["height"] = height;
|
|
56
|
-
return v;
|
|
57
|
-
}))
|
|
58
|
-
.force("link", this._links)
|
|
59
|
-
.force("charge", this._charge)
|
|
60
|
-
.force("center", this._center)
|
|
61
|
-
.force("x", fx)
|
|
62
|
-
.force("y", fy)
|
|
63
|
-
.alpha(this._options.alpha / 2)
|
|
64
|
-
.alphaMin(this._options.alphaMin)
|
|
65
|
-
.alphaDecay(this._options.alphaDecay)
|
|
66
|
-
.velocityDecay(this._options.velocityDecay)
|
|
67
|
-
.stop()
|
|
68
|
-
;
|
|
69
|
-
|
|
70
|
-
return this;
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
stop() {
|
|
75
|
-
this._simulation.stop();
|
|
76
|
-
return super.stop();
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export class ForceDirected extends ForceDirectedBase {
|
|
81
|
-
|
|
82
|
-
start() {
|
|
83
|
-
return super.start().then(() => {
|
|
84
|
-
this._simulation.tick(this._options.iterations);
|
|
85
|
-
this.stop();
|
|
86
|
-
this._graph
|
|
87
|
-
.moveVertices(false)
|
|
88
|
-
.moveEdges(false)
|
|
89
|
-
;
|
|
90
|
-
return this;
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export class ForceDirectedAnimated extends ForceDirectedBase {
|
|
96
|
-
|
|
97
|
-
start() {
|
|
98
|
-
return super.start().then(() => {
|
|
99
|
-
return new Promise<this>(resolve => {
|
|
100
|
-
this._simulation
|
|
101
|
-
.on("tick", () => {
|
|
102
|
-
this._graph
|
|
103
|
-
.moveVertices(false)
|
|
104
|
-
.moveEdges(false)
|
|
105
|
-
;
|
|
106
|
-
this._graph.progress("layout-tick");
|
|
107
|
-
})
|
|
108
|
-
.on("end", () => {
|
|
109
|
-
this._running = false;
|
|
110
|
-
resolve(this);
|
|
111
|
-
})
|
|
112
|
-
.restart()
|
|
113
|
-
;
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
}
|
|
1
|
+
import { forceCenter as d3ForceCenter, forceLink as d3ForceLink, forceManyBody as d3ForceManyBody, forceSimulation as d3ForceSimulation, forceX as d3ForceX, forceY as d3ForceY } from "d3-force";
|
|
2
|
+
|
|
3
|
+
import { Layout } from "./layout";
|
|
4
|
+
|
|
5
|
+
import { Options } from "./forceDirectedWorker";
|
|
6
|
+
|
|
7
|
+
// Non worker ---
|
|
8
|
+
|
|
9
|
+
export class ForceDirectedBase extends Layout {
|
|
10
|
+
|
|
11
|
+
protected _links;
|
|
12
|
+
protected _charge;
|
|
13
|
+
protected _center;
|
|
14
|
+
protected _simulation;
|
|
15
|
+
|
|
16
|
+
constructor(graph, readonly _options: Options) {
|
|
17
|
+
super(graph);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
start() {
|
|
21
|
+
return super.start().then(() => {
|
|
22
|
+
|
|
23
|
+
const size = this._graph.size();
|
|
24
|
+
const data = this._graph.graphData();
|
|
25
|
+
const vertices = data.allVertices();
|
|
26
|
+
const edges = data.allEdges();
|
|
27
|
+
edges.forEach(e => delete e.points);
|
|
28
|
+
|
|
29
|
+
this._links = d3ForceLink(edges)
|
|
30
|
+
.id(d => d.id)
|
|
31
|
+
.distance(this._options.linkDistance)
|
|
32
|
+
.strength(this._options.linkStrength)
|
|
33
|
+
;
|
|
34
|
+
|
|
35
|
+
this._charge = d3ForceManyBody()
|
|
36
|
+
.strength(this._options.repulsionStrength)
|
|
37
|
+
.distanceMin(this._options.distanceMin)
|
|
38
|
+
.distanceMax(this._options.distanceMax)
|
|
39
|
+
;
|
|
40
|
+
|
|
41
|
+
this._center = d3ForceCenter(size.width / 2, size.height / 2);
|
|
42
|
+
|
|
43
|
+
const fx = d3ForceX()
|
|
44
|
+
.strength(this._options.forceStrength)
|
|
45
|
+
;
|
|
46
|
+
const fy = d3ForceY()
|
|
47
|
+
.strength(this._options.forceStrength)
|
|
48
|
+
;
|
|
49
|
+
|
|
50
|
+
this._simulation = d3ForceSimulation(vertices.map(v => {
|
|
51
|
+
const { width, height } = v.element.node().getBBox();
|
|
52
|
+
v.fx = (this._options.pinCentroid && v.props.centroid) ? size.width / 2 : undefined;
|
|
53
|
+
v.fy = (this._options.pinCentroid && v.props.centroid) ? size.height / 2 : undefined;
|
|
54
|
+
v["width"] = width;
|
|
55
|
+
v["height"] = height;
|
|
56
|
+
return v;
|
|
57
|
+
}))
|
|
58
|
+
.force("link", this._links)
|
|
59
|
+
.force("charge", this._charge)
|
|
60
|
+
.force("center", this._center)
|
|
61
|
+
.force("x", fx)
|
|
62
|
+
.force("y", fy)
|
|
63
|
+
.alpha(this._options.alpha / 2)
|
|
64
|
+
.alphaMin(this._options.alphaMin)
|
|
65
|
+
.alphaDecay(this._options.alphaDecay)
|
|
66
|
+
.velocityDecay(this._options.velocityDecay)
|
|
67
|
+
.stop()
|
|
68
|
+
;
|
|
69
|
+
|
|
70
|
+
return this;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
stop() {
|
|
75
|
+
this._simulation.stop();
|
|
76
|
+
return super.stop();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export class ForceDirected extends ForceDirectedBase {
|
|
81
|
+
|
|
82
|
+
start() {
|
|
83
|
+
return super.start().then(() => {
|
|
84
|
+
this._simulation.tick(this._options.iterations);
|
|
85
|
+
this.stop();
|
|
86
|
+
this._graph
|
|
87
|
+
.moveVertices(false)
|
|
88
|
+
.moveEdges(false)
|
|
89
|
+
;
|
|
90
|
+
return this;
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export class ForceDirectedAnimated extends ForceDirectedBase {
|
|
96
|
+
|
|
97
|
+
start() {
|
|
98
|
+
return super.start().then(() => {
|
|
99
|
+
return new Promise<this>(resolve => {
|
|
100
|
+
this._simulation
|
|
101
|
+
.on("tick", () => {
|
|
102
|
+
this._graph
|
|
103
|
+
.moveVertices(false)
|
|
104
|
+
.moveEdges(false)
|
|
105
|
+
;
|
|
106
|
+
this._graph.progress("layout-tick");
|
|
107
|
+
})
|
|
108
|
+
.on("end", () => {
|
|
109
|
+
this._running = false;
|
|
110
|
+
resolve(this);
|
|
111
|
+
})
|
|
112
|
+
.restart()
|
|
113
|
+
;
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|