sankey.reversibleLink = function()

in app/assets/javascripts/sankey.js [111:216]


  sankey.reversibleLink = function() {
    var curvature = .5;

    // Used when source is behind target, the first and last paths are simple
    // lines at the start and end node while the second path is the spline
    function forwardLink(part, d) {
      var x0 = d.source.x + d.source.dx,
          x1 = d.target.x,
          xi = d3.interpolateNumber(x0, x1),
          x2 = xi(curvature),
          x3 = xi(1 - curvature),
          y0 = d.source.y + d.sy,
          y1 = d.target.y + d.ty,
          y2 = d.source.y + d.sy + d.dy,
          y3 = d.target.y + d.ty + d.dy;

      switch (part) {
        case 0:
          return "M" + x0 + "," + y0 + "L" + x0 + "," + (y0 + d.dy);

        case 1:
          return "M" + x0 + "," + y0
               + "C" + x2 + "," + y0 + " " + x3 + "," + y1 + " " + x1 + "," + y1
               + "L" + x1 + "," + y3
               + "C" + x3 + "," + y3 + " " + x2 + "," + y2 + " " + x0 + "," + y2
               + "Z";
      
        case 2:
          return "M" + x1 + "," + y1 + "L" + x1 + "," + (y1 + d.dy);
      }
    }

    // Used for self loops and when the source is actually in front of the 
    // target; the first element is a turning path from the source to the 
    // destination, the second element connects the two twists and the last 
    // twists into the target element.
    //
    // 
    //  /--Target
    //  \----------------------\
    //                 Source--/
    //
    function backwardLink(part, d) {

      var curveExtension = 30;
      var curveDepth = 15;

      function getDir(d) {
        return d.source.y + d.sy > d.target.y + d.ty ? -1 : 1;
      }

      function p(x, y) {
        return x + "," + y + " ";
      }

      var dt = getDir(d) * curveDepth,
          x0 = d.source.x + d.source.dx,
          y0 = d.source.y + d.sy,
          x1 = d.target.x,
          y1 = d.target.y + d.ty;

      switch (part) {
        case 0:
          return "M" + p(x0, y0) + 
                 "C" + p(x0, y0) +
                       p(x0 + curveExtension, y0) +
                       p(x0 + curveExtension, y0 + dt) +
                 "L" + p(x0 + curveExtension, y0 + dt + d.dy) +
                 "C" + p(x0 + curveExtension, y0 + d.dy) +
                       p(x0, y0 + d.dy) +
                       p(x0, y0 + d.dy) +
                 "Z";
        case 1:
          return "M" + p(x0 + curveExtension, y0 + dt) + 
                 "C" + p(x0 + curveExtension, y0 + 3 * dt) +
                       p(x1 - curveExtension, y1 - 3 * dt) +
                       p(x1 - curveExtension, y1 - dt) +
                 "L" + p(x1 - curveExtension, y1 - dt + d.dy) +
                 "C" + p(x1 - curveExtension, y1 - 3 * dt + d.dy) +
                       p(x0 + curveExtension, y0 + 3 * dt + d.dy) +
                       p(x0 + curveExtension, y0 + dt + d.dy) +
                 "Z";

        case 2:
          return "M" + p(x1 - curveExtension, y1 - dt) + 
                 "C" + p(x1 - curveExtension, y1) +
                       p(x1, y1) +
                       p(x1, y1) +
                 "L" + p(x1, y1 + d.dy) +
                 "C" + p(x1, y1 + d.dy) +
                       p(x1 - curveExtension, y1 + d.dy) +
                       p(x1 - curveExtension, y1 + d.dy - dt) +
                 "Z";
      }
    }

    return function(part) {
      return function(d) {
        if (d.source.x < d.target.x) {
          return forwardLink(part, d);
        } else {
          return backwardLink(part, d);
        }
      }
    }
  };