function computeNodeBreadths(graph, iterations, id) {
    var columns = nest().key(function (d) {
      return d.column;
    }).sortKeys(ascending).entries(graph.nodes).map(function (d) {
      return d.values;
    });

    initializeNodeBreadth(id);
    resolveCollisions();

    for (var alpha = 1, n = iterations; n > 0; --n) {
      relaxLeftAndRight(alpha *= 0.99, id);
      resolveCollisions();
    }

    function initializeNodeBreadth(id) {

      //override py if nodePadding has been set
      if (paddingRatio) {
        var padding = Infinity;
        columns.forEach(function (nodes) {
          var thisPadding = y1 * paddingRatio / (nodes.length + 1);
          padding = thisPadding < padding ? thisPadding : padding;
        });
        py = padding;
      }

      var ky = min(columns, function (nodes) {
        return (y1 - y0 - (nodes.length - 1) * py) / sum(nodes, value);
      });

      //calculate the widths of the links
      ky = ky * scale;

      graph.links.forEach(function (link) {
        link.width = link.value * ky;
      });

      //determine how much to scale down the chart, based on circular links
      var margin = getCircleMargins(graph);
      var ratio = scaleSankeySize(graph, margin);

      //re-calculate widths
      ky = ky * ratio;

      graph.links.forEach(function (link) {
        link.width = link.value * ky;
      });

      columns.forEach(function (nodes) {
        var nodesLength = nodes.length;
        nodes.forEach(function (node, i) {
          if (node.depth == columns.length - 1 && nodesLength == 1) {
            node.y0 = y1 / 2 - node.value * ky;
            node.y1 = node.y0 + node.value * ky;
          } else if (node.depth == 0 && nodesLength == 1) {
            node.y0 = y1 / 2 - node.value * ky;
            node.y1 = node.y0 + node.value * ky;
          } else if (node.partOfCycle) {
            if (numberOfNonSelfLinkingCycles(node, id) == 0) {
              node.y0 = y1 / 2 + i;
              node.y1 = node.y0 + node.value * ky;
            } else if (node.circularLinkType == 'top') {
              node.y0 = y0 + i;
              node.y1 = node.y0 + node.value * ky;
            } else {
              node.y0 = y1 - node.value * ky - i;
              node.y1 = node.y0 + node.value * ky;
            }
          } else {
            if (margin.top == 0 || margin.bottom == 0) {
              node.y0 = (y1 - y0) / nodesLength * i;
              node.y1 = node.y0 + node.value * ky;
            } else {
              node.y0 = (y1 - y0) / 2 - nodesLength / 2 + i;
              node.y1 = node.y0 + node.value * ky;
            }
          }
        });
      });
    }

    // For each node in each column, check the node's vertical position in relation to its targets and sources vertical position
    // and shift up/down to be closer to the vertical middle of those targets and sources
    function relaxLeftAndRight(alpha, id) {
      var columnsLength = columns.length;

      columns.forEach(function (nodes) {
        var n = nodes.length;
        var depth = nodes[0].depth;

        nodes.forEach(function (node) {
          // check the node is not an orphan
          var nodeHeight;
          if (node.sourceLinks.length || node.targetLinks.length) {
            if (node.partOfCycle && numberOfNonSelfLinkingCycles(node, id) > 0) ; else if (depth == 0 && n == 1) {
              nodeHeight = node.y1 - node.y0;

              node.y0 = y1 / 2 - nodeHeight / 2;
              node.y1 = y1 / 2 + nodeHeight / 2;
            } else if (depth == columnsLength - 1 && n == 1) {
              nodeHeight = node.y1 - node.y0;

              node.y0 = y1 / 2 - nodeHeight / 2;
              node.y1 = y1 / 2 + nodeHeight / 2;
            } else {
              var avg = 0;

              var avgTargetY = mean(node.sourceLinks, linkTargetCenter);
              var avgSourceY = mean(node.targetLinks, linkSourceCenter);

              if (avgTargetY && avgSourceY) {
                avg = (avgTargetY + avgSourceY) / 2;
              } else {
                avg = avgTargetY || avgSourceY;
              }

              var dy = (avg - nodeCenter(node)) * alpha;
              // positive if it node needs to move down
              node.y0 += dy;
              node.y1 += dy;
            }
          }
        });
      });
    }

    // For each column, check if nodes are overlapping, and if so, shift up/down
    function resolveCollisions() {
      columns.forEach(function (nodes) {
        var node,
            dy,
            y = y0,
            n = nodes.length,
            i;

        // Push any overlapping nodes down.
        nodes.sort(ascendingBreadth);

        for (i = 0; i < n; ++i) {
          node = nodes[i];
          dy = y - node.y0;

          if (dy > 0) {
            node.y0 += dy;
            node.y1 += dy;
          }
          y = node.y1 + py;
        }

        // If the bottommost node goes outside the bounds, push it back up.
        dy = y - py - y1;
        if (dy > 0) {
          y = node.y0 -= dy, node.y1 -= dy;

          // Push any overlapping nodes back up.
          for (i = n - 2; i >= 0; --i) {
            node = nodes[i];
            dy = node.y1 + py - y;
            if (dy > 0) node.y0 -= dy, node.y1 -= dy;
            y = node.y0;
          }
        }
      });
    }
  }
