Skip to content

Commit

Permalink
Redo the horizontal compaction phase
Browse files Browse the repository at this point in the history
I continue to come across new cases where the BK horizontal compaction
algorithm fails. Instead of trying to continue to patch it, I've
replaced it entirely with a simpler two-sweep DFS algorithm which works
for all of the failing cases I've tested.

Fixes #125
Fixes #158
  • Loading branch information
cpettitt committed Dec 15, 2014
1 parent 7602baf commit bd1c10b
Showing 1 changed file with 56 additions and 58 deletions.
114 changes: 56 additions & 58 deletions lib/position/bk.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use strict";

var _ = require("../lodash"),
Graph = require("../graphlib").Graph,
util = require("../util");

/*
Expand Down Expand Up @@ -204,74 +205,71 @@ function verticalAlignment(g, layering, conflicts, neighborFn) {
}

function horizontalCompaction(g, layering, root, align, reverseSep) {
// We use local variables for these parameters instead of manipulating the
// graph because it becomes more verbose to access them in a chained manner.
var shift = {},
shiftNeighbor = {},
sink = {},
xs = {},
pred = {},
graphLabel = g.graph(),
sepFn = sep(graphLabel.nodesep, graphLabel.edgesep, reverseSep);
// This portion of the algorithm differs from BK due to a number of problems.
// Instead of their algorithm we construct a new block graph and do two
// sweeps. The first sweep places blocks with the smallest possible
// coordinates. The second sweep removes unused space by moving blocks to the
// greatest coordinates without violating separation.
var xs = {},
blockG = buildBlockGraph(g, layering, root, reverseSep);

// First pass, assign smallest coordinates via DFS
var visited = {};
function pass1(v) {
if (!_.has(visited, v)) {
visited[v] = true;
xs[v] = _.reduce(blockG.inEdges(v), function(max, e) {
pass1(e.v);
return Math.max(max, xs[e.v] + blockG.edge(e));
}, 0);
}
}
_.each(blockG.nodes(), pass1);

function pass2(v) {
if (visited[v] !== 2) {
visited[v]++;
var min = _.reduce(blockG.outEdges(v), function(min, e) {
pass2(e.w);
return Math.min(min, xs[e.w] - blockG.edge(e));
}, Number.POSITIVE_INFINITY);
if (min !== Number.POSITIVE_INFINITY) {
xs[v] = Math.max(xs[v], min);
}
}
}
_.each(blockG.nodes(), pass2);

_.each(layering, function(layer) {
_.each(layer, function(v, order) {
sink[v] = v;
shift[v] = Number.POSITIVE_INFINITY;
pred[v] = layer[order - 1];
});
});

_.each(g.nodes(), function(v) {
if (root[v] === v) {
placeBlock(g, layering, sepFn, root, align, shift, shiftNeighbor, sink, pred, xs, v);
}
// Assign x coordinates to all nodes
_.each(align, function(v) {
xs[v] = xs[root[v]];
});

return xs;
}


function buildBlockGraph(g, layering, root, reverseSep) {
var blockGraph = new Graph(),
graphLabel = g.graph(),
sepFn = sep(graphLabel.nodesep, graphLabel.edgesep, reverseSep);

_.each(layering, function(layer) {
var u;
_.each(layer, function(v) {
xs[v] = xs[root[v]];
// This line differs from the source paper. See
// http://www.inf.uni-konstanz.de/~brandes/publications/ for details.
if (v === root[v] && shift[sink[root[v]]] < Number.POSITIVE_INFINITY) {
xs[v] += shift[sink[root[v]]];

// Cascade shifts as necessary
var w = shiftNeighbor[sink[root[v]]];
if (w && shift[w] !== Number.POSITIVE_INFINITY) {
xs[v] += shift[w];
}
var vRoot = root[v];
blockGraph.setNode(vRoot);
if (u) {
var uRoot = root[u],
prevMax = blockGraph.edge(uRoot, vRoot);
blockGraph.setEdge(uRoot, vRoot, Math.max(sepFn(g, v, u), prevMax || 0));
}
u = v;
});
});

return xs;
}

function placeBlock(g, layering, sepFn, root, align, shift, shiftNeighbor, sink, pred, xs, v) {
if (_.has(xs, v)) return;
xs[v] = 0;

var w = v,
u;
do {
if (pred[w]) {
u = root[pred[w]];
placeBlock(g, layering, sepFn, root, align, shift, shiftNeighbor, sink, pred, xs, u);
if (sink[v] === v) {
sink[v] = sink[u];
}

var delta = sepFn(g, w, pred[w]);
if (sink[v] !== sink[u]) {
shift[sink[u]] = Math.min(shift[sink[u]], xs[v] - xs[u] - delta);
shiftNeighbor[sink[u]] = sink[v];
} else {
xs[v] = Math.max(xs[v], xs[u] + delta);
}
}
w = align[w];
} while (w !== v);
return blockGraph;
}

/*
Expand Down

0 comments on commit bd1c10b

Please sign in to comment.