Barnsley fern
Inspired by a Youtube video: Chaos Game - Numberphile
Learn more about how this works at Wikipedia: https://en.wikipedia.org/wiki/Barnsley_fern
Built with blockbuilder.org
license: mit |
Barnsley fern
Inspired by a Youtube video: Chaos Game - Numberphile
Learn more about how this works at Wikipedia: https://en.wikipedia.org/wiki/Barnsley_fern
Built with blockbuilder.org
<!DOCTYPE html> | |
<head> | |
<meta charset="utf-8"> | |
<style> | |
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; } | |
</style> | |
</head> | |
<body> | |
<canvas id="mycanvas" width="960" height="500"></canvas> | |
<script> | |
var canvas = document.getElementById('mycanvas'), | |
ctx = canvas.getContext('2d'), | |
i = 0, | |
dot = { | |
x: 0, | |
y: 0, | |
hue: 0.001, | |
lightness: 0.35 | |
}; | |
// https://en.wikipedia.org/wiki/Barnsley_fern#Construction | |
var barnsley = [ | |
{a: 0.00, b: 0.00, c: 0.00, d: 0.16, e: 0.00, f: 0.00, p: 0.01}, | |
{a: 0.85, b: 0.04, c: -0.04, d: 0.85, e: 0.00, f: 1.60, p: 0.01 + 0.85}, | |
{a: 0.20, b: -0.26, c: 0.23, d: 0.22, e: 0.00, f: 1.60, p: 0.01 + 0.85 + 0.07}, | |
{a: -0.15, b: 0.28, c: 0.26, d: 0.24, e: 0.00, f: 0.44, p: 0.01 + 0.85 + 0.07 + 0.07} | |
]; | |
function fern_step(obj) { | |
var p = Math.random(); | |
var b = null; | |
for (i = 0; i < barnsley.length; i++) { | |
b = barnsley[i]; | |
if (p < b.p) { | |
return Object.assign(obj, { | |
x: b.a * obj.x + b.b * obj.y + b.e, | |
y: b.c * obj.x + b.d * obj.y + b.f, | |
hue: p < 1e-3 ? (obj.hue > 1 ? 1e-4 : obj.hue + 1e-3) : obj.hue | |
// hue: 0.4 | |
}); | |
} | |
} | |
} | |
// Keep track of how many frames we draw. | |
var frames = 0; | |
function step() { | |
// Stop drawing frames after some limit. | |
if (frames++ > 1e3) { | |
return; | |
} | |
// Draw a variable number of points each frame. | |
for (var k = 0; k < frames; k++) { | |
draw_circle( | |
ctx, | |
dot.x * 105 + canvas.width / 2, | |
dot.y * -49 + canvas.height, | |
0.09, // radius | |
dot.hue, // hue | |
dot.lightness // lightness | |
); | |
dot = fern_step(dot); | |
} | |
// Draw another frame. | |
window.requestAnimationFrame(step); | |
} | |
// Draw a frame. | |
window.requestAnimationFrame(step); | |
function draw_circle(ctx, x, y, radius, hue, lightness) { | |
ctx.fillRect(x, y, 1, 1); | |
var rgb = hslToRgb(hue, 0.75, lightness); | |
ctx.fillStyle = 'rgba(' + | |
rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ',' + 0.5 + | |
')'; | |
ctx.fill(); | |
} | |
/** | |
* http://stackoverflow.com/a/9493060/330558 | |
* | |
* Converts an HSL color value to RGB. Conversion formula | |
* adapted from http://en.wikipedia.org/wiki/HSL_color_space. | |
* Assumes h, s, and l are contained in the set [0, 1] and | |
* returns r, g, and b in the set [0, 255]. | |
* | |
* @param {number} h The hue | |
* @param {number} s The saturation | |
* @param {number} l The lightness | |
* @return {Array} The RGB representation | |
*/ | |
function hslToRgb(h, s, l){ | |
var r, g, b; | |
if (s == 0) { | |
r = g = b = l; // achromatic | |
} else { | |
var hue2rgb = function hue2rgb(p, q, t) { | |
if (t < 0) t += 1; | |
if (t > 1) t -= 1; | |
if (t < 1/6) return p + (q - p) * 6 * t; | |
if (t < 1/2) return q; | |
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6; | |
return p; | |
} | |
var q = l < 0.5 ? l * (1 + s) : l + s - l * s; | |
var p = 2 * l - q; | |
r = hue2rgb(p, q, h + 1/3); | |
g = hue2rgb(p, q, h); | |
b = hue2rgb(p, q, h - 1/3); | |
} | |
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)]; | |
} | |
</script> | |
</body> |