function computeNodeBreadths(graph, iterations, id) {
    var columns = nest().key(function (d) {
      return d.column;
    }).sortKeys(ascending).entries(graph.nodes).map(function (d) {
      return d.values;
    });

    initializeNodeBreadth(id);
    resolveCollisions();

    for (var alpha = 1, n = iterations; n > 0; --n) {
      relaxLeftAndRight(alpha *= 0.99, id);
      resolveCollisions();
    }

    function initializeNodeBreadth(id) {

      
      if (paddingRatio) {
        var padding = Infinity;
        columns.forEach(function (nodes) {
          var thisPadding = y1 * paddingRatio / (nodes.length + 1);
          padding = thisPadding < padding ? thisPadding : padding;
        });
        py = padding;
      }

      var ky = min(columns, function (nodes) {
        return (y1 - y0 - (nodes.length - 1) * py) / sum(nodes, value);
      });

      
      ky = ky * scale;

      graph.links.forEach(function (link) {
        link.width = link.value * ky;
      });

      
      var margin = getCircleMargins(graph);
      var ratio = scaleSankeySize(graph, margin);

      
      ky = ky * ratio;

      graph.links.forEach(function (link) {
        link.width = link.value * ky;
      });

      columns.forEach(function (nodes) {
        var nodesLength = nodes.length;
        nodes.forEach(function (node, i) {
          if (node.depth == columns.length - 1 && nodesLength == 1) {
            node.y0 = y1 / 2 - node.value * ky;
            node.y1 = node.y0 + node.value * ky;
          } else if (node.depth == 0 && nodesLength == 1) {
            node.y0 = y1 / 2 - node.value * ky;
            node.y1 = node.y0 + node.value * ky;
          } else if (node.partOfCycle) {
            if (numberOfNonSelfLinkingCycles(node, id) == 0) {
              node.y0 = y1 / 2 + i;
              node.y1 = node.y0 + node.value * ky;
            } else if (node.circularLinkType == 'top') {
              node.y0 = y0 + i;
              node.y1 = node.y0 + node.value * ky;
            } else {
              node.y0 = y1 - node.value * ky - i;
              node.y1 = node.y0 + node.value * ky;
            }
          } else {
            if (margin.top == 0 || margin.bottom == 0) {
              node.y0 = (y1 - y0) / nodesLength * i;
              node.y1 = node.y0 + node.value * ky;
            } else {
              node.y0 = (y1 - y0) / 2 - nodesLength / 2 + i;
              node.y1 = node.y0 + node.value * ky;
            }
          }
        });
      });
    }

    
    
    function relaxLeftAndRight(alpha, id) {
      var columnsLength = columns.length;

      columns.forEach(function (nodes) {
        var n = nodes.length;
        var depth = nodes[0].depth;

        nodes.forEach(function (node) {
          
          var nodeHeight;
          if (node.sourceLinks.length || node.targetLinks.length) {
            if (node.partOfCycle && numberOfNonSelfLinkingCycles(node, id) > 0) ; else if (depth == 0 && n == 1) {
              nodeHeight = node.y1 - node.y0;

              node.y0 = y1 / 2 - nodeHeight / 2;
              node.y1 = y1 / 2 + nodeHeight / 2;
            } else if (depth == columnsLength - 1 && n == 1) {
              nodeHeight = node.y1 - node.y0;

              node.y0 = y1 / 2 - nodeHeight / 2;
              node.y1 = y1 / 2 + nodeHeight / 2;
            } else {
              var avg = 0;

              var avgTargetY = mean(node.sourceLinks, linkTargetCenter);
              var avgSourceY = mean(node.targetLinks, linkSourceCenter);

              if (avgTargetY && avgSourceY) {
                avg = (avgTargetY + avgSourceY) / 2;
              } else {
                avg = avgTargetY || avgSourceY;
              }

              var dy = (avg - nodeCenter(node)) * alpha;
              
              node.y0 += dy;
              node.y1 += dy;
            }
          }
        });
      });
    }

    
    function resolveCollisions() {
      columns.forEach(function (nodes) {
        var node,
            dy,
            y = y0,
            n = nodes.length,
            i;

        
        nodes.sort(ascendingBreadth);

        for (i = 0; i < n; ++i) {
          node = nodes[i];
          dy = y - node.y0;

          if (dy > 0) {
            node.y0 += dy;
            node.y1 += dy;
          }
          y = node.y1 + py;
        }

        
        dy = y - py - y1;
        if (dy > 0) {
          y = node.y0 -= dy, node.y1 -= dy;

          
          for (i = n - 2; i >= 0; --i) {
            node = nodes[i];
            dy = node.y1 + py - y;
            if (dy > 0) node.y0 -= dy, node.y1 -= dy;
            y = node.y0;
          }
        }
      });
    }
  }
