|
1 | 1 | "use strict";
|
2 | 2 |
|
3 | 3 | var _ = require("../lodash"),
|
| 4 | + Graph = require("../graphlib").Graph, |
4 | 5 | util = require("../util");
|
5 | 6 |
|
6 | 7 | /*
|
@@ -204,74 +205,71 @@ function verticalAlignment(g, layering, conflicts, neighborFn) {
|
204 | 205 | }
|
205 | 206 |
|
206 | 207 | function horizontalCompaction(g, layering, root, align, reverseSep) {
|
207 |
| - // We use local variables for these parameters instead of manipulating the |
208 |
| - // graph because it becomes more verbose to access them in a chained manner. |
209 |
| - var shift = {}, |
210 |
| - shiftNeighbor = {}, |
211 |
| - sink = {}, |
212 |
| - xs = {}, |
213 |
| - pred = {}, |
214 |
| - graphLabel = g.graph(), |
215 |
| - sepFn = sep(graphLabel.nodesep, graphLabel.edgesep, reverseSep); |
| 208 | + // This portion of the algorithm differs from BK due to a number of problems. |
| 209 | + // Instead of their algorithm we construct a new block graph and do two |
| 210 | + // sweeps. The first sweep places blocks with the smallest possible |
| 211 | + // coordinates. The second sweep removes unused space by moving blocks to the |
| 212 | + // greatest coordinates without violating separation. |
| 213 | + var xs = {}, |
| 214 | + blockG = buildBlockGraph(g, layering, root, reverseSep); |
| 215 | + |
| 216 | + // First pass, assign smallest coordinates via DFS |
| 217 | + var visited = {}; |
| 218 | + function pass1(v) { |
| 219 | + if (!_.has(visited, v)) { |
| 220 | + visited[v] = true; |
| 221 | + xs[v] = _.reduce(blockG.inEdges(v), function(max, e) { |
| 222 | + pass1(e.v); |
| 223 | + return Math.max(max, xs[e.v] + blockG.edge(e)); |
| 224 | + }, 0); |
| 225 | + } |
| 226 | + } |
| 227 | + _.each(blockG.nodes(), pass1); |
| 228 | + |
| 229 | + function pass2(v) { |
| 230 | + if (visited[v] !== 2) { |
| 231 | + visited[v]++; |
| 232 | + var min = _.reduce(blockG.outEdges(v), function(min, e) { |
| 233 | + pass2(e.w); |
| 234 | + return Math.min(min, xs[e.w] - blockG.edge(e)); |
| 235 | + }, Number.POSITIVE_INFINITY); |
| 236 | + if (min !== Number.POSITIVE_INFINITY) { |
| 237 | + xs[v] = Math.max(xs[v], min); |
| 238 | + } |
| 239 | + } |
| 240 | + } |
| 241 | + _.each(blockG.nodes(), pass2); |
216 | 242 |
|
217 |
| - _.each(layering, function(layer) { |
218 |
| - _.each(layer, function(v, order) { |
219 |
| - sink[v] = v; |
220 |
| - shift[v] = Number.POSITIVE_INFINITY; |
221 |
| - pred[v] = layer[order - 1]; |
222 |
| - }); |
223 |
| - }); |
224 | 243 |
|
225 |
| - _.each(g.nodes(), function(v) { |
226 |
| - if (root[v] === v) { |
227 |
| - placeBlock(g, layering, sepFn, root, align, shift, shiftNeighbor, sink, pred, xs, v); |
228 |
| - } |
| 244 | + // Assign x coordinates to all nodes |
| 245 | + _.each(align, function(v) { |
| 246 | + xs[v] = xs[root[v]]; |
229 | 247 | });
|
230 | 248 |
|
| 249 | + return xs; |
| 250 | +} |
| 251 | + |
| 252 | + |
| 253 | +function buildBlockGraph(g, layering, root, reverseSep) { |
| 254 | + var blockGraph = new Graph(), |
| 255 | + graphLabel = g.graph(), |
| 256 | + sepFn = sep(graphLabel.nodesep, graphLabel.edgesep, reverseSep); |
| 257 | + |
231 | 258 | _.each(layering, function(layer) {
|
| 259 | + var u; |
232 | 260 | _.each(layer, function(v) {
|
233 |
| - xs[v] = xs[root[v]]; |
234 |
| - // This line differs from the source paper. See |
235 |
| - // http://www.inf.uni-konstanz.de/~brandes/publications/ for details. |
236 |
| - if (v === root[v] && shift[sink[root[v]]] < Number.POSITIVE_INFINITY) { |
237 |
| - xs[v] += shift[sink[root[v]]]; |
238 |
| - |
239 |
| - // Cascade shifts as necessary |
240 |
| - var w = shiftNeighbor[sink[root[v]]]; |
241 |
| - if (w && shift[w] !== Number.POSITIVE_INFINITY) { |
242 |
| - xs[v] += shift[w]; |
243 |
| - } |
| 261 | + var vRoot = root[v]; |
| 262 | + blockGraph.setNode(vRoot); |
| 263 | + if (u) { |
| 264 | + var uRoot = root[u], |
| 265 | + prevMax = blockGraph.edge(uRoot, vRoot); |
| 266 | + blockGraph.setEdge(uRoot, vRoot, Math.max(sepFn(g, v, u), prevMax || 0)); |
244 | 267 | }
|
| 268 | + u = v; |
245 | 269 | });
|
246 | 270 | });
|
247 | 271 |
|
248 |
| - return xs; |
249 |
| -} |
250 |
| - |
251 |
| -function placeBlock(g, layering, sepFn, root, align, shift, shiftNeighbor, sink, pred, xs, v) { |
252 |
| - if (_.has(xs, v)) return; |
253 |
| - xs[v] = 0; |
254 |
| - |
255 |
| - var w = v, |
256 |
| - u; |
257 |
| - do { |
258 |
| - if (pred[w]) { |
259 |
| - u = root[pred[w]]; |
260 |
| - placeBlock(g, layering, sepFn, root, align, shift, shiftNeighbor, sink, pred, xs, u); |
261 |
| - if (sink[v] === v) { |
262 |
| - sink[v] = sink[u]; |
263 |
| - } |
264 |
| - |
265 |
| - var delta = sepFn(g, w, pred[w]); |
266 |
| - if (sink[v] !== sink[u]) { |
267 |
| - shift[sink[u]] = Math.min(shift[sink[u]], xs[v] - xs[u] - delta); |
268 |
| - shiftNeighbor[sink[u]] = sink[v]; |
269 |
| - } else { |
270 |
| - xs[v] = Math.max(xs[v], xs[u] + delta); |
271 |
| - } |
272 |
| - } |
273 |
| - w = align[w]; |
274 |
| - } while (w !== v); |
| 272 | + return blockGraph; |
275 | 273 | }
|
276 | 274 |
|
277 | 275 | /*
|
|
0 commit comments