Nice visualisation based on sandpiles
This commit is contained in:
parent
a37b87166c
commit
a000624a7e
12 changed files with 13377 additions and 4 deletions
|
@ -12,6 +12,7 @@ default = ["console_error_panic_hook"]
|
|||
|
||||
[dependencies]
|
||||
wasm-bindgen = "0.2.63"
|
||||
js-sys = "0.3"
|
||||
|
||||
# The `console_error_panic_hook` crate provides better debugging of panics by
|
||||
# logging them with `console.error`. This is great for development, but requires
|
||||
|
@ -32,3 +33,6 @@ wasm-bindgen-test = "0.3.13"
|
|||
[profile.release]
|
||||
# Tell `rustc` to optimize for small code size.
|
||||
opt-level = "s"
|
||||
|
||||
[package.metadata.wasm-pack.profile.release]
|
||||
wasm-opt = ["-Oz", "--enable-mutable-globals"]
|
||||
|
|
24
README.md
Normal file
24
README.md
Normal file
|
@ -0,0 +1,24 @@
|
|||
rust-wasm
|
||||
=========
|
||||
|
||||
Playground for rust stuff in the browser.
|
||||
Currently it is a nice visualisation.
|
||||
|
||||
## Build
|
||||
|
||||
```
|
||||
wasm-pack build
|
||||
cd www
|
||||
npm install
|
||||
```
|
||||
|
||||
## Run
|
||||
|
||||
```
|
||||
cd www
|
||||
npm run start
|
||||
```
|
||||
|
||||
This command automatically watches the files, so you can build
|
||||
rust stuff and it will automatically appear.
|
||||
|
112
src/lib.rs
112
src/lib.rs
|
@ -1,6 +1,7 @@
|
|||
mod utils;
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
extern crate js_sys;
|
||||
|
||||
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
|
||||
// allocator.
|
||||
|
@ -8,12 +9,115 @@ use wasm_bindgen::prelude::*;
|
|||
#[global_allocator]
|
||||
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
||||
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern {
|
||||
fn alert(s: &str);
|
||||
pub struct Universe {
|
||||
width: i32,
|
||||
height: i32,
|
||||
stress: i32,
|
||||
|
||||
cells: Vec<u8>,
|
||||
base: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Universe {
|
||||
fn get_index(&self, row: i32, col: i32) -> usize {
|
||||
(row * self.width + col) as usize
|
||||
}
|
||||
|
||||
fn in_bounds(&self, row: i32, col: i32) -> bool {
|
||||
0 <= row && row < self.height && 0 <= col && col < self.width
|
||||
}
|
||||
|
||||
fn incr_cell(&mut self, row: i32, col: i32) {
|
||||
if !self.in_bounds(row, col) { return; }
|
||||
|
||||
let idx = self.get_index(row, col);
|
||||
self.base[idx] += 1;
|
||||
|
||||
if self.base[idx] >= 4 {
|
||||
self.cells[idx] = 100;
|
||||
self.base[idx] = 0;
|
||||
for dc in [-1, 1].iter() {
|
||||
let col2 = col + dc;
|
||||
self.incr_cell(row, col2);
|
||||
}
|
||||
for dr in [-1, 1].iter() {
|
||||
let row2 = row + dr;
|
||||
self.incr_cell(row2, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn incr_random_cell(&mut self) {
|
||||
let row = (js_sys::Math::random() * self.height as f64) as i32;
|
||||
let col = (js_sys::Math::random() * self.width as f64) as i32;
|
||||
let idx = self.get_index(row, col);
|
||||
let count = self.base[idx];
|
||||
|
||||
if js_sys::Math::random() < 1.0 / (count + 1) as f64 {
|
||||
self.incr_cell(row, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn greet() {
|
||||
alert("Hello, rust-wasm!");
|
||||
impl Universe {
|
||||
pub fn tick(&mut self) {
|
||||
self.stress = 0;
|
||||
|
||||
for _i in 1..2 {
|
||||
self.incr_random_cell();
|
||||
}
|
||||
|
||||
for row in 0..self.height {
|
||||
for col in 0..self.width {
|
||||
let idx = self.get_index(row, col);
|
||||
|
||||
self.stress += self.base[idx] as i32;
|
||||
|
||||
if self.cells[idx] > 0 {
|
||||
self.cells[idx] -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> Universe {
|
||||
let width = 180;
|
||||
let height = 81;
|
||||
let stress = 0;
|
||||
|
||||
let cells = (0..width * height)
|
||||
.map(|_i| 0)
|
||||
.collect();
|
||||
|
||||
let base = (0..width * height)
|
||||
.map(|_i| 0)
|
||||
.collect();
|
||||
|
||||
Universe {width, height, stress, cells, base}
|
||||
}
|
||||
|
||||
pub fn init(&mut self) {
|
||||
for _i in 1..50000 {
|
||||
self.incr_random_cell();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn width(&self) -> i32 {
|
||||
self.width
|
||||
}
|
||||
|
||||
pub fn height(&self) -> i32 {
|
||||
self.height
|
||||
}
|
||||
|
||||
pub fn stress(&self) -> i32 {
|
||||
self.stress
|
||||
}
|
||||
|
||||
pub fn cells(&self) -> *const u8 {
|
||||
self.cells.as_ptr()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#[allow(dead_code)]
|
||||
pub fn set_panic_hook() {
|
||||
// When the `console_error_panic_hook` feature is enabled, we can call the
|
||||
// `set_panic_hook` function at least once during initialization, and then
|
||||
|
|
24
www/.bin/create-wasm-app.js
Executable file
24
www/.bin/create-wasm-app.js
Executable file
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const { spawn } = require("child_process");
|
||||
const fs = require("fs");
|
||||
|
||||
let folderName = '.';
|
||||
|
||||
if (process.argv.length >= 3) {
|
||||
folderName = process.argv[2];
|
||||
if (!fs.existsSync(folderName)) {
|
||||
fs.mkdirSync(folderName);
|
||||
}
|
||||
}
|
||||
|
||||
const clone = spawn("git", ["clone", "https://github.com/rustwasm/create-wasm-app.git", folderName]);
|
||||
|
||||
clone.on("close", code => {
|
||||
if (code !== 0) {
|
||||
console.error("cloning the template failed!")
|
||||
process.exit(code);
|
||||
} else {
|
||||
console.log("🦀 Rust + 🕸 Wasm = ❤");
|
||||
}
|
||||
});
|
2
www/.gitignore
vendored
Normal file
2
www/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
node_modules
|
||||
dist
|
5
www/bootstrap.js
vendored
Normal file
5
www/bootstrap.js
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
// A dependency graph that contains any wasm must all be imported
|
||||
// asynchronously. This `bootstrap.js` file does the single async import, so
|
||||
// that no one else needs to worry about it again.
|
||||
import("./index.js")
|
||||
.catch(e => console.error("Error importing `index.js`:", e));
|
27
www/index.html
Normal file
27
www/index.html
Normal file
|
@ -0,0 +1,27 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Hello wasm-pack!</title>
|
||||
<style>
|
||||
body {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="game-of-life-canvas"></canvas>
|
||||
<div style="text-align: center;">chaos: <span id="chaos"></span>, max: <span id="max"></span>, mv avg: <span id="avg"></span></div>
|
||||
|
||||
<noscript>This page contains webassembly and javascript content, please enable javascript in your browser.</noscript>
|
||||
<script src="./bootstrap.js"></script>
|
||||
</body>
|
||||
</html>
|
94
www/index.js
Normal file
94
www/index.js
Normal file
|
@ -0,0 +1,94 @@
|
|||
import { Universe, Cell } from "rust-wasm";
|
||||
import { memory } from "rust-wasm/rust_wasm_bg";
|
||||
|
||||
const CELL_SIZE = 5; // px
|
||||
const GRID_COLOR = "#CCCCCC";
|
||||
const COLOURS = ["#FFF", "#FF8", "#FF0", "#F80", "#F00", "#800", "#000"];
|
||||
|
||||
// Construct the universe, and get its width and height.
|
||||
const universe = Universe.new();
|
||||
universe.init();
|
||||
const width = universe.width();
|
||||
const height = universe.height();
|
||||
|
||||
// Give the canvas room for all of our cells and a 1px border
|
||||
// around each of them.
|
||||
const infochaos = document.getElementById("chaos");
|
||||
const infomax = document.getElementById("max");
|
||||
const infoavg = document.getElementById("avg");
|
||||
var maxstress = 0;
|
||||
var avgstress = 31000;
|
||||
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');
|
||||
|
||||
const renderLoop = () => {
|
||||
universe.tick();
|
||||
|
||||
//drawGrid();
|
||||
drawCells();
|
||||
|
||||
const st = universe.stress();
|
||||
if (st > maxstress) maxstress = st;
|
||||
avgstress = 0.999 * avgstress + 0.001 * st
|
||||
infochaos.innerHTML = st;
|
||||
infomax.innerHTML = maxstress;
|
||||
infoavg.innerHTML = Math.round(avgstress);
|
||||
|
||||
// setTimeout(renderLoop, 200);
|
||||
requestAnimationFrame(renderLoop);
|
||||
};
|
||||
|
||||
// setTimeout(renderLoop, 1);
|
||||
requestAnimationFrame(renderLoop);
|
||||
|
||||
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();
|
||||
};
|
||||
|
||||
const getIndex = (row, column) => {
|
||||
return row * width + column;
|
||||
};
|
||||
|
||||
const drawCells = () => {
|
||||
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 = getIndex(row, col);
|
||||
const rat = Math.round(COLOURS.length * cells[idx] / 101);
|
||||
const colour = COLOURS[rat];
|
||||
|
||||
ctx.fillStyle = colour;
|
||||
|
||||
ctx.fillRect(
|
||||
col * (CELL_SIZE + 1) + 1,
|
||||
row * (CELL_SIZE + 1) + 1,
|
||||
CELL_SIZE,
|
||||
CELL_SIZE
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ctx.stroke();
|
||||
};
|
13051
www/package-lock.json
generated
Normal file
13051
www/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
23
www/package.json
Normal file
23
www/package.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"name": "create-wasm-app",
|
||||
"version": "0.1.0",
|
||||
"description": "create an app to consume rust-generated wasm packages",
|
||||
"main": "index.js",
|
||||
"bin": {
|
||||
"create-wasm-app": ".bin/create-wasm-app.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "webpack --config webpack.config.js",
|
||||
"start": "webpack-dev-server"
|
||||
},
|
||||
"dependencies": {
|
||||
"rust-wasm": "file:../pkg"
|
||||
},
|
||||
"devDependencies": {
|
||||
"copy-webpack-plugin": "^5.0.0",
|
||||
"hello-wasm-pack": "^0.1.0",
|
||||
"webpack": "^4.29.3",
|
||||
"webpack-cli": "^3.1.0",
|
||||
"webpack-dev-server": "^3.1.5"
|
||||
}
|
||||
}
|
14
www/webpack.config.js
Normal file
14
www/webpack.config.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
const CopyWebpackPlugin = require("copy-webpack-plugin");
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: "./bootstrap.js",
|
||||
output: {
|
||||
path: path.resolve(__dirname, "dist"),
|
||||
filename: "bootstrap.js",
|
||||
},
|
||||
mode: "development",
|
||||
plugins: [
|
||||
new CopyWebpackPlugin(['index.html'])
|
||||
],
|
||||
};
|
Loading…
Add table
Reference in a new issue