src/chart/sankey/sankeyLayout.js
function computeNodeBreadths(nodes, edges, nodeWidth, width, height, orient, nodeAlign) {
    // Used to mark whether the edge is deleted. if it is deleted,
    // the value is 0, otherwise it is 1.
    var remainEdges = [];
    // Storage each node's indegree.
    var indegreeArr = [];
    //Used to storage the node with indegree is equal to 0.
    var zeroIndegrees = [];
    var nextTargetNode = [];
    var x = 0;
    var kx = 0;

    for (var i = 0; i < edges.length; i++) {
        remainEdges[i] = 1;
    }
    for (i = 0; i < nodes.length; i++) {
        indegreeArr[i] = nodes[i].inEdges.length;
        if (indegreeArr[i] === 0) {
            zeroIndegrees.push(nodes[i]);
        }
    }
    var maxNodeDepth = -1;
    // Traversing nodes using topological sorting to calculate the
    // horizontal(if orient === 'horizontal') or vertical(if orient === 'vertical')
    // position of the nodes.
    while (zeroIndegrees.length) {
        for (var idx = 0; idx < zeroIndegrees.length; idx++) {
            var node = zeroIndegrees[idx];
            var item = node.hostGraph.data.getRawDataItem(node.dataIndex);
            var isItemDepth = item.depth != null && item.depth >= 0;
            if (isItemDepth && item.depth > maxNodeDepth) {
                maxNodeDepth = item.depth;
            }
            node.setLayout({depth: isItemDepth ? item.depth : x}, true);
            orient === 'vertical'
                ? node.setLayout({dy: nodeWidth}, true)
                : node.setLayout({dx: nodeWidth}, true);

            for (var edgeIdx = 0; edgeIdx < node.outEdges.length; edgeIdx++) {
                var edge = node.outEdges[edgeIdx];
                var indexEdge = edges.indexOf(edge);
                remainEdges[indexEdge] = 0;
                var targetNode = edge.node2;
                var nodeIndex = nodes.indexOf(targetNode);
                if (--indegreeArr[nodeIndex] === 0 && nextTargetNode.indexOf(targetNode) < 0) {
                    nextTargetNode.push(targetNode);
                }
            }
        }
        ++x;
        zeroIndegrees = nextTargetNode;
        nextTargetNode = [];
    }

    for (i = 0; i < remainEdges.length; i++) {
        if (remainEdges[i] === 1) {
            throw new Error('Sankey is a DAG, the original data has cycle!');
        }
    }

    var maxDepth = maxNodeDepth > x - 1 ? maxNodeDepth : x - 1;
    if (nodeAlign && nodeAlign !== 'left') {
        adjustNodeWithNodeAlign(nodes, nodeAlign, orient, maxDepth);
    }
    var kx = orient === 'vertical'
                ? (height - nodeWidth) / maxDepth
                : (width - nodeWidth) / maxDepth;

    scaleNodeBreadths(nodes, kx, orient);
}
function computeNodeBreadths(nodes, edges, nodeWidth, width, height, orient, nodeAlign) {
    // Used to mark whether the edge is deleted. if it is deleted,
    // the value is 0, otherwise it is 1.
    var remainEdges = [];
    // Storage each node's indegree.
    var indegreeArr = [];
    //Used to storage the node with indegree is equal to 0.
    var zeroIndegrees = [];
    var nextTargetNode = [];
    var x = 0;
    var kx = 0;

    for (var i = 0; i < edges.length; i++) {
        remainEdges[i] = 1;
    }
    for (i = 0; i < nodes.length; i++) {
        indegreeArr[i] = nodes[i].inEdges.length;
        if (indegreeArr[i] === 0) {
            zeroIndegrees.push(nodes[i]);
        }
    }
    var maxNodeDepth = -1;
    // Traversing nodes using topological sorting to calculate the
    // horizontal(if orient === 'horizontal') or vertical(if orient === 'vertical')
    // position of the nodes.
    while (zeroIndegrees.length) {
        for (var idx = 0; idx < zeroIndegrees.length; idx++) {
            var node = zeroIndegrees[idx];
            var item = node.hostGraph.data.getRawDataItem(node.dataIndex);
            var isItemDepth = item.depth != null && item.depth >= 0;
            if (isItemDepth && item.depth > maxNodeDepth) {
                maxNodeDepth = item.depth;
            }
            node.setLayout({depth: isItemDepth ? item.depth : x}, true);
            orient === 'vertical'
                ? node.setLayout({dy: nodeWidth}, true)
                : node.setLayout({dx: nodeWidth}, true);

            for (var edgeIdx = 0; edgeIdx < node.outEdges.length; edgeIdx++) {
                var edge = node.outEdges[edgeIdx];
                var indexEdge = edges.indexOf(edge);
                remainEdges[indexEdge] = 0;
                var targetNode = edge.node2;
                var nodeIndex = nodes.indexOf(targetNode);
                if (--indegreeArr[nodeIndex] === 0 && nextTargetNode.indexOf(targetNode) < 0) {
                    nextTargetNode.push(targetNode);
                }
            }
        }
        ++x;
        zeroIndegrees = nextTargetNode;
        nextTargetNode = [];
    }

    for (i = 0; i < remainEdges.length; i++) {
        if (remainEdges[i] === 1) {
            throw new Error('Sankey is a DAG, the original data has cycle!');
        }
    }

    var maxDepth = maxNodeDepth > x - 1 ? maxNodeDepth : x - 1;
    if (nodeAlign && nodeAlign !== 'left') {
        adjustNodeWithNodeAlign(nodes, nodeAlign, orient, maxDepth);
    }
    var kx = orient === 'vertical'
                ? (height - nodeWidth) / maxDepth
                : (width - nodeWidth) / maxDepth;

    scaleNodeBreadths(nodes, kx, orient);
}
function computeNodeBreadths ( nodes , edges , nodeWidth , width , height , orient , nodeAlign ) { // Used to mark whether the edge is deleted. if it is deleted, // the value is 0, otherwise it is 1. var remainEdges = [ ] ; // Storage each node's indegree. var indegreeArr = [ ] ; //Used to storage the node with indegree is equal to 0. var zeroIndegrees = [ ] ; var nextTargetNode = [ ] ; var x = 0 ; var kx = 0 ; for ( var i = 0 ; i < edges . length ; i ++ ) { remainEdges [ i ] = 1 ; } for ( i = 0 ; i < nodes . length ; i ++ ) { indegreeArr [ i ] = nodes [ i ] . inEdges . length ; if ( indegreeArr [ i ] === 0 ) { zeroIndegrees . push ( nodes [ i ] ) ; } } var maxNodeDepth = - 1 ; // Traversing nodes using topological sorting to calculate the // horizontal(if orient === 'horizontal') or vertical(if orient === 'vertical') // position of the nodes. while ( zeroIndegrees . length ) { for ( var idx = 0 ; idx < zeroIndegrees . length ; idx ++ ) { var node = zeroIndegrees [ idx ] ; var item = node . hostGraph . data . getRawDataItem ( node . dataIndex ) ; var isItemDepth = item . depth != null && item . depth >= 0 ; if ( isItemDepth && item . depth > maxNodeDepth ) { maxNodeDepth = item . depth ; } node . setLayout ( { depth : isItemDepth ? item . depth : x } , true ) ; orient === 'vertical' ? node . setLayout ( { dy : nodeWidth } , true ) : node . setLayout ( { dx : nodeWidth } , true ) ; for ( var edgeIdx = 0 ; edgeIdx < node . outEdges . length ; edgeIdx ++ ) { var edge = node . outEdges [ edgeIdx ] ; var indexEdge = edges . indexOf ( edge ) ; remainEdges [ indexEdge ] = 0 ; var targetNode = edge . node2 ; var nodeIndex = nodes . indexOf ( targetNode ) ; if ( -- indegreeArr [ nodeIndex ] === 0 && nextTargetNode . indexOf ( targetNode ) < 0 ) { nextTargetNode . push ( targetNode ) ; } } } ++ x ; zeroIndegrees = nextTargetNode ; nextTargetNode = [ ] ; } for ( i = 0 ; i < remainEdges . length ; i ++ ) { if ( remainEdges [ i ] === 1 ) { throw new Error ( 'Sankey is a DAG, the original data has cycle!' ) ; } } var maxDepth = maxNodeDepth > x - 1 ? maxNodeDepth : x - 1 ; if ( nodeAlign && nodeAlign !== 'left' ) { adjustNodeWithNodeAlign ( nodes , nodeAlign , orient , maxDepth ) ; } var kx = orient === 'vertical' ? ( height - nodeWidth ) / maxDepth : ( width - nodeWidth ) / maxDepth ; scaleNodeBreadths ( nodes , kx , orient ) ; }
Compute the x-position for each node.

Here we use Kahn algorithm to detect cycle when we traverse
the node to computer the initial x position.

@param {module:echarts/data/Graph~Node} nodes  node of sankey view
@param  {number} nodeWidth  the dx of the node
@param  {number} width  the whole width of the area to draw the view
Compute the x - position for each node .