@hpcc-js/graph 3.7.4 → 3.7.6
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/assets/dagre-B-z4SP0u.js.map +1 -1
- package/dist/assets/graphviz-BkFKgQ-y.js.map +1 -0
- package/dist/index.js +26 -26
- package/dist/index.js.map +1 -1
- package/dist/index.umd.cjs +22 -22
- package/dist/index.umd.cjs.map +1 -1
- package/package.json +12 -10
- package/src/AdjacencyGraph.ts +224 -224
- package/src/Edge.css +22 -22
- package/src/Edge.ts +257 -257
- package/src/Graph.css +18 -18
- package/src/Graph.ts +1077 -1077
- package/src/GraphData.ts +187 -187
- package/src/GraphLayouts.ts +214 -214
- package/src/Sankey.css +44 -44
- package/src/Sankey.ts +304 -304
- package/src/Subgraph.css +9 -9
- package/src/Subgraph.ts +165 -165
- package/src/Vertex.css +2 -2
- 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/common/graphT.css +38 -38
- package/src/common/graphT.ts +1363 -1363
- package/src/common/index.ts +3 -3
- package/src/common/layouts/circle.ts +37 -37
- package/src/common/layouts/dagre.ts +145 -145
- package/src/common/layouts/dagreWorker.ts +24 -24
- package/src/common/layouts/forceDirected.ts +117 -117
- package/src/common/layouts/forceDirectedWorker.ts +22 -22
- package/src/common/layouts/geoForceDirected.ts +112 -112
- package/src/common/layouts/graphviz.ts +137 -137
- package/src/common/layouts/graphvizWorker.ts +27 -27
- package/src/common/layouts/index.ts +7 -7
- package/src/common/layouts/layout.ts +147 -147
- package/src/common/layouts/null.ts +39 -39
- package/src/common/layouts/placeholders.ts +113 -113
- package/src/common/layouts/tree.ts +326 -326
- package/src/common/layouts/workers/dagre.ts +46 -46
- package/src/common/layouts/workers/dagreOptions.ts +35 -35
- package/src/common/layouts/workers/forceDirected.ts +38 -38
- package/src/common/layouts/workers/forceDirectedOptions.ts +30 -30
- package/src/common/layouts/workers/graphviz.ts +225 -225
- package/src/common/layouts/workers/graphvizOptions.ts +70 -70
- package/src/common/liteMap.ts +72 -72
- package/src/common/liteSVGZooom.ts +61 -61
- package/src/common/sankeyGraph.css +44 -44
- package/src/common/sankeyGraph.ts +345 -345
- package/src/html/annotation.ts +71 -71
- package/src/html/component.ts +18 -18
- package/src/html/edge.ts +15 -15
- package/src/html/graphHtml.ts +11 -11
- package/src/html/graphHtmlT.ts +117 -117
- package/src/html/icon.ts +64 -64
- package/src/html/image.ts +26 -26
- package/src/html/imageChar.ts +18 -18
- package/src/html/index.ts +8 -8
- package/src/html/intersection.ts +110 -110
- package/src/html/shape.ts +141 -141
- package/src/html/text.ts +59 -59
- package/src/html/textBox.ts +45 -45
- package/src/html/vertex.ts +67 -67
- package/src/index.ts +10 -10
- package/src/react/dataGraph.ts +345 -345
- package/src/react/graphReact.ts +177 -177
- package/src/react/graphReactT.ts +44 -44
- package/src/react/index.ts +4 -4
- package/src/react/subgraph.tsx +30 -30
- package/src/react/vertex.tsx +31 -31
- package/dist/assets/graphviz-BK7FEJlA.js.map +0 -1
|
@@ -1,327 +1,327 @@
|
|
|
1
|
-
import { Graph2 } from "@hpcc-js/util";
|
|
2
|
-
import { cluster, hierarchy, tree } from "d3-hierarchy";
|
|
3
|
-
import { linkHorizontal as d3LinkHorizontal } from "d3-shape";
|
|
4
|
-
import { Hierarchy } from "./dagreWorker.ts";
|
|
5
|
-
import { Layout, Point } from "./layout.ts";
|
|
6
|
-
import { VertexPlaceholder, EdgePlaceholder, SubgraphPlaceholder } from "./placeholders.ts";
|
|
7
|
-
|
|
8
|
-
const linkHorizontal = d3LinkHorizontal<any, { x: number, y: number }>()
|
|
9
|
-
.x(d => d.x)
|
|
10
|
-
.y(d => d.y)
|
|
11
|
-
;
|
|
12
|
-
|
|
13
|
-
class Bounds {
|
|
14
|
-
|
|
15
|
-
get x() {
|
|
16
|
-
return this.x1;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
get y() {
|
|
20
|
-
return this.y1;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
get width() {
|
|
24
|
-
return this.x2 - this.x1;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
get height() {
|
|
28
|
-
return this.y2 - this.y1;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
get center() {
|
|
32
|
-
return {
|
|
33
|
-
x: this.x1 + this.width / 2,
|
|
34
|
-
y: this.y1 + this.height / 2
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
constructor(public x1: number = 0, public y1: number = 0, public x2: number = x1, public y2: number = y1) {
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
reset(x: number, y: number) {
|
|
42
|
-
this.x1 = x;
|
|
43
|
-
this.x2 = x;
|
|
44
|
-
this.y1 = y;
|
|
45
|
-
this.y2 = y;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
expand(x: number, y: number) {
|
|
49
|
-
if (this.x1 > x) {
|
|
50
|
-
this.x1 = x;
|
|
51
|
-
} else if (this.x2 < x) {
|
|
52
|
-
this.x2 = x;
|
|
53
|
-
}
|
|
54
|
-
if (this.y1 > y) {
|
|
55
|
-
this.y1 = y;
|
|
56
|
-
} else if (this.y2 < y) {
|
|
57
|
-
this.y2 = y;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
interface Node {
|
|
63
|
-
origData: VertexPlaceholder;
|
|
64
|
-
children: Node[];
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export interface EdgeLayout {
|
|
68
|
-
path: string;
|
|
69
|
-
labelPos: Point;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export interface Options {
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export interface TidyTreeOptions extends Options {
|
|
77
|
-
rankdir: "TB" | "LR";
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export class TidyTreeBase extends Layout {
|
|
81
|
-
|
|
82
|
-
constructor(graph, protected options: Options) {
|
|
83
|
-
super(graph);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
private _visited = {};
|
|
87
|
-
protected _tree: Node;
|
|
88
|
-
protected _d3Hierarchy: Hierarchy;
|
|
89
|
-
|
|
90
|
-
protected sortTree(data: Graph2, node?: Node) {
|
|
91
|
-
if (!node) {
|
|
92
|
-
node = this._tree;
|
|
93
|
-
}
|
|
94
|
-
// Place busiest children at the top of the list ---
|
|
95
|
-
node.children.sort((l, r) => data.neighbors(r.origData.id).length - data.neighbors(l.origData.id).length);
|
|
96
|
-
|
|
97
|
-
// Move busiest children to the middle of the list ---
|
|
98
|
-
const children: Node[] = [];
|
|
99
|
-
node.children.forEach((n, i) => {
|
|
100
|
-
if (i % 2 === 0) {
|
|
101
|
-
children.push(n);
|
|
102
|
-
} else {
|
|
103
|
-
children.unshift(n);
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
node.children = children;
|
|
107
|
-
|
|
108
|
-
// Recurse ---
|
|
109
|
-
node.children.forEach(cnode => this.sortTree(data, cnode));
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
protected depthFirst(data: Graph2<VertexPlaceholder, EdgePlaceholder, SubgraphPlaceholder>, v: VertexPlaceholder, parent?) {
|
|
113
|
-
if (parent === undefined) {
|
|
114
|
-
this._visited = {};
|
|
115
|
-
this._tree = undefined;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (!this._visited[v.id]) {
|
|
119
|
-
this._visited[v.id] = v;
|
|
120
|
-
const node: Node = {
|
|
121
|
-
origData: v,
|
|
122
|
-
children: []
|
|
123
|
-
};
|
|
124
|
-
if (parent === undefined) {
|
|
125
|
-
this._tree = node;
|
|
126
|
-
} else {
|
|
127
|
-
parent.children.push(node);
|
|
128
|
-
}
|
|
129
|
-
data.neighbors(v.id).forEach(n => this.depthFirst(data, n, node));
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
protected breadthFirst(data: Graph2<VertexPlaceholder, EdgePlaceholder, SubgraphPlaceholder>, v: VertexPlaceholder) {
|
|
134
|
-
this._visited = {};
|
|
135
|
-
this._visited[v.id] = v;
|
|
136
|
-
this._tree = {
|
|
137
|
-
origData: v,
|
|
138
|
-
children: []
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
const q: Node[] = [];
|
|
142
|
-
q.push(this._tree);
|
|
143
|
-
while (q.length) {
|
|
144
|
-
const node = q.shift();
|
|
145
|
-
data.neighbors(node.origData.id).forEach(n => {
|
|
146
|
-
if (!this._visited[n.id]) {
|
|
147
|
-
this._visited[n.id] = n;
|
|
148
|
-
const nnode = {
|
|
149
|
-
origData: n,
|
|
150
|
-
children: []
|
|
151
|
-
};
|
|
152
|
-
node.children.push(nnode);
|
|
153
|
-
q.push(nnode);
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
this.sortTree(data);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
start(): Promise<this> {
|
|
161
|
-
return super.start().then(() => {
|
|
162
|
-
const data = this._graph.graphData();
|
|
163
|
-
const vertices = data.allVertices();
|
|
164
|
-
|
|
165
|
-
let centroid; // TODO Could Be Many (default should be all with 0 in edges?)
|
|
166
|
-
for (const v of vertices) {
|
|
167
|
-
delete v.fx;
|
|
168
|
-
delete v.fy;
|
|
169
|
-
if (centroid === undefined) {
|
|
170
|
-
centroid = v;
|
|
171
|
-
}
|
|
172
|
-
if (v.props.centroid) {
|
|
173
|
-
centroid = v;
|
|
174
|
-
break;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const edges = data.allEdges();
|
|
179
|
-
edges.forEach(e => delete e.points);
|
|
180
|
-
|
|
181
|
-
this.breadthFirst(data, centroid);
|
|
182
|
-
this._d3Hierarchy = hierarchy(this._tree);
|
|
183
|
-
return this;
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
finalize(nodes: Node[]) {
|
|
188
|
-
const size = this._graph.size();
|
|
189
|
-
|
|
190
|
-
const bounds = new Bounds();
|
|
191
|
-
nodes.forEach((d, i) => {
|
|
192
|
-
if (i === 0) {
|
|
193
|
-
bounds.reset(d.origData.x, d.origData.y);
|
|
194
|
-
} else {
|
|
195
|
-
bounds.expand(d.origData.x, d.origData.y);
|
|
196
|
-
}
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
const offset = {
|
|
200
|
-
x: size.width / 2 - bounds.center.x,
|
|
201
|
-
y: size.height / 2 - bounds.center.y
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
nodes.forEach(d => {
|
|
205
|
-
d.origData.x += offset.x;
|
|
206
|
-
d.origData.y += offset.y;
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
this._graph
|
|
210
|
-
.moveVertices(true)
|
|
211
|
-
.moveEdges(true)
|
|
212
|
-
;
|
|
213
|
-
this.stop();
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
export class Tree extends TidyTreeBase {
|
|
218
|
-
|
|
219
|
-
constructor(graph, protected options: TidyTreeOptions) {
|
|
220
|
-
super(graph, options);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
start(): Promise<this> {
|
|
224
|
-
return super.start().then(() => {
|
|
225
|
-
const treeFunc = tree().nodeSize([this.options.rankdir === "TB" ? 200 : 100, this.options.rankdir === "TB" ? 100 : 200]);
|
|
226
|
-
const root = treeFunc(this._d3Hierarchy);
|
|
227
|
-
const nodes: Node[] = root.descendants().map((d) => {
|
|
228
|
-
d.data.origData.x = this.options.rankdir === "TB" ? d.x : d.y;
|
|
229
|
-
d.data.origData.y = this.options.rankdir === "TB" ? d.y : d.x;
|
|
230
|
-
return d.data;
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
this.finalize(nodes);
|
|
234
|
-
return this;
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
edgePath(ep: EdgePlaceholder, curveDepth: number): EdgeLayout {
|
|
239
|
-
const { source, target } = this.edgeLine(ep);
|
|
240
|
-
return {
|
|
241
|
-
path: linkHorizontal({ source, target }),
|
|
242
|
-
labelPos: this.center([[source.x, source.y], [target.x, target.y]])
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
export class RadialTree extends TidyTreeBase {
|
|
248
|
-
|
|
249
|
-
constructor(graph) {
|
|
250
|
-
super(graph, {});
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
start(): Promise<this> {
|
|
254
|
-
return super.start().then(() => {
|
|
255
|
-
const treeFunc = tree()
|
|
256
|
-
.size([2 * Math.PI, 1024 / 2])
|
|
257
|
-
.separation((a, b) => (a.parent == b.parent ? 1 : 2) / a.depth)
|
|
258
|
-
;
|
|
259
|
-
const root = treeFunc(this._d3Hierarchy);
|
|
260
|
-
const nodes: Node[] = root.descendants().map((d) => {
|
|
261
|
-
d.data.origData.angle = d.x;
|
|
262
|
-
d.data.origData.radius = d.y;
|
|
263
|
-
d.data.origData.x = Math.sin(d.x) * d.y;
|
|
264
|
-
d.data.origData.y = Math.cos(d.x) * d.y;
|
|
265
|
-
return d.data;
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
this.finalize(nodes);
|
|
269
|
-
return this;
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
export class Dendrogram extends TidyTreeBase {
|
|
275
|
-
|
|
276
|
-
constructor(graph, protected options: TidyTreeOptions) {
|
|
277
|
-
super(graph, options);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
start(): Promise<this> {
|
|
281
|
-
return super.start().then(() => {
|
|
282
|
-
const treeFunc = cluster().nodeSize([this.options.rankdir === "TB" ? 200 : 100, this.options.rankdir === "TB" ? 100 : 200]);
|
|
283
|
-
const root = treeFunc(this._d3Hierarchy);
|
|
284
|
-
const nodes: Node[] = root.descendants().map((d) => {
|
|
285
|
-
d.data.origData.x = this.options.rankdir === "TB" ? d.x : d.y;
|
|
286
|
-
d.data.origData.y = this.options.rankdir === "TB" ? d.y : d.x;
|
|
287
|
-
return d.data;
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
this.finalize(nodes);
|
|
291
|
-
return this;
|
|
292
|
-
});
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
edgePath(ep: EdgePlaceholder, curveDepth: number): EdgeLayout {
|
|
296
|
-
const { source, target } = this.edgeLine(ep);
|
|
297
|
-
return {
|
|
298
|
-
path: linkHorizontal({ source, target }),
|
|
299
|
-
labelPos: this.center([[source.x, source.y], [target.x, target.y]])
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
export class RadialDendrogram extends TidyTreeBase {
|
|
305
|
-
|
|
306
|
-
constructor(graph) {
|
|
307
|
-
super(graph, {});
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
start(): Promise<this> {
|
|
311
|
-
return super.start().then(() => {
|
|
312
|
-
const treeFunc = cluster()
|
|
313
|
-
.size([2 * Math.PI, 1024 / 2])
|
|
314
|
-
.separation((a, b) => (a.parent == b.parent ? 1 : 2) / a.depth)
|
|
315
|
-
;
|
|
316
|
-
const root = treeFunc(this._d3Hierarchy);
|
|
317
|
-
const nodes: Node[] = root.descendants().map((d) => {
|
|
318
|
-
d.data.origData.x = Math.sin(d.x) * d.y;
|
|
319
|
-
d.data.origData.y = Math.cos(d.x) * d.y;
|
|
320
|
-
return d.data;
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
this.finalize(nodes);
|
|
324
|
-
return this;
|
|
325
|
-
});
|
|
326
|
-
}
|
|
1
|
+
import { Graph2 } from "@hpcc-js/util";
|
|
2
|
+
import { cluster, hierarchy, tree } from "d3-hierarchy";
|
|
3
|
+
import { linkHorizontal as d3LinkHorizontal } from "d3-shape";
|
|
4
|
+
import { Hierarchy } from "./dagreWorker.ts";
|
|
5
|
+
import { Layout, Point } from "./layout.ts";
|
|
6
|
+
import { VertexPlaceholder, EdgePlaceholder, SubgraphPlaceholder } from "./placeholders.ts";
|
|
7
|
+
|
|
8
|
+
const linkHorizontal = d3LinkHorizontal<any, { x: number, y: number }>()
|
|
9
|
+
.x(d => d.x)
|
|
10
|
+
.y(d => d.y)
|
|
11
|
+
;
|
|
12
|
+
|
|
13
|
+
class Bounds {
|
|
14
|
+
|
|
15
|
+
get x() {
|
|
16
|
+
return this.x1;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get y() {
|
|
20
|
+
return this.y1;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get width() {
|
|
24
|
+
return this.x2 - this.x1;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get height() {
|
|
28
|
+
return this.y2 - this.y1;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
get center() {
|
|
32
|
+
return {
|
|
33
|
+
x: this.x1 + this.width / 2,
|
|
34
|
+
y: this.y1 + this.height / 2
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
constructor(public x1: number = 0, public y1: number = 0, public x2: number = x1, public y2: number = y1) {
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
reset(x: number, y: number) {
|
|
42
|
+
this.x1 = x;
|
|
43
|
+
this.x2 = x;
|
|
44
|
+
this.y1 = y;
|
|
45
|
+
this.y2 = y;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
expand(x: number, y: number) {
|
|
49
|
+
if (this.x1 > x) {
|
|
50
|
+
this.x1 = x;
|
|
51
|
+
} else if (this.x2 < x) {
|
|
52
|
+
this.x2 = x;
|
|
53
|
+
}
|
|
54
|
+
if (this.y1 > y) {
|
|
55
|
+
this.y1 = y;
|
|
56
|
+
} else if (this.y2 < y) {
|
|
57
|
+
this.y2 = y;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface Node {
|
|
63
|
+
origData: VertexPlaceholder;
|
|
64
|
+
children: Node[];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface EdgeLayout {
|
|
68
|
+
path: string;
|
|
69
|
+
labelPos: Point;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface Options {
|
|
73
|
+
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface TidyTreeOptions extends Options {
|
|
77
|
+
rankdir: "TB" | "LR";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export class TidyTreeBase extends Layout {
|
|
81
|
+
|
|
82
|
+
constructor(graph, protected options: Options) {
|
|
83
|
+
super(graph);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private _visited = {};
|
|
87
|
+
protected _tree: Node;
|
|
88
|
+
protected _d3Hierarchy: Hierarchy;
|
|
89
|
+
|
|
90
|
+
protected sortTree(data: Graph2, node?: Node) {
|
|
91
|
+
if (!node) {
|
|
92
|
+
node = this._tree;
|
|
93
|
+
}
|
|
94
|
+
// Place busiest children at the top of the list ---
|
|
95
|
+
node.children.sort((l, r) => data.neighbors(r.origData.id).length - data.neighbors(l.origData.id).length);
|
|
96
|
+
|
|
97
|
+
// Move busiest children to the middle of the list ---
|
|
98
|
+
const children: Node[] = [];
|
|
99
|
+
node.children.forEach((n, i) => {
|
|
100
|
+
if (i % 2 === 0) {
|
|
101
|
+
children.push(n);
|
|
102
|
+
} else {
|
|
103
|
+
children.unshift(n);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
node.children = children;
|
|
107
|
+
|
|
108
|
+
// Recurse ---
|
|
109
|
+
node.children.forEach(cnode => this.sortTree(data, cnode));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
protected depthFirst(data: Graph2<VertexPlaceholder, EdgePlaceholder, SubgraphPlaceholder>, v: VertexPlaceholder, parent?) {
|
|
113
|
+
if (parent === undefined) {
|
|
114
|
+
this._visited = {};
|
|
115
|
+
this._tree = undefined;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (!this._visited[v.id]) {
|
|
119
|
+
this._visited[v.id] = v;
|
|
120
|
+
const node: Node = {
|
|
121
|
+
origData: v,
|
|
122
|
+
children: []
|
|
123
|
+
};
|
|
124
|
+
if (parent === undefined) {
|
|
125
|
+
this._tree = node;
|
|
126
|
+
} else {
|
|
127
|
+
parent.children.push(node);
|
|
128
|
+
}
|
|
129
|
+
data.neighbors(v.id).forEach(n => this.depthFirst(data, n, node));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
protected breadthFirst(data: Graph2<VertexPlaceholder, EdgePlaceholder, SubgraphPlaceholder>, v: VertexPlaceholder) {
|
|
134
|
+
this._visited = {};
|
|
135
|
+
this._visited[v.id] = v;
|
|
136
|
+
this._tree = {
|
|
137
|
+
origData: v,
|
|
138
|
+
children: []
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const q: Node[] = [];
|
|
142
|
+
q.push(this._tree);
|
|
143
|
+
while (q.length) {
|
|
144
|
+
const node = q.shift();
|
|
145
|
+
data.neighbors(node.origData.id).forEach(n => {
|
|
146
|
+
if (!this._visited[n.id]) {
|
|
147
|
+
this._visited[n.id] = n;
|
|
148
|
+
const nnode = {
|
|
149
|
+
origData: n,
|
|
150
|
+
children: []
|
|
151
|
+
};
|
|
152
|
+
node.children.push(nnode);
|
|
153
|
+
q.push(nnode);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
this.sortTree(data);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
start(): Promise<this> {
|
|
161
|
+
return super.start().then(() => {
|
|
162
|
+
const data = this._graph.graphData();
|
|
163
|
+
const vertices = data.allVertices();
|
|
164
|
+
|
|
165
|
+
let centroid; // TODO Could Be Many (default should be all with 0 in edges?)
|
|
166
|
+
for (const v of vertices) {
|
|
167
|
+
delete v.fx;
|
|
168
|
+
delete v.fy;
|
|
169
|
+
if (centroid === undefined) {
|
|
170
|
+
centroid = v;
|
|
171
|
+
}
|
|
172
|
+
if (v.props.centroid) {
|
|
173
|
+
centroid = v;
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const edges = data.allEdges();
|
|
179
|
+
edges.forEach(e => delete e.points);
|
|
180
|
+
|
|
181
|
+
this.breadthFirst(data, centroid);
|
|
182
|
+
this._d3Hierarchy = hierarchy(this._tree);
|
|
183
|
+
return this;
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
finalize(nodes: Node[]) {
|
|
188
|
+
const size = this._graph.size();
|
|
189
|
+
|
|
190
|
+
const bounds = new Bounds();
|
|
191
|
+
nodes.forEach((d, i) => {
|
|
192
|
+
if (i === 0) {
|
|
193
|
+
bounds.reset(d.origData.x, d.origData.y);
|
|
194
|
+
} else {
|
|
195
|
+
bounds.expand(d.origData.x, d.origData.y);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const offset = {
|
|
200
|
+
x: size.width / 2 - bounds.center.x,
|
|
201
|
+
y: size.height / 2 - bounds.center.y
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
nodes.forEach(d => {
|
|
205
|
+
d.origData.x += offset.x;
|
|
206
|
+
d.origData.y += offset.y;
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
this._graph
|
|
210
|
+
.moveVertices(true)
|
|
211
|
+
.moveEdges(true)
|
|
212
|
+
;
|
|
213
|
+
this.stop();
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export class Tree extends TidyTreeBase {
|
|
218
|
+
|
|
219
|
+
constructor(graph, protected options: TidyTreeOptions) {
|
|
220
|
+
super(graph, options);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
start(): Promise<this> {
|
|
224
|
+
return super.start().then(() => {
|
|
225
|
+
const treeFunc = tree().nodeSize([this.options.rankdir === "TB" ? 200 : 100, this.options.rankdir === "TB" ? 100 : 200]);
|
|
226
|
+
const root = treeFunc(this._d3Hierarchy);
|
|
227
|
+
const nodes: Node[] = root.descendants().map((d) => {
|
|
228
|
+
d.data.origData.x = this.options.rankdir === "TB" ? d.x : d.y;
|
|
229
|
+
d.data.origData.y = this.options.rankdir === "TB" ? d.y : d.x;
|
|
230
|
+
return d.data;
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
this.finalize(nodes);
|
|
234
|
+
return this;
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
edgePath(ep: EdgePlaceholder, curveDepth: number): EdgeLayout {
|
|
239
|
+
const { source, target } = this.edgeLine(ep);
|
|
240
|
+
return {
|
|
241
|
+
path: linkHorizontal({ source, target }),
|
|
242
|
+
labelPos: this.center([[source.x, source.y], [target.x, target.y]])
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export class RadialTree extends TidyTreeBase {
|
|
248
|
+
|
|
249
|
+
constructor(graph) {
|
|
250
|
+
super(graph, {});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
start(): Promise<this> {
|
|
254
|
+
return super.start().then(() => {
|
|
255
|
+
const treeFunc = tree()
|
|
256
|
+
.size([2 * Math.PI, 1024 / 2])
|
|
257
|
+
.separation((a, b) => (a.parent == b.parent ? 1 : 2) / a.depth)
|
|
258
|
+
;
|
|
259
|
+
const root = treeFunc(this._d3Hierarchy);
|
|
260
|
+
const nodes: Node[] = root.descendants().map((d) => {
|
|
261
|
+
d.data.origData.angle = d.x;
|
|
262
|
+
d.data.origData.radius = d.y;
|
|
263
|
+
d.data.origData.x = Math.sin(d.x) * d.y;
|
|
264
|
+
d.data.origData.y = Math.cos(d.x) * d.y;
|
|
265
|
+
return d.data;
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
this.finalize(nodes);
|
|
269
|
+
return this;
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export class Dendrogram extends TidyTreeBase {
|
|
275
|
+
|
|
276
|
+
constructor(graph, protected options: TidyTreeOptions) {
|
|
277
|
+
super(graph, options);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
start(): Promise<this> {
|
|
281
|
+
return super.start().then(() => {
|
|
282
|
+
const treeFunc = cluster().nodeSize([this.options.rankdir === "TB" ? 200 : 100, this.options.rankdir === "TB" ? 100 : 200]);
|
|
283
|
+
const root = treeFunc(this._d3Hierarchy);
|
|
284
|
+
const nodes: Node[] = root.descendants().map((d) => {
|
|
285
|
+
d.data.origData.x = this.options.rankdir === "TB" ? d.x : d.y;
|
|
286
|
+
d.data.origData.y = this.options.rankdir === "TB" ? d.y : d.x;
|
|
287
|
+
return d.data;
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
this.finalize(nodes);
|
|
291
|
+
return this;
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
edgePath(ep: EdgePlaceholder, curveDepth: number): EdgeLayout {
|
|
296
|
+
const { source, target } = this.edgeLine(ep);
|
|
297
|
+
return {
|
|
298
|
+
path: linkHorizontal({ source, target }),
|
|
299
|
+
labelPos: this.center([[source.x, source.y], [target.x, target.y]])
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export class RadialDendrogram extends TidyTreeBase {
|
|
305
|
+
|
|
306
|
+
constructor(graph) {
|
|
307
|
+
super(graph, {});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
start(): Promise<this> {
|
|
311
|
+
return super.start().then(() => {
|
|
312
|
+
const treeFunc = cluster()
|
|
313
|
+
.size([2 * Math.PI, 1024 / 2])
|
|
314
|
+
.separation((a, b) => (a.parent == b.parent ? 1 : 2) / a.depth)
|
|
315
|
+
;
|
|
316
|
+
const root = treeFunc(this._d3Hierarchy);
|
|
317
|
+
const nodes: Node[] = root.descendants().map((d) => {
|
|
318
|
+
d.data.origData.x = Math.sin(d.x) * d.y;
|
|
319
|
+
d.data.origData.y = Math.cos(d.x) * d.y;
|
|
320
|
+
return d.data;
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
this.finalize(nodes);
|
|
324
|
+
return this;
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
327
|
}
|