@dagrejs/dagre 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -7
- package/dist/dagre.js +443 -445
- package/dist/dagre.min.js +52 -52
- package/index.d.ts +8 -1
- package/lib/acyclic.js +13 -13
- package/lib/add-border-segments.js +7 -7
- package/lib/coordinate-system.js +8 -8
- package/lib/data/list.js +34 -32
- package/lib/debug.js +9 -11
- package/lib/greedy-fas.js +29 -29
- package/lib/layout.js +96 -96
- package/lib/nesting-graph.js +20 -22
- package/lib/normalize.js +13 -13
- package/lib/order/add-subgraph-constraints.js +3 -3
- package/lib/order/barycenter.js +3 -3
- package/lib/order/build-layer-graph.js +8 -8
- package/lib/order/cross-count.js +11 -11
- package/lib/order/index.js +15 -15
- package/lib/order/init-order.js +7 -7
- package/lib/order/resolve-conflicts.js +12 -12
- package/lib/order/sort-subgraph.js +16 -16
- package/lib/order/sort.js +7 -7
- package/lib/parent-dummy-chains.js +19 -19
- package/lib/position/bk.js +67 -67
- package/lib/position/index.js +6 -6
- package/lib/rank/feasible-tree.js +1 -1
- package/lib/rank/network-simplex.js +4 -4
- package/lib/util.js +32 -32
- package/lib/version.js +1 -1
- package/package.json +1 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
let util = require("./util");
|
|
2
2
|
|
|
3
3
|
module.exports = addBorderSegments;
|
|
4
4
|
|
|
5
5
|
function addBorderSegments(g) {
|
|
6
6
|
function dfs(v) {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
let children = g.children(v);
|
|
8
|
+
let node = g.node(v);
|
|
9
9
|
if (children.length) {
|
|
10
10
|
children.forEach(dfs);
|
|
11
11
|
}
|
|
@@ -13,7 +13,7 @@ function addBorderSegments(g) {
|
|
|
13
13
|
if (node.hasOwnProperty("minRank")) {
|
|
14
14
|
node.borderLeft = [];
|
|
15
15
|
node.borderRight = [];
|
|
16
|
-
for (
|
|
16
|
+
for (let rank = node.minRank, maxRank = node.maxRank + 1;
|
|
17
17
|
rank < maxRank;
|
|
18
18
|
++rank) {
|
|
19
19
|
addBorderNode(g, "borderLeft", "_bl", v, node, rank);
|
|
@@ -26,9 +26,9 @@ function addBorderSegments(g) {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
function addBorderNode(g, prop, prefix, sg, sgNode, rank) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
let label = { width: 0, height: 0, rank: rank, borderType: prop };
|
|
30
|
+
let prev = sgNode[prop][rank - 1];
|
|
31
|
+
let curr = util.addDummyNode(g, "border", label, prefix);
|
|
32
32
|
sgNode[prop][rank] = curr;
|
|
33
33
|
g.setParent(curr, sg);
|
|
34
34
|
if (prev) {
|
package/lib/coordinate-system.js
CHANGED
|
@@ -6,14 +6,14 @@ module.exports = {
|
|
|
6
6
|
};
|
|
7
7
|
|
|
8
8
|
function adjust(g) {
|
|
9
|
-
|
|
9
|
+
let rankDir = g.graph().rankdir.toLowerCase();
|
|
10
10
|
if (rankDir === "lr" || rankDir === "rl") {
|
|
11
11
|
swapWidthHeight(g);
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
function undo(g) {
|
|
16
|
-
|
|
16
|
+
let rankDir = g.graph().rankdir.toLowerCase();
|
|
17
17
|
if (rankDir === "bt" || rankDir === "rl") {
|
|
18
18
|
reverseY(g);
|
|
19
19
|
}
|
|
@@ -30,7 +30,7 @@ function swapWidthHeight(g) {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
function swapWidthHeightOne(attrs) {
|
|
33
|
-
|
|
33
|
+
let w = attrs.width;
|
|
34
34
|
attrs.width = attrs.height;
|
|
35
35
|
attrs.height = w;
|
|
36
36
|
}
|
|
@@ -38,8 +38,8 @@ function swapWidthHeightOne(attrs) {
|
|
|
38
38
|
function reverseY(g) {
|
|
39
39
|
g.nodes().forEach(v => reverseYOne(g.node(v)));
|
|
40
40
|
|
|
41
|
-
g.edges().forEach(
|
|
42
|
-
|
|
41
|
+
g.edges().forEach(e => {
|
|
42
|
+
let edge = g.edge(e);
|
|
43
43
|
edge.points.forEach(reverseYOne);
|
|
44
44
|
if (edge.hasOwnProperty("y")) {
|
|
45
45
|
reverseYOne(edge);
|
|
@@ -54,8 +54,8 @@ function reverseYOne(attrs) {
|
|
|
54
54
|
function swapXY(g) {
|
|
55
55
|
g.nodes().forEach(v => swapXYOne(g.node(v)));
|
|
56
56
|
|
|
57
|
-
g.edges().forEach(
|
|
58
|
-
|
|
57
|
+
g.edges().forEach(e => {
|
|
58
|
+
let edge = g.edge(e);
|
|
59
59
|
edge.points.forEach(swapXYOne);
|
|
60
60
|
if (edge.hasOwnProperty("x")) {
|
|
61
61
|
swapXYOne(edge);
|
|
@@ -64,7 +64,7 @@ function swapXY(g) {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
function swapXYOne(attrs) {
|
|
67
|
-
|
|
67
|
+
let x = attrs.x;
|
|
68
68
|
attrs.x = attrs.y;
|
|
69
69
|
attrs.y = x;
|
|
70
70
|
}
|
package/lib/data/list.js
CHANGED
|
@@ -3,44 +3,44 @@
|
|
|
3
3
|
* "Introduction to Algorithms".
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
6
|
+
class List {
|
|
7
|
+
constructor() {
|
|
8
|
+
let sentinel = {};
|
|
9
|
+
sentinel._next = sentinel._prev = sentinel;
|
|
10
|
+
this._sentinel = sentinel;
|
|
11
|
+
}
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
dequeue() {
|
|
14
|
+
let sentinel = this._sentinel;
|
|
15
|
+
let entry = sentinel._prev;
|
|
16
|
+
if (entry !== sentinel) {
|
|
17
|
+
unlink(entry);
|
|
18
|
+
return entry;
|
|
19
|
+
}
|
|
20
20
|
}
|
|
21
|
-
};
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
enqueue(entry) {
|
|
23
|
+
let sentinel = this._sentinel;
|
|
24
|
+
if (entry._prev && entry._next) {
|
|
25
|
+
unlink(entry);
|
|
26
|
+
}
|
|
27
|
+
entry._next = sentinel._next;
|
|
28
|
+
sentinel._next._prev = entry;
|
|
29
|
+
sentinel._next = entry;
|
|
30
|
+
entry._prev = sentinel;
|
|
27
31
|
}
|
|
28
|
-
entry._next = sentinel._next;
|
|
29
|
-
sentinel._next._prev = entry;
|
|
30
|
-
sentinel._next = entry;
|
|
31
|
-
entry._prev = sentinel;
|
|
32
|
-
};
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
toString() {
|
|
34
|
+
let strs = [];
|
|
35
|
+
let sentinel = this._sentinel;
|
|
36
|
+
let curr = sentinel._prev;
|
|
37
|
+
while (curr !== sentinel) {
|
|
38
|
+
strs.push(JSON.stringify(curr, filterOutLinks));
|
|
39
|
+
curr = curr._prev;
|
|
40
|
+
}
|
|
41
|
+
return "[" + strs.join(", ") + "]";
|
|
41
42
|
}
|
|
42
|
-
|
|
43
|
-
};
|
|
43
|
+
}
|
|
44
44
|
|
|
45
45
|
function unlink(entry) {
|
|
46
46
|
entry._prev._next = entry._next;
|
|
@@ -54,3 +54,5 @@ function filterOutLinks(k, v) {
|
|
|
54
54
|
return v;
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
+
|
|
58
|
+
module.exports = List;
|
package/lib/debug.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
let util = require("./util");
|
|
2
|
+
let Graph = require("@dagrejs/graphlib").Graph;
|
|
3
3
|
|
|
4
4
|
module.exports = {
|
|
5
5
|
debugOrdering: debugOrdering
|
|
@@ -7,23 +7,21 @@ module.exports = {
|
|
|
7
7
|
|
|
8
8
|
/* istanbul ignore next */
|
|
9
9
|
function debugOrdering(g) {
|
|
10
|
-
|
|
10
|
+
let layerMatrix = util.buildLayerMatrix(g);
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
let h = new Graph({ compound: true, multigraph: true }).setGraph({});
|
|
13
13
|
|
|
14
|
-
g.nodes().forEach(
|
|
14
|
+
g.nodes().forEach(v => {
|
|
15
15
|
h.setNode(v, { label: v });
|
|
16
16
|
h.setParent(v, "layer" + g.node(v).rank);
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
-
g.edges().forEach(
|
|
20
|
-
h.setEdge(e.v, e.w, {}, e.name);
|
|
21
|
-
});
|
|
19
|
+
g.edges().forEach(e => h.setEdge(e.v, e.w, {}, e.name));
|
|
22
20
|
|
|
23
|
-
layerMatrix.forEach(
|
|
24
|
-
|
|
21
|
+
layerMatrix.forEach((layer, i) => {
|
|
22
|
+
let layerV = "layer" + i;
|
|
25
23
|
h.setNode(layerV, { rank: "same" });
|
|
26
|
-
layer.reduce(
|
|
24
|
+
layer.reduce((u, v) => {
|
|
27
25
|
h.setEdge(u, v, { style: "invis" });
|
|
28
26
|
return v;
|
|
29
27
|
});
|
package/lib/greedy-fas.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
let Graph = require("@dagrejs/graphlib").Graph;
|
|
2
|
+
let List = require("./data/list");
|
|
3
3
|
|
|
4
4
|
/*
|
|
5
5
|
* A greedy heuristic for finding a feedback arc set for a graph. A feedback
|
|
@@ -10,30 +10,30 @@ var List = require("./data/list");
|
|
|
10
10
|
*/
|
|
11
11
|
module.exports = greedyFAS;
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
let DEFAULT_WEIGHT_FN = () => 1;
|
|
14
14
|
|
|
15
15
|
function greedyFAS(g, weightFn) {
|
|
16
16
|
if (g.nodeCount() <= 1) {
|
|
17
17
|
return [];
|
|
18
18
|
}
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
let state = buildState(g, weightFn || DEFAULT_WEIGHT_FN);
|
|
20
|
+
let results = doGreedyFAS(state.graph, state.buckets, state.zeroIdx);
|
|
21
21
|
|
|
22
22
|
// Expand multi-edges
|
|
23
23
|
return results.flatMap(e => g.outEdges(e.v, e.w));
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
function doGreedyFAS(g, buckets, zeroIdx) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
let results = [];
|
|
28
|
+
let sources = buckets[buckets.length - 1];
|
|
29
|
+
let sinks = buckets[0];
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
let entry;
|
|
32
32
|
while (g.nodeCount()) {
|
|
33
33
|
while ((entry = sinks.dequeue())) { removeNode(g, buckets, zeroIdx, entry); }
|
|
34
34
|
while ((entry = sources.dequeue())) { removeNode(g, buckets, zeroIdx, entry); }
|
|
35
35
|
if (g.nodeCount()) {
|
|
36
|
-
for (
|
|
36
|
+
for (let i = buckets.length - 2; i > 0; --i) {
|
|
37
37
|
entry = buckets[i].dequeue();
|
|
38
38
|
if (entry) {
|
|
39
39
|
results = results.concat(removeNode(g, buckets, zeroIdx, entry, true));
|
|
@@ -47,11 +47,11 @@ function doGreedyFAS(g, buckets, zeroIdx) {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
function removeNode(g, buckets, zeroIdx, entry, collectPredecessors) {
|
|
50
|
-
|
|
50
|
+
let results = collectPredecessors ? [] : undefined;
|
|
51
51
|
|
|
52
|
-
g.inEdges(entry.v).forEach(
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
g.inEdges(entry.v).forEach(edge => {
|
|
53
|
+
let weight = g.edge(edge);
|
|
54
|
+
let uEntry = g.node(edge.v);
|
|
55
55
|
|
|
56
56
|
if (collectPredecessors) {
|
|
57
57
|
results.push({ v: edge.v, w: edge.w });
|
|
@@ -61,10 +61,10 @@ function removeNode(g, buckets, zeroIdx, entry, collectPredecessors) {
|
|
|
61
61
|
assignBucket(buckets, zeroIdx, uEntry);
|
|
62
62
|
});
|
|
63
63
|
|
|
64
|
-
g.outEdges(entry.v).forEach(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
g.outEdges(entry.v).forEach(edge => {
|
|
65
|
+
let weight = g.edge(edge);
|
|
66
|
+
let w = edge.w;
|
|
67
|
+
let wEntry = g.node(w);
|
|
68
68
|
wEntry["in"] -= weight;
|
|
69
69
|
assignBucket(buckets, zeroIdx, wEntry);
|
|
70
70
|
});
|
|
@@ -75,29 +75,29 @@ function removeNode(g, buckets, zeroIdx, entry, collectPredecessors) {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
function buildState(g, weightFn) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
let fasGraph = new Graph();
|
|
79
|
+
let maxIn = 0;
|
|
80
|
+
let maxOut = 0;
|
|
81
81
|
|
|
82
|
-
g.nodes().forEach(
|
|
82
|
+
g.nodes().forEach(v => {
|
|
83
83
|
fasGraph.setNode(v, { v: v, "in": 0, out: 0 });
|
|
84
84
|
});
|
|
85
85
|
|
|
86
86
|
// Aggregate weights on nodes, but also sum the weights across multi-edges
|
|
87
87
|
// into a single edge for the fasGraph.
|
|
88
|
-
g.edges().forEach(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
g.edges().forEach(e => {
|
|
89
|
+
let prevWeight = fasGraph.edge(e.v, e.w) || 0;
|
|
90
|
+
let weight = weightFn(e);
|
|
91
|
+
let edgeWeight = prevWeight + weight;
|
|
92
92
|
fasGraph.setEdge(e.v, e.w, edgeWeight);
|
|
93
93
|
maxOut = Math.max(maxOut, fasGraph.node(e.v).out += weight);
|
|
94
94
|
maxIn = Math.max(maxIn, fasGraph.node(e.w)["in"] += weight);
|
|
95
95
|
});
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
let buckets = range(maxOut + maxIn + 3).map(() => new List());
|
|
98
|
+
let zeroIdx = maxIn + 1;
|
|
99
99
|
|
|
100
|
-
fasGraph.nodes().forEach(
|
|
100
|
+
fasGraph.nodes().forEach(v => {
|
|
101
101
|
assignBucket(buckets, zeroIdx, fasGraph.node(v));
|
|
102
102
|
});
|
|
103
103
|
|
package/lib/layout.js
CHANGED
|
@@ -1,59 +1,59 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
3
|
+
let acyclic = require("./acyclic");
|
|
4
|
+
let normalize = require("./normalize");
|
|
5
|
+
let rank = require("./rank");
|
|
6
|
+
let normalizeRanks = require("./util").normalizeRanks;
|
|
7
|
+
let parentDummyChains = require("./parent-dummy-chains");
|
|
8
|
+
let removeEmptyRanks = require("./util").removeEmptyRanks;
|
|
9
|
+
let nestingGraph = require("./nesting-graph");
|
|
10
|
+
let addBorderSegments = require("./add-border-segments");
|
|
11
|
+
let coordinateSystem = require("./coordinate-system");
|
|
12
|
+
let order = require("./order");
|
|
13
|
+
let position = require("./position");
|
|
14
|
+
let util = require("./util");
|
|
15
|
+
let Graph = require("@dagrejs/graphlib").Graph;
|
|
16
16
|
|
|
17
17
|
module.exports = layout;
|
|
18
18
|
|
|
19
19
|
function layout(g, opts) {
|
|
20
|
-
|
|
21
|
-
time("layout",
|
|
22
|
-
|
|
23
|
-
time(" buildLayoutGraph",
|
|
24
|
-
time(" runLayout",
|
|
25
|
-
time(" updateInputGraph",
|
|
20
|
+
let time = opts && opts.debugTiming ? util.time : util.notime;
|
|
21
|
+
time("layout", () => {
|
|
22
|
+
let layoutGraph =
|
|
23
|
+
time(" buildLayoutGraph", () => buildLayoutGraph(g));
|
|
24
|
+
time(" runLayout", () => runLayout(layoutGraph, time));
|
|
25
|
+
time(" updateInputGraph", () => updateInputGraph(g, layoutGraph));
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
function runLayout(g, time) {
|
|
30
|
-
time(" makeSpaceForEdgeLabels",
|
|
31
|
-
time(" removeSelfEdges",
|
|
32
|
-
time(" acyclic",
|
|
33
|
-
time(" nestingGraph.run",
|
|
34
|
-
time(" rank",
|
|
35
|
-
time(" injectEdgeLabelProxies",
|
|
36
|
-
time(" removeEmptyRanks",
|
|
37
|
-
time(" nestingGraph.cleanup",
|
|
38
|
-
time(" normalizeRanks",
|
|
39
|
-
time(" assignRankMinMax",
|
|
40
|
-
time(" removeEdgeLabelProxies",
|
|
41
|
-
time(" normalize.run",
|
|
42
|
-
time(" parentDummyChains",
|
|
43
|
-
time(" addBorderSegments",
|
|
44
|
-
time(" order",
|
|
45
|
-
time(" insertSelfEdges",
|
|
46
|
-
time(" adjustCoordinateSystem",
|
|
47
|
-
time(" position",
|
|
48
|
-
time(" positionSelfEdges",
|
|
49
|
-
time(" removeBorderNodes",
|
|
50
|
-
time(" normalize.undo",
|
|
51
|
-
time(" fixupEdgeLabelCoords",
|
|
52
|
-
time(" undoCoordinateSystem",
|
|
53
|
-
time(" translateGraph",
|
|
54
|
-
time(" assignNodeIntersects",
|
|
55
|
-
time(" reversePoints",
|
|
56
|
-
time(" acyclic.undo",
|
|
30
|
+
time(" makeSpaceForEdgeLabels", () => makeSpaceForEdgeLabels(g));
|
|
31
|
+
time(" removeSelfEdges", () => removeSelfEdges(g));
|
|
32
|
+
time(" acyclic", () => acyclic.run(g));
|
|
33
|
+
time(" nestingGraph.run", () => nestingGraph.run(g));
|
|
34
|
+
time(" rank", () => rank(util.asNonCompoundGraph(g)));
|
|
35
|
+
time(" injectEdgeLabelProxies", () => injectEdgeLabelProxies(g));
|
|
36
|
+
time(" removeEmptyRanks", () => removeEmptyRanks(g));
|
|
37
|
+
time(" nestingGraph.cleanup", () => nestingGraph.cleanup(g));
|
|
38
|
+
time(" normalizeRanks", () => normalizeRanks(g));
|
|
39
|
+
time(" assignRankMinMax", () => assignRankMinMax(g));
|
|
40
|
+
time(" removeEdgeLabelProxies", () => removeEdgeLabelProxies(g));
|
|
41
|
+
time(" normalize.run", () => normalize.run(g));
|
|
42
|
+
time(" parentDummyChains", () => parentDummyChains(g));
|
|
43
|
+
time(" addBorderSegments", () => addBorderSegments(g));
|
|
44
|
+
time(" order", () => order(g));
|
|
45
|
+
time(" insertSelfEdges", () => insertSelfEdges(g));
|
|
46
|
+
time(" adjustCoordinateSystem", () => coordinateSystem.adjust(g));
|
|
47
|
+
time(" position", () => position(g));
|
|
48
|
+
time(" positionSelfEdges", () => positionSelfEdges(g));
|
|
49
|
+
time(" removeBorderNodes", () => removeBorderNodes(g));
|
|
50
|
+
time(" normalize.undo", () => normalize.undo(g));
|
|
51
|
+
time(" fixupEdgeLabelCoords", () => fixupEdgeLabelCoords(g));
|
|
52
|
+
time(" undoCoordinateSystem", () => coordinateSystem.undo(g));
|
|
53
|
+
time(" translateGraph", () => translateGraph(g));
|
|
54
|
+
time(" assignNodeIntersects", () => assignNodeIntersects(g));
|
|
55
|
+
time(" reversePoints", () => reversePointsForReversedEdges(g));
|
|
56
|
+
time(" acyclic.undo", () => acyclic.undo(g));
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
/*
|
|
@@ -64,8 +64,8 @@ function runLayout(g, time) {
|
|
|
64
64
|
*/
|
|
65
65
|
function updateInputGraph(inputGraph, layoutGraph) {
|
|
66
66
|
inputGraph.nodes().forEach(v => {
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
let inputLabel = inputGraph.node(v);
|
|
68
|
+
let layoutLabel = layoutGraph.node(v);
|
|
69
69
|
|
|
70
70
|
if (inputLabel) {
|
|
71
71
|
inputLabel.x = layoutLabel.x;
|
|
@@ -80,8 +80,8 @@ function updateInputGraph(inputGraph, layoutGraph) {
|
|
|
80
80
|
});
|
|
81
81
|
|
|
82
82
|
inputGraph.edges().forEach(e => {
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
let inputLabel = inputGraph.edge(e);
|
|
84
|
+
let layoutLabel = layoutGraph.edge(e);
|
|
85
85
|
|
|
86
86
|
inputLabel.points = layoutLabel.points;
|
|
87
87
|
if (layoutLabel.hasOwnProperty("x")) {
|
|
@@ -94,17 +94,17 @@ function updateInputGraph(inputGraph, layoutGraph) {
|
|
|
94
94
|
inputGraph.graph().height = layoutGraph.graph().height;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
97
|
+
let graphNumAttrs = ["nodesep", "edgesep", "ranksep", "marginx", "marginy"];
|
|
98
|
+
let graphDefaults = { ranksep: 50, edgesep: 20, nodesep: 50, rankdir: "tb" };
|
|
99
|
+
let graphAttrs = ["acyclicer", "ranker", "rankdir", "align"];
|
|
100
|
+
let nodeNumAttrs = ["width", "height"];
|
|
101
|
+
let nodeDefaults = { width: 0, height: 0 };
|
|
102
|
+
let edgeNumAttrs = ["minlen", "weight", "width", "height", "labeloffset"];
|
|
103
|
+
let edgeDefaults = {
|
|
104
104
|
minlen: 1, weight: 1, width: 0, height: 0,
|
|
105
105
|
labeloffset: 10, labelpos: "r"
|
|
106
106
|
};
|
|
107
|
-
|
|
107
|
+
let edgeAttrs = ["labelpos"];
|
|
108
108
|
|
|
109
109
|
/*
|
|
110
110
|
* Constructs a new graph from the input graph, which can be used for layout.
|
|
@@ -113,8 +113,8 @@ var edgeAttrs = ["labelpos"];
|
|
|
113
113
|
* attributes can influence layout.
|
|
114
114
|
*/
|
|
115
115
|
function buildLayoutGraph(inputGraph) {
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
let g = new Graph({ multigraph: true, compound: true });
|
|
117
|
+
let graph = canonicalize(inputGraph.graph());
|
|
118
118
|
|
|
119
119
|
g.setGraph(Object.assign({},
|
|
120
120
|
graphDefaults,
|
|
@@ -122,7 +122,7 @@ function buildLayoutGraph(inputGraph) {
|
|
|
122
122
|
util.pick(graph, graphAttrs)));
|
|
123
123
|
|
|
124
124
|
inputGraph.nodes().forEach(v => {
|
|
125
|
-
|
|
125
|
+
let node = canonicalize(inputGraph.node(v));
|
|
126
126
|
const newNode = selectNumberAttrs(node, nodeNumAttrs);
|
|
127
127
|
Object.keys(nodeDefaults).forEach(k => {
|
|
128
128
|
if (newNode[k] === undefined) {
|
|
@@ -135,7 +135,7 @@ function buildLayoutGraph(inputGraph) {
|
|
|
135
135
|
});
|
|
136
136
|
|
|
137
137
|
inputGraph.edges().forEach(e => {
|
|
138
|
-
|
|
138
|
+
let edge = canonicalize(inputGraph.edge(e));
|
|
139
139
|
g.setEdge(e, Object.assign({},
|
|
140
140
|
edgeDefaults,
|
|
141
141
|
selectNumberAttrs(edge, edgeNumAttrs),
|
|
@@ -154,10 +154,10 @@ function buildLayoutGraph(inputGraph) {
|
|
|
154
154
|
* away from the edge itself a bit.
|
|
155
155
|
*/
|
|
156
156
|
function makeSpaceForEdgeLabels(g) {
|
|
157
|
-
|
|
157
|
+
let graph = g.graph();
|
|
158
158
|
graph.ranksep /= 2;
|
|
159
159
|
g.edges().forEach(e => {
|
|
160
|
-
|
|
160
|
+
let edge = g.edge(e);
|
|
161
161
|
edge.minlen *= 2;
|
|
162
162
|
if (edge.labelpos.toLowerCase() !== "c") {
|
|
163
163
|
if (graph.rankdir === "TB" || graph.rankdir === "BT") {
|
|
@@ -177,20 +177,20 @@ function makeSpaceForEdgeLabels(g) {
|
|
|
177
177
|
*/
|
|
178
178
|
function injectEdgeLabelProxies(g) {
|
|
179
179
|
g.edges().forEach(e => {
|
|
180
|
-
|
|
180
|
+
let edge = g.edge(e);
|
|
181
181
|
if (edge.width && edge.height) {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
182
|
+
let v = g.node(e.v);
|
|
183
|
+
let w = g.node(e.w);
|
|
184
|
+
let label = { rank: (w.rank - v.rank) / 2 + v.rank, e: e };
|
|
185
185
|
util.addDummyNode(g, "edge-proxy", label, "_ep");
|
|
186
186
|
}
|
|
187
187
|
});
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
function assignRankMinMax(g) {
|
|
191
|
-
|
|
191
|
+
let maxRank = 0;
|
|
192
192
|
g.nodes().forEach(v => {
|
|
193
|
-
|
|
193
|
+
let node = g.node(v);
|
|
194
194
|
if (node.borderTop) {
|
|
195
195
|
node.minRank = g.node(node.borderTop).rank;
|
|
196
196
|
node.maxRank = g.node(node.borderBottom).rank;
|
|
@@ -202,7 +202,7 @@ function assignRankMinMax(g) {
|
|
|
202
202
|
|
|
203
203
|
function removeEdgeLabelProxies(g) {
|
|
204
204
|
g.nodes().forEach(v => {
|
|
205
|
-
|
|
205
|
+
let node = g.node(v);
|
|
206
206
|
if (node.dummy === "edge-proxy") {
|
|
207
207
|
g.edge(node.e).labelRank = node.rank;
|
|
208
208
|
g.removeNode(v);
|
|
@@ -211,19 +211,19 @@ function removeEdgeLabelProxies(g) {
|
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
function translateGraph(g) {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
214
|
+
let minX = Number.POSITIVE_INFINITY;
|
|
215
|
+
let maxX = 0;
|
|
216
|
+
let minY = Number.POSITIVE_INFINITY;
|
|
217
|
+
let maxY = 0;
|
|
218
|
+
let graphLabel = g.graph();
|
|
219
|
+
let marginX = graphLabel.marginx || 0;
|
|
220
|
+
let marginY = graphLabel.marginy || 0;
|
|
221
221
|
|
|
222
222
|
function getExtremes(attrs) {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
223
|
+
let x = attrs.x;
|
|
224
|
+
let y = attrs.y;
|
|
225
|
+
let w = attrs.width;
|
|
226
|
+
let h = attrs.height;
|
|
227
227
|
minX = Math.min(minX, x - w / 2);
|
|
228
228
|
maxX = Math.max(maxX, x + w / 2);
|
|
229
229
|
minY = Math.min(minY, y - h / 2);
|
|
@@ -232,7 +232,7 @@ function translateGraph(g) {
|
|
|
232
232
|
|
|
233
233
|
g.nodes().forEach(v => getExtremes(g.node(v)));
|
|
234
234
|
g.edges().forEach(e => {
|
|
235
|
-
|
|
235
|
+
let edge = g.edge(e);
|
|
236
236
|
if (edge.hasOwnProperty("x")) {
|
|
237
237
|
getExtremes(edge);
|
|
238
238
|
}
|
|
@@ -242,13 +242,13 @@ function translateGraph(g) {
|
|
|
242
242
|
minY -= marginY;
|
|
243
243
|
|
|
244
244
|
g.nodes().forEach(v => {
|
|
245
|
-
|
|
245
|
+
let node = g.node(v);
|
|
246
246
|
node.x -= minX;
|
|
247
247
|
node.y -= minY;
|
|
248
248
|
});
|
|
249
249
|
|
|
250
250
|
g.edges().forEach(e => {
|
|
251
|
-
|
|
251
|
+
let edge = g.edge(e);
|
|
252
252
|
edge.points.forEach(p => {
|
|
253
253
|
p.x -= minX;
|
|
254
254
|
p.y -= minY;
|
|
@@ -263,10 +263,10 @@ function translateGraph(g) {
|
|
|
263
263
|
|
|
264
264
|
function assignNodeIntersects(g) {
|
|
265
265
|
g.edges().forEach(e => {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
266
|
+
let edge = g.edge(e);
|
|
267
|
+
let nodeV = g.node(e.v);
|
|
268
|
+
let nodeW = g.node(e.w);
|
|
269
|
+
let p1, p2;
|
|
270
270
|
if (!edge.points) {
|
|
271
271
|
edge.points = [];
|
|
272
272
|
p1 = nodeW;
|
|
@@ -282,7 +282,7 @@ function assignNodeIntersects(g) {
|
|
|
282
282
|
|
|
283
283
|
function fixupEdgeLabelCoords(g) {
|
|
284
284
|
g.edges().forEach(e => {
|
|
285
|
-
|
|
285
|
+
let edge = g.edge(e);
|
|
286
286
|
if (edge.hasOwnProperty("x")) {
|
|
287
287
|
if (edge.labelpos === "l" || edge.labelpos === "r") {
|
|
288
288
|
edge.width -= edge.labeloffset;
|
|
@@ -297,7 +297,7 @@ function fixupEdgeLabelCoords(g) {
|
|
|
297
297
|
|
|
298
298
|
function reversePointsForReversedEdges(g) {
|
|
299
299
|
g.edges().forEach(e => {
|
|
300
|
-
|
|
300
|
+
let edge = g.edge(e);
|
|
301
301
|
if (edge.reversed) {
|
|
302
302
|
edge.points.reverse();
|
|
303
303
|
}
|
|
@@ -307,11 +307,11 @@ function reversePointsForReversedEdges(g) {
|
|
|
307
307
|
function removeBorderNodes(g) {
|
|
308
308
|
g.nodes().forEach(v => {
|
|
309
309
|
if (g.children(v).length) {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
310
|
+
let node = g.node(v);
|
|
311
|
+
let t = g.node(node.borderTop);
|
|
312
|
+
let b = g.node(node.borderBottom);
|
|
313
|
+
let l = g.node(node.borderLeft[node.borderLeft.length - 1]);
|
|
314
|
+
let r = g.node(node.borderRight[node.borderRight.length - 1]);
|
|
315
315
|
|
|
316
316
|
node.width = Math.abs(r.x - l.x);
|
|
317
317
|
node.height = Math.abs(b.y - t.y);
|