diff --git a/lib/layout.js b/lib/layout.js index 26b07304..e68d8e79 100644 --- a/lib/layout.js +++ b/lib/layout.js @@ -97,7 +97,7 @@ function updateInputGraph(inputGraph, layoutGraph) { var graphNumAttrs = ["nodesep", "edgesep", "ranksep", "marginx", "marginy"]; var graphDefaults = { ranksep: 50, edgesep: 20, nodesep: 50, rankdir: "tb" }; var graphAttrs = ["acyclicer", "ranker", "rankdir", "align"]; -var nodeNumAttrs = ["width", "height"]; +var nodeNumAttrs = ["width", "height", "rank"]; var nodeDefaults = { width: 0, height: 0 }; var edgeNumAttrs = ["minlen", "weight", "width", "height", "labeloffset"]; var edgeDefaults = { diff --git a/lib/order/cross-count.js b/lib/order/cross-count.js index 2f8b6b0f..b5c93601 100644 --- a/lib/order/cross-count.js +++ b/lib/order/cross-count.js @@ -22,12 +22,40 @@ module.exports = crossCount; */ function crossCount(g, layering) { var cc = 0; - for (var i = 1; i < layering.length; ++i) { - cc += twoLayerCrossCount(g, layering[i-1], layering[i]); + for (var i = 0; i < layering.length; ++i) { + cc += singleLayerCrossCount(g, layering[i]); + if (i > 0) { + cc += twoLayerCrossCount(g, layering[i-1], layering[i]); + } } return cc; } + +// Count crossings between a single layer, in cases where manual ranking +// enables edges between nodes with the same rank. +function singleLayerCrossCount(g, layer) { + var layerRank = g.node(layer[0]).rank; + var layerIndex = _.zipObject( + layer, + _.map(layer, function (v, i) { return i; }) + ); + + var cc = 0; + _.forEach(layer, function(n, i) { + _.forEach(g.inEdges(n), function(e) { + if (g.node(e.v).rank == layerRank) { + var otherPos = layerIndex[e.v]; + if (Math.abs(otherPos - i) > 1) { + cc++; + } + } + }); + }); + return cc; +} + + function twoLayerCrossCount(g, northLayer, southLayer) { // Sort all of the edges between the north and south layers by their position // in the north layer and then the south. Map these edges to the position of diff --git a/lib/rank/index.js b/lib/rank/index.js index 5ec26c8d..58c38b9a 100644 --- a/lib/rank/index.js +++ b/lib/rank/index.js @@ -27,14 +27,21 @@ module.exports = rank; * fix them up later. */ function rank(g) { + var ranker = g.graph().ranker; + if (ranker instanceof Function) { + return ranker(g) + } + switch(g.graph().ranker) { - case "network-simplex": networkSimplexRanker(g); break; - case "tight-tree": tightTreeRanker(g); break; - case "longest-path": longestPathRanker(g); break; - default: networkSimplexRanker(g); + case "network-simplex": networkSimplexRanker(g); break; + case "tight-tree": tightTreeRanker(g); break; + case "longest-path": longestPathRanker(g); break; + case "none": break; + default: networkSimplexRanker(g); } } + // A fast and simple ranker, but results are far from optimal. var longestPathRanker = longestPath;