Last active
October 27, 2019 19:15
-
-
Save robinhouston/6027749 to your computer and use it in GitHub Desktop.
Algorithmic painting
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> | |
<title>Maze flood fill</title> | |
<style> | |
canvas { display: block; margin: auto; } | |
</style> | |
<canvas width="1601" height="1001" style="display: none;"></canvas> | |
<canvas width="800" height="500"></canvas> | |
<script> | |
// http://paulirish.com/2011/requestanimationframe-for-smart-animating/ | |
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating | |
// requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel | |
// MIT license | |
(function() { | |
var lastTime = 0; | |
var vendors = ['ms', 'moz', 'webkit', 'o']; | |
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { | |
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; | |
window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] | |
|| window[vendors[x]+'CancelRequestAnimationFrame']; | |
} | |
if (!window.requestAnimationFrame) | |
window.requestAnimationFrame = function(callback, element) { | |
var currTime = new Date().getTime(); | |
var timeToCall = Math.max(0, 16 - (currTime - lastTime)); | |
var id = window.setTimeout(function() { callback(currTime + timeToCall); }, | |
timeToCall); | |
lastTime = currTime + timeToCall; | |
return id; | |
}; | |
if (!window.cancelAnimationFrame) | |
window.cancelAnimationFrame = function(id) { | |
clearTimeout(id); | |
}; | |
}()); | |
</script> | |
<script> | |
var PIXELS_PER_FRAME = 5; | |
var canvases = document.getElementsByTagName("canvas"), | |
canvas = canvases[0], | |
output = canvases[1]; | |
var cx = canvas.getContext("2d"), | |
output_cx = output.getContext("2d"); | |
function loadAndFill(image_url, finished) { | |
var image = new Image(); | |
image.addEventListener("load", function() { | |
cx.drawImage(image, (canvas.width - image.width)/2, (canvas.height - image.height)/2); | |
floodFill(finished); | |
}); | |
image.src = image_url; | |
} | |
var fill_color = {r: 0xFF, g: 0, b: 0}; | |
function floodFill(finished) { | |
var image_data_object = cx.getImageData(0, 0, canvas.width, canvas.height), | |
image_data = image_data_object.data, | |
output_data_object = output_cx.getImageData(0, 0, output.width, output.height), | |
output_data = output_data_object.data; | |
function white(x, y) { | |
var base_index = (canvas.width * y + x) * 4, | |
r = image_data[base_index], | |
g = image_data[base_index + 1], | |
b = image_data[base_index + 2], | |
a = image_data[base_index + 3]; | |
return (r == 0xFF && g == 0xFF && b == 0xFF); | |
} | |
function redden(x, y) { | |
var base_index = (canvas.width * y + x) * 4; | |
image_data[base_index] = fill_color.r; | |
image_data[base_index + 1] = fill_color.g; | |
image_data[base_index + 2] = fill_color.b; | |
image_data[base_index + 3] = 0xFF; | |
if ( x%2 == 1 && y%2 == 1) { | |
base_index = (output.width * (y-1)/2 + (x-1)/2)*4; | |
output_data[base_index] = fill_color.r; | |
output_data[base_index + 1] = fill_color.g; | |
output_data[base_index + 2] = fill_color.b; | |
output_data[base_index + 3] = 0xFF; | |
} | |
} | |
var x = 801, y = 501; | |
var queue = [[x,y]]; | |
function doFill() { | |
var num_reddened = 0, | |
num_fronts = queue.length; | |
queue_loop: while (queue.length > 0) { | |
var p = queue.shift(), x = p[0], y = p[1]; | |
if (!white(x, y)) continue; | |
while (x > 0 && white(x-1, y)) x--; | |
while (x < canvas.width && white(x, y)) { | |
redden(x, y); | |
if (y > 0 && white(x, y-1)) queue.push([x, y-1]); | |
if (y < canvas.height-1 && white(x, y+1)) queue.push([x, y+1]); | |
x++; | |
if (++num_reddened == PIXELS_PER_FRAME * num_fronts) { | |
queue.unshift([x, y]); | |
break queue_loop; | |
} | |
} | |
} | |
output_cx.putImageData(output_data_object, 0, 0); | |
} | |
function animate() { | |
doFill(); | |
fill_color.r = (fill_color.r + 1) % 0x100; | |
fill_color.g = (fill_color.g + 0xFE) % 0x100; | |
if (queue.length > 0) requestAnimationFrame(animate); | |
else if (finished) finished(); | |
} | |
requestAnimationFrame(animate); | |
} | |
// Maze image generated uniformly at random using Wilson’s algorithm, specifically: | |
// python make_1bit_maze.py -c 800 -r 500 -o 800x500.png | |
loadAndFill("800x500.png"); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment