|
(function() { |
|
var distance, margin, max_depth, rand_tree, width; |
|
|
|
rand_tree = function(d, MAX_D, MAX_N, P, index) { |
|
/* return a tree with maximum depth MAX_D that branches with probability P at most N times for each internal node |
|
*/ |
|
var child_i, children, i, n; |
|
if (d === MAX_D) { |
|
return { |
|
index: Infinity |
|
}; |
|
} |
|
/* if the tree branches, at least one branch is made |
|
*/ |
|
n = Math.floor(Math.random() * MAX_N) + 1; |
|
child_i = 0; |
|
children = []; |
|
for (i = 0; 0 <= n ? i < n : i > n; 0 <= n ? i++ : i--) { |
|
if (P >= Math.random()) { |
|
child_i += 1; |
|
children.push(rand_tree(d + 1, MAX_D, MAX_N, P, child_i)); |
|
} else { |
|
children.push({ |
|
index: Infinity |
|
}); |
|
} |
|
} |
|
children.sort(function(a, b) { |
|
return a.index - b.index; |
|
}); |
|
return { |
|
index: index, |
|
children: children |
|
}; |
|
}; |
|
|
|
width = 960; |
|
|
|
distance = 14; |
|
|
|
margin = 40; |
|
|
|
max_depth = 8; |
|
|
|
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, 4, 0.5, 0); |
|
root.index = ''; |
|
/* 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); |
|
}); |
|
/* define a method for computing an internal node's prefix |
|
*/ |
|
nodes.filter(function(d) { |
|
return d.children; |
|
}).forEach(function(n, i) { |
|
return n.prefix = function() { |
|
return (n.parent != null ? n.parent.prefix() : '') + n.index; |
|
}; |
|
}); |
|
/* 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 'gray'; |
|
} |
|
}); |
|
node.filter(function(d) { |
|
return d.children; |
|
}).append('text').attr('dx', function(d) { |
|
if (d.children) { |
|
return -8; |
|
} else { |
|
return 8; |
|
} |
|
}).attr('dy', 3).attr('text-anchor', function(d) { |
|
if (d.children) { |
|
return 'end'; |
|
} else { |
|
return 'start'; |
|
} |
|
}).text(function(d) { |
|
return d.prefix(); |
|
}); |
|
/* adapt bl.ocks.org frame to the tree |
|
*/ |
|
return d3.select(self.frameElement).transition().duration(500).style('height', "" + (height + 2 * margin) + "px"); |
|
}; |
|
|
|
}).call(this); |