@dagrejs/dagre 1.0.1 → 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 +139 -0
- 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 +3 -1
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);
|
package/lib/nesting-graph.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
let util = require("./util");
|
|
2
2
|
|
|
3
3
|
module.exports = {
|
|
4
|
-
run
|
|
5
|
-
cleanup
|
|
4
|
+
run,
|
|
5
|
+
cleanup,
|
|
6
6
|
};
|
|
7
7
|
|
|
8
8
|
/*
|
|
9
9
|
* A nesting graph creates dummy nodes for the tops and bottoms of subgraphs,
|
|
10
10
|
* adds appropriate edges to ensure that all cluster nodes are placed between
|
|
11
|
-
* these
|
|
11
|
+
* these boundaries, and ensures that the graph is connected.
|
|
12
12
|
*
|
|
13
13
|
* In addition we ensure, through the use of the minlen property, that nodes
|
|
14
14
|
* and subgraph border nodes to not end up on the same rank.
|
|
@@ -29,10 +29,10 @@ module.exports = {
|
|
|
29
29
|
* Graphs."
|
|
30
30
|
*/
|
|
31
31
|
function run(g) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
let root = util.addDummyNode(g, "root", {}, "_root");
|
|
33
|
+
let depths = treeDepths(g);
|
|
34
|
+
let height = Math.max(...Object.values(depths)) - 1; // Note: depths is an Object not an array
|
|
35
|
+
let nodeSep = 2 * height + 1;
|
|
36
36
|
|
|
37
37
|
g.graph().nestingRoot = root;
|
|
38
38
|
|
|
@@ -40,12 +40,10 @@ function run(g) {
|
|
|
40
40
|
g.edges().forEach(e => g.edge(e).minlen *= nodeSep);
|
|
41
41
|
|
|
42
42
|
// Calculate a weight that is sufficient to keep subgraphs vertically compact
|
|
43
|
-
|
|
43
|
+
let weight = sumWeights(g) + 1;
|
|
44
44
|
|
|
45
45
|
// Create border nodes and link them up
|
|
46
|
-
g.children().forEach(
|
|
47
|
-
dfs(g, root, nodeSep, weight, height, depths, child);
|
|
48
|
-
});
|
|
46
|
+
g.children().forEach(child => dfs(g, root, nodeSep, weight, height, depths, child));
|
|
49
47
|
|
|
50
48
|
// Save the multiplier for node layers for later removal of empty border
|
|
51
49
|
// layers.
|
|
@@ -53,7 +51,7 @@ function run(g) {
|
|
|
53
51
|
}
|
|
54
52
|
|
|
55
53
|
function dfs(g, root, nodeSep, weight, height, depths, v) {
|
|
56
|
-
|
|
54
|
+
let children = g.children(v);
|
|
57
55
|
if (!children.length) {
|
|
58
56
|
if (v !== root) {
|
|
59
57
|
g.setEdge(root, v, { weight: 0, minlen: nodeSep });
|
|
@@ -61,23 +59,23 @@ function dfs(g, root, nodeSep, weight, height, depths, v) {
|
|
|
61
59
|
return;
|
|
62
60
|
}
|
|
63
61
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
let top = util.addBorderNode(g, "_bt");
|
|
63
|
+
let bottom = util.addBorderNode(g, "_bb");
|
|
64
|
+
let label = g.node(v);
|
|
67
65
|
|
|
68
66
|
g.setParent(top, v);
|
|
69
67
|
label.borderTop = top;
|
|
70
68
|
g.setParent(bottom, v);
|
|
71
69
|
label.borderBottom = bottom;
|
|
72
70
|
|
|
73
|
-
children.forEach(
|
|
71
|
+
children.forEach(child => {
|
|
74
72
|
dfs(g, root, nodeSep, weight, height, depths, child);
|
|
75
73
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
74
|
+
let childNode = g.node(child);
|
|
75
|
+
let childTop = childNode.borderTop ? childNode.borderTop : child;
|
|
76
|
+
let childBottom = childNode.borderBottom ? childNode.borderBottom : child;
|
|
77
|
+
let thisWeight = childNode.borderTop ? weight : 2 * weight;
|
|
78
|
+
let minlen = childTop !== childBottom ? 1 : height - depths[v] + 1;
|
|
81
79
|
|
|
82
80
|
g.setEdge(top, childTop, {
|
|
83
81
|
weight: thisWeight,
|
package/lib/normalize.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
let util = require("./util");
|
|
4
4
|
|
|
5
5
|
module.exports = {
|
|
6
6
|
run: run,
|
|
@@ -29,19 +29,19 @@ function run(g) {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
function normalizeEdge(g, e) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
let v = e.v;
|
|
33
|
+
let vRank = g.node(v).rank;
|
|
34
|
+
let w = e.w;
|
|
35
|
+
let wRank = g.node(w).rank;
|
|
36
|
+
let name = e.name;
|
|
37
|
+
let edgeLabel = g.edge(e);
|
|
38
|
+
let labelRank = edgeLabel.labelRank;
|
|
39
39
|
|
|
40
40
|
if (wRank === vRank + 1) return;
|
|
41
41
|
|
|
42
42
|
g.removeEdge(e);
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
let dummy, attrs, i;
|
|
45
45
|
for (i = 0, ++vRank; vRank < wRank; ++i, ++vRank) {
|
|
46
46
|
edgeLabel.points = [];
|
|
47
47
|
attrs = {
|
|
@@ -67,10 +67,10 @@ function normalizeEdge(g, e) {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
function undo(g) {
|
|
70
|
-
g.graph().dummyChains.forEach(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
g.graph().dummyChains.forEach(v => {
|
|
71
|
+
let node = g.node(v);
|
|
72
|
+
let origLabel = node.edgeLabel;
|
|
73
|
+
let w;
|
|
74
74
|
g.setEdge(node.edgeObj, origLabel);
|
|
75
75
|
while (node.dummy) {
|
|
76
76
|
w = g.successors(v)[0];
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
module.exports = addSubgraphConstraints;
|
|
2
2
|
|
|
3
3
|
function addSubgraphConstraints(g, cg, vs) {
|
|
4
|
-
|
|
4
|
+
let prev = {},
|
|
5
5
|
rootPrev;
|
|
6
6
|
|
|
7
|
-
vs.forEach(
|
|
8
|
-
|
|
7
|
+
vs.forEach(v => {
|
|
8
|
+
let child = g.parent(v),
|
|
9
9
|
parent,
|
|
10
10
|
prevChild;
|
|
11
11
|
while (child) {
|
package/lib/order/barycenter.js
CHANGED
|
@@ -2,12 +2,12 @@ module.exports = barycenter;
|
|
|
2
2
|
|
|
3
3
|
function barycenter(g, movable = []) {
|
|
4
4
|
return movable.map(v => {
|
|
5
|
-
|
|
5
|
+
let inV = g.inEdges(v);
|
|
6
6
|
if (!inV.length) {
|
|
7
7
|
return { v: v };
|
|
8
8
|
} else {
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
let result = inV.reduce((acc, e) => {
|
|
10
|
+
let edge = g.edge(e),
|
|
11
11
|
nodeU = g.node(e.v);
|
|
12
12
|
return {
|
|
13
13
|
sum: acc.sum + (edge.weight * nodeU.order),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
let Graph = require("@dagrejs/graphlib").Graph;
|
|
2
|
+
let util = require("../util");
|
|
3
3
|
|
|
4
4
|
module.exports = buildLayerGraph;
|
|
5
5
|
|
|
@@ -34,12 +34,12 @@ module.exports = buildLayerGraph;
|
|
|
34
34
|
* graph is not a multi-graph.
|
|
35
35
|
*/
|
|
36
36
|
function buildLayerGraph(g, rank, relationship) {
|
|
37
|
-
|
|
37
|
+
let root = createRootNode(g),
|
|
38
38
|
result = new Graph({ compound: true }).setGraph({ root: root })
|
|
39
|
-
.setDefaultNodeLabel(
|
|
39
|
+
.setDefaultNodeLabel(v => g.node(v));
|
|
40
40
|
|
|
41
|
-
g.nodes().forEach(
|
|
42
|
-
|
|
41
|
+
g.nodes().forEach(v => {
|
|
42
|
+
let node = g.node(v),
|
|
43
43
|
parent = g.parent(v);
|
|
44
44
|
|
|
45
45
|
if (node.rank === rank || node.minRank <= rank && rank <= node.maxRank) {
|
|
@@ -47,8 +47,8 @@ function buildLayerGraph(g, rank, relationship) {
|
|
|
47
47
|
result.setParent(v, parent || root);
|
|
48
48
|
|
|
49
49
|
// This assumes we have only short edges!
|
|
50
|
-
g[relationship](v).forEach(
|
|
51
|
-
|
|
50
|
+
g[relationship](v).forEach(e => {
|
|
51
|
+
let u = e.v === v ? e.w : e.v,
|
|
52
52
|
edge = result.edge(u, v),
|
|
53
53
|
weight = edge !== undefined ? edge.weight : 0;
|
|
54
54
|
result.setEdge(u, v, { weight: g.edge(e).weight + weight });
|
package/lib/order/cross-count.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
let zipObject = require("../util").zipObject;
|
|
4
4
|
|
|
5
5
|
module.exports = crossCount;
|
|
6
6
|
|
|
@@ -21,8 +21,8 @@ module.exports = crossCount;
|
|
|
21
21
|
* This algorithm is derived from Barth, et al., "Bilayer Cross Counting."
|
|
22
22
|
*/
|
|
23
23
|
function crossCount(g, layering) {
|
|
24
|
-
|
|
25
|
-
for (
|
|
24
|
+
let cc = 0;
|
|
25
|
+
for (let i = 1; i < layering.length; ++i) {
|
|
26
26
|
cc += twoLayerCrossCount(g, layering[i-1], layering[i]);
|
|
27
27
|
}
|
|
28
28
|
return cc;
|
|
@@ -32,26 +32,26 @@ function twoLayerCrossCount(g, northLayer, southLayer) {
|
|
|
32
32
|
// Sort all of the edges between the north and south layers by their position
|
|
33
33
|
// in the north layer and then the south. Map these edges to the position of
|
|
34
34
|
// their head in the south layer.
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
let southPos = zipObject(southLayer, southLayer.map((v, i) => i));
|
|
36
|
+
let southEntries = northLayer.flatMap(v => {
|
|
37
37
|
return g.outEdges(v).map(e => {
|
|
38
38
|
return { pos: southPos[e.w], weight: g.edge(e).weight };
|
|
39
39
|
}).sort((a, b) => a.pos - b.pos);
|
|
40
40
|
});
|
|
41
41
|
|
|
42
42
|
// Build the accumulator tree
|
|
43
|
-
|
|
43
|
+
let firstIndex = 1;
|
|
44
44
|
while (firstIndex < southLayer.length) firstIndex <<= 1;
|
|
45
|
-
|
|
45
|
+
let treeSize = 2 * firstIndex - 1;
|
|
46
46
|
firstIndex -= 1;
|
|
47
|
-
|
|
47
|
+
let tree = new Array(treeSize).fill(0);
|
|
48
48
|
|
|
49
49
|
// Calculate the weighted crossings
|
|
50
|
-
|
|
50
|
+
let cc = 0;
|
|
51
51
|
southEntries.forEach(entry => {
|
|
52
|
-
|
|
52
|
+
let index = entry.pos + firstIndex;
|
|
53
53
|
tree[index] += entry.weight;
|
|
54
|
-
|
|
54
|
+
let weightSum = 0;
|
|
55
55
|
while (index > 0) {
|
|
56
56
|
if (index % 2) {
|
|
57
57
|
weightSum += tree[index + 1];
|
package/lib/order/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
let initOrder = require("./init-order");
|
|
4
|
+
let crossCount = require("./cross-count");
|
|
5
|
+
let sortSubgraph = require("./sort-subgraph");
|
|
6
|
+
let buildLayerGraph = require("./build-layer-graph");
|
|
7
|
+
let addSubgraphConstraints = require("./add-subgraph-constraints");
|
|
8
|
+
let Graph = require("@dagrejs/graphlib").Graph;
|
|
9
|
+
let util = require("../util");
|
|
10
10
|
|
|
11
11
|
module.exports = order;
|
|
12
12
|
|
|
@@ -26,21 +26,21 @@ module.exports = order;
|
|
|
26
26
|
* algorithm.
|
|
27
27
|
*/
|
|
28
28
|
function order(g) {
|
|
29
|
-
|
|
29
|
+
let maxRank = util.maxRank(g),
|
|
30
30
|
downLayerGraphs = buildLayerGraphs(g, util.range(1, maxRank + 1), "inEdges"),
|
|
31
31
|
upLayerGraphs = buildLayerGraphs(g, util.range(maxRank - 1, -1, -1), "outEdges");
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
let layering = initOrder(g);
|
|
34
34
|
assignOrder(g, layering);
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
let bestCC = Number.POSITIVE_INFINITY,
|
|
37
37
|
best;
|
|
38
38
|
|
|
39
|
-
for (
|
|
39
|
+
for (let i = 0, lastBest = 0; lastBest < 4; ++i, ++lastBest) {
|
|
40
40
|
sweepLayerGraphs(i % 2 ? downLayerGraphs : upLayerGraphs, i % 4 >= 2);
|
|
41
41
|
|
|
42
42
|
layering = util.buildLayerMatrix(g);
|
|
43
|
-
|
|
43
|
+
let cc = crossCount(g, layering);
|
|
44
44
|
if (cc < bestCC) {
|
|
45
45
|
lastBest = 0;
|
|
46
46
|
best = Object.assign({}, layering);
|
|
@@ -58,10 +58,10 @@ function buildLayerGraphs(g, ranks, relationship) {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
function sweepLayerGraphs(layerGraphs, biasRight) {
|
|
61
|
-
|
|
61
|
+
let cg = new Graph();
|
|
62
62
|
layerGraphs.forEach(function(lg) {
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
let root = lg.graph().root;
|
|
64
|
+
let sorted = sortSubgraph(lg, root, cg, biasRight);
|
|
65
65
|
sorted.vs.forEach((v, i) => lg.node(v).order = i);
|
|
66
66
|
addSubgraphConstraints(lg, cg, sorted.vs);
|
|
67
67
|
});
|