// Import the WebAssembly memory at the top of the file. import { memory } from "wasm-game-of-life/wasm_game_of_life_bg"; import { Universe, Cell } from "wasm-game-of-life"; let req_id; let stop = false; const CELL_SIZE = 40; // px const GRID_COLOR = "#CCCCCC"; const DEAD_COLOR = "#FFFFFF"; const ALIVE_COLOR = "#3399FF"; const universe = Universe.new(10,10); const width = universe.width(); const height = universe.height(); const canvas = document.getElementById("game-of-life-canvas"); canvas.height = (CELL_SIZE + 1) * height + 1; canvas.width = (CELL_SIZE + 1) * width + 1; const ctx = canvas.getContext('2d'); // The JavaScript runs in a requestAnimationFrame loop. const renderLoop = async () => { await new Promise(r => setTimeout(r, 200)); universe.tick(); drawCells(); if (stop == false) req_id = requestAnimationFrame(renderLoop); }; // Return Cell position function getCursorPos(canvas, evt) { const rect = canvas.getBoundingClientRect(); // Bound check: It seems that the last case can overlap let x = Math.trunc((evt.clientX - rect.left) / CELL_SIZE); if (x >= (width)) x = x - 1; let y = Math.trunc((evt.clientY - rect.top) / CELL_SIZE); if (y >= (height)) y = y - 1; return { x, y, }; } canvas.addEventListener('mouseover', function(evt) { console.log("Stopping Animation Frame"); stop = true; }); canvas.addEventListener('mouseout', function(evt) { console.log("Starting Animation Frame"); stop = false; start(); }); // Get Mouse position canvas.addEventListener('click', function(evt) { var mousePos = getCursorPos(canvas, evt); let cellsPtr = universe.cells(); let cells = new Uint8Array(memory.buffer, cellsPtr, width * height); console.log("Click position : X = " + (mousePos.x) + ", Y = " + (mousePos.y)); const idx = universe.get_index(mousePos.y, mousePos.x); if (cells[idx] === Cell.Dead) console.log("Cell[" + idx + "] is : " + "Dead !"); else console.log("Cell[" + idx + "] is : " + "Alive !"); // Change to inverse ! cells[idx] = (cells[idx] === Cell.Dead) ? Cell.Alive : Cell.Dead; drawCells(); }); const drawGrid = () => { ctx.beginPath(); ctx.strokeStyle = GRID_COLOR; // Vertical lines. for (let i = 0; i <= width; i++) { ctx.moveTo(i * (CELL_SIZE + 1) + 1, 0); ctx.lineTo(i * (CELL_SIZE + 1) + 1, (CELL_SIZE + 1) * height + 1); } // Horizontal lines. for (let j = 0; j <= height; j++) { ctx.moveTo(0, j * (CELL_SIZE + 1) + 1); ctx.lineTo((CELL_SIZE + 1) * width + 1, j * (CELL_SIZE + 1) + 1); } ctx.stroke(); }; // We can directly access WebAssembly's linear memory via memory, which is defined in the raw wasm module wasm_game_of_life_bg. // To draw the cells, we get a pointer to the universe's cells, construct a Uint8Array overlaying the cells buffer, iterate over each cell, and draw a white or black rectangle depending on whether the cell is dead or alive, respectively. // By working with pointers and overlays, we avoid copying the cells across the boundary on every tick. const drawCells = () => { // Modify Plus const const cellsPtr = universe.cells(); const cells = new Uint8Array(memory.buffer, cellsPtr, width * height); ctx.beginPath(); for (let row = 0; row < height; row++) { for (let col = 0; col < width; col++) { const idx = universe.get_index(row, col); ctx.fillStyle = (cells[idx] === Cell.Dead) ? DEAD_COLOR : ALIVE_COLOR; ctx.fillRect( col * (CELL_SIZE + 1) + 1, // x row * (CELL_SIZE + 1) + 1, // y CELL_SIZE, // Width CELL_SIZE // Height ); } } ctx.stroke(); }; function start() { drawGrid(); drawCells(); req_id = requestAnimationFrame(renderLoop); } start();