Last active
June 14, 2017 22:04
-
-
Save mootari/fb37437067fbfaa7ad3d23c07d726057 to your computer and use it in GitHub Desktop.
Random trees without RNGs
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> | |
<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> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment