74 lines
2.5 KiB
JavaScript
74 lines
2.5 KiB
JavaScript
![]() |
var _ = require("../lodash");
|
||
|
var Graph = require("../graphlib").Graph;
|
||
|
|
||
|
module.exports = buildLayerGraph;
|
||
|
|
||
|
/*
|
||
|
* Constructs a graph that can be used to sort a layer of nodes. The graph will
|
||
|
* contain all base and subgraph nodes from the request layer in their original
|
||
|
* hierarchy and any edges that are incident on these nodes and are of the type
|
||
|
* requested by the "relationship" parameter.
|
||
|
*
|
||
|
* Nodes from the requested rank that do not have parents are assigned a root
|
||
|
* node in the output graph, which is set in the root graph attribute. This
|
||
|
* makes it easy to walk the hierarchy of movable nodes during ordering.
|
||
|
*
|
||
|
* Pre-conditions:
|
||
|
*
|
||
|
* 1. Input graph is a DAG
|
||
|
* 2. Base nodes in the input graph have a rank attribute
|
||
|
* 3. Subgraph nodes in the input graph has minRank and maxRank attributes
|
||
|
* 4. Edges have an assigned weight
|
||
|
*
|
||
|
* Post-conditions:
|
||
|
*
|
||
|
* 1. Output graph has all nodes in the movable rank with preserved
|
||
|
* hierarchy.
|
||
|
* 2. Root nodes in the movable layer are made children of the node
|
||
|
* indicated by the root attribute of the graph.
|
||
|
* 3. Non-movable nodes incident on movable nodes, selected by the
|
||
|
* relationship parameter, are included in the graph (without hierarchy).
|
||
|
* 4. Edges incident on movable nodes, selected by the relationship
|
||
|
* parameter, are added to the output graph.
|
||
|
* 5. The weights for copied edges are aggregated as need, since the output
|
||
|
* graph is not a multi-graph.
|
||
|
*/
|
||
|
function buildLayerGraph(g, rank, relationship) {
|
||
|
var root = createRootNode(g),
|
||
|
result = new Graph({ compound: true }).setGraph({ root: root })
|
||
|
.setDefaultNodeLabel(function(v) { return g.node(v); });
|
||
|
|
||
|
_.forEach(g.nodes(), function(v) {
|
||
|
var node = g.node(v),
|
||
|
parent = g.parent(v);
|
||
|
|
||
|
if (node.rank === rank || node.minRank <= rank && rank <= node.maxRank) {
|
||
|
result.setNode(v);
|
||
|
result.setParent(v, parent || root);
|
||
|
|
||
|
// This assumes we have only short edges!
|
||
|
_.forEach(g[relationship](v), function(e) {
|
||
|
var u = e.v === v ? e.w : e.v,
|
||
|
edge = result.edge(u, v),
|
||
|
weight = !_.isUndefined(edge) ? edge.weight : 0;
|
||
|
result.setEdge(u, v, { weight: g.edge(e).weight + weight });
|
||
|
});
|
||
|
|
||
|
if (_.has(node, "minRank")) {
|
||
|
result.setNode(v, {
|
||
|
borderLeft: node.borderLeft[rank],
|
||
|
borderRight: node.borderRight[rank]
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
function createRootNode(g) {
|
||
|
var v;
|
||
|
while (g.hasNode((v = _.uniqueId("_root"))));
|
||
|
return v;
|
||
|
}
|