@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,22 +1,22 @@
|
|
|
1
|
-
import { type Data, type Options } from "./workers/forceDirectedOptions.ts";
|
|
2
|
-
// @ts-ignore
|
|
3
|
-
import ForceDirectedWorker from "./workers/forceDirected.ts?worker&inline";
|
|
4
|
-
|
|
5
|
-
export {
|
|
6
|
-
type Options
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export function forceDirected(data: Data, options: Options) {
|
|
10
|
-
const worker = new ForceDirectedWorker();
|
|
11
|
-
const response = new Promise<string>(resolve => {
|
|
12
|
-
worker.onmessage = event => {
|
|
13
|
-
resolve(event.data);
|
|
14
|
-
worker.terminate();
|
|
15
|
-
};
|
|
16
|
-
worker.postMessage([data, options]);
|
|
17
|
-
});
|
|
18
|
-
return {
|
|
19
|
-
terminate: () => worker.terminate(),
|
|
20
|
-
response
|
|
21
|
-
};
|
|
22
|
-
}
|
|
1
|
+
import { type Data, type Options } from "./workers/forceDirectedOptions.ts";
|
|
2
|
+
// @ts-ignore
|
|
3
|
+
import ForceDirectedWorker from "./workers/forceDirected.ts?worker&inline";
|
|
4
|
+
|
|
5
|
+
export {
|
|
6
|
+
type Options
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export function forceDirected(data: Data, options: Options) {
|
|
10
|
+
const worker = new ForceDirectedWorker();
|
|
11
|
+
const response = new Promise<string>(resolve => {
|
|
12
|
+
worker.onmessage = event => {
|
|
13
|
+
resolve(event.data);
|
|
14
|
+
worker.terminate();
|
|
15
|
+
};
|
|
16
|
+
worker.postMessage([data, options]);
|
|
17
|
+
});
|
|
18
|
+
return {
|
|
19
|
+
terminate: () => worker.terminate(),
|
|
20
|
+
response
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -1,112 +1,112 @@
|
|
|
1
|
-
export const tmp = 42;
|
|
2
|
-
/*
|
|
3
|
-
import { forceLink as d3ForceLink, forceManyBody as d3ForceManyBody, forceSimulation as d3ForceSimulation } from "d3-force";
|
|
4
|
-
import { Layout } from "./layout";
|
|
5
|
-
|
|
6
|
-
export class GeoForceDirectedBase extends Layout {
|
|
7
|
-
|
|
8
|
-
protected _links;
|
|
9
|
-
protected _charge;
|
|
10
|
-
protected _simulation;
|
|
11
|
-
|
|
12
|
-
constructor(graph, readonly _options = defaultOptions) {
|
|
13
|
-
super(graph);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
start() {
|
|
17
|
-
return super.start().then(() => {
|
|
18
|
-
const data = this._graph.graphData();
|
|
19
|
-
this._links = d3ForceLink(data.edges())
|
|
20
|
-
.id(d => d.id)
|
|
21
|
-
.distance(this._options.linkDistance)
|
|
22
|
-
.strength(this._options.linkStrength)
|
|
23
|
-
;
|
|
24
|
-
this._charge = d3ForceManyBody()
|
|
25
|
-
.strength(d => this._options.charge * Math.max(d.width, d.height))
|
|
26
|
-
;
|
|
27
|
-
this._simulation = d3ForceSimulation()
|
|
28
|
-
.force("link", this._links)
|
|
29
|
-
.force("charge", this._charge)
|
|
30
|
-
.nodes(data.vertices().map(v => {
|
|
31
|
-
// const { width, height } = v.widget.getBBox();
|
|
32
|
-
// v["width"] = width;
|
|
33
|
-
// v["height"] = height;
|
|
34
|
-
return v;
|
|
35
|
-
}))
|
|
36
|
-
.stop()
|
|
37
|
-
;
|
|
38
|
-
|
|
39
|
-
return this;
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
stop() {
|
|
44
|
-
this._simulation.stop();
|
|
45
|
-
return super.stop();
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export class GeoForceDirected extends GeoForceDirectedBase {
|
|
50
|
-
|
|
51
|
-
start() {
|
|
52
|
-
return super.start().then(() => {
|
|
53
|
-
const vertices = this._graph.graphData().vertices();
|
|
54
|
-
vertices.forEach(vp => {
|
|
55
|
-
if (vp.lat && vp.lng) {
|
|
56
|
-
// const [x, y] = this._graph.project(vp.lat, vp.lng);
|
|
57
|
-
// vp.fx = x;
|
|
58
|
-
// vp.fy = y;
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
this._simulation
|
|
63
|
-
// .velocityDecay(0.1)
|
|
64
|
-
.restart()
|
|
65
|
-
;
|
|
66
|
-
let total = vertices.length;
|
|
67
|
-
total = Math.min(total * total, 500);
|
|
68
|
-
for (let i = 0; i < total; ++i) {
|
|
69
|
-
this._simulation.tick();
|
|
70
|
-
}
|
|
71
|
-
this._simulation.stop();
|
|
72
|
-
vertices.forEach(vp => {
|
|
73
|
-
if (vp.lat && vp.lng) {
|
|
74
|
-
delete vp.fx;
|
|
75
|
-
delete vp.fy;
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
this._graph
|
|
80
|
-
.moveVertices(true)
|
|
81
|
-
.moveEdges(true)
|
|
82
|
-
;
|
|
83
|
-
this.stop();
|
|
84
|
-
|
|
85
|
-
return this;
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export class GeoForceDirectedAnimated extends GeoForceDirectedBase {
|
|
91
|
-
|
|
92
|
-
start() {
|
|
93
|
-
super.start();
|
|
94
|
-
return new Promise<this>(resolve => {
|
|
95
|
-
this._simulation
|
|
96
|
-
.velocityDecay(this._options.velocityDecay)
|
|
97
|
-
.on("tick", () => {
|
|
98
|
-
this._graph
|
|
99
|
-
.moveVertices(false)
|
|
100
|
-
.moveEdges(false)
|
|
101
|
-
;
|
|
102
|
-
})
|
|
103
|
-
.on("end", () => {
|
|
104
|
-
this._running = false;
|
|
105
|
-
resolve(this);
|
|
106
|
-
})
|
|
107
|
-
.restart()
|
|
108
|
-
;
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
*/
|
|
1
|
+
export const tmp = 42;
|
|
2
|
+
/*
|
|
3
|
+
import { forceLink as d3ForceLink, forceManyBody as d3ForceManyBody, forceSimulation as d3ForceSimulation } from "d3-force";
|
|
4
|
+
import { Layout } from "./layout";
|
|
5
|
+
|
|
6
|
+
export class GeoForceDirectedBase extends Layout {
|
|
7
|
+
|
|
8
|
+
protected _links;
|
|
9
|
+
protected _charge;
|
|
10
|
+
protected _simulation;
|
|
11
|
+
|
|
12
|
+
constructor(graph, readonly _options = defaultOptions) {
|
|
13
|
+
super(graph);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
start() {
|
|
17
|
+
return super.start().then(() => {
|
|
18
|
+
const data = this._graph.graphData();
|
|
19
|
+
this._links = d3ForceLink(data.edges())
|
|
20
|
+
.id(d => d.id)
|
|
21
|
+
.distance(this._options.linkDistance)
|
|
22
|
+
.strength(this._options.linkStrength)
|
|
23
|
+
;
|
|
24
|
+
this._charge = d3ForceManyBody()
|
|
25
|
+
.strength(d => this._options.charge * Math.max(d.width, d.height))
|
|
26
|
+
;
|
|
27
|
+
this._simulation = d3ForceSimulation()
|
|
28
|
+
.force("link", this._links)
|
|
29
|
+
.force("charge", this._charge)
|
|
30
|
+
.nodes(data.vertices().map(v => {
|
|
31
|
+
// const { width, height } = v.widget.getBBox();
|
|
32
|
+
// v["width"] = width;
|
|
33
|
+
// v["height"] = height;
|
|
34
|
+
return v;
|
|
35
|
+
}))
|
|
36
|
+
.stop()
|
|
37
|
+
;
|
|
38
|
+
|
|
39
|
+
return this;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
stop() {
|
|
44
|
+
this._simulation.stop();
|
|
45
|
+
return super.stop();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class GeoForceDirected extends GeoForceDirectedBase {
|
|
50
|
+
|
|
51
|
+
start() {
|
|
52
|
+
return super.start().then(() => {
|
|
53
|
+
const vertices = this._graph.graphData().vertices();
|
|
54
|
+
vertices.forEach(vp => {
|
|
55
|
+
if (vp.lat && vp.lng) {
|
|
56
|
+
// const [x, y] = this._graph.project(vp.lat, vp.lng);
|
|
57
|
+
// vp.fx = x;
|
|
58
|
+
// vp.fy = y;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
this._simulation
|
|
63
|
+
// .velocityDecay(0.1)
|
|
64
|
+
.restart()
|
|
65
|
+
;
|
|
66
|
+
let total = vertices.length;
|
|
67
|
+
total = Math.min(total * total, 500);
|
|
68
|
+
for (let i = 0; i < total; ++i) {
|
|
69
|
+
this._simulation.tick();
|
|
70
|
+
}
|
|
71
|
+
this._simulation.stop();
|
|
72
|
+
vertices.forEach(vp => {
|
|
73
|
+
if (vp.lat && vp.lng) {
|
|
74
|
+
delete vp.fx;
|
|
75
|
+
delete vp.fy;
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
this._graph
|
|
80
|
+
.moveVertices(true)
|
|
81
|
+
.moveEdges(true)
|
|
82
|
+
;
|
|
83
|
+
this.stop();
|
|
84
|
+
|
|
85
|
+
return this;
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export class GeoForceDirectedAnimated extends GeoForceDirectedBase {
|
|
91
|
+
|
|
92
|
+
start() {
|
|
93
|
+
super.start();
|
|
94
|
+
return new Promise<this>(resolve => {
|
|
95
|
+
this._simulation
|
|
96
|
+
.velocityDecay(this._options.velocityDecay)
|
|
97
|
+
.on("tick", () => {
|
|
98
|
+
this._graph
|
|
99
|
+
.moveVertices(false)
|
|
100
|
+
.moveEdges(false)
|
|
101
|
+
;
|
|
102
|
+
})
|
|
103
|
+
.on("end", () => {
|
|
104
|
+
this._running = false;
|
|
105
|
+
resolve(this);
|
|
106
|
+
})
|
|
107
|
+
.restart()
|
|
108
|
+
;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
*/
|
|
@@ -1,137 +1,137 @@
|
|
|
1
|
-
import { curveBasis as d3CurveBasis, line as d3Line } from "d3-shape";
|
|
2
|
-
import { Cluster, graphviz as gvWorker, Node, isLayoutSuccess, LayoutError } from "./graphvizWorker.ts";
|
|
3
|
-
import { Layout, Point } from "./layout.ts";
|
|
4
|
-
import { EdgePlaceholder } from "./placeholders.ts";
|
|
5
|
-
|
|
6
|
-
type Engine = "circo" | "dot" | "fdp" | "neato" | "osage" | "patchwork" | "twopi";
|
|
7
|
-
|
|
8
|
-
const lineBasis = d3Line<Point>()
|
|
9
|
-
.x(d => d[0])
|
|
10
|
-
.y(d => d[1])
|
|
11
|
-
.curve(d3CurveBasis)
|
|
12
|
-
;
|
|
13
|
-
|
|
14
|
-
export class Graphviz extends Layout {
|
|
15
|
-
|
|
16
|
-
_engine: Engine;
|
|
17
|
-
_wasmFolder: string;
|
|
18
|
-
|
|
19
|
-
constructor(graph, engine: Engine, wasmFolder: string) {
|
|
20
|
-
super(graph);
|
|
21
|
-
this._engine = engine;
|
|
22
|
-
this._wasmFolder = wasmFolder;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
start() {
|
|
26
|
-
super.start();
|
|
27
|
-
const size = this._graph.size();
|
|
28
|
-
const data = this._graph.graphData();
|
|
29
|
-
const nodeIdx = {};
|
|
30
|
-
const hierarchy: Array<Cluster | Node> = data.hierarchy((type, item, children) => {
|
|
31
|
-
switch (type) {
|
|
32
|
-
case "subgraph":
|
|
33
|
-
return {
|
|
34
|
-
id: item.id,
|
|
35
|
-
text: item.props.text,
|
|
36
|
-
children
|
|
37
|
-
};
|
|
38
|
-
case "vertex":
|
|
39
|
-
delete item["fx"];
|
|
40
|
-
delete item["fy"];
|
|
41
|
-
const bbox = this.vertexSize(item);
|
|
42
|
-
const retVal = {
|
|
43
|
-
id: item.id,
|
|
44
|
-
text: item.props.text,
|
|
45
|
-
width: bbox.width,
|
|
46
|
-
height: bbox.height
|
|
47
|
-
};
|
|
48
|
-
nodeIdx[retVal.id] = retVal;
|
|
49
|
-
return retVal;
|
|
50
|
-
}
|
|
51
|
-
}) as Array<Cluster | Node>;
|
|
52
|
-
return gvWorker({
|
|
53
|
-
items: hierarchy,
|
|
54
|
-
links: data.allEdges().map(e => ({
|
|
55
|
-
id: String(e.props.id),
|
|
56
|
-
source: nodeIdx[e.source.id],
|
|
57
|
-
target: nodeIdx[e.target.id],
|
|
58
|
-
text: e.props.label || ""
|
|
59
|
-
})),
|
|
60
|
-
raw: ""
|
|
61
|
-
}, {
|
|
62
|
-
engine: this._engine
|
|
63
|
-
}).response.then(response => {
|
|
64
|
-
if (this.running()) {
|
|
65
|
-
if (isLayoutSuccess(response)) {
|
|
66
|
-
response.clusters.forEach(n => {
|
|
67
|
-
const sg = data.subgraph(n.id);
|
|
68
|
-
sg.x = n.x + size.width / 2 - n.width;
|
|
69
|
-
sg.y = n.y + size.height / 2 - n.height;
|
|
70
|
-
sg.props.width = n.width;
|
|
71
|
-
sg.props.height = n.height;
|
|
72
|
-
});
|
|
73
|
-
response.nodes.forEach(n => {
|
|
74
|
-
const v = data.vertex(n.id);
|
|
75
|
-
v.x = n.x + size.width / 2;
|
|
76
|
-
v.y = n.y + size.height / 2;
|
|
77
|
-
});
|
|
78
|
-
response.links.forEach(l => {
|
|
79
|
-
const e = data.edge(l.id);
|
|
80
|
-
const start: [number, number] = [e.source.x, e.source.y];
|
|
81
|
-
const mid: [number, number][] = l.points !== undefined ? l.points.map(p => [p[0] + size.width / 2, p[1] + size.height / 2]) : [];
|
|
82
|
-
const end: [number, number] = [e.target.x, e.target.y];
|
|
83
|
-
e.points = [start, ...mid, end];
|
|
84
|
-
// e.points = l.points.map(p => [p[0] + size.width / 2, p[1] + size.height / 2]);
|
|
85
|
-
});
|
|
86
|
-
this._graph
|
|
87
|
-
.moveSubgraphs(true)
|
|
88
|
-
.moveVertices(true)
|
|
89
|
-
.moveEdges(true)
|
|
90
|
-
;
|
|
91
|
-
this.stop();
|
|
92
|
-
} else {
|
|
93
|
-
const err = response as LayoutError;
|
|
94
|
-
console.error(`Graphviz layout fail: ${err.error}`, err.errorDot);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
return this;
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
edgePath(ep: EdgePlaceholder, curveDepth: number): { path: string, labelPos: Point } {
|
|
102
|
-
let points = [];
|
|
103
|
-
let hasNaN = false;
|
|
104
|
-
if (ep.points) {
|
|
105
|
-
const line = this.edgeLine(ep);
|
|
106
|
-
points = ep.points.map((p, idx) => {
|
|
107
|
-
let x
|
|
108
|
-
let y
|
|
109
|
-
if (idx === 0) {
|
|
110
|
-
x = this._graph.rproject(line.source.x);
|
|
111
|
-
y = this._graph.rproject(line.source.y);
|
|
112
|
-
} else if (idx === ep.points.length - 1) {
|
|
113
|
-
x = this._graph.rproject(line.target.x);
|
|
114
|
-
y = this._graph.rproject(line.target.y);
|
|
115
|
-
} else {
|
|
116
|
-
x = p[0];
|
|
117
|
-
y = p[1];
|
|
118
|
-
}
|
|
119
|
-
x = this._graph.project(x, false);
|
|
120
|
-
y = this._graph.project(y, false);
|
|
121
|
-
if (isNaN(x) || isNaN(y)) {
|
|
122
|
-
hasNaN = true;
|
|
123
|
-
}
|
|
124
|
-
return [x, y];
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
if (hasNaN || points.length < 2) {
|
|
128
|
-
return super.edgePath(ep, curveDepth);
|
|
129
|
-
}
|
|
130
|
-
return {
|
|
131
|
-
path: lineBasis(points),
|
|
132
|
-
labelPos: this.center(points)
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
|
|
1
|
+
import { curveBasis as d3CurveBasis, line as d3Line } from "d3-shape";
|
|
2
|
+
import { Cluster, graphviz as gvWorker, Node, isLayoutSuccess, LayoutError } from "./graphvizWorker.ts";
|
|
3
|
+
import { Layout, Point } from "./layout.ts";
|
|
4
|
+
import { EdgePlaceholder } from "./placeholders.ts";
|
|
5
|
+
|
|
6
|
+
type Engine = "circo" | "dot" | "fdp" | "neato" | "osage" | "patchwork" | "twopi";
|
|
7
|
+
|
|
8
|
+
const lineBasis = d3Line<Point>()
|
|
9
|
+
.x(d => d[0])
|
|
10
|
+
.y(d => d[1])
|
|
11
|
+
.curve(d3CurveBasis)
|
|
12
|
+
;
|
|
13
|
+
|
|
14
|
+
export class Graphviz extends Layout {
|
|
15
|
+
|
|
16
|
+
_engine: Engine;
|
|
17
|
+
_wasmFolder: string;
|
|
18
|
+
|
|
19
|
+
constructor(graph, engine: Engine, wasmFolder: string) {
|
|
20
|
+
super(graph);
|
|
21
|
+
this._engine = engine;
|
|
22
|
+
this._wasmFolder = wasmFolder;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
start() {
|
|
26
|
+
super.start();
|
|
27
|
+
const size = this._graph.size();
|
|
28
|
+
const data = this._graph.graphData();
|
|
29
|
+
const nodeIdx = {};
|
|
30
|
+
const hierarchy: Array<Cluster | Node> = data.hierarchy((type, item, children) => {
|
|
31
|
+
switch (type) {
|
|
32
|
+
case "subgraph":
|
|
33
|
+
return {
|
|
34
|
+
id: item.id,
|
|
35
|
+
text: item.props.text,
|
|
36
|
+
children
|
|
37
|
+
};
|
|
38
|
+
case "vertex":
|
|
39
|
+
delete item["fx"];
|
|
40
|
+
delete item["fy"];
|
|
41
|
+
const bbox = this.vertexSize(item);
|
|
42
|
+
const retVal = {
|
|
43
|
+
id: item.id,
|
|
44
|
+
text: item.props.text,
|
|
45
|
+
width: bbox.width,
|
|
46
|
+
height: bbox.height
|
|
47
|
+
};
|
|
48
|
+
nodeIdx[retVal.id] = retVal;
|
|
49
|
+
return retVal;
|
|
50
|
+
}
|
|
51
|
+
}) as Array<Cluster | Node>;
|
|
52
|
+
return gvWorker({
|
|
53
|
+
items: hierarchy,
|
|
54
|
+
links: data.allEdges().map(e => ({
|
|
55
|
+
id: String(e.props.id),
|
|
56
|
+
source: nodeIdx[e.source.id],
|
|
57
|
+
target: nodeIdx[e.target.id],
|
|
58
|
+
text: e.props.label || ""
|
|
59
|
+
})),
|
|
60
|
+
raw: ""
|
|
61
|
+
}, {
|
|
62
|
+
engine: this._engine
|
|
63
|
+
}).response.then(response => {
|
|
64
|
+
if (this.running()) {
|
|
65
|
+
if (isLayoutSuccess(response)) {
|
|
66
|
+
response.clusters.forEach(n => {
|
|
67
|
+
const sg = data.subgraph(n.id);
|
|
68
|
+
sg.x = n.x + size.width / 2 - n.width;
|
|
69
|
+
sg.y = n.y + size.height / 2 - n.height;
|
|
70
|
+
sg.props.width = n.width;
|
|
71
|
+
sg.props.height = n.height;
|
|
72
|
+
});
|
|
73
|
+
response.nodes.forEach(n => {
|
|
74
|
+
const v = data.vertex(n.id);
|
|
75
|
+
v.x = n.x + size.width / 2;
|
|
76
|
+
v.y = n.y + size.height / 2;
|
|
77
|
+
});
|
|
78
|
+
response.links.forEach(l => {
|
|
79
|
+
const e = data.edge(l.id);
|
|
80
|
+
const start: [number, number] = [e.source.x, e.source.y];
|
|
81
|
+
const mid: [number, number][] = l.points !== undefined ? l.points.map(p => [p[0] + size.width / 2, p[1] + size.height / 2]) : [];
|
|
82
|
+
const end: [number, number] = [e.target.x, e.target.y];
|
|
83
|
+
e.points = [start, ...mid, end];
|
|
84
|
+
// e.points = l.points.map(p => [p[0] + size.width / 2, p[1] + size.height / 2]);
|
|
85
|
+
});
|
|
86
|
+
this._graph
|
|
87
|
+
.moveSubgraphs(true)
|
|
88
|
+
.moveVertices(true)
|
|
89
|
+
.moveEdges(true)
|
|
90
|
+
;
|
|
91
|
+
this.stop();
|
|
92
|
+
} else {
|
|
93
|
+
const err = response as LayoutError;
|
|
94
|
+
console.error(`Graphviz layout fail: ${err.error}`, err.errorDot);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return this;
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
edgePath(ep: EdgePlaceholder, curveDepth: number): { path: string, labelPos: Point } {
|
|
102
|
+
let points = [];
|
|
103
|
+
let hasNaN = false;
|
|
104
|
+
if (ep.points) {
|
|
105
|
+
const line = this.edgeLine(ep);
|
|
106
|
+
points = ep.points.map((p, idx) => {
|
|
107
|
+
let x: number;
|
|
108
|
+
let y: number;
|
|
109
|
+
if (idx === 0) {
|
|
110
|
+
x = this._graph.rproject(line.source.x);
|
|
111
|
+
y = this._graph.rproject(line.source.y);
|
|
112
|
+
} else if (idx === ep.points.length - 1) {
|
|
113
|
+
x = this._graph.rproject(line.target.x);
|
|
114
|
+
y = this._graph.rproject(line.target.y);
|
|
115
|
+
} else {
|
|
116
|
+
x = p[0];
|
|
117
|
+
y = p[1];
|
|
118
|
+
}
|
|
119
|
+
x = this._graph.project(x, false);
|
|
120
|
+
y = this._graph.project(y, false);
|
|
121
|
+
if (isNaN(x) || isNaN(y)) {
|
|
122
|
+
hasNaN = true;
|
|
123
|
+
}
|
|
124
|
+
return [x, y];
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
if (hasNaN || points.length < 2) {
|
|
128
|
+
return super.edgePath(ep, curveDepth);
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
path: lineBasis(points),
|
|
132
|
+
labelPos: this.center(points)
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
}
|
|
137
|
+
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import { type Cluster, type Data, type Engine, isLayoutSuccess, type Node, type LayoutError, type Options } from "./workers/graphvizOptions.ts";
|
|
2
|
-
// @ts-ignore
|
|
3
|
-
import GraphvizWorker from "./workers/graphviz.ts?worker&inline";
|
|
4
|
-
|
|
5
|
-
export {
|
|
6
|
-
type Cluster,
|
|
7
|
-
type Engine,
|
|
8
|
-
isLayoutSuccess,
|
|
9
|
-
type Node,
|
|
10
|
-
type LayoutError,
|
|
11
|
-
type Options
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export function graphviz(data: Data, options: Options) {
|
|
15
|
-
const worker = new GraphvizWorker();
|
|
16
|
-
const response = new Promise<string>(resolve => {
|
|
17
|
-
worker.onmessage = event => {
|
|
18
|
-
resolve(event.data);
|
|
19
|
-
worker.terminate();
|
|
20
|
-
};
|
|
21
|
-
worker.postMessage([data, options]);
|
|
22
|
-
});
|
|
23
|
-
return {
|
|
24
|
-
terminate: () => worker.terminate(),
|
|
25
|
-
response
|
|
26
|
-
};
|
|
27
|
-
}
|
|
1
|
+
import { type Cluster, type Data, type Engine, isLayoutSuccess, type Node, type LayoutError, type Options } from "./workers/graphvizOptions.ts";
|
|
2
|
+
// @ts-ignore
|
|
3
|
+
import GraphvizWorker from "./workers/graphviz.ts?worker&inline";
|
|
4
|
+
|
|
5
|
+
export {
|
|
6
|
+
type Cluster,
|
|
7
|
+
type Engine,
|
|
8
|
+
isLayoutSuccess,
|
|
9
|
+
type Node,
|
|
10
|
+
type LayoutError,
|
|
11
|
+
type Options
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function graphviz(data: Data, options: Options) {
|
|
15
|
+
const worker = new GraphvizWorker();
|
|
16
|
+
const response = new Promise<string>(resolve => {
|
|
17
|
+
worker.onmessage = event => {
|
|
18
|
+
resolve(event.data);
|
|
19
|
+
worker.terminate();
|
|
20
|
+
};
|
|
21
|
+
worker.postMessage([data, options]);
|
|
22
|
+
});
|
|
23
|
+
return {
|
|
24
|
+
terminate: () => worker.terminate(),
|
|
25
|
+
response
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export * from "./circle.ts";
|
|
2
|
-
export * from "./dagre.ts";
|
|
3
|
-
export * from "./forceDirected.ts";
|
|
4
|
-
export * from "./geoForceDirected.ts";
|
|
5
|
-
export * from "./graphviz.ts";
|
|
6
|
-
export type { ILayout } from "./layout.ts";
|
|
7
|
-
export * from "./null.ts";
|
|
1
|
+
export * from "./circle.ts";
|
|
2
|
+
export * from "./dagre.ts";
|
|
3
|
+
export * from "./forceDirected.ts";
|
|
4
|
+
export * from "./geoForceDirected.ts";
|
|
5
|
+
export * from "./graphviz.ts";
|
|
6
|
+
export type { ILayout } from "./layout.ts";
|
|
7
|
+
export * from "./null.ts";
|