@dagrejs/dagre 1.0.4 → 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/index.d.ts +10 -2
- package/lib/acyclic.js +10 -18
- package/lib/add-border-segments.js +19 -11
- package/lib/coordinate-system.js +5 -15
- package/lib/data/list.js +8 -7
- package/lib/debug.js +25 -14
- package/lib/greedy-fas.js +35 -30
- package/lib/index.js +38 -0
- package/lib/layout.js +105 -102
- package/lib/nesting-graph.js +18 -21
- package/lib/normalize.js +22 -18
- package/lib/order/add-subgraph-constraints.js +6 -2
- package/lib/order/barycenter.js +14 -6
- package/lib/order/build-layer-graph.js +19 -13
- package/lib/order/cross-count.js +13 -10
- package/lib/order/index.js +33 -24
- package/lib/order/init-order.js +8 -7
- package/lib/order/resolve-conflicts.js +9 -19
- package/lib/order/sort-subgraph.js +16 -22
- package/lib/order/sort.js +13 -12
- package/lib/parent-dummy-chains.js +17 -19
- package/lib/position/bk.js +42 -84
- package/lib/position/index.js +10 -9
- package/lib/rank/feasible-tree.js +14 -17
- package/lib/rank/index.js +25 -15
- package/lib/rank/network-simplex.js +18 -39
- package/lib/rank/util.js +6 -12
- package/lib/util.js +42 -57
- 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
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
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
165
|
var candidates = g.edges().filter(edge => {
|
|
181
|
-
return flip === isDescendant(t, t.node(edge.v), tailLabel) &&
|
|
182
|
-
flip !== isDescendant(t, t.node(edge.w), tailLabel);
|
|
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,7 +181,6 @@ 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);
|
|
@@ -209,12 +189,10 @@ function updateRanks(t, g) {
|
|
|
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,30 +2,29 @@
|
|
|
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
|
*/
|
|
@@ -34,7 +33,6 @@ function addDummyNode(g, type, attrs, name) {
|
|
|
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,10 +43,13 @@ function addDummyNode(g, type, attrs, name) {
|
|
|
45
43
|
* associated with multi-edges.
|
|
46
44
|
*/
|
|
47
45
|
function simplify(g) {
|
|
48
|
-
let simplified = new Graph().setGraph(g.graph());
|
|
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
|
-
let simpleLabel = simplified.edge(e.v, e.w) || {
|
|
49
|
+
let simpleLabel = simplified.edge(e.v, e.w) || {
|
|
50
|
+
weight: 0,
|
|
51
|
+
minlen: 1
|
|
52
|
+
};
|
|
52
53
|
let label = g.edge(e);
|
|
53
54
|
simplified.setEdge(e.v, e.w, {
|
|
54
55
|
weight: simpleLabel.weight + label.weight,
|
|
@@ -57,9 +58,10 @@ function simplify(g) {
|
|
|
57
58
|
});
|
|
58
59
|
return simplified;
|
|
59
60
|
}
|
|
60
|
-
|
|
61
61
|
function asNonCompoundGraph(g) {
|
|
62
|
-
let simplified = new Graph({
|
|
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,7 +72,6 @@ function asNonCompoundGraph(g) {
|
|
|
70
72
|
});
|
|
71
73
|
return simplified;
|
|
72
74
|
}
|
|
73
|
-
|
|
74
75
|
function successorWeights(g) {
|
|
75
76
|
let weightMap = g.nodes().map(v => {
|
|
76
77
|
let sucs = {};
|
|
@@ -81,7 +82,6 @@ function successorWeights(g) {
|
|
|
81
82
|
});
|
|
82
83
|
return zipObject(g.nodes(), weightMap);
|
|
83
84
|
}
|
|
84
|
-
|
|
85
85
|
function predecessorWeights(g) {
|
|
86
86
|
let weightMap = g.nodes().map(v => {
|
|
87
87
|
let preds = {};
|
|
@@ -107,11 +107,9 @@ function intersectRect(rect, point) {
|
|
|
107
107
|
let dy = point.y - y;
|
|
108
108
|
let w = rect.width / 2;
|
|
109
109
|
let h = rect.height / 2;
|
|
110
|
-
|
|
111
110
|
if (!dx && !dy) {
|
|
112
111
|
throw new Error("Not possible to find intersection inside of the rectangle");
|
|
113
112
|
}
|
|
114
|
-
|
|
115
113
|
let sx, sy;
|
|
116
114
|
if (Math.abs(dy) * w > Math.abs(dx) * h) {
|
|
117
115
|
// Intersection is top or bottom of rect.
|
|
@@ -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
|
/*
|
|
@@ -158,7 +158,6 @@ function normalizeRanks(g) {
|
|
|
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 => {
|
|
@@ -168,11 +167,9 @@ function normalizeRanks(g) {
|
|
|
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
172
|
let offset = Math.min(...g.nodes().map(v => g.node(v).rank));
|
|
175
|
-
|
|
176
173
|
let layers = [];
|
|
177
174
|
g.nodes().forEach(v => {
|
|
178
175
|
let rank = g.node(v).rank - offset;
|
|
@@ -181,7 +178,6 @@ function removeEmptyRanks(g) {
|
|
|
181
178
|
}
|
|
182
179
|
layers[rank].push(v);
|
|
183
180
|
});
|
|
184
|
-
|
|
185
181
|
let delta = 0;
|
|
186
182
|
let nodeRankFactor = g.graph().nodeRankFactor;
|
|
187
183
|
Array.from(layers).forEach((vs, i) => {
|
|
@@ -192,7 +188,6 @@ 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,
|
|
@@ -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
|
-
let result = {
|
|
218
|
+
let result = {
|
|
219
|
+
lhs: [],
|
|
220
|
+
rhs: []
|
|
221
|
+
};
|
|
226
222
|
collection.forEach(value => {
|
|
227
223
|
if (fn(value)) {
|
|
228
224
|
result.lhs.push(value);
|
|
@@ -245,36 +241,29 @@ function time(name, fn) {
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
export function adjust(g) {
|
|
4
|
+
let rankDir = g.graph().rankdir.toLowerCase();
|
|
5
|
+
if (rankDir === "lr" || rankDir === "rl") {
|
|
6
|
+
swapWidthHeight(g);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function undo(g) {
|
|
11
|
+
let rankDir = g.graph().rankdir.toLowerCase();
|
|
12
|
+
if (rankDir === "bt" || rankDir === "rl") {
|
|
13
|
+
reverseY(g);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (rankDir === "lr" || rankDir === "rl") {
|
|
17
|
+
swapXY(g);
|
|
18
|
+
swapWidthHeight(g);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function swapWidthHeight(g) {
|
|
23
|
+
g.nodes().forEach(v => swapWidthHeightOne(g.node(v)));
|
|
24
|
+
g.edges().forEach(e => swapWidthHeightOne(g.edge(e)));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function swapWidthHeightOne(attrs) {
|
|
28
|
+
let w = attrs.width;
|
|
29
|
+
attrs.width = attrs.height;
|
|
30
|
+
attrs.height = w;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function reverseY(g) {
|
|
34
|
+
g.nodes().forEach(v => reverseYOne(g.node(v)));
|
|
35
|
+
|
|
36
|
+
g.edges().forEach(e => {
|
|
37
|
+
let edge = g.edge(e);
|
|
38
|
+
edge.points.forEach(reverseYOne);
|
|
39
|
+
if (edge.hasOwnProperty("y")) {
|
|
40
|
+
reverseYOne(edge);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function reverseYOne(attrs) {
|
|
46
|
+
attrs.y = -attrs.y;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function swapXY(g) {
|
|
50
|
+
g.nodes().forEach(v => swapXYOne(g.node(v)));
|
|
51
|
+
|
|
52
|
+
g.edges().forEach(e => {
|
|
53
|
+
let edge = g.edge(e);
|
|
54
|
+
edge.points.forEach(swapXYOne);
|
|
55
|
+
if (edge.hasOwnProperty("x")) {
|
|
56
|
+
swapXYOne(edge);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function swapXYOne(attrs) {
|
|
62
|
+
let x = attrs.x;
|
|
63
|
+
attrs.x = attrs.y;
|
|
64
|
+
attrs.y = x;
|
|
65
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Simple doubly linked list implementation derived from Cormen, et al.,
|
|
3
|
+
* "Introduction to Algorithms".
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export default class List {
|
|
7
|
+
constructor() {
|
|
8
|
+
let sentinel = {};
|
|
9
|
+
sentinel._next = sentinel._prev = sentinel;
|
|
10
|
+
this._sentinel = sentinel;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
dequeue() {
|
|
14
|
+
let sentinel = this._sentinel;
|
|
15
|
+
let entry = sentinel._prev;
|
|
16
|
+
if (entry !== sentinel) {
|
|
17
|
+
unlink(entry);
|
|
18
|
+
return entry;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
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;
|
|
31
|
+
}
|
|
32
|
+
|
|
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(", ") + "]";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function unlink(entry) {
|
|
46
|
+
entry._prev._next = entry._next;
|
|
47
|
+
entry._next._prev = entry._prev;
|
|
48
|
+
delete entry._next;
|
|
49
|
+
delete entry._prev;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function filterOutLinks(k, v) {
|
|
53
|
+
if (k !== "_next" && k !== "_prev") {
|
|
54
|
+
return v;
|
|
55
|
+
}
|
|
56
|
+
}
|