Created
February 5, 2015 23:50
-
-
Save amoeba/4acfb22c99e623d2c78d to your computer and use it in GitHub Desktop.
Random Right-Angled Trees
This file contains hidden or 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"> | |
| <h1>Random Trees</h1> | |
| <p>Written by <a href="http://brycemecum.com/">Bryce Mecum</a></p> | |
| <p>Code adapted from <a href="http://www.jasondavies.com/">Jason Davies</a> for generating right-angled trees and <a href="https://github.com/yuanyan/">yuanyan</a> for the Poisson distribution code.</p> | |
| <p> | |
| <button id="start" onclick="start()">Start</button> | |
| <button id="stop" onclick="stop()">Stop</button> | |
| </p> | |
| <div id="tree"></div> | |
| <style type="text/css"> | |
| .overlay { | |
| fill: none; | |
| pointer-events: all; | |
| } | |
| #tree | |
| { | |
| width: 600px; | |
| height: 600px; | |
| border: 1px solid black; | |
| } | |
| </style> | |
| <script src="http://d3js.org/d3.v3.min.js"></script> | |
| <script type="text/javascript"> | |
| var running = false; | |
| var timer = null; | |
| var start = function() { | |
| if(!running) { | |
| running = true; | |
| timer = window.setInterval(generate_nodes, 1000); | |
| } | |
| } | |
| var stop = function() { | |
| if(running) { | |
| running = false; | |
| window.clearInterval(timer); | |
| } | |
| } | |
| function poisson(expectvalue) { | |
| var n = 0, | |
| limit = Math.exp(-expectvalue), | |
| x = Math.random(); | |
| while(x > limit) { | |
| n++; | |
| x *= Math.random(); | |
| } | |
| return n; | |
| } | |
| var add_nodes = function(node) { | |
| depth++; | |
| var num_children = poisson(3) | |
| num_children += (depth == 1) ? 1 : 0 | |
| for(var i = 0; i < num_children; i++) { | |
| node.children.push({ | |
| "children" : [] | |
| }) | |
| if (depth < 3) | |
| { | |
| add_nodes(node.children[i]) | |
| } | |
| } | |
| depth--; | |
| } | |
| var clear = function() { | |
| var tree = document.getElementById("tree"), | |
| child = tree.firstChild; | |
| while(child) { | |
| tree.removeChild(child); | |
| child = tree.firstChild; | |
| } | |
| } | |
| var nodes = {}; | |
| var depth = 0; | |
| var generate_nodes = function() { | |
| nodes = {"children":[]}; | |
| depth = 0; | |
| clear(); | |
| add_nodes(nodes); | |
| draw_tree(nodes); | |
| } | |
| rightAngleDiagonal = function() { | |
| var projection = function(d) { return [d.x, d.y]; } | |
| var path = function(pathData) { | |
| return "M" + pathData[0] + ' ' + pathData[1] + " " + pathData[2]; | |
| } | |
| function diagonal(diagonalPath, i) { | |
| var source = diagonalPath.source, | |
| target = diagonalPath.target, | |
| midpointX = (source.x + target.x) / 2, | |
| midpointY = (source.y + target.y) / 2, | |
| pathData = [source, {x: target.x, y: source.y}, target]; | |
| pathData = pathData.map(projection); | |
| return path(pathData) | |
| } | |
| return diagonal; | |
| } | |
| var draw_tree = function(nodes) { | |
| var width = 600, | |
| height = 600; | |
| var tree = d3.layout.cluster() | |
| .size([width, height]) | |
| .separation(function(a, b) { | |
| return 1 | |
| }); | |
| var diagonal = rightAngleDiagonal(); | |
| var vis = d3.select("#tree").append("svg:svg") | |
| .attr("width", width) | |
| .attr("height", height) | |
| .append("svg:g") | |
| .attr("transform", "translate(0, 0)"); | |
| vis.append("rect") | |
| .attr("class", "overlay") | |
| .attr("width", width + width / 2) | |
| .attr("height", height + height / 2); | |
| var nodes = tree(nodes); | |
| // Visit all nodes and adjust y pos width distance metric | |
| var visitPreOrder = function(root, callback) { | |
| callback(root) | |
| if (root.children) { | |
| for (var i = root.children.length - 1; i >= 0; i--){ | |
| visitPreOrder(root.children[i], callback) | |
| }; | |
| } | |
| } | |
| visitPreOrder(nodes[0], function(node) { | |
| node.rootDist = (node.parent ? node.parent.rootDist : 0) + 10 | |
| }) | |
| var rootDists = nodes.map(function(n) { return n.rootDist; }); | |
| var yscale = d3.scale.linear() | |
| .domain([0, d3.max(rootDists) * 1.15]) | |
| .range([0, width * 1]); | |
| visitPreOrder(nodes[0], function(node) { | |
| node.y = yscale(node.rootDist) | |
| }) | |
| var link = vis.selectAll("path.link") | |
| .data(tree.links(nodes)) | |
| .enter().append("svg:path") | |
| .attr("class", "link") | |
| .attr("d", diagonal) | |
| .attr("fill", "none") | |
| .attr("stroke", "black") | |
| .attr("stroke-width", "1px"); | |
| var node = vis.selectAll("g.node") | |
| .data(nodes) | |
| .enter().append("svg:g") | |
| .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }) | |
| node.append("svg:circle") | |
| .attr("r", 4.5) | |
| .attr('stroke', "black") | |
| .attr('fill', "white") | |
| .attr('stroke-width', '1px'); | |
| } | |
| start(); | |
| </script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment