var size; var hertz; var height_px; var width_px; var height_c; var width_c; var cells; var timer; var effects; // set start/stop button value function btn_control_set_name(name) { var btn = document.getElementById('control-btn'); btn.value = name; } // generate an 2d array of cells based on current geometry function gen_cells() { var new_cells = new Array(height_c); for(var i = 0; i < height_c; i++) { new_cells[i] = new Array(width_c); } for (var y = 0; y < height_c; y++) { for (var x = 0; x < width_c; x++) { new_cells[y][x] = 0; } } return new_cells; } // init the cells function init_cells() { cells = gen_cells(); } // Generate and populate the geometry variables // possibly over complicated from my experiment with an elastic resizeable board function get_demensions() { var canvas = document.getElementById('board'); size = parseInt( document.getElementById('density').value); width_px = canvas.offsetWidth - (canvas.offsetWidth%size); canvas.width = width_px; width_c = width_px/ size; height_px = canvas.offsetHeight - (canvas.offsetHeight%size); canvas.height = height_px; height_c = height_px/size; //console.log(width_px + 'x' + height_px + ' => ' + width_c + 'x' +height_c); } // init the board // set vars // get geometry // generate cells for 1st time // draw function board_init() { var canvas = document.getElementById('board'); document.getElementById('density').value = 10; get_demensions(); btn_control_set_name('Start'); document.getElementById('speed').value = 8; change_speed(); init_cells(); document.getElementById('effects').value=0; effects=0; redraw(); } // Draw the current board: gird, then cells function redraw() { var canvas = document.getElementById('board'); var c = canvas.getContext('2d'); get_demensions(); c.globalCompositeOperation = 'destination-over'; c.clearRect(0, 0, width_px, height_px); c.fillStyle='rgba(0,0,0,0.)'; c.strokeStyle= 'rgba(192,192,192,1)'; c.beginPath(); draw_grid(c); c.stroke(); draw_cells(c); } // Generate a cycling darkening with age rainbow color // based on math/code from http://www.krazydad.com/makecolors.php function setStrokeAgeRainbow(l, c) { var frequency = 0.3; var center = 200; //128; var width = 55; //127; var red = Math.floor( (Math.sin(frequency*l + 0) * width + center) -l) ; var green = Math.floor( (Math.sin(frequency*l + 2) * width + center) -l); var blue = Math.floor( (Math.sin(frequency*l + 4) * width + center) -l); red = Math.max(0, red); green = Math.max(0, green); blue = Math.max(0, blue); c.strokeStyle= 'rgba('+red+','+green+','+blue+','+1+')'; } // Draw all the cells onto the board inside the grid function draw_cells(c) { for (var y = 0; y < height_c; y++) { for (var x =0; x < width_c; x ++) { if (cells[y][x] != 0) { if(effects == 0) { c.strokeStyle = 'rgba(0,0,0,1)'; } else { setStrokeAgeRainbow(cells[y][x], c); } c.fillStyle = c.strokeStyle; c.beginPath(); c.moveTo(x*size +1, y*size+1); c.lineTo(x*size+ size-1, y*size+1); c.lineTo(x*size+ size-1, y*size + size-1); c.lineTo(x*size+1, y*size + size-1); // auto return home c.stroke(); c.fill(); } } } } // draw the grid and border function draw_grid(c) { // Horizontal lines for (var y = 0; y <= height_px; y += size) { c.moveTo(0, y); c.lineTo(width_px, y); } // Vertical lines for (var x = 0; x <= width_px; x += size) { c.moveTo(x, 0); c.lineTo(x, height_px); } } // When we've loaded, run! window.onload = board_init; // is a cell alive or dead // 0 for dead, 1 for alive // walls count as dead // used by num_neighbours function cell_value(x, y) { //console.log('(x:' + x + ',y:' + y + ') w/s:' + (width/size) + ' h/s:' + (height/size) ); if (x < 0 || x >= width_c || y < 0 || y >= height_c) { return 0; } else { return cells[y][x] == 0 ? 0 : 1; } } // return the number of neighbours for a cell // walls count as dead function num_neighbours(x, y) { var n = 0; n += cell_value(x-1, y-1); n += cell_value(x, y-1); n += cell_value(x+1, y-1); n += cell_value(x-1, y); n += cell_value(x+1, y); n += cell_value(x-1, y+1); n += cell_value(x, y+1); n += cell_value(x+1, y+1); return n; } // Simulate one generation of cells // create a new cells 2d array and simulate the next generation // into it and then replace the current cells with it function step() { var new_cells = gen_cells(); for(var y=0; y < height_c; y++) { for(var x =0 ; x < width_c; x++) { var n = num_neighbours(x, y); if (cells[y][x] != 0) { if (n < 2 || n > 3) { new_cells[y][x] = 0; } else { new_cells[y][x] = cells[y][x] +1; } } else if (n == 3) { new_cells[y][x] = 1; } else { new_cells[y][x] = 0; } } } //new_cells[Math.floor(Math.random()*8)][ Math.floor(Math.random()*8) ] = true; cells = new_cells; } // Stop/Start button press function control_click() { var btn = document.getElementById('control-btn'); if (btn.value == 'Start') { tick(); btn.value = 'Stop'; } else { clearTimeout(timmer); btn.value = 'Start'; } } // Function to call on timmer // simulate on e generation, // redrawn // reset timmer function tick() { step(); redraw(); timmer = setTimeout('tick();', 1000.0/hertz); } // Handler for a click on the board to place/unplace a cell function board_click(e) { var canvas = document.getElementById('board'); var x = Math.floor((e.pageX-canvas.offsetLeft)/size); var y = Math.floor((e.pageY-canvas.offsetTop)/size); cells[y][x] = ! cells[y][x]; redraw(); } // Change speed handler function change_speed() { hertz = parseInt(document.getElementById('speed').value); } // Resize board handler (kind of not used) // Left over from experiment in resiable elastic board // Abandoned because of problems with lining up mouse clicks on board // and weird stretcing issues function resize_cells() { var new_cells = gen_cells(); var w = Math.min(width_c, cells[0].length); var h = Math.min(height_c, cells.length); for(var y = 0; y < h; y++) { for(x = 0; x < w; x++) { new_cells[y][x] = cells[y][x]; } } cells = new_cells; } // What to do on a document resize (more usefull for elastic boards) function canvas_resize() { get_demensions(); resize_cells(); redraw(); }