Skip to content

Instantly share code, notes, and snippets.

@mbostock
Last active February 26, 2019 22:34
Show Gist options
  • Save mbostock/999346 to your computer and use it in GitHub Desktop.
Save mbostock/999346 to your computer and use it in GitHub Desktop.
Random Tree
license: gpl-3.0
redirect: https://observablehq.com/@mbostock/random-tree

This example builds a random tree using the Reingold-Tilford "tidy" algorithm, as described in "Tidier Drawings of Trees" and implemented by d3.layout.tree. As each node is added to the graph, it enters from the previous position of the parent node. Thus, the existing nodes and the new node transition smoothly to their new positions. The animation stops when 500 nodes have been added to the tree.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
.node {
stroke: #fff;
stroke-width: 2px;
}
.link {
fill: none;
stroke: #000;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var width = 960,
height = 500;
var tree = d3.layout.tree()
.size([width - 20, height - 20]);
var root = {},
nodes = tree(root);
root.parent = root;
root.px = root.x;
root.py = root.y;
var diagonal = d3.svg.diagonal();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(10,10)");
var node = svg.selectAll(".node"),
link = svg.selectAll(".link");
var duration = 750,
timer = setInterval(update, duration);
function update() {
if (nodes.length >= 500) return clearInterval(timer);
// Add a new node to a random parent.
var n = {id: nodes.length},
p = nodes[Math.random() * nodes.length | 0];
if (p.children) p.children.push(n); else p.children = [n];
nodes.push(n);
// Recompute the layout and data join.
node = node.data(tree.nodes(root), function(d) { return d.id; });
link = link.data(tree.links(nodes), function(d) { return d.source.id + "-" + d.target.id; });
// Add entering nodes in the parent’s old position.
node.enter().append("circle")
.attr("class", "node")
.attr("r", 4)
.attr("cx", function(d) { return d.parent.px; })
.attr("cy", function(d) { return d.parent.py; });
// Add entering links in the parent’s old position.
link.enter().insert("path", ".node")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: d.source.px, y: d.source.py};
return diagonal({source: o, target: o});
});
// Transition nodes and links to their new positions.
var t = svg.transition()
.duration(duration);
t.selectAll(".link")
.attr("d", diagonal);
t.selectAll(".node")
.attr("cx", function(d) { return d.px = d.x; })
.attr("cy", function(d) { return d.py = d.y; });
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment