in app/assets/javascripts/sankey.js [336:446]
function computeNodeBreadths() {
layerComponents();
components.forEach(function(component, i){
bfs(component.root, function(node){
var result = node.sourceLinks
.filter(function(sourceLink){
return sourceLink.target.component == i;
})
.map(function(sourceLink){
return sourceLink.target;
});
return result;
});
});
var max = 0;
var componentsByBreadth = d3.nest()
.key(function(d) { return d.x; })
// .sortKeys(d3.ascending)
.entries(components)
.map(function(d) { return d.values; });
var max = -1, nextMax = -1;
componentsByBreadth.forEach(function(c){
c.forEach(function(component){
component.x = max + 1;
component.scc.forEach(function(node){
if (node.layer)
node.x = node.layer;
else
node.x = component.x + node.x;
nextMax = Math.max(nextMax, node.x);
});
});
max = nextMax;
});
nodes
.filter(function(node) {
var outLinks = node.sourceLinks.filter(function(link){ return link.source.name != link.target.name; });
return (outLinks.length == 0);
})
.forEach(function(node) { node.x = max; })
scaleNodeBreadths((size[0] - nodeWidth) / Math.max(max, 1));
function flatten(a) {
return [].concat.apply([], a);
}
function layerComponents() {
var remainingComponents = components,
nextComponents,
visitedIndex,
x = 0;
while (remainingComponents.length) {
nextComponents = [];
visitedIndex = {};
remainingComponents.forEach(function(component) {
component.x = x;
component.scc.forEach(function(n) {
n.sourceLinks.forEach(function(l) {
if (!visitedIndex.hasOwnProperty(l.target.component) &&
l.target.component != component.index) {
nextComponents.push(components[l.target.component]);
visitedIndex[l.target.component] = true;
}
})
});
});
remainingComponents = nextComponents;
++x;
}
}
function bfs(node, extractTargets) {
var queue = [node], currentCount = 1, nextCount = 0;
var x = 0;
while(currentCount > 0) {
var currentNode = queue.shift();
currentCount--;
if (!currentNode.hasOwnProperty('x')) {
currentNode.x = x;
currentNode.dx = nodeWidth;
var targets = extractTargets(currentNode);
queue = queue.concat(targets);
nextCount += targets.length;
}
if (currentCount == 0) { // level change
x++;
currentCount = nextCount;
nextCount = 0;
}
}
}
}