replace play checkbox with button, added description
This commit is contained in:
parent
de8ef49de3
commit
07d2e0bb68
@ -94,7 +94,7 @@ impl Universe {
|
||||
impl Universe {
|
||||
pub fn tick(&mut self) {
|
||||
// log!("ticking");
|
||||
let _timer = time::Timer::new("Universe::tick"); // will stop when dropped
|
||||
// let _timer = time::Timer::new("Universe::tick"); // will stop when dropped
|
||||
let mut next = self.cells.clone();
|
||||
|
||||
for row in 0..self.height {
|
||||
|
@ -30,19 +30,24 @@
|
||||
<body>
|
||||
<div class="card container-xs text-center p-4 m-3">
|
||||
<div class="card-header">
|
||||
<h1>Game of Life</h1>
|
||||
<h1>Game of Life 🚀</h1>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row p-1">
|
||||
<div class="col-sm-12">
|
||||
<p class="text-muted">An implementation of the standard <a href="https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life">Conway Game of Life</a>. The game logic is written in WASM-targeted Rust with a light Js frontend. Read through the source code <a href="https://github.com/Sarsoo/game-of-life">here</a>. It was written following the guides in the <a href="https://rustwasm.github.io/docs/book">Rust & WebAssembly book</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row p-3">
|
||||
<div class="col-sm-6">
|
||||
<input type="range"
|
||||
id="frameRate"
|
||||
name="frameRate"
|
||||
min="1" max="500" value="50"
|
||||
min="1" max="500" value="100"
|
||||
class="form-range">
|
||||
<label for="frameRate"
|
||||
id="frameRate-label"
|
||||
class="form-label">Frame Interval: 50</label>
|
||||
class="form-label">Frame Interval: 100ms</label>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<input type="range"
|
||||
@ -52,7 +57,7 @@
|
||||
class="form-range">
|
||||
<label for="randThreshold"
|
||||
id="randThreshold-label"
|
||||
class="form-label">Rand Threshold: 50</label>
|
||||
class="form-label">Random Threshold: 50%</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row p-3">
|
||||
@ -75,18 +80,19 @@
|
||||
</div>
|
||||
<div class="row p-3 align-items-center">
|
||||
<div class="col-sm-6">
|
||||
<input type="checkbox"
|
||||
<!-- <input type="checkbox"
|
||||
id="play-check"
|
||||
name="play-check"
|
||||
class="form-check-input">
|
||||
<label for="play-check">Play</label>
|
||||
<label for="play-check">Play</label> -->
|
||||
<button id="play" class="btn btn-success">Play</button>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="row p-1">
|
||||
<button id="step" class="btn btn-primary">Step</button>
|
||||
<button id="step" class="btn btn-secondary">Step</button>
|
||||
</div>
|
||||
<div class="row p-1">
|
||||
<button id="reset" class="btn btn-primary">Reset</button>
|
||||
<button id="reset" class="btn btn-secondary">Reset</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -98,9 +104,7 @@
|
||||
|
||||
<script src="./bootstrap.js"></script>
|
||||
|
||||
<img src="https://storage.googleapis.com/sarsooxyzstatic/andy.png" class=" pb-2" style="width: 150px" />
|
||||
|
||||
<!-- <div id="fps"></div> -->
|
||||
<a href="https://github.com/sarsoo"><img src="https://storage.googleapis.com/sarsooxyzstatic/andy.png" class=" pb-2" style="width: 150px" /></a>
|
||||
</body>
|
||||
|
||||
</html>
|
134
www/index.js
134
www/index.js
@ -15,6 +15,7 @@ const ALIVE_COLOR = "#FF55AA";
|
||||
let universe = Universe.new(100, 100, randSlider.value, new Date().getTime() / 1000);
|
||||
let width = universe.width();
|
||||
let height = universe.height();
|
||||
let play = false;
|
||||
|
||||
const canvas = document.getElementById("game-of-life-canvas");
|
||||
canvas.height = (CELL_SIZE + 1) * height + 1;
|
||||
@ -22,6 +23,9 @@ canvas.width = (CELL_SIZE + 1) * width + 1;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
/**
|
||||
* Draw grid onto canvas prior to painting cells
|
||||
*/
|
||||
const drawGrid = () => {
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = GRID_COLOR;
|
||||
@ -41,10 +45,19 @@ const drawGrid = () => {
|
||||
ctx.stroke();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get linear index from row and column indices
|
||||
* @param {*} row Row index
|
||||
* @param {*} column Column index
|
||||
* @returns Linear index
|
||||
*/
|
||||
const getIndex = (row, column) => {
|
||||
return row * width + column;
|
||||
};
|
||||
|
||||
/**
|
||||
* Paint alive cells onto grid
|
||||
*/
|
||||
const drawCells = () => {
|
||||
const cellsPtr = universe.cells();
|
||||
const cells = new Uint8Array(memory.buffer, cellsPtr, width * height);
|
||||
@ -71,6 +84,9 @@ const drawCells = () => {
|
||||
ctx.stroke();
|
||||
};
|
||||
|
||||
/**
|
||||
* Single frame/step of game, tick universe, refresh UI
|
||||
*/
|
||||
const renderSingle = () => {
|
||||
// fps.render(); //new
|
||||
universe.tick();
|
||||
@ -79,53 +95,62 @@ const renderSingle = () => {
|
||||
drawCells();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start interval timer to periodically iterate frames
|
||||
*/
|
||||
const start = () => {
|
||||
if(loop != null) clearInterval(loop);
|
||||
loop = setInterval(renderSingle, frameInterval);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear interval timer to stop animation loop
|
||||
*/
|
||||
const stop = () => {
|
||||
if(loop != null) clearInterval(loop);
|
||||
loop = null;
|
||||
}
|
||||
|
||||
// const renderLoop = () => {
|
||||
// if(PLAY){
|
||||
// renderSingle();
|
||||
// requestAnimationFrame(renderLoop);
|
||||
// }
|
||||
// };
|
||||
|
||||
// renderSingle();
|
||||
// requestAnimationFrame(renderLoop);
|
||||
|
||||
var frameInterval = 50;
|
||||
// var loop = setInterval(renderSingle, frameInterval);
|
||||
var loop = null;
|
||||
|
||||
// SLIDERS
|
||||
|
||||
const frameSlider = document.getElementById("frameRate");
|
||||
const frameSliderLabel = document.getElementById("frameRate-label");
|
||||
/**
|
||||
* Handler for frame interval slider change, stop, change interval, start
|
||||
*/
|
||||
const onFrameSlider = () => {
|
||||
stop();
|
||||
|
||||
frameInterval = frameSlider.value;
|
||||
frameSliderLabel.innerHTML = `Frame Interval: ${frameSlider.value}`;
|
||||
frameSliderLabel.innerHTML = `Frame Interval: ${frameSlider.value}ms`;
|
||||
|
||||
if(playCheck.checked) start();
|
||||
if(play) start();
|
||||
}
|
||||
frameSlider.onchange = onFrameSlider;
|
||||
frameSlider.value = 100;
|
||||
|
||||
/**
|
||||
* Handler for random threshold slider change, get a new universe with new threshold
|
||||
*/
|
||||
const onRandSlider = () => {
|
||||
stop();
|
||||
|
||||
universe = Universe.new(width, height, randSlider.value, new Date().getTime() / 1000);
|
||||
refreshCanvas();
|
||||
randSliderLabel.innerHTML = `Rand Threshold: ${randSlider.value}`;
|
||||
randSliderLabel.innerHTML = `Random Threshold: ${randSlider.value}%`;
|
||||
|
||||
if(playCheck.checked) start();
|
||||
if(play) start();
|
||||
}
|
||||
randSlider.onchange = onRandSlider;
|
||||
randSlider.value = 50;
|
||||
|
||||
/**
|
||||
* Refresh existing canvas, calculate dimensions and draw
|
||||
*/
|
||||
const refreshCanvas = () => {
|
||||
canvas.width = (CELL_SIZE + 1) * width + 1;
|
||||
canvas.height = (CELL_SIZE + 1) * height + 1;
|
||||
@ -133,7 +158,12 @@ const refreshCanvas = () => {
|
||||
drawCells();
|
||||
}
|
||||
|
||||
// INPUT BOXES
|
||||
|
||||
const widthBox = document.getElementById("width");
|
||||
/**
|
||||
* Handler for width input box change, get a new universe of given size
|
||||
*/
|
||||
const onWidth = () => {
|
||||
// PLAY = false;
|
||||
width = widthBox.value;
|
||||
@ -145,6 +175,9 @@ const onWidth = () => {
|
||||
widthBox.onchange = onWidth;
|
||||
|
||||
const heightBox = document.getElementById("height");
|
||||
/**
|
||||
* Handler for height input box change, get a new universe of given size
|
||||
*/
|
||||
const onHeight = () => {
|
||||
// PLAY = false;
|
||||
height = heightBox.value;
|
||||
@ -155,26 +188,42 @@ const onHeight = () => {
|
||||
}
|
||||
heightBox.onchange = onHeight;
|
||||
|
||||
const playCheck = document.getElementById("play-check");
|
||||
// BUTTONS
|
||||
|
||||
/**
|
||||
* Click handler for step button, make single move
|
||||
*/
|
||||
const onPlay = () => {
|
||||
console.log("play: " + playCheck.checked);
|
||||
if(playCheck.checked) {
|
||||
play = !play;
|
||||
|
||||
// console.log("play: " + play);
|
||||
if(play) {
|
||||
playButton.classList.remove("btn-success");
|
||||
playButton.classList.add("btn-danger");
|
||||
playButton.innerText = "Stop";
|
||||
start();
|
||||
}else {
|
||||
playButton.classList.add("btn-success");
|
||||
playButton.classList.remove("btn-danger");
|
||||
playButton.innerText = "Play";
|
||||
stop();
|
||||
}
|
||||
// PLAY = playCheck.checked;
|
||||
// requestAnimationFrame(renderLoop);
|
||||
}
|
||||
playCheck.onchange = onPlay;
|
||||
const playButton = document.getElementById("play");
|
||||
playButton.onclick = onPlay;
|
||||
|
||||
/**
|
||||
* Click handler for step button, make single move
|
||||
*/
|
||||
const onStep = () => {
|
||||
console.log("stepping");
|
||||
renderSingle();
|
||||
}
|
||||
document.getElementById("step").onclick = onStep;
|
||||
|
||||
|
||||
/**
|
||||
* Click handler for reset button, generate a new universe and refresh the canvas
|
||||
*/
|
||||
const onReset = () => {
|
||||
universe = Universe.new(width, height, randSlider.value, new Date().getTime() / 1000);
|
||||
refreshCanvas();
|
||||
@ -183,46 +232,3 @@ document.getElementById("reset").onclick = onReset;
|
||||
|
||||
drawGrid();
|
||||
drawCells();
|
||||
|
||||
const fps = new class {
|
||||
constructor() {
|
||||
this.fps = document.getElementById("fps");
|
||||
this.frames = [];
|
||||
this.lastFrameTimeStamp = performance.now();
|
||||
}
|
||||
|
||||
render() {
|
||||
// Convert the delta time since the last frame render into a measure
|
||||
// of frames per second.
|
||||
const now = performance.now();
|
||||
const delta = now - this.lastFrameTimeStamp;
|
||||
this.lastFrameTimeStamp = now;
|
||||
const fps = 1 / delta * 1000;
|
||||
|
||||
// Save only the latest 100 timings.
|
||||
this.frames.push(fps);
|
||||
if (this.frames.length > 100) {
|
||||
this.frames.shift();
|
||||
}
|
||||
|
||||
// Find the max, min, and mean of our 100 latest timings.
|
||||
let min = Infinity;
|
||||
let max = -Infinity;
|
||||
let sum = 0;
|
||||
for (let i = 0; i < this.frames.length; i++) {
|
||||
sum += this.frames[i];
|
||||
min = Math.min(this.frames[i], min);
|
||||
max = Math.max(this.frames[i], max);
|
||||
}
|
||||
let mean = sum / this.frames.length;
|
||||
|
||||
// Render the statistics.
|
||||
this.fps.textContent = `
|
||||
Frames per Second:
|
||||
latest = ${Math.round(fps)} //
|
||||
avg of last 100 = ${Math.round(mean)} //
|
||||
min of last 100 = ${Math.round(min)} //
|
||||
max of last 100 = ${Math.round(max)}
|
||||
`.trim();
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue
Block a user