diff --git a/lib/position/bk.js b/lib/position/bk.js index 60a6d360..6eab697d 100644 --- a/lib/position/bk.js +++ b/lib/position/bk.js @@ -207,6 +207,7 @@ 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 = {}, @@ -223,7 +224,7 @@ function horizontalCompaction(g, layering, root, align, reverseSep) { _.each(g.nodes(), function(v) { if (root[v] === v) { - placeBlock(g, layering, sepFn, root, align, shift, sink, pred, xs, v); + placeBlock(g, layering, sepFn, root, align, shift, shiftNeighbor, sink, pred, xs, v); } }); @@ -234,6 +235,12 @@ function horizontalCompaction(g, layering, root, align, reverseSep) { // 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]; + } } }); }); @@ -241,7 +248,7 @@ function horizontalCompaction(g, layering, root, align, reverseSep) { return xs; } -function placeBlock(g, layering, sepFn, root, align, shift, sink, pred, xs, v) { +function placeBlock(g, layering, sepFn, root, align, shift, shiftNeighbor, sink, pred, xs, v) { if (_.has(xs, v)) return; xs[v] = 0; @@ -250,7 +257,7 @@ function placeBlock(g, layering, sepFn, root, align, shift, sink, pred, xs, v) { do { if (pred[w]) { u = root[pred[w]]; - placeBlock(g, layering, sepFn, root, align, shift, sink, pred, xs, u); + placeBlock(g, layering, sepFn, root, align, shift, shiftNeighbor, sink, pred, xs, u); if (sink[v] === v) { sink[v] = sink[u]; } @@ -258,6 +265,7 @@ function placeBlock(g, layering, sepFn, root, align, shift, sink, pred, xs, v) { 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); } diff --git a/test/position/bk-test.js b/test/position/bk-test.js index fada0657..a109bcfa 100644 --- a/test/position/bk-test.js +++ b/test/position/bk-test.js @@ -416,6 +416,29 @@ describe("position/bk", function() { expect(xs.d).to.equal(60 / 2 + 75 + 150 / 2); }); + it("cascades class shift", function() { + var root = { a: "a", b: "b", c: "c", d: "d", e: "b", f: "f", g: "d" }, + align = { a: "a", b: "e", c: "c", d: "g", e: "b", f: "f", g: "d" }; + g.graph().nodesep = 75; + g.setNode("a", { rank: 0, order: 0, width: 50 }); + g.setNode("b", { rank: 0, order: 1, width: 50 }); + g.setNode("c", { rank: 1, order: 0, width: 50 }); + g.setNode("d", { rank: 1, order: 1, width: 50 }); + g.setNode("e", { rank: 1, order: 2, width: 50 }); + g.setNode("f", { rank: 2, order: 0, width: 50 }); + g.setNode("g", { rank: 2, order: 1, width: 50 }); + + var xs = horizontalCompaction(g, buildLayerMatrix(g), root, align); + + // Use f as 0, everything is relative to it + expect(xs.a).to.equal(xs.b - 50 / 2 - 75 - 50 / 2); + expect(xs.b).to.equal(xs.e); + expect(xs.c).to.equal(xs.f); + expect(xs.d).to.equal(xs.c + 50 / 2 + 75 + 50 / 2); + expect(xs.e).to.equal(xs.d + 50 / 2 + 75 + 50 / 2); + expect(xs.g).to.equal(xs.f + 50 / 2 + 75 + 50 / 2); + }); + it("handles labelpos = l", function() { var root = { a: "a", b: "b", c: "c" }, align = { a: "a", b: "b", c: "c" };