@dagrejs/dagre 1.0.2 → 1.1.0
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 +18 -3
- package/lib/acyclic.js +20 -28
- package/lib/add-border-segments.js +23 -15
- package/lib/coordinate-system.js +13 -23
- package/lib/data/list.js +40 -37
- package/lib/debug.js +31 -22
- package/lib/greedy-fas.js +60 -55
- package/lib/index.js +38 -0
- package/lib/layout.js +158 -155
- package/lib/nesting-graph.js +35 -40
- package/lib/normalize.js +34 -30
- package/lib/order/add-subgraph-constraints.js +9 -5
- package/lib/order/barycenter.js +17 -9
- package/lib/order/build-layer-graph.js +24 -18
- package/lib/order/cross-count.js +22 -19
- package/lib/order/index.js +37 -28
- package/lib/order/init-order.js +14 -13
- package/lib/order/resolve-conflicts.js +20 -30
- package/lib/order/sort-subgraph.js +25 -31
- package/lib/order/sort.js +18 -17
- package/lib/parent-dummy-chains.js +36 -38
- package/lib/position/bk.js +103 -145
- package/lib/position/index.js +14 -13
- package/lib/rank/feasible-tree.js +15 -18
- package/lib/rank/index.js +25 -15
- package/lib/rank/network-simplex.js +22 -43
- package/lib/rank/util.js +6 -12
- package/lib/util.js +69 -84
- package/lib/version.js +8 -1
- package/mjs-lib/acyclic.js +62 -0
- package/mjs-lib/add-border-segments.js +35 -0
- package/mjs-lib/coordinate-system.js +65 -0
- package/mjs-lib/data/list.js +56 -0
- package/mjs-lib/debug.js +30 -0
- package/mjs-lib/greedy-fas.js +125 -0
- package/mjs-lib/index.js +9 -0
- package/mjs-lib/layout.js +405 -0
- package/mjs-lib/nesting-graph.js +120 -0
- package/mjs-lib/normalize.js +84 -0
- package/mjs-lib/order/add-subgraph-constraints.js +49 -0
- package/mjs-lib/order/barycenter.js +24 -0
- package/mjs-lib/order/build-layer-graph.js +71 -0
- package/mjs-lib/order/cross-count.js +64 -0
- package/mjs-lib/order/index.js +70 -0
- package/mjs-lib/order/init-order.js +34 -0
- package/mjs-lib/order/resolve-conflicts.js +116 -0
- package/mjs-lib/order/sort-subgraph.js +71 -0
- package/mjs-lib/order/sort.js +54 -0
- package/mjs-lib/parent-dummy-chains.js +82 -0
- package/mjs-lib/position/bk.js +409 -0
- package/mjs-lib/position/index.js +30 -0
- package/mjs-lib/rank/feasible-tree.js +93 -0
- package/mjs-lib/rank/index.js +46 -0
- package/mjs-lib/rank/network-simplex.js +233 -0
- package/mjs-lib/rank/util.js +58 -0
- package/mjs-lib/util.js +305 -0
- package/mjs-lib/version.js +1 -0
- package/package.json +14 -3
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
var
|
|
8
|
-
var
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = networkSimplex;
|
|
7
|
+
var _feasibleTree = _interopRequireDefault(require("./feasible-tree.js"));
|
|
8
|
+
var _util = require("./util.js");
|
|
9
|
+
var _graphlib = require("@dagrejs/graphlib");
|
|
10
|
+
var _util2 = require("../util.js");
|
|
11
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
12
|
+
var preorder = _graphlib.alg.preorder;
|
|
13
|
+
var postorder = _graphlib.alg.postorder;
|
|
12
14
|
// Expose some internals for testing purposes
|
|
13
15
|
networkSimplex.initLowLimValues = initLowLimValues;
|
|
14
16
|
networkSimplex.initCutValues = initCutValues;
|
|
@@ -51,14 +53,13 @@ networkSimplex.exchangeEdges = exchangeEdges;
|
|
|
51
53
|
* structure of the overall algorithm.
|
|
52
54
|
*/
|
|
53
55
|
function networkSimplex(g) {
|
|
54
|
-
g = simplify(g);
|
|
55
|
-
|
|
56
|
-
var t =
|
|
56
|
+
g = (0, _util2.simplify)(g);
|
|
57
|
+
(0, _util.longestPath)(g);
|
|
58
|
+
var t = (0, _feasibleTree.default)(g);
|
|
57
59
|
initLowLimValues(t);
|
|
58
60
|
initCutValues(t, g);
|
|
59
|
-
|
|
60
61
|
var e, f;
|
|
61
|
-
while (
|
|
62
|
+
while (e = leaveEdge(t)) {
|
|
62
63
|
f = enterEdge(t, g, e);
|
|
63
64
|
exchangeEdges(t, g, e, f);
|
|
64
65
|
}
|
|
@@ -72,7 +73,6 @@ function initCutValues(t, g) {
|
|
|
72
73
|
vs = vs.slice(0, vs.length - 1);
|
|
73
74
|
vs.forEach(v => assignCutValue(t, g, v));
|
|
74
75
|
}
|
|
75
|
-
|
|
76
76
|
function assignCutValue(t, g, child) {
|
|
77
77
|
var childLab = t.node(child);
|
|
78
78
|
var parent = childLab.parent;
|
|
@@ -92,22 +92,17 @@ function calcCutValue(t, g, child) {
|
|
|
92
92
|
var graphEdge = g.edge(child, parent);
|
|
93
93
|
// The accumulated cut value for the edge between this node and its parent
|
|
94
94
|
var cutValue = 0;
|
|
95
|
-
|
|
96
95
|
if (!graphEdge) {
|
|
97
96
|
childIsTail = false;
|
|
98
97
|
graphEdge = g.edge(parent, child);
|
|
99
98
|
}
|
|
100
|
-
|
|
101
99
|
cutValue = graphEdge.weight;
|
|
102
|
-
|
|
103
|
-
g.nodeEdges(child).forEach(function(e) {
|
|
100
|
+
g.nodeEdges(child).forEach(e => {
|
|
104
101
|
var isOutEdge = e.v === child,
|
|
105
102
|
other = isOutEdge ? e.w : e.v;
|
|
106
|
-
|
|
107
103
|
if (other !== parent) {
|
|
108
104
|
var pointsToHead = isOutEdge === childIsTail,
|
|
109
105
|
otherWeight = g.edge(e).weight;
|
|
110
|
-
|
|
111
106
|
cutValue += pointsToHead ? otherWeight : -otherWeight;
|
|
112
107
|
if (isTreeEdge(t, child, other)) {
|
|
113
108
|
var otherCutValue = t.edge(child, other).cutvalue;
|
|
@@ -115,28 +110,23 @@ function calcCutValue(t, g, child) {
|
|
|
115
110
|
}
|
|
116
111
|
}
|
|
117
112
|
});
|
|
118
|
-
|
|
119
113
|
return cutValue;
|
|
120
114
|
}
|
|
121
|
-
|
|
122
115
|
function initLowLimValues(tree, root) {
|
|
123
116
|
if (arguments.length < 2) {
|
|
124
117
|
root = tree.nodes()[0];
|
|
125
118
|
}
|
|
126
119
|
dfsAssignLowLim(tree, {}, 1, root);
|
|
127
120
|
}
|
|
128
|
-
|
|
129
121
|
function dfsAssignLowLim(tree, visited, nextLim, v, parent) {
|
|
130
122
|
var low = nextLim;
|
|
131
123
|
var label = tree.node(v);
|
|
132
|
-
|
|
133
124
|
visited[v] = true;
|
|
134
|
-
tree.neighbors(v).forEach(
|
|
125
|
+
tree.neighbors(v).forEach(w => {
|
|
135
126
|
if (!visited.hasOwnProperty(w)) {
|
|
136
127
|
nextLim = dfsAssignLowLim(tree, visited, nextLim, w, v);
|
|
137
128
|
}
|
|
138
129
|
});
|
|
139
|
-
|
|
140
130
|
label.low = low;
|
|
141
131
|
label.lim = nextLim++;
|
|
142
132
|
if (parent) {
|
|
@@ -145,14 +135,11 @@ function dfsAssignLowLim(tree, visited, nextLim, v, parent) {
|
|
|
145
135
|
// TODO should be able to remove this when we incrementally update low lim
|
|
146
136
|
delete label.parent;
|
|
147
137
|
}
|
|
148
|
-
|
|
149
138
|
return nextLim;
|
|
150
139
|
}
|
|
151
|
-
|
|
152
140
|
function leaveEdge(tree) {
|
|
153
141
|
return tree.edges().find(e => tree.edge(e).cutvalue < 0);
|
|
154
142
|
}
|
|
155
|
-
|
|
156
143
|
function enterEdge(t, g, edge) {
|
|
157
144
|
var v = edge.v;
|
|
158
145
|
var w = edge.w;
|
|
@@ -164,7 +151,6 @@ function enterEdge(t, g, edge) {
|
|
|
164
151
|
v = edge.w;
|
|
165
152
|
w = edge.v;
|
|
166
153
|
}
|
|
167
|
-
|
|
168
154
|
var vLabel = t.node(v);
|
|
169
155
|
var wLabel = t.node(w);
|
|
170
156
|
var tailLabel = vLabel;
|
|
@@ -176,21 +162,16 @@ function enterEdge(t, g, edge) {
|
|
|
176
162
|
tailLabel = wLabel;
|
|
177
163
|
flip = true;
|
|
178
164
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
return flip === isDescendant(t, t.node(edge.v), tailLabel) &&
|
|
182
|
-
flip !== isDescendant(t, t.node(edge.w), tailLabel);
|
|
165
|
+
var candidates = g.edges().filter(edge => {
|
|
166
|
+
return flip === isDescendant(t, t.node(edge.v), tailLabel) && flip !== isDescendant(t, t.node(edge.w), tailLabel);
|
|
183
167
|
});
|
|
184
|
-
|
|
185
168
|
return candidates.reduce((acc, edge) => {
|
|
186
|
-
if (slack(g, edge) < slack(g, acc)) {
|
|
169
|
+
if ((0, _util.slack)(g, edge) < (0, _util.slack)(g, acc)) {
|
|
187
170
|
return edge;
|
|
188
171
|
}
|
|
189
|
-
|
|
190
172
|
return acc;
|
|
191
173
|
});
|
|
192
174
|
}
|
|
193
|
-
|
|
194
175
|
function exchangeEdges(t, g, e, f) {
|
|
195
176
|
var v = e.v;
|
|
196
177
|
var w = e.w;
|
|
@@ -200,21 +181,18 @@ function exchangeEdges(t, g, e, f) {
|
|
|
200
181
|
initCutValues(t, g);
|
|
201
182
|
updateRanks(t, g);
|
|
202
183
|
}
|
|
203
|
-
|
|
204
184
|
function updateRanks(t, g) {
|
|
205
185
|
var root = t.nodes().find(v => !g.node(v).parent);
|
|
206
186
|
var vs = preorder(t, root);
|
|
207
187
|
vs = vs.slice(1);
|
|
208
|
-
vs.forEach(
|
|
188
|
+
vs.forEach(v => {
|
|
209
189
|
var parent = t.node(v).parent,
|
|
210
190
|
edge = g.edge(v, parent),
|
|
211
191
|
flipped = false;
|
|
212
|
-
|
|
213
192
|
if (!edge) {
|
|
214
193
|
edge = g.edge(parent, v);
|
|
215
194
|
flipped = true;
|
|
216
195
|
}
|
|
217
|
-
|
|
218
196
|
g.node(v).rank = g.node(parent).rank + (flipped ? edge.minlen : -edge.minlen);
|
|
219
197
|
});
|
|
220
198
|
}
|
|
@@ -233,3 +211,4 @@ function isTreeEdge(tree, u, v) {
|
|
|
233
211
|
function isDescendant(tree, vLabel, rootLabel) {
|
|
234
212
|
return rootLabel.low <= vLabel.lim && vLabel.lim <= rootLabel.lim;
|
|
235
213
|
}
|
|
214
|
+
module.exports = exports.default;
|
package/lib/rank/util.js
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
module.exports = {
|
|
4
|
-
longestPath: longestPath,
|
|
5
|
-
slack: slack
|
|
6
|
-
};
|
|
7
|
-
|
|
8
3
|
/*
|
|
9
4
|
* Initializes ranks for the input graph using the longest path algorithm. This
|
|
10
5
|
* algorithm scales well and is fast in practice, it yields rather poor
|
|
@@ -26,31 +21,30 @@ module.exports = {
|
|
|
26
21
|
*
|
|
27
22
|
* 1. Each node will be assign an (unnormalized) "rank" property.
|
|
28
23
|
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", {
|
|
25
|
+
value: true
|
|
26
|
+
});
|
|
27
|
+
exports.longestPath = longestPath;
|
|
28
|
+
exports.slack = slack;
|
|
29
29
|
function longestPath(g) {
|
|
30
30
|
var visited = {};
|
|
31
|
-
|
|
32
31
|
function dfs(v) {
|
|
33
32
|
var label = g.node(v);
|
|
34
33
|
if (visited.hasOwnProperty(v)) {
|
|
35
34
|
return label.rank;
|
|
36
35
|
}
|
|
37
36
|
visited[v] = true;
|
|
38
|
-
|
|
39
37
|
var rank = Math.min(...g.outEdges(v).map(e => {
|
|
40
38
|
if (e == null) {
|
|
41
39
|
return Number.POSITIVE_INFINITY;
|
|
42
40
|
}
|
|
43
|
-
|
|
44
41
|
return dfs(e.w) - g.edge(e).minlen;
|
|
45
42
|
}));
|
|
46
|
-
|
|
47
43
|
if (rank === Number.POSITIVE_INFINITY) {
|
|
48
44
|
rank = 0;
|
|
49
45
|
}
|
|
50
|
-
|
|
51
|
-
return (label.rank = rank);
|
|
46
|
+
return label.rank = rank;
|
|
52
47
|
}
|
|
53
|
-
|
|
54
48
|
g.sources().forEach(dfs);
|
|
55
49
|
}
|
|
56
50
|
|
package/lib/util.js
CHANGED
|
@@ -2,39 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
exports.addBorderNode = addBorderNode;
|
|
9
|
+
exports.addDummyNode = addDummyNode;
|
|
10
|
+
exports.asNonCompoundGraph = asNonCompoundGraph;
|
|
11
|
+
exports.buildLayerMatrix = buildLayerMatrix;
|
|
12
|
+
exports.intersectRect = intersectRect;
|
|
13
|
+
exports.mapValues = mapValues;
|
|
14
|
+
exports.maxRank = maxRank;
|
|
15
|
+
exports.normalizeRanks = normalizeRanks;
|
|
16
|
+
exports.notime = notime;
|
|
17
|
+
exports.partition = partition;
|
|
18
|
+
exports.pick = pick;
|
|
19
|
+
exports.predecessorWeights = predecessorWeights;
|
|
20
|
+
exports.range = range;
|
|
21
|
+
exports.removeEmptyRanks = removeEmptyRanks;
|
|
22
|
+
exports.simplify = simplify;
|
|
23
|
+
exports.successorWeights = successorWeights;
|
|
24
|
+
exports.time = time;
|
|
25
|
+
exports.uniqueId = uniqueId;
|
|
26
|
+
exports.zipObject = zipObject;
|
|
27
|
+
var _graphlib = require("@dagrejs/graphlib");
|
|
29
28
|
/*
|
|
30
29
|
* Adds a dummy node to the graph and return v.
|
|
31
30
|
*/
|
|
32
31
|
function addDummyNode(g, type, attrs, name) {
|
|
33
|
-
|
|
32
|
+
let v;
|
|
34
33
|
do {
|
|
35
34
|
v = uniqueId(name);
|
|
36
35
|
} while (g.hasNode(v));
|
|
37
|
-
|
|
38
36
|
attrs.dummy = type;
|
|
39
37
|
g.setNode(v, attrs);
|
|
40
38
|
return v;
|
|
@@ -45,11 +43,14 @@ function addDummyNode(g, type, attrs, name) {
|
|
|
45
43
|
* associated with multi-edges.
|
|
46
44
|
*/
|
|
47
45
|
function simplify(g) {
|
|
48
|
-
|
|
46
|
+
let simplified = new _graphlib.Graph().setGraph(g.graph());
|
|
49
47
|
g.nodes().forEach(v => simplified.setNode(v, g.node(v)));
|
|
50
48
|
g.edges().forEach(e => {
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
let simpleLabel = simplified.edge(e.v, e.w) || {
|
|
50
|
+
weight: 0,
|
|
51
|
+
minlen: 1
|
|
52
|
+
};
|
|
53
|
+
let label = g.edge(e);
|
|
53
54
|
simplified.setEdge(e.v, e.w, {
|
|
54
55
|
weight: simpleLabel.weight + label.weight,
|
|
55
56
|
minlen: Math.max(simpleLabel.minlen, label.minlen)
|
|
@@ -57,9 +58,10 @@ function simplify(g) {
|
|
|
57
58
|
});
|
|
58
59
|
return simplified;
|
|
59
60
|
}
|
|
60
|
-
|
|
61
61
|
function asNonCompoundGraph(g) {
|
|
62
|
-
|
|
62
|
+
let simplified = new _graphlib.Graph({
|
|
63
|
+
multigraph: g.isMultigraph()
|
|
64
|
+
}).setGraph(g.graph());
|
|
63
65
|
g.nodes().forEach(v => {
|
|
64
66
|
if (!g.children(v).length) {
|
|
65
67
|
simplified.setNode(v, g.node(v));
|
|
@@ -70,10 +72,9 @@ function asNonCompoundGraph(g) {
|
|
|
70
72
|
});
|
|
71
73
|
return simplified;
|
|
72
74
|
}
|
|
73
|
-
|
|
74
75
|
function successorWeights(g) {
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
let weightMap = g.nodes().map(v => {
|
|
77
|
+
let sucs = {};
|
|
77
78
|
g.outEdges(v).forEach(e => {
|
|
78
79
|
sucs[e.w] = (sucs[e.w] || 0) + g.edge(e).weight;
|
|
79
80
|
});
|
|
@@ -81,10 +82,9 @@ function successorWeights(g) {
|
|
|
81
82
|
});
|
|
82
83
|
return zipObject(g.nodes(), weightMap);
|
|
83
84
|
}
|
|
84
|
-
|
|
85
85
|
function predecessorWeights(g) {
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
let weightMap = g.nodes().map(v => {
|
|
87
|
+
let preds = {};
|
|
88
88
|
g.inEdges(v).forEach(e => {
|
|
89
89
|
preds[e.v] = (preds[e.v] || 0) + g.edge(e).weight;
|
|
90
90
|
});
|
|
@@ -98,21 +98,19 @@ function predecessorWeights(g) {
|
|
|
98
98
|
* ({x, y, width, height}) if it were pointing at the rectangle's center.
|
|
99
99
|
*/
|
|
100
100
|
function intersectRect(rect, point) {
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
let x = rect.x;
|
|
102
|
+
let y = rect.y;
|
|
103
103
|
|
|
104
104
|
// Rectangle intersection algorithm from:
|
|
105
105
|
// http://math.stackexchange.com/questions/108113/find-edge-between-two-boxes
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
106
|
+
let dx = point.x - x;
|
|
107
|
+
let dy = point.y - y;
|
|
108
|
+
let w = rect.width / 2;
|
|
109
|
+
let h = rect.height / 2;
|
|
111
110
|
if (!dx && !dy) {
|
|
112
111
|
throw new Error("Not possible to find intersection inside of the rectangle");
|
|
113
112
|
}
|
|
114
|
-
|
|
115
|
-
var sx, sy;
|
|
113
|
+
let sx, sy;
|
|
116
114
|
if (Math.abs(dy) * w > Math.abs(dx) * h) {
|
|
117
115
|
// Intersection is top or bottom of rect.
|
|
118
116
|
if (dy < 0) {
|
|
@@ -128,8 +126,10 @@ function intersectRect(rect, point) {
|
|
|
128
126
|
sx = w;
|
|
129
127
|
sy = w * dy / dx;
|
|
130
128
|
}
|
|
131
|
-
|
|
132
|
-
|
|
129
|
+
return {
|
|
130
|
+
x: x + sx,
|
|
131
|
+
y: y + sy
|
|
132
|
+
};
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
/*
|
|
@@ -137,10 +137,10 @@ function intersectRect(rect, point) {
|
|
|
137
137
|
* function will produce a matrix with the ids of each node.
|
|
138
138
|
*/
|
|
139
139
|
function buildLayerMatrix(g) {
|
|
140
|
-
|
|
140
|
+
let layering = range(maxRank(g) + 1).map(() => []);
|
|
141
141
|
g.nodes().forEach(v => {
|
|
142
|
-
|
|
143
|
-
|
|
142
|
+
let node = g.node(v);
|
|
143
|
+
let rank = node.rank;
|
|
144
144
|
if (rank !== undefined) {
|
|
145
145
|
layering[rank][node.order] = v;
|
|
146
146
|
}
|
|
@@ -153,37 +153,33 @@ function buildLayerMatrix(g) {
|
|
|
153
153
|
* rank(v) >= 0 and at least one node w has rank(w) = 0.
|
|
154
154
|
*/
|
|
155
155
|
function normalizeRanks(g) {
|
|
156
|
-
|
|
157
|
-
|
|
156
|
+
let min = Math.min(...g.nodes().map(v => {
|
|
157
|
+
let rank = g.node(v).rank;
|
|
158
158
|
if (rank === undefined) {
|
|
159
159
|
return Number.MAX_VALUE;
|
|
160
160
|
}
|
|
161
|
-
|
|
162
161
|
return rank;
|
|
163
162
|
}));
|
|
164
163
|
g.nodes().forEach(v => {
|
|
165
|
-
|
|
164
|
+
let node = g.node(v);
|
|
166
165
|
if (node.hasOwnProperty("rank")) {
|
|
167
166
|
node.rank -= min;
|
|
168
167
|
}
|
|
169
168
|
});
|
|
170
169
|
}
|
|
171
|
-
|
|
172
170
|
function removeEmptyRanks(g) {
|
|
173
171
|
// Ranks may not start at 0, so we need to offset them
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
var layers = [];
|
|
172
|
+
let offset = Math.min(...g.nodes().map(v => g.node(v).rank));
|
|
173
|
+
let layers = [];
|
|
177
174
|
g.nodes().forEach(v => {
|
|
178
|
-
|
|
175
|
+
let rank = g.node(v).rank - offset;
|
|
179
176
|
if (!layers[rank]) {
|
|
180
177
|
layers[rank] = [];
|
|
181
178
|
}
|
|
182
179
|
layers[rank].push(v);
|
|
183
180
|
});
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
var nodeRankFactor = g.graph().nodeRankFactor;
|
|
181
|
+
let delta = 0;
|
|
182
|
+
let nodeRankFactor = g.graph().nodeRankFactor;
|
|
187
183
|
Array.from(layers).forEach((vs, i) => {
|
|
188
184
|
if (vs === undefined && i % nodeRankFactor !== 0) {
|
|
189
185
|
--delta;
|
|
@@ -192,9 +188,8 @@ function removeEmptyRanks(g) {
|
|
|
192
188
|
}
|
|
193
189
|
});
|
|
194
190
|
}
|
|
195
|
-
|
|
196
191
|
function addBorderNode(g, prefix, rank, order) {
|
|
197
|
-
|
|
192
|
+
let node = {
|
|
198
193
|
width: 0,
|
|
199
194
|
height: 0
|
|
200
195
|
};
|
|
@@ -204,14 +199,12 @@ function addBorderNode(g, prefix, rank, order) {
|
|
|
204
199
|
}
|
|
205
200
|
return addDummyNode(g, "border", node, prefix);
|
|
206
201
|
}
|
|
207
|
-
|
|
208
202
|
function maxRank(g) {
|
|
209
203
|
return Math.max(...g.nodes().map(v => {
|
|
210
|
-
|
|
204
|
+
let rank = g.node(v).rank;
|
|
211
205
|
if (rank === undefined) {
|
|
212
206
|
return Number.MIN_VALUE;
|
|
213
207
|
}
|
|
214
|
-
|
|
215
208
|
return rank;
|
|
216
209
|
}));
|
|
217
210
|
}
|
|
@@ -222,7 +215,10 @@ function maxRank(g) {
|
|
|
222
215
|
* into `rhs.
|
|
223
216
|
*/
|
|
224
217
|
function partition(collection, fn) {
|
|
225
|
-
|
|
218
|
+
let result = {
|
|
219
|
+
lhs: [],
|
|
220
|
+
rhs: []
|
|
221
|
+
};
|
|
226
222
|
collection.forEach(value => {
|
|
227
223
|
if (fn(value)) {
|
|
228
224
|
result.lhs.push(value);
|
|
@@ -238,43 +234,36 @@ function partition(collection, fn) {
|
|
|
238
234
|
* time it takes to execute the function.
|
|
239
235
|
*/
|
|
240
236
|
function time(name, fn) {
|
|
241
|
-
|
|
237
|
+
let start = Date.now();
|
|
242
238
|
try {
|
|
243
239
|
return fn();
|
|
244
240
|
} finally {
|
|
245
241
|
console.log(name + " time: " + (Date.now() - start) + "ms");
|
|
246
242
|
}
|
|
247
243
|
}
|
|
248
|
-
|
|
249
244
|
function notime(name, fn) {
|
|
250
245
|
return fn();
|
|
251
246
|
}
|
|
252
|
-
|
|
253
247
|
let idCounter = 0;
|
|
254
248
|
function uniqueId(prefix) {
|
|
255
249
|
var id = ++idCounter;
|
|
256
250
|
return toString(prefix) + id;
|
|
257
251
|
}
|
|
258
|
-
|
|
259
252
|
function range(start, limit, step = 1) {
|
|
260
253
|
if (limit == null) {
|
|
261
254
|
limit = start;
|
|
262
255
|
start = 0;
|
|
263
256
|
}
|
|
264
|
-
|
|
265
|
-
let endCon = (i) => i < limit;
|
|
257
|
+
let endCon = i => i < limit;
|
|
266
258
|
if (step < 0) {
|
|
267
|
-
endCon =
|
|
259
|
+
endCon = i => limit < i;
|
|
268
260
|
}
|
|
269
|
-
|
|
270
261
|
const range = [];
|
|
271
262
|
for (let i = start; endCon(i); i += step) {
|
|
272
263
|
range.push(i);
|
|
273
264
|
}
|
|
274
|
-
|
|
275
265
|
return range;
|
|
276
266
|
}
|
|
277
|
-
|
|
278
267
|
function pick(source, keys) {
|
|
279
268
|
const dest = {};
|
|
280
269
|
for (const key of keys) {
|
|
@@ -282,22 +271,18 @@ function pick(source, keys) {
|
|
|
282
271
|
dest[key] = source[key];
|
|
283
272
|
}
|
|
284
273
|
}
|
|
285
|
-
|
|
286
274
|
return dest;
|
|
287
275
|
}
|
|
288
|
-
|
|
289
276
|
function mapValues(obj, funcOrProp) {
|
|
290
277
|
let func = funcOrProp;
|
|
291
278
|
if (typeof funcOrProp === 'string') {
|
|
292
|
-
func =
|
|
279
|
+
func = val => val[funcOrProp];
|
|
293
280
|
}
|
|
294
|
-
|
|
295
281
|
return Object.entries(obj).reduce((acc, [k, v]) => {
|
|
296
282
|
acc[k] = func(v, k);
|
|
297
283
|
return acc;
|
|
298
284
|
}, {});
|
|
299
285
|
}
|
|
300
|
-
|
|
301
286
|
function zipObject(props, values) {
|
|
302
287
|
return props.reduce((acc, key, i) => {
|
|
303
288
|
acc[key] = values[i];
|
package/lib/version.js
CHANGED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { default as greedyFAS } from "./greedy-fas.js";
|
|
4
|
+
import { uniqueId as uniqueId } from "./util.js";
|
|
5
|
+
|
|
6
|
+
export function run(g) {
|
|
7
|
+
let fas = (g.graph().acyclicer === "greedy"
|
|
8
|
+
? greedyFAS(g, weightFn(g))
|
|
9
|
+
: dfsFAS(g));
|
|
10
|
+
fas.forEach(e => {
|
|
11
|
+
let label = g.edge(e);
|
|
12
|
+
g.removeEdge(e);
|
|
13
|
+
label.forwardName = e.name;
|
|
14
|
+
label.reversed = true;
|
|
15
|
+
g.setEdge(e.w, e.v, label, uniqueId("rev"));
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
function weightFn(g) {
|
|
19
|
+
return e => {
|
|
20
|
+
return g.edge(e).weight;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function dfsFAS(g) {
|
|
26
|
+
let fas = [];
|
|
27
|
+
let stack = {};
|
|
28
|
+
let visited = {};
|
|
29
|
+
|
|
30
|
+
function dfs(v) {
|
|
31
|
+
if (visited.hasOwnProperty(v)) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
visited[v] = true;
|
|
35
|
+
stack[v] = true;
|
|
36
|
+
g.outEdges(v).forEach(e => {
|
|
37
|
+
if (stack.hasOwnProperty(e.w)) {
|
|
38
|
+
fas.push(e);
|
|
39
|
+
} else {
|
|
40
|
+
dfs(e.w);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
delete stack[v];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
g.nodes().forEach(dfs);
|
|
47
|
+
return fas;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function undo(g) {
|
|
51
|
+
g.edges().forEach(e => {
|
|
52
|
+
let label = g.edge(e);
|
|
53
|
+
if (label.reversed) {
|
|
54
|
+
g.removeEdge(e);
|
|
55
|
+
|
|
56
|
+
let forwardName = label.forwardName;
|
|
57
|
+
delete label.reversed;
|
|
58
|
+
delete label.forwardName;
|
|
59
|
+
g.setEdge(e.w, e.v, label, forwardName);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as util from "./util.js";
|
|
2
|
+
|
|
3
|
+
export default function addBorderSegments(g) {
|
|
4
|
+
function dfs(v) {
|
|
5
|
+
let children = g.children(v);
|
|
6
|
+
let node = g.node(v);
|
|
7
|
+
if (children.length) {
|
|
8
|
+
children.forEach(dfs);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (node.hasOwnProperty("minRank")) {
|
|
12
|
+
node.borderLeft = [];
|
|
13
|
+
node.borderRight = [];
|
|
14
|
+
for (let rank = node.minRank, maxRank = node.maxRank + 1;
|
|
15
|
+
rank < maxRank;
|
|
16
|
+
++rank) {
|
|
17
|
+
addBorderNode(g, "borderLeft", "_bl", v, node, rank);
|
|
18
|
+
addBorderNode(g, "borderRight", "_br", v, node, rank);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
g.children().forEach(dfs);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function addBorderNode(g, prop, prefix, sg, sgNode, rank) {
|
|
27
|
+
let label = { width: 0, height: 0, rank: rank, borderType: prop };
|
|
28
|
+
let prev = sgNode[prop][rank - 1];
|
|
29
|
+
let curr = util.addDummyNode(g, "border", label, prefix);
|
|
30
|
+
sgNode[prop][rank] = curr;
|
|
31
|
+
g.setParent(curr, sg);
|
|
32
|
+
if (prev) {
|
|
33
|
+
g.setEdge(prev, curr, { weight: 1 });
|
|
34
|
+
}
|
|
35
|
+
}
|