2020-06-10 19:11:26 +02:00
// 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 ;
2020-06-10 19:48:00 +02:00
const CELL _SIZE = 40 ; // px
2020-06-10 19:11:26 +02:00
const GRID _COLOR = "#CCCCCC" ;
const DEAD _COLOR = "#FFFFFF" ;
const ALIVE _COLOR = "#3399FF" ;
2020-06-10 19:48:00 +02:00
const universe = Universe . new ( 10 , 10 ) ;
2020-06-10 19:11:26 +02:00
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 ( ) ;