function computeNodeBreadths ( graph , iterations , id ) { var columns = nest ( ) . key ( function ( d ) { return d . column ; } ) . sortKeys ( ascending ) . entries ( graph . nodes ) . map ( function ( d ) { return d . values ; } ) ; initializeNodeBreadth ( id ) ; resolveCollisions ( ) ; for ( var alpha = 1 , n = iterations ; n > 0 ; -- n ) { relaxLeftAndRight ( alpha *= 0.99 , id ) ; resolveCollisions ( ) ; } function initializeNodeBreadth ( id ) { //override py if nodePadding has been set if ( paddingRatio ) { var padding = Infinity ; columns . forEach ( function ( nodes ) { var thisPadding = y1 * paddingRatio / ( nodes . length + 1 ) ; padding = thisPadding < padding ? thisPadding : padding ; } ) ; py = padding ; } var ky = min ( columns , function ( nodes ) { return ( y1 - y0 - ( nodes . length - 1 ) * py ) / sum ( nodes , value ) ; } ) ; //calculate the widths of the links ky = ky * scale ; graph . links . forEach ( function ( link ) { link . width = link . value * ky ; } ) ; //determine how much to scale down the chart, based on circular links var margin = getCircleMargins ( graph ) ; var ratio = scaleSankeySize ( graph , margin ) ; //re-calculate widths ky = ky * ratio ; graph . links . forEach ( function ( link ) { link . width = link . value * ky ; } ) ; columns . forEach ( function ( nodes ) { var nodesLength = nodes . length ; nodes . forEach ( function ( node , i ) { if ( node . depth == columns . length - 1 && nodesLength == 1 ) { node . y0 = y1 / 2 - node . value * ky ; node . y1 = node . y0 + node . value * ky ; } else if ( node . depth == 0 && nodesLength == 1 ) { node . y0 = y1 / 2 - node . value * ky ; node . y1 = node . y0 + node . value * ky ; } else if ( node . partOfCycle ) { if ( numberOfNonSelfLinkingCycles ( node , id ) == 0 ) { node . y0 = y1 / 2 + i ; node . y1 = node . y0 + node . value * ky ; } else if ( node . circularLinkType == 'top' ) { node . y0 = y0 + i ; node . y1 = node . y0 + node . value * ky ; } else { node . y0 = y1 - node . value * ky - i ; node . y1 = node . y0 + node . value * ky ; } } else { if ( margin . top == 0 || margin . bottom == 0 ) { node . y0 = ( y1 - y0 ) / nodesLength * i ; node . y1 = node . y0 + node . value * ky ; } else { node . y0 = ( y1 - y0 ) / 2 - nodesLength / 2 + i ; node . y1 = node . y0 + node . value * ky ; } } } ) ; } ) ; } // For each node in each column, check the node's vertical position in relation to its targets and sources vertical position // and shift up/down to be closer to the vertical middle of those targets and sources function relaxLeftAndRight ( alpha , id ) { var columnsLength = columns . length ; columns . forEach ( function ( nodes ) { var n = nodes . length ; var depth = nodes [ 0 ] . depth ; nodes . forEach ( function ( node ) { // check the node is not an orphan var nodeHeight ; if ( node . sourceLinks . length || node . targetLinks . length ) { if ( node . partOfCycle && numberOfNonSelfLinkingCycles ( node , id ) > 0 ) ; else if ( depth == 0 && n == 1 ) { nodeHeight = node . y1 - node . y0 ; node . y0 = y1 / 2 - nodeHeight / 2 ; node . y1 = y1 / 2 + nodeHeight / 2 ; } else if ( depth == columnsLength - 1 && n == 1 ) { nodeHeight = node . y1 - node . y0 ; node . y0 = y1 / 2 - nodeHeight / 2 ; node . y1 = y1 / 2 + nodeHeight / 2 ; } else { var avg = 0 ; var avgTargetY = mean ( node . sourceLinks , linkTargetCenter ) ; var avgSourceY = mean ( node . targetLinks , linkSourceCenter ) ; if ( avgTargetY && avgSourceY ) { avg = ( avgTargetY + avgSourceY ) / 2 ; } else { avg = avgTargetY || avgSourceY ; } var dy = ( avg - nodeCenter ( node ) ) * alpha ; // positive if it node needs to move down node . y0 += dy ; node . y1 += dy ; } } } ) ; } ) ; } // For each column, check if nodes are overlapping, and if so, shift up/down function resolveCollisions ( ) { columns . forEach ( function ( nodes ) { var node , dy , y = y0 , n = nodes . length , i ; // Push any overlapping nodes down. nodes . sort ( ascendingBreadth ) ; for ( i = 0 ; i < n ; ++ i ) { node = nodes [ i ] ; dy = y - node . y0 ; if ( dy > 0 ) { node . y0 += dy ; node . y1 += dy ; } y = node . y1 + py ; } // If the bottommost node goes outside the bounds, push it back up. dy = y - py - y1 ; if ( dy > 0 ) { y = node . y0 -= dy , node . y1 -= dy ; // Push any overlapping nodes back up. for ( i = n - 2 ; i >= 0 ; -- i ) { node = nodes [ i ] ; dy = node . y1 + py - y ; if ( dy > 0 ) node . y0 -= dy , node . y1 -= dy ; y = node . y0 ; } } } ) ; } }
Assign nodes' breadths, and then shift nodes that overlap (resolveCollisions)
Assign nodes breadths and then shift nodes that overlap ( resolveCollisions )