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]
|
[dependencies]
|
||||||
wasm-bindgen = "0.2.63"
|
wasm-bindgen = "0.2.63"
|
||||||
|
js-sys = "0.3"
|
||||||
|
|
||||||
# The `console_error_panic_hook` crate provides better debugging of panics by
|
# The `console_error_panic_hook` crate provides better debugging of panics by
|
||||||
# logging them with `console.error`. This is great for development, but requires
|
# logging them with `console.error`. This is great for development, but requires
|
||||||
|
@ -32,3 +33,6 @@ wasm-bindgen-test = "0.3.13"
|
||||||
[profile.release]
|
[profile.release]
|
||||||
# Tell `rustc` to optimize for small code size.
|
# Tell `rustc` to optimize for small code size.
|
||||||
opt-level = "s"
|
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;
|
mod utils;
|
||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
extern crate js_sys;
|
||||||
|
|
||||||
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
|
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
|
||||||
// allocator.
|
// allocator.
|
||||||
|
@ -8,12 +9,115 @@ use wasm_bindgen::prelude::*;
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
||||||
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
extern {
|
pub struct Universe {
|
||||||
fn alert(s: &str);
|
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]
|
#[wasm_bindgen]
|
||||||
pub fn greet() {
|
impl Universe {
|
||||||
alert("Hello, rust-wasm!");
|
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() {
|
pub fn set_panic_hook() {
|
||||||
// When the `console_error_panic_hook` feature is enabled, we can call the
|
// When the `console_error_panic_hook` feature is enabled, we can call the
|
||||||
// `set_panic_hook` function at least once during initialization, and then
|
// `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