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.
Last active
February 26, 2019 22:34
-
-
Save mbostock/999346 to your computer and use it in GitHub Desktop.
Random Tree
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: gpl-3.0 | |
redirect: https://observablehq.com/@mbostock/random-tree |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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