diff --git a/lib/layout.js b/lib/layout.js index 26b07304..19773322 100644 --- a/lib/layout.js +++ b/lib/layout.js @@ -17,17 +17,18 @@ var Graph = require("./graphlib").Graph; module.exports = layout; -function layout(g, opts) { - var time = opts && opts.debugTiming ? util.time : util.notime; +function layout(g, inputOpts) { + var opts = inputOpts || {}; + var time = opts.debugTiming ? util.time : util.notime; time("layout", function() { var layoutGraph = time(" buildLayoutGraph", function() { return buildLayoutGraph(g); }); - time(" runLayout", function() { runLayout(layoutGraph, time); }); + time(" runLayout", function() { runLayout(layoutGraph, time, opts); }); time(" updateInputGraph", function() { updateInputGraph(g, layoutGraph); }); }); } -function runLayout(g, time) { +function runLayout(g, time, opts) { time(" makeSpaceForEdgeLabels", function() { makeSpaceForEdgeLabels(g); }); time(" removeSelfEdges", function() { removeSelfEdges(g); }); time(" acyclic", function() { acyclic.run(g); }); @@ -42,7 +43,7 @@ function runLayout(g, time) { time(" normalize.run", function() { normalize.run(g); }); time(" parentDummyChains", function() { parentDummyChains(g); }); time(" addBorderSegments", function() { addBorderSegments(g); }); - time(" order", function() { order(g); }); + time(" order", function() { order(g, opts); }); time(" insertSelfEdges", function() { insertSelfEdges(g); }); time(" adjustCoordinateSystem", function() { coordinateSystem.adjust(g); }); time(" position", function() { position(g); }); diff --git a/lib/order/index.js b/lib/order/index.js index 4ac2d9fa..5aaaf9de 100644 --- a/lib/order/index.js +++ b/lib/order/index.js @@ -25,8 +25,9 @@ module.exports = order; * * 1. Graph nodes will have an "order" attribute based on the results of the * algorithm. + * */ -function order(g) { +function order(g, opts) { var maxRank = util.maxRank(g), downLayerGraphs = buildLayerGraphs(g, _.range(1, maxRank + 1), "inEdges"), upLayerGraphs = buildLayerGraphs(g, _.range(maxRank - 1, -1, -1), "outEdges"); @@ -37,8 +38,10 @@ function order(g) { var bestCC = Number.POSITIVE_INFINITY, best; + var constraints = opts.constraints || []; + for (var i = 0, lastBest = 0; lastBest < 4; ++i, ++lastBest) { - sweepLayerGraphs(i % 2 ? downLayerGraphs : upLayerGraphs, i % 4 >= 2); + sweepLayerGraphs(i % 2 ? downLayerGraphs : upLayerGraphs, i % 4 >= 2, constraints); layering = util.buildLayerMatrix(g); var cc = crossCount(g, layering); @@ -46,6 +49,8 @@ function order(g) { lastBest = 0; best = _.cloneDeep(layering); bestCC = cc; + } else if (cc === bestCC) { + best = _.cloneDeep(layering); } } @@ -58,8 +63,13 @@ function buildLayerGraphs(g, ranks, relationship) { }); } -function sweepLayerGraphs(layerGraphs, biasRight) { +function sweepLayerGraphs(layerGraphs, biasRight, constraints) { var cg = new Graph(); + + _.forEach(constraints, function(constraint) { + cg.setEdge(constraint.left, constraint.right); + }); + _.forEach(layerGraphs, function(lg) { var root = lg.graph().root; var sorted = sortSubgraph(lg, root, cg, biasRight); diff --git a/test/order/order-test.js b/test/order/order-test.js index 9b209f34..8ac0becd 100644 --- a/test/order/order-test.js +++ b/test/order/order-test.js @@ -20,7 +20,7 @@ describe("order", function() { g.setPath(["a", "b", "c"]); g.setEdge("b", "d"); g.setPath(["a", "e", "f"]); - order(g); + order(g, {}); var layering = util.buildLayerMatrix(g); expect(crossCount(g, layering)).to.equal(0); }); @@ -29,8 +29,8 @@ describe("order", function() { // This graph resulted in a single crossing for previous versions of dagre. _.forEach(["a", "d"], function(v) { g.setNode(v, { rank: 1 }); }); _.forEach(["b", "f", "e"], function(v) { g.setNode(v, { rank: 2 }); }); - _.forEach(["c", "g"], function(v) { g.setNode(v, { rank: 3 }); }); - order(g); + _.forEach(["c", "g"], function (v) { g.setNode(v, { rank: 3 }); }); + order(g, {}); var layering = util.buildLayerMatrix(g); expect(crossCount(g, layering)).to.equal(0); }); @@ -40,7 +40,7 @@ describe("order", function() { _.forEach(["b", "e", "g"], function(v) { g.setNode(v, { rank: 2 }); }); _.forEach(["c", "f", "h"], function(v) { g.setNode(v, { rank: 3 }); }); g.setNode("d", { rank: 4 }); - order(g); + order(g, {}); var layering = util.buildLayerMatrix(g); expect(crossCount(g, layering)).to.be.lte(1); });