// 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 = 9; // px const GRID_COLOR = "#CCCCCC"; const DEAD_COLOR = "#FFFFFF"; const ALIVE_COLOR = "#3399FF"; const universe = Universe.new(128,128); 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(); const scaleX = canvas.width / rect.width; const scaleY = canvas.height / rect.height; const canvasLeft = (evt.clientX - rect.left) * scaleX; const canvasTop = (evt.clientY - rect.top) * scaleY; const row = Math.min(Math.floor(canvasTop / (CELL_SIZE + 1)), height - 1); const col = Math.min(Math.floor(canvasLeft / (CELL_SIZE + 1)), width - 1); return { row, col }; } 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.row) + ", Y = " + (mousePos.col)); const idx = universe.get_index(mousePos.row, mousePos.col); if (cells[idx] === Cell.Dead) console.log("Cell[" + idx + "] is : " + "Dead !"); else console.log("Cell[" + idx + "] is : " + "Alive !"); // Toggle it ! TODO: Do it (like in the tutorial) from Rust ! 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();