D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
mootari
Full window
Github gist
Random trees without RNGs
<!DOCTYPE html> <html> <head> <style> canvas {display:block; margin: 20px auto; border: 2px solid #4892c6; max-width: 100%; box-sizing: border-box; } </style> </head> <body> <script>(function() { 'use strict'; var _options = { // Everything above -3 is boring cellIndex: -41, // Range: -3 to 3 edgeIndex: -2, // Cells per axis width: 160, height: 80, // Render scale in px scale: 6, // Rate of depth color change depthScale: .03, // Iteration delay delay: 10, renderInterval: 25, drawDepth: true, drawLines: true }; var _stack = []; var _points = new Map(); var _live = document.createElement('canvas').getContext('2d'); _live.canvas.width = _options.width * _options.scale; _live.canvas.height = _options.height * _options.scale; document.body.appendChild(_live.canvas); var _offscreen = _live.canvas.cloneNode().getContext('2d'); _offscreen.lineWidth = _options.scale * .2; _offscreen.lineCap = 'round'; _offscreen.strokeStyle = '#000'; var root = addPoint([~~(_options.width / 2), ~~(_options.height / 2)]); renderPoint(_offscreen, root); play(); function play() { var i = _options.renderInterval; while(_stack.length) { var point = iterate(); if(point) { renderPoint(_offscreen, point); if(--i <= 0 && _options.delay >= 0) { setTimeout(play, _options.delay); publish(); return; } } } publish(); } function iterate() { var added; var cell = removeIndex(_stack, _options.cellIndex); var edges = mask(cell) .filter(isInBounds) .filter(isAvailable); var edge = removeIndex(edges, _options.edgeIndex); if(edge) { added = addPoint(edge, cell); if(edges.length) { _stack.push(cell); } } return added; } // Registers and initializes the given point and adds it to the stack. function addPoint(point, parent) { var o = getOffset(point); var p = point.slice(); p.parent = parent || null; p.depth = parent ? parent.depth + 1 : 0; _points.set(o, p); _stack.push(p); return p; } // Returns a string ID for a point. function getOffset(point) { return point.join(','); } // Checks if a point is inside the extent. function isInBounds(point) { return point[0] >= 0 && point[0] < _options.width && point[1] >= 0 && point[1] < _options.height; } // Checks if a point's offset is not yet blocked. function isAvailable(point) { return !_points.has(getOffset(point)); } // Normalizes an index and extracts the element at the resulting index. function removeIndex(arr, index) { if(!arr.length) { return null; } var i = index < 0 ? Math.max(0, arr.length + index) : Math.min(arr.length - 1, index); return arr.splice(i, 1)[0]; } // Produces edge coordinates for a point. function mask(point) { var mask = [ [ 1, 0], [ 0, 1], [-1, 0], [ 0, -1] ]; var i = mask.length; while(i--) { mask[i][0] += point[0]; mask[i][1] += point[1]; } return mask; } function renderPoint(ctx, point) { var s = _options.scale, sh = s / 2; var x = point[0] * s; var y = point[1] * s; if(_options.drawDepth) { var level = ~~(70 + 185 * Math.abs(Math.cos(point.depth * _options.depthScale))); ctx.fillStyle = 'rgb(' + level + ',' + level + ',' + level + ')'; ctx.fillRect(x, y, s, s); } if(_options.drawLines && point.parent) { ctx.beginPath(); ctx.moveTo(point.parent[0] * s + sh, point.parent[1] * s + sh); ctx.lineTo(x + sh, y + sh); ctx.stroke(); } } function publish() { _live.clearRect(0, 0, _live.canvas.width, _live.canvas.height); _live.drawImage(_offscreen.canvas, 0, 0); } }());</script> </body> </html>