What does M, V, H of elbow connectors mean in d3js?

Viewed 613

I'm currently working on a tree graph using d3js. My connectors are currently curved lines and I'm trying hard to convert it into vertical lines. I came across the elbow function as below:

 function elbow(d, i) {  return "M" + d.source.y + "," + d.source.x
   + "H" + d.target.x + "V" + d.target.y
   + (d.target.children ? "" : "h" + margin.right);}

My output is coming funny, so I need to understand how the M, H and V used in elbow connector works?

My entire code is below - you can view the output too.

var sampleData = [
  {
    "ChangeFlowsFromParent": "false",
    "ChangeFlowsToParent": "false",
    "StreamType": "Mainline",
    "streamName": "ArgOS_2_0",
    "Parent": "none",
    "Compliance": "Released",
    "children": [
      {
        "ChangeFlowsFromParent": "true",
        "ChangeFlowsToParent": "true",
        "StreamType": "Development",
        "streamName": "ArgOS_2_0_DHAL",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released"
      },
      {
        "ChangeFlowsFromParent": "true",
        "ChangeFlowsToParent": "true",
        "StreamType": "Development",
        "streamName": "ArgOS_2_0_Dev",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released",
        "children": [
          {
            "ChangeFlowsFromParent": "true",
            "ChangeFlowsToParent": "true",
            "StreamType": "Release",
            "streamName": "r_ArgOS_0230",
            "Parent": "ArgOS_2_0_Dev",
            "Compliance": "Released"
          }
        ]
      },
      {
        "ChangeFlowsFromParent": "true",
        "ChangeFlowsToParent": "true",
        "StreamType": "Development",
        "streamName": "ArgOS_2_0_IPC-Tracer",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released"
      },
      {
        "ChangeFlowsFromParent": "true",
        "ChangeFlowsToParent": "true",
        "StreamType": "Development",
        "streamName": "ArgOS_2_0_NSW_Temp",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released"
      },
      {
        "ChangeFlowsFromParent": "true",
        "ChangeFlowsToParent": "true",
        "StreamType": "Development",
        "streamName": "ArgOS_2_0_Test",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released"
      },
      {
        "ChangeFlowsFromParent": "true",
        "ChangeFlowsToParent": "true",
        "StreamType": "Development",
        "streamName": "ArgOS_2_CBD",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released"
      },
      {
        "ChangeFlowsFromParent": "true",
        "ChangeFlowsToParent": "true",
        "StreamType": "Development",
        "streamName": "ArgOS_2_CSGDK",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released"
      },
      {
        "ChangeFlowsFromParent": "true",
        "ChangeFlowsToParent": "true",
        "StreamType": "Development",
        "streamName": "ArgOS_2x_Dev",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released"
      },
      {
        "ChangeFlowsFromParent": "true",
        "ChangeFlowsToParent": "true",
        "StreamType": "Development",
        "streamName": "ArgOS_ION_DEV_wp",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released"
      },
      {
        "ChangeFlowsFromParent": "true",
        "ChangeFlowsToParent": "true",
        "StreamType": "Development",
        "streamName": "ArgOS_IPC",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released"
      },
      {
        "ChangeFlowsFromParent": "true",
        "ChangeFlowsToParent": "true",
        "StreamType": "Development",
        "streamName": "d_ArgOS_2_0_ASP",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released"
      },
      {
        "ChangeFlowsFromParent": "true",
        "ChangeFlowsToParent": "true",
        "StreamType": "Development",
        "streamName": "d_ArgOS_2_0_G2S",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released"
      },
      {
        "ChangeFlowsFromParent": "true",
        "ChangeFlowsToParent": "true",
        "StreamType": "Development",
        "streamName": "d_ArgOS_2_0_MGAM",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released"
      },
      {
        "ChangeFlowsFromParent": "true",
        "ChangeFlowsToParent": "true",
        "StreamType": "Development",
        "streamName": "d_ArgOS_2_0_MGAM2",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released"
      },
      {
        "ChangeFlowsFromParent": "true",
        "ChangeFlowsToParent": "true",
        "StreamType": "Development",
        "streamName": "d_ArgOS_2_0_NSW_TEMP",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released"
      },
      {
        "ChangeFlowsFromParent": "true",
        "ChangeFlowsToParent": "true",
        "StreamType": "Development",
        "streamName": "d_ArgOS_2_0_QUARTZ",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released"
      },
      {
        "ChangeFlowsFromParent": "true",
        "ChangeFlowsToParent": "true",
        "StreamType": "Development",
        "streamName": "d_ArgOS_DMG",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released",
        "children": [
          {
            "ChangeFlowsFromParent": "true",
            "ChangeFlowsToParent": "true",
            "StreamType": "Development",
            "streamName": "d_ArgOS_DMG_PS",
            "Parent": "d_ArgOS_DMG",
            "Compliance": "Released"
          }
        ]
      },
      {
        "ChangeFlowsFromParent": "true",
        "ChangeFlowsToParent": "true",
        "StreamType": "Development",
        "streamName": "d_ArgOS_Lounge_Table",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released"
      },
      {
        "ChangeFlowsFromParent": "true",
        "ChangeFlowsToParent": "true",
        "StreamType": "Development",
        "streamName": "d_ArgOS_Prizm",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released",
        "children": [
          {
            "ChangeFlowsFromParent": "true",
            "ChangeFlowsToParent": "true",
            "StreamType": "Development",
            "streamName": "Merge_mainline_into_prizm",
            "Parent": "d_ArgOS_Prizm",
            "Compliance": "Released"
          }
        ]
      },
      {
        "ChangeFlowsFromParent": "true",
        "ChangeFlowsToParent": "true",
        "StreamType": "Development",
        "streamName": "d_ArgOS_Prizm_Merge",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released"
      },
      {
        "ChangeFlowsFromParent": "true",
        "ChangeFlowsToParent": "true",
        "StreamType": "Development",
        "streamName": "d_ArgOS_QCOM",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released"
      },
      {
        "ChangeFlowsFromParent": "false",
        "ChangeFlowsToParent": "true",
        "StreamType": "Release",
        "streamName": "r_ArgOS_0200",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released",
        "children": [
          {
            "ChangeFlowsFromParent": "true",
            "ChangeFlowsToParent": "true",
            "StreamType": "Release",
            "streamName": "r_ArgOS_0200D",
            "Parent": "r_ArgOS_0200",
            "Compliance": "Released"
          }
        ]
      },
      {
        "ChangeFlowsFromParent": "false",
        "ChangeFlowsToParent": "true",
        "StreamType": "Release",
        "streamName": "r_ArgOS_0200A",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released"
      },
      {
        "ChangeFlowsFromParent": "false",
        "ChangeFlowsToParent": "true",
        "StreamType": "Release",
        "streamName": "r_ArgOS_0210",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released"
      },
      {
        "ChangeFlowsFromParent": "false",
        "ChangeFlowsToParent": "true",
        "StreamType": "Release",
        "streamName": "r_ArgOS_0212",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released",
        "children": [
          {
            "ChangeFlowsFromParent": "false",
            "ChangeFlowsToParent": "true",
            "StreamType": "Release",
            "streamName": "r_ArgOS_0211",
            "Parent": "r_ArgOS_0212",
            "Compliance": "Released"
          }
        ]
      },
      {
        "ChangeFlowsFromParent": "false",
        "ChangeFlowsToParent": "true",
        "StreamType": "Release",
        "streamName": "r_ArgOS_0214",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released",
        "children": [
          {
            "ChangeFlowsFromParent": "false",
            "ChangeFlowsToParent": "true",
            "StreamType": "Release",
            "streamName": "r_ArgOS_0213",
            "Parent": "r_ArgOS_0214",
            "Compliance": "Released",
            "children": [
              {
                "ChangeFlowsFromParent": "true",
                "ChangeFlowsToParent": "true",
                "StreamType": "Development",
                "streamName": "ArgOS_WindowManager",
                "Parent": "r_ArgOS_0213",
                "Compliance": "Released"
              },
              {
                "ChangeFlowsFromParent": "true",
                "ChangeFlowsToParent": "true",
                "StreamType": "Release",
                "streamName": "r_ArgOS_0215",
                "Parent": "r_ArgOS_0213",
                "Compliance": "Released"
              }
            ]
          }
        ]
      },
      {
        "ChangeFlowsFromParent": "false",
        "ChangeFlowsToParent": "true",
        "StreamType": "Release",
        "streamName": "r_ArgOS_0216",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released"
      },
      {
        "ChangeFlowsFromParent": "false",
        "ChangeFlowsToParent": "true",
        "StreamType": "Release",
        "streamName": "r_ArgOS_0218",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released"
      },
      {
        "ChangeFlowsFromParent": "false",
        "ChangeFlowsToParent": "true",
        "StreamType": "Release",
        "streamName": "r_ArgOS_0225",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released",
        "children": [
          {
            "ChangeFlowsFromParent": "false",
            "ChangeFlowsToParent": "true",
            "StreamType": "Release",
            "streamName": "r_ArgOS_0220",
            "Parent": "r_ArgOS_0225",
            "Compliance": "Released"
          }
        ]
      },
      {
        "ChangeFlowsFromParent": "true",
        "ChangeFlowsToParent": "true",
        "StreamType": "Development",
        "streamName": "test_mergewp",
        "Parent": "ArgOS_2_0",
        "Compliance": "Released"
      }
    ]
  }
]

