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> |