UNPKG

dig

Version:

Graph algorithms

308 lines (274 loc) 8.37 kB
dig.dot.layout.position = function() { var _nodeSep = 50; var _rankSep = 50; function nodeSep(newVal) { if (arguments.length > 0) { _nodeSep = newVal; return self; } else { return _nodeSep; } } function rankSep(newVal) { if (arguments.length > 0) { _rankSep = newVal; return self; } else { return _rankSep; } } function makeOrderArray(g) { var ordering = []; dig_util_forEach(g.nodes(), function(u) { var attrs = g.node(u); var rank = ordering[attrs.rank]; if (rank === undefined) { rank = ordering[attrs.rank] = []; } rank[attrs.order] = u; }); return ordering; } function makeOrderMap(ordering) { var map = {}; for (var i = 0; i < ordering.length; ++i) { for (var j = 0; j < ordering[i].length; ++j) { map[ordering[i][j]] = { rank: i, order: j }; } } return map; } function makeMedianMap(g, orderArray, orderMap) { var medianMap = findMedians(g, orderArray, orderMap); filterMedianConflicts(g, orderArray, orderMap, medianMap); return medianMap; } function findMedians(g, orderArray, orderMap) { var medians = {}; dig_util_forEach(g.nodes(), function(u) { medians[u] = []; }); var prevLayer = null; for (var i = 0; i < orderArray.length; ++i) { var currLayer = orderArray[i]; if (prevLayer !== null) { for (var j = 0; j < currLayer.length; ++j) { var u = currLayer[j]; var preds = []; dig_util_forEach(g.neighbors(u, "both"), function(v) { if (orderMap[v].rank === i - 1) { preds.push(v); } }); var vs = dig_util_radixSort(preds, 1, function(_, x) { return orderMap[x].order; }); if (vs.length > 0) { var mid = (vs.length - 1) / 2; medians[u] = vs.slice(Math.floor(mid), Math.ceil(mid) + 1); } } } prevLayer = currLayer; } return medians; } function filterMedianConflicts(g, orderArray, orderMap, medianMap) { var prevLayer = null; for (var i = 0; i < orderArray.length; ++i) { var currLayer = orderArray[i]; if (prevLayer !== null) { var prevStart = 0; var prevEnd; var currStart = 0; for (var currPos = 0; currPos < currLayer.length; ++currPos) { var u = currLayer[currPos]; var inner = null; if (g.node(u).dummy) { dig_util_forEach(medianMap[u], function(v) { if (g.node(v).dummy) { inner = v; } }); } if (currPos + 1 === currLayer.length || inner !== null) { prevEnd = prevLayer.length - 1; if (inner !== null) { prevEnd = orderMap[inner]; } for (; currStart <= currPos; ++currStart) { u = currLayer[currStart]; var meds = medianMap[u]; for (var j = 0; j < meds.length; ++j) { var v = meds[j]; var pos = orderMap[v].order; if ((pos < prevStart || pos > prevEnd) && !(g.node(u).dummy && g.node(v).dummy)) { meds.splice(j, 1); --j; } } } prevStart = prevEnd; } } } prevLayer = currLayer; } } function verticalAlignment(g, orderArray, orderMap, medianMap) { var root = {}; dig_util_forEach(g.nodes(), function(u) { root[u] = u; }); var align = {}; dig_util_forEach(g.nodes(), function(u) { align[u] = u; }); for (var i = 1; i < orderArray.length; ++i) { var r = -1; var prevLayer = orderArray[i - 1]; var currLayer = orderArray[i]; for (var j = 0; j < currLayer.length; ++j) { var v = currLayer[j]; for (var k = 0; k < medianMap[v].length; ++k) { var u = medianMap[v][k]; var uPos = orderMap[u].order; if (align[v] == v && r < uPos) { align[u] = v; align[v] = root[v] = root[u]; r = uPos; } } } } return { root: root, align: align } } function horizontalCompaction(g, orderArray, orderMap, alignment) { var sink = {}; dig_util_forEach(g.nodes(), function(v) { sink[v] = v; }); var shift = {}; dig_util_forEach(g.nodes(), function(v) { shift[v] = Number.POSITIVE_INFINITY; }); var x = {}; function placeBlock(v) { if (!x[v]) { x[v] = 0; var w = v; do { if (orderMap[w].order > 0) { var pred = orderArray[orderMap[w].rank][orderMap[w].order - 1]; var u = alignment.root[pred]; placeBlock(u); if (sink[v] === v) { sink[v] = sink[u]; } if (sink[v] !== sink[u]) { shift[sink[u]] = Math.min(shift[sink[u]], x[v] - x[u] - g.node(u).width - _nodeSep); } else { x[v] = Math.max(x[v], x[u] + g.node(u).width + _nodeSep); } } w = alignment.align[w]; } while (w !== v); } } dig_util_forEach(g.nodes(), function(u) { if (alignment.root[u] === u) { placeBlock(u); } }); dig_util_forEach(g.nodes(), function(u) { x[u] = x[alignment.root[u]]; var delta = shift[sink[alignment.root[u]]]; if (delta < Number.POSITIVE_INFINITY) { x[u] = x[u] + delta; } }); return x; } function flipOrderHoriz(ordering) { for (var i = 0; i < ordering.length; ++i) { ordering[i].reverse(); } } function flipOrderVert(ordering) { ordering.reverse(); } function flipPosition(g, orderArray, xs) { var maxCoord = Number.NEGATIVE_INFINITY; dig_util_forEach(g.nodes(), function(u) { var coord = xs[u] + g.node(u).width; if (coord > maxCoord) { maxCoord = coord; } }); for (var i = 0; i < orderArray.length; ++i) { var rank = orderArray[i]; var last = rank[rank.length - 1]; for (var j = 0; j < rank.length; ++j) { var u = rank[j]; xs[u] = maxCoord - xs[u] - g.node(u).width; } } return xs; } function flipMedians(medianMap) { for (var k in medianMap) { medianMap[k].reverse(); } } function pass(g, orderArray, orderMap, medianMap) { var alignment = verticalAlignment(g, orderArray, orderMap, medianMap); return horizontalCompaction(g, orderArray, orderMap, alignment); } /* * Runs the position algorithm on the given graph with the configuration * set in the instance of the position algorithm. */ function graph(g) { dig_util_forEach(g.nodes(), function(u) { g.node(u).y = _rankSep * g.node(u).rank; if (!("width" in g.node(u))) { g.node(u).width = g.node(u).dummy ? 1 : 50; } }); var orderArray = makeOrderArray(g); var originalOrderArray = orderArray.slice(0); var xs = []; // Upper Left var orderMap = makeOrderMap(orderArray); var medianMap = makeMedianMap(g, orderArray, orderMap); xs.push(pass(g, orderArray, orderMap, medianMap)); // Upper Right flipOrderHoriz(orderArray); orderMap = makeOrderMap(orderArray); flipMedians(medianMap); xs.push(flipPosition(g, originalOrderArray, pass(g, orderArray, orderMap, medianMap))); // Lower Right flipOrderVert(orderArray); orderMap = makeOrderMap(orderArray); medianMap = makeMedianMap(g, orderArray, orderMap); xs.push(flipPosition(g, originalOrderArray, pass(g, orderArray, orderMap, medianMap))); // Lower Left flipOrderHoriz(orderArray); orderMap = makeOrderMap(orderArray); flipMedians(medianMap); xs.push(pass(g, orderArray, orderMap, medianMap)); dig_util_forEach(g.nodes(), function(u) { var xArray = []; for (var i = 0; i < xs.length; ++i) { xArray.push(xs[i][u]); } xArray.sort(); g.node(u).x = (xArray[1] + xArray[2]) / 2; }); } var self = { graph: graph, nodeSep: nodeSep, rankSep: rankSep } return self; }