// ************** Generate the tree diagram  *****************
var margin = {top: 20, right: 120, bottom: 20, left: 120},
 width = 1960 - margin.right - margin.left,
 height = 800 - margin.top - margin.bottom;
 
    var viewerWidth = $(document).width();
    var viewerHeight = $(document).height();
 
var i = 0,
 duration = 750,
 root;

var tree = d3.layout.tree(); //draw tree with size of nodes specified
tree.nodeSize([150, 40]); 

tree.separation(function separation(a, b) {
  return (a.parent == b.parent ? 1 : 2) / a.depth;
});

 
var diagonal = d3.svg.diagonal().projection(function(d) { return [d.x-30, -d.y + 280]; });
 
var svg = d3.select("body").append("svg")
   .attr("width", width + margin.right + margin.left)
   .attr("height", height + margin.top + margin.bottom)
   .append("g")
      //.attr("transform", "translate(" + margin.left + "," + margin.top + ")") 
   .attr("transform", "translate(800,450)") 
   .on("click", clicksvg);
      
    
root = sampleData[0];

root.x0 = height / 2;
root.y0 = 0;

update(root);

<!-- d3.select(self.frameElement).style("height", "500px"); -->

function update(source) {

  // Compute the new tree layout.
  var nodes = tree.nodes(root).reverse(),
   links = tree.links(nodes);

  // Normalize for fixed-depth.
  nodes.forEach(function(d) { d.y = d.depth * 180; });

  // Update the nodes…
  var node = svg.selectAll("g.node")
   .data(nodes, function(d) { return d.id || (d.id = ++i); });
   
 
  // Enter any new nodes at the parent's previous position.
    var nodeEnter = node.enter() //all nodes should be inside g - only then text seems to appear
   .append("g")
   <!-- .attr("height", 40) -->
   <!-- .attr("width", 100) -->
   <!-- .style("fill","yellow") -->
   .attr("class", "node")
   .attr("transform", function(d) { return "translate(" +  source.y0 + "," + source.x0 + ")"; })
   .on("click", click);

  nodeEnter.append("rect")
   .attr("height", 40)
   .attr("width", 100)
   .style("fill", "lightsteelblue");

  nodeEnter.append("text")
   .attr("x", "50" )
   .attr("y", "50")
   <!-- .attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; }) -->
   .text(function(d) { return d.streamName; })
   .style("fill","black")
  
   <!-- .style("fill-opacity", 1e-6); -->

  // Transition nodes to their new position.
  
  
  var nodeUpdate = node.transition()
   .duration(duration)
   .attr("transform", function(d) { 
   var negY = -d.y +280; 
   return "translate(" + d.x + "," + negY + ")"; }
   );

  nodeUpdate.select("rect")
  .attr("height", 40)
   .attr("width", 100)
   .attr("x",-80)
   .attr("y", 0)
   .style("fill", function(d) {return d.Parent == "none" ? "yellow" : "skyblue"});
  

  nodeUpdate.select("text")
   .attr("x",-75)
   .attr("dy", -25)
    .style("font-weight",  "bold")
   .style("fill-opacity", 1)
   ;
  // Update the links…
  var link = svg.selectAll("path.link")
   .data(links, function(d) { return d.target.id; });
   

  // Enter any new links at the parent's previous position.
  link.enter().insert("path", "g")
   .attr("class","link")
   .style("stroke", function(d){
   <!-- console.log(d.target); -->
   if(d.target.ChangeFlowsFromParent == "true") { return  "green" } else {return "red"}})
   .attr("d", elbow);
   <!-- .attr("d", function(d) { -->
   <!-- var o = {x: source.y0, y: -source.x0}; -->
  <!-- return diagonal({source: o, target: o}); -->
   <!-- }); -->
   
  // Transition links to their new position.
  link.transition()
   .duration(duration)
   .attr("d", elbow);

   // Stash the old positions for transition.
  nodes.forEach(function(d) {
 d.x0 = d.y;
 d.y0 = d.x;
  });
  
 }

// open children info as a pop-up on click.
function click(d) {

d3.event.stopPropagation();

d3.select("#foreign").remove();

var xcor = d.x0 - 30;
var ycor = d.y0;

var pop = d3.select(this)
   .append('g')
   //.attr("transform", function(d) { return "translate(" + (xcor - 300) + "," + 0 + ")"; })
    .attr("id","foreign");

 pop.append("rect")
      .attr("height", 200)
    .attr("width", 300)
    .attr("x", -20)
    .attr("y", -60)
    .attr("fill","white")
    .attr("stroke", "black");
    
  <!-- pop.append("text") -->
  <!-- .attr("x", 30) -->
    <!-- .attr("dy", -40) -->
     <!-- .style("fill-opacity", 5) -->
     <!-- .attr("text-anchor", "middle") -->
  
  var foreign = pop.append("foreignObject").
                    attr("class","foreign");
  
  foreign.append("xhtml:div")
  .attr("height", 200)
    .attr("width", 500)
         .html(function(d) {
                return "<span style='font-weight:bold'>StreamName: </span>" + d.streamName + "<br/>" +
              "<strong>ParentName: </strong> <span style='color:red'>" + d.Parent + "</span><br/>" +
              "<strong>Compliance: </strong> <span style='color:red'>" + d.Compliance + "</span><br/>";
              })
  }

function clicksvg() {
 var thisclick = this;
 thisclick.selectAll("#foreign").remove(); 
  }  
  
  function elbow(d, i) {
  return "M" + d.source.y + "," + d.source.x
       + "H" + d.target.x + "V" + d.target.y
       + (d.target.children ? "" : "h" + margin.right);
}
  
 .node {
  cursor: pointer;
  overflow:auto;
 }

 .node circle {
   fill: #fff;
   stroke: steelblue;
   stroke-width: 3px;
 }

 .node text {
   font: 12px sans-serif;
 }

 .linkFrom {
   fill: none;
   stroke: blue;
   stroke-width: 2px;
 }
 
 .linkTo {
  fill: none;
   stroke: red;
   stroke-width: 2px;
 }
 
 .link{
 fill:none;
 stroke:  #CCC;
 stroke-width: 2px;
 }
 
 .popup rect{
  cursor: pointer;
  position: absolute;
        text-align: left;
        <!-- width: 100px; -->
        <!-- height: 80px; -->
        padding: 8px;
        font: 10px sans-serif;
        background-color: gray;
        border: solid 1px #aaa;
        border-radius: 8px;
 
 }
 
 .foreign{
 cursor: pointer;
 }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.9/d3.min.js"></script>

1 Answers
Related