(function() { var distance, margin, max_depth, rand_tree, width; rand_tree = function(d, MAX_D, MAX_N) { /* return a tree with maximum depth MAX_D that branches with probability p at most N times for each internal node. p starts from 1 and decreases linearly with d, reaching zero at MAX_D */ /* this still seems to be necessary to avoid infinte recursion (floating point precision?) */ var children, i, n, p; if (d === MAX_D) { return { height: 0 }; } p = (MAX_D - d) / MAX_D; /* if the tree branches, at least one branch is made */ n = Math.floor(Math.random() * MAX_N) + 1; children = []; for (i = 0; 0 <= n ? i < n : i > n; 0 <= n ? i++ : i--) { if (p >= Math.random()) { children.push(rand_tree(d + 1, MAX_D, MAX_N)); } else { children.push({ height: 0 }); } } children.sort(function(a, b) { return b.height - a.height; }); return { height: d3.max(children, function(d) { return d.height; }) + 1, children: children }; }; width = 960; distance = 14; margin = 40; max_depth = 10; d3.select(self.frameElement).style('height', "" + (2 * margin) + "px"); window.main = function() { /* create the tree */ var diagonal, height, link, links, node, nodes, root, tree, vis; root = rand_tree(1, max_depth, 3); /* initialize the layout */ tree = d3.layout.tree().size([0, 0]); nodes = tree.nodes(root); links = tree.links(nodes); height = 0; /* force the layout to display nodes in fixed rows and columns */ nodes.forEach(function(n) { if ((n.parent != null) && n.parent.children[0] !== n) height += distance; n.x = height; return n.y = n.depth * (width / max_depth); }); /* draw the vis */ diagonal = d3.svg.diagonal().projection(function(d) { return [d.y, d.x]; }); vis = d3.select('body').append('svg').attr('width', width).attr('height', height + 2 * margin).append('g').attr('transform', "translate(" + margin + "," + margin + ")"); link = vis.selectAll('path.link').data(links).enter().append('path').attr('class', 'link').attr('d', diagonal); node = vis.selectAll('g.node').data(nodes).enter().append('g').attr('class', 'node').attr('transform', function(d) { return "translate(" + d.y + "," + d.x + ")"; }); node.append('circle').attr('r', 4).attr('fill', function(d) { if (d.children) { return 'white'; } else { return 'steelblue'; } }); /* adapt bl.ocks.org frame to the tree */ return d3.select(self.frameElement).transition().duration(500).style('height', "" + (height + 2 * margin) + "px"); }; }).call(this);