Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

d3.tree => transform does not seem to work

I create a tree layout graph using d3js. The nodes are clickable to toggle showing the children. The child node should be inserted at a predefined position and then transitioned to the desired location. The problem is that the insertion coordinates are always off. When debugging with Firebug, it shows that directly after appending the new node its coordinates are x = 51.42857142857142 and y = 200.0 even though the line
.attr("transform", "translate(90,100)" ) should alter them (I use fixed values here, to pin down the problem further.

Where is my mistake?

Full code:

// Toggle children.
function toggle(d) {
   if (d.children) {
     d._children = d.children;
     d.children = null;
   } else {
     d.children = d._children;
     d._children = null;
  }
} 

function toggleAll(d) {
   if (d.children) {
      d.children.forEach(toggleAll);
      toggle(d);
   }
}

function update(source) {
   // Node movement delay
   var duration = d3.event && 1500;

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

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

   // Update the nodes…
   var node = vis.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().append("svg:g")
    .attr("class", "node")
    .attr("transform", "translate(90,100)" )
    .on("click", function(d) { toggle(d); update(d); });

  nodeEnter.append("svg:circle")
    .attr("r", 1e-6);


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

  nodeUpdate.select("circle")
    .attr("r", 4.5)
    .style("fill", function(d) { return d._children ? "red" : "#fff"; });



  // Transition exiting nodes to the parent's new position.
  var nodeExit = node.exit().transition()
     .duration(duration)
     .attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
    .remove();

 // While moving the nodes, they need to shrink
 nodeExit.select("circle")
   .attr("r", 1e-6);


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


var i = 0, root;
var radius = 960 / 2;
var tree = d3.layout.tree()
   .size([360, radius - 120]);
var diagonal = d3.svg.diagonal.radial()
   .projection(function(d) { return [d.y, d.x / 180 * Math.PI]; });

var vis = d3.select("#body").append("svg:svg")
   .attr("width", radius * 2)
   .attr("height", radius * 2 - 50)
   .append("g")
   .attr("transform", "translate(" + radius + "," + radius + ")");

d3.json("flare.json", function(json) {
  root = json;
  root.x0 = 0;
  root.y0 = 0;


  // Initialize the display to show a few nodes.
  root.children.forEach(toggleAll);
  toggle(root.children[1]);

  update(root);
});

like image 964
Sven Avatar asked Sep 07 '12 15:09

Sven


1 Answers

You change the insertion coordinates by adjusting the initial transform, i.e. changing

.attr("transform", "translate(90,100)")

For example, if you want to insert the new nodes at the position of their parent node, you can do this by passing a reference to the parent element to the update function and getting its transform value:

var sourceNode = d3.select(parentElement);

// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("svg:g")
    .attr("class", "node")
    .attr("transform", parentElement ? sourceNode.attr("transform") : "translate(90,100)")
    .on("click", function (d) {
    toggle(d);
    update(d, this);
});

Complete jsfiddle here.

like image 128
Lars Kotthoff Avatar answered Nov 15 '22 00:11

Lars Kotthoff