|
@@ -0,0 +1,127 @@
|
|
|
+
|
|
|
+
|
|
|
+const canvas = document.getElementById('canvas');
|
|
|
+
|
|
|
+canvas.width = window.innerWidth - 50;
|
|
|
+canvas.height = window.innerHeight - 50;
|
|
|
+
|
|
|
+const canvwidth = canvas.width,
|
|
|
+ canvheight = canvas.height,
|
|
|
+ ctx = canvas.getContext('2d');
|
|
|
+
|
|
|
+function randIndivC(a = 0, b = 255) {
|
|
|
+ return Math.floor(Math.random() * (b-a) + a);
|
|
|
+}
|
|
|
+function cts(c) {
|
|
|
+ const r = c.r < 16 ? '0' + c.r.toString(16) : c.r.toString(16),
|
|
|
+ g = c.g < 16 ? '0' + c.g.toString(16) : c.g.toString(16),
|
|
|
+ b = c.b < 16 ? '0' + c.b.toString(16) : c.b.toString(16);
|
|
|
+ return '#' + r + g + b;
|
|
|
+}
|
|
|
+function mixedColor(base) {
|
|
|
+ let randr = Math.floor(Math.random() * 200 + 55),
|
|
|
+ randg = Math.floor(Math.random() * 200 + 55),
|
|
|
+ randb = Math.floor(Math.random() * 200 + 55);
|
|
|
+ if( base ) {
|
|
|
+ randr = Math.floor((base.r + randr) / 2);
|
|
|
+ randg = Math.floor((base.g + randg) / 2);
|
|
|
+ randb = Math.floor((base.b + randb) / 2);
|
|
|
+ }
|
|
|
+ return cts({r:randr, g:randg, b:randb});
|
|
|
+}
|
|
|
+function primaryOrSecondary() {
|
|
|
+ const t = Math.random(),
|
|
|
+ w = Math.floor(Math.random() * 3);
|
|
|
+ if( t < 0.45 ) {
|
|
|
+ const r = w === 0 ? 255 : 0,
|
|
|
+ g = w === 1 ? 255 : 0,
|
|
|
+ b = w === 2 ? 255 : 0;
|
|
|
+ return {r, g, b};
|
|
|
+ }
|
|
|
+ else if( t < 0.9 ) {
|
|
|
+ const r = w !== 0 ? 255 : 0,
|
|
|
+ g = w !== 1 ? 255 : 0,
|
|
|
+ b = w !== 2 ? 255 : 0;
|
|
|
+ return {r, g, b};
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ const r = randIndivC(100, 255),
|
|
|
+ g = randIndivC(100, 255),
|
|
|
+ b = randIndivC(100, 255);
|
|
|
+ return {r, g, b};
|
|
|
+ }
|
|
|
+}
|
|
|
+function spot(x, y, colour) {
|
|
|
+ ctx.save();
|
|
|
+ ctx.fillStyle = colour;
|
|
|
+ ctx.fillRect(x, y, 10, 10);
|
|
|
+ ctx.restore();
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+function generateInitialRow(lx = canvwidth / 10 + 1) {
|
|
|
+ const row = [];
|
|
|
+ for( let i = 0; i < lx; i++ ) {
|
|
|
+ row.push( Math.random() < 0.5 ? false : true );
|
|
|
+ }
|
|
|
+ return row;
|
|
|
+}
|
|
|
+const rules = {
|
|
|
+ "111": false,
|
|
|
+ "110": true,
|
|
|
+ "101": true,
|
|
|
+ "100": false,
|
|
|
+ "011": true,
|
|
|
+ "010": true,
|
|
|
+ "001": true,
|
|
|
+ "000": false,
|
|
|
+ };
|
|
|
+function applyRules(first, second, third) {
|
|
|
+ return rules[ [+first, +second, +third].join('') ];
|
|
|
+}
|
|
|
+function addRow(field) {
|
|
|
+ if( !field.length ) {
|
|
|
+ return field;
|
|
|
+ }
|
|
|
+ if( field[0].length < 3 ) {
|
|
|
+ return field;
|
|
|
+ }
|
|
|
+ const last = field[ field.length - 1 ],
|
|
|
+ newRow = last.map(function iter(val, ind) {
|
|
|
+ const first = last[ind - 1] || 0,
|
|
|
+ second = val,
|
|
|
+ third = last[ind + 1] || 0;
|
|
|
+ return applyRules(first, second, third);
|
|
|
+ });
|
|
|
+ field.push(newRow);
|
|
|
+ return field;
|
|
|
+}
|
|
|
+function drawRow(row, y) {
|
|
|
+ row.forEach(function spotter(val, ind) {
|
|
|
+ if( val ) {
|
|
|
+ const c = mixedColor(primaryOrSecondary());
|
|
|
+ spot(ind * 10, y, c);
|
|
|
+ }
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+function step(field) {
|
|
|
+ requestAnimationFrame(descend.bind(null, field));
|
|
|
+}
|
|
|
+function descend(field) {
|
|
|
+ const n = field.length - 1;
|
|
|
+ drawRow(field[n], n * 10);
|
|
|
+ field = addRow(field);
|
|
|
+ if( n * 10 < canvheight ) {
|
|
|
+ setTimeout(step, 100, field);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ console.log('STOP!');
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+step([ generateInitialRow() ]);
|
|
|
+
|
|
|
+
|