Repository: anoopelias/boids
Branch: main
Commit: 98b68c1e46ec
Files: 24
Total size: 25.7 KB
Directory structure:
gitextract_3d267ym7/
├── .gitignore
├── .jshintrc
├── .vimrc
├── LICENSE.md
├── README.md
├── benchmark.html
├── index.html
├── js/
│ ├── benchmark.js
│ ├── boid.js
│ ├── bootstrap.js
│ ├── browser-benchmark.js
│ ├── demo.js
│ ├── index.js
│ ├── vector.js
│ └── wasm-boids.js
├── package.json
├── test/
│ ├── indexTest.js
│ └── vectorTest.js
├── wasm/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── src/
│ │ ├── lib.rs
│ │ └── utils.rs
│ └── tests/
│ └── web.rs
└── webpack.config.babel.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
bundle.js
node_modules
*.swp
dist
.idea
================================================
FILE: .jshintrc
================================================
{
"esversion": 6
}
================================================
FILE: .vimrc
================================================
set tabstop=2
set shiftwidth=2
set softtabstop=2
================================================
FILE: LICENSE.md
================================================
This software is released under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
# boids #
A lightweight JavaScript implementation of [boids](http://en.wikipedia.org/wiki/Boids).
This is a fork of [hughsk/boids](https://github.com/hughsk/boids) in terms of UI, and basic build structure. However the flocking algorithm is rewritten ground up.
[Check out the demo](http://anoopelias.github.io/boids)
================================================
FILE: benchmark.html
================================================
Benchmark Boids
Please check console for benchmark
================================================
FILE: index.html
================================================
Boids
boids
This is a lightweight 2D JavaScript implementation of Craig Reynolds'
Boids algorithm.
It's a classic example of emergence
and a suprisingly simple way of mimicking
not only flocks, but any form of swarm or herd or crowd.
Right now you're watching many boids being
simulated at 60 fps.
Source on GitHub
Fork of hughsk/boids
================================================
FILE: js/benchmark.js
================================================
import Boids from './index.js';
function benchmark(boids) {
function test(count, ticks) {
var b = boids({ boids: count }),
i = ticks,
start;
var warmup = 10000;
while (warmup--) b.tick();
start = +new Date();
while (i--) b.tick();
return ticks / ((new Date() - start) / 1000);
}
for (var i = 50; i <= 500; i += 50) {
console.log(i + " boids: " + ~~test(i, 5000) + " ticks/sec");
}
}
benchmark(Boids);
================================================
FILE: js/boid.js
================================================
export default function Boid(position, speed) {
this.position = position;
this.speed = speed;
}
Boid.prototype.compare = function(that, isEven) {
return this.position.compare(that.position, isEven);
};
Boid.prototype.toString = function() {
return this.position.toString();
};
================================================
FILE: js/bootstrap.js
================================================
/* jshint ignore:start */
// 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("./demo.js")
.catch(e => console.error("Error importing `index.js`:", e));
/* jshint ignore:end */
================================================
FILE: js/browser-benchmark.js
================================================
import Boids from './index.js';
function tickBenchmark() {
const boidNums = [150, 300, 450, 600, 750];
let numReports = [];
console.log("Running benchmark, please wait..");
for (let n of boidNums) {
const boids = Boids({ boids: n });
for (let i = 0; i < 1000; i++) {
const startTime = performance.now();
boids.tick();
(numReports[i] = numReports[i] || []).push(performance.now() - startTime);
}
}
let report = boidNums.map(boidNum => boidNum + " Boids").join(", ") + "\n";
report += numReports.map(numReport => numReport.join(", ")).join("\n");
console.log(report);
}
tickBenchmark();
================================================
FILE: js/demo.js
================================================
import fps from "fps";
import ticker from "ticker";
import debounce from "debounce";
import Boids from "./index.js";
import Vector from "./vector.js";
import Boid from "./boid.js";
const anchor = document.createElement("a"),
canvas = document.createElement("canvas"),
ctx = canvas.getContext("2d"),
boids = Boids();
canvas.addEventListener("click", function(e) {
let x = e.pageX,
y = e.pageY,
halfHeight = canvas.height / 2,
halfWidth = canvas.width / 2;
x = x - halfWidth;
y = y - halfHeight;
if (boids.boids.length < 500)
boids.boids.push(
new Boid(
new Vector(x, y),
new Vector(Math.random() * 6 - 3, Math.random() * 6 - 3)
)
);
});
window.onresize = debounce(function() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}, 100);
window.onresize();
anchor.setAttribute("href", "#");
anchor.appendChild(canvas);
document.body.style.margin = "0";
document.body.style.padding = "0";
document.body.appendChild(anchor);
ticker(window, 60)
.on("tick", function() {
frames.tick();
boids.tick();
})
.on("draw", function() {
const boidData = boids.boids,
halfHeight = canvas.height / 2,
halfWidth = canvas.width / 2;
ctx.fillStyle = "rgba(255,241,235,0.25)"; // '#FFF1EB'
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#543D5E";
for (let i = 0, l = boidData.length, x, y; i < l; i += 1) {
x = boidData[i].position.x;
y = boidData[i].position.y;
// wrap around the screen
boidData[i].position.x =
x > halfWidth ? -halfWidth : -x > halfWidth ? halfWidth : x;
boidData[i].position.y =
y > halfHeight ? -halfHeight : -y > halfHeight ? halfHeight : y;
ctx.fillRect(x + halfWidth, y + halfHeight, 2, 2);
}
});
const frameText = document.querySelector("[data-fps]");
const countText = document.querySelector("[data-count]");
const frames = fps({ every: 10, decay: 0.04 }).on("data", function(rate) {
for (let i = 0; i < 3; i += 1) {
if (rate <= 56 && boids.boids.length > 10) boids.boids.pop();
if (rate >= 60 && boids.boids.length < 300)
boids.boids.push(
new Boid(
new Vector(0, 0),
new Vector(Math.random() * 6 - 3, Math.random() * 6 - 3)
)
);
}
frameText.innerHTML = String(Math.round(rate));
countText.innerHTML = String(boids.boids.length);
});
================================================
FILE: js/index.js
================================================
import Vector from "./vector.js";
import Boid from "./boid.js";
export default function Boids(opts) {
if (!(this instanceof Boids)) return new Boids(opts);
opts = opts || {};
this.speedLimit = opts.speedLimit || 1;
this.accelerationLimit = opts.accelerationLimit || 0.03;
this.separationDistance = opts.separationDistance || 30;
this.separationDistanceSq = Math.pow(this.separationDistance, 2);
this.alignmentDistance = opts.alignmentDistance || 60;
this.alignmentDistanceSq = Math.pow(this.alignmentDistance, 2);
this.cohesionDistance = opts.cohesionDistance || 60;
this.cohesionDistanceSq = Math.pow(this.cohesionDistance, 2);
/* jshint laxbreak: true */
this.separationForce = !isNaN(opts.separationForce)
? opts.separationForce
: 2;
this.cohesionForce = !isNaN(opts.cohesionForce) ? opts.cohesionForce : 1;
this.alignmentForce = !isNaN(opts.alignmentForce) ? opts.alignmentForce : 1;
this.maxDistSq = Math.max(
this.separationDistanceSq,
this.cohesionDistanceSq,
this.alignmentDistanceSq
);
const boids = (this.boids = []);
for (
let i = 0, l = opts.boids === undefined ? 150 : opts.boids;
i < l;
i += 1
) {
boids[i] = new Boid(
new Vector(Math.random() * 100 - 50, Math.random() * 100 - 50),
new Vector(0, 0)
);
}
}
Boids.prototype.findNeighbors = function(point) {
const neighbors = [];
for (let i = 0; i < this.boids.length; i++) {
const boid = this.boids[i];
if (point === boid.position) {
continue;
}
const distSq = boid.position.distSquared(point);
if (distSq < this.maxDistSq) {
neighbors.push({
neighbor: this.boids[i],
distSq: distSq
});
}
}
return neighbors;
};
Boids.prototype.calcCohesion = function(boid, neighbors) {
let total = new Vector(0, 0),
count = 0;
for (let i = 0; i < neighbors.length; i++) {
const target = neighbors[i].neighbor;
if (boid === target) continue;
const distSq = neighbors[i].distSq;
if (
distSq < this.cohesionDistanceSq &&
isInFrontOf(boid, target.position)
) {
total = total.add(target.position);
count++;
}
}
if (count === 0) return new Vector(0, 0);
return total
.divideBy(count)
.subtract(boid.position)
.normalize()
.subtract(boid.speed)
.limit(this.accelerationLimit);
};
Boids.prototype.calcSeparation = function(boid, neighbors) {
let total = new Vector(0, 0),
count = 0;
for (let i = 0; i < neighbors.length; i++) {
const target = neighbors[i].neighbor;
if (boid === target) continue;
const distSq = neighbors[i].distSq;
if (distSq < this.separationDistanceSq) {
total = total.add(
target.position
.subtract(boid.position)
.normalize()
.divideBy(target.position.distance(boid.position))
);
count++;
}
}
if (count === 0) return new Vector(0, 0);
return total
.divideBy(count)
.normalize()
.add(boid.speed) // Adding speed instead of subtracting because separation is repulsive
.limit(this.accelerationLimit);
};
Boids.prototype.calcAlignment = function(boid, neighbors) {
let total = new Vector(0, 0),
count = 0;
for (let i = 0; i < neighbors.length; i++) {
const target = neighbors[i].neighbor;
if (boid === target) continue;
const distSq = neighbors[i].distSq;
if (
distSq < this.alignmentDistanceSq &&
isInFrontOf(boid, target.position)
) {
total = total.add(target.speed);
count++;
}
}
if (count === 0) return new Vector(0, 0);
return total
.divideBy(count)
.normalize()
.subtract(boid.speed)
.limit(this.accelerationLimit);
};
Boids.prototype.tick = function() {
const accelerations = [];
for (let i = 0; i < this.boids.length; i++) {
let boid = this.boids[i];
let neighbors = this.findNeighbors(boid.position);
const acceleration = this.calcCohesion(boid, neighbors)
.multiplyBy(this.cohesionForce)
.add(this.calcAlignment(boid, neighbors).multiplyBy(this.alignmentForce))
.subtract(this.calcSeparation(boid, neighbors).multiplyBy(this.separationForce));
accelerations.push(acceleration);
}
for (let j = 0; j < this.boids.length; j++) {
const boid = this.boids[j];
boid.speed = boid.speed.add(accelerations[j]).limit(this.speedLimit);
boid.position = boid.position.add(boid.speed);
}
};
function isInFrontOf(boid, point) {
return (
boid.position.angle(boid.position.add(boid.speed), point) <= Math.PI / 3
);
}
================================================
FILE: js/vector.js
================================================
function Vector(x, y) {
this.x = x;
this.y = y;
}
Vector.prototype.add = function(v) {
return new Vector(this.x + v.x, this.y + v.y);
};
Vector.prototype.distSquared = function(v) {
return Math.pow(this.x - v.x, 2) + Math.pow(this.y - v.y, 2);
};
Vector.prototype.distance = function(v) {
return Math.sqrt(this.distSquared(v));
};
Vector.prototype.multiplyBy = function(s) {
return new Vector(this.x * s, this.y * s);
};
Vector.prototype.neg = function(v) {
return new Vector(-this.x, -this.y);
};
Vector.prototype.magnitude = function() {
return this.distance(new Vector(0, 0));
};
Vector.prototype.normalize = function() {
const magnitude = this.magnitude();
if (magnitude === 0) return new Vector(0, 0);
return new Vector(this.x / magnitude, this.y / magnitude);
};
Vector.prototype.subtract = function(v) {
return this.add(v.neg());
};
Vector.prototype.divideBy = function(s) {
if (s === 0) return new Vector(0, 0);
return this.multiplyBy(1 / s);
};
Vector.prototype.limit = function(s) {
if (this.magnitude() > s) return this.normalize().multiplyBy(s);
return this;
};
Vector.prototype.angle = function(p1, p2) {
const v1 = this.subtract(p1).normalize(),
v2 = this.subtract(p2).normalize(),
// Rounding is because sometimes the value goes beyond 1.0
// due to floating point precision errors
cos = Math.round((v1.x * v2.x + v1.y * v2.y) * 10000) / 10000;
return Math.acos(cos);
};
Vector.prototype.compare = function(that, y) {
return (
(y && (this.y - that.y || this.x - that.x)) ||
(this.x - that.x || this.y - that.y)
);
};
Vector.prototype.toString = function() {
return "{x:" + this.x + ", y:" + this.y + "}";
};
export default Vector;
================================================
FILE: js/wasm-boids.js
================================================
import { Boids as WasmBoids } from '../wasm/pkg/wasm.js';
import { memory } from '../wasm/pkg/wasm_bg.wasm';
let wasmBoids = WasmBoids.new();
let boidsPtr = wasmBoids.get_boids();
const boids = new Float64Array(memory.buffer, boidsPtr, 4);
console.log(boids);
================================================
FILE: package.json
================================================
{
"name": "boids",
"description": "A fast JavaScript implementation of the boids algorithm ",
"version": "1.0.0",
"devDependencies": {
"@babel/core": "^7.4.0",
"@babel/preset-env": "7.23.9",
"@babel/register": "^7.23.7",
"babel-loader": "^9.1.3",
"debounce": "1.2.0",
"fps": "0.0.3",
"html-webpack-plugin": "^5.6.0",
"inherits": "~2.0.3",
"jshint": "^2.13.6",
"mocha": "^10.3.0",
"prettier": "^1.9.2",
"ticker": "0.1.0",
"webpack": "^5.90.3",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.0.2"
},
"main": "index.html",
"type": "module",
"scripts": {
"prettier": "prettier --write js/** test/**",
"test": "jshint js/ test/ && mocha test",
"start": "webpack-dev-server --port 3000",
"build-wasm-nodejs": "cd wasm && wasm-pack build --target nodejs",
"build-wasm": "cd wasm && wasm-pack build",
"build": "webpack",
"benchmark": "node js/benchmark.js"
},
"repository": {
"type": "git",
"url": "git://github.com/anoopelias/boids.git"
},
"keywords": [
"boids",
"flocking",
"algorithm",
"emergence"
],
"author": "Anoop Elias (http:/anoopelias.github.io/)",
"license": "MIT"
}
================================================
FILE: test/indexTest.js
================================================
import assert from 'assert';
import Boids from '../js/index.js';
import Vector from '../js/vector.js';
import Boid from '../js/boid.js';
function makeOptions(force) {
return {
speedLimit: 1,
accelerationLimit: 0.03,
separationDistance: 30,
alignmentDistance: 60,
cohesionDistance: 60,
separationForce: !isNaN(force && force[0]) ? force[0] : 2,
cohesionForce: !isNaN(force && force[1]) ? force[1] : 1,
alignmentForce: !isNaN(force && force[2]) ? force[2] : 2
};
}
function newBoid(posX, posY, velX, velY) {
return new Boid(new Vector(posX, posY), new Vector(velX, velY));
}
describe("Boid", function() {
describe("Separation", function() {
it("should slow down for a boid in front", function() {
const boids = new Boids(makeOptions([1, 0, 0]));
boids.boids = [newBoid(0, 0, 0.5, 0.5), newBoid(10, 10, 0, 0)];
boids.tick();
assertApprox(boids.boids[0].position.x, 0.4788);
assertApprox(boids.boids[0].position.y, 0.4788);
});
it("should ignore far away boids", function() {
const boids = new Boids(makeOptions([1, 0, 0]));
boids.boids = [newBoid(0, 0, 0.5, 0.5), newBoid(60, 60, 0, 0)];
boids.tick();
assert.equal(boids.boids[0].position.x, 0.5);
assert.equal(boids.boids[0].position.y, 0.5);
});
it("should speed up for boids behind", function() {
const boids = new Boids(makeOptions([1, 0, 0]));
boids.boids = [newBoid(0, 0, 0.5, 0.5), newBoid(-10, -10, 0, 0)];
boids.tick();
assertApprox(boids.boids[0].position.x, 0.5212);
assertApprox(boids.boids[0].position.y, 0.5212);
});
});
describe("Cohesion", function() {
it("should move towards the nearby boids", function() {
const boids = new Boids(makeOptions([0, 1, 0]));
boids.boids = [newBoid(0, 0, 0.5, 0.5), newBoid(10, 10, 0, 0)];
boids.tick();
assertApprox(boids.boids[0].position.x, 0.5212);
assertApprox(boids.boids[0].position.y, 0.5212);
});
it("should ignore far away boids", function() {
const boids = new Boids(makeOptions([0, 1, 0]));
boids.boids = [newBoid(0, 0, 0.5, 0.5), newBoid(60, 60, 1, 0)];
boids.tick();
assertApprox(boids.boids[0].position.x, 0.5);
assertApprox(boids.boids[0].position.y, 0.5);
});
it("should ignore boids behind", function() {
const boids = new Boids(makeOptions([0, 1, 0]));
boids.boids = [newBoid(0, 0, 0.5, 0.5), newBoid(-10, -10, 1, 1)];
boids.tick();
assertApprox(boids.boids[0].position.x, 0.5);
assertApprox(boids.boids[0].position.y, 0.5);
});
});
describe("Alignment", function() {
it("should align towards nearby boid", function() {
const boids = new Boids(makeOptions([0, 0, 1]));
boids.boids = [newBoid(0, 0, 0.5, 0.5), newBoid(10, 10, 0.5, 0)];
boids.tick();
assertApprox(boids.boids[0].speed.x, 0.5212);
assertApprox(boids.boids[0].speed.y, 0.4788);
});
it("should avoid far away boids", function() {
const boids = new Boids(makeOptions([0, 0, 1]));
boids.boids = [newBoid(0, 0, 0.5, 0.5), newBoid(60, 60, 0, 0)];
boids.tick();
assertApprox(boids.boids[0].speed.x, 0.5);
assertApprox(boids.boids[0].speed.y, 0.5);
});
it("should avoid boids behind", function() {
const boids = new Boids(makeOptions([0, 0, 1]));
boids.boids = [newBoid(0, 0, 0.5, 0.5), newBoid(-10, -10, 0.5, 0)];
boids.tick();
assertApprox(boids.boids[0].speed.x, 0.5);
assertApprox(boids.boids[0].speed.y, 0.5);
});
});
it("should tick", function() {
const boids = new Boids(makeOptions());
const boid1 = newBoid(0, 0, 0.5, 0.5),
boid2 = newBoid(10, 10, 0, 0),
boid3 = newBoid(60, 60, 0, 0),
boid4 = newBoid(-10, -10, 0, 0);
boids.boids = [boid1, boid2, boid3, boid4];
boids.tick();
assertBoid(boid1, [0.4364, 0.4364, 0.4364, 0.4364]);
assertBoid(boid2, [10.0424, 10.0424, 0.0424, 0.0424]);
assertBoid(boid3, [60, 60, 0, 0]);
assertBoid(boid4, [-10.0424, -10.0424, -0.0424, -0.0424]);
boids.tick();
assertBoid(boid1, [0.8939, 0.8939, 0.4576, 0.4576]);
assertBoid(boid2, [10.1273, 10.1273, 0.0849, 0.0849]);
assertBoid(boid3, [60, 60, 0, 0]);
assertBoid(boid4, [-10.1273, -10.1273, -0.0849, -0.0849]);
boids.tick();
assertBoid(boid1, [1.3727, 1.3727, 0.4788, 0.4788]);
assertBoid(boid2, [10.2546, 10.2546, 0.1273, 0.1273]);
assertBoid(boid3, [60, 60, 0, 0]);
assertBoid(boid4, [-10.2546, -10.2546, -0.1273, -0.1273]);
boids.tick();
assertBoid(boid1, [1.8727, 1.8727, 0.5, 0.5]);
assertBoid(boid2, [10.4243, 10.4243, 0.1697, 0.1697]);
assertBoid(boid3, [60, 60, 0, 0]);
assertBoid(boid4, [-10.4243, -10.4243, -0.1697, -0.1697]);
});
function assertBoid(boid, val) {
assertApprox(boid.position.x, val[0]);
assertApprox(boid.position.y, val[1]);
assertApprox(boid.speed.x, val[2]);
assertApprox(boid.speed.y, val[3]);
}
function assertApprox(val, val2) {
assert.equal(val.toFixed(4), val2.toFixed(4));
}
});
================================================
FILE: test/vectorTest.js
================================================
import assert from 'assert';
import Vector from '../js/vector.js';
describe("Vector", function() {
it("should compare on x", function() {
let c = new Vector(5, 19).compare(new Vector(10, 16), false);
assert(c < 0);
c = new Vector(5, 15).compare(new Vector(1, 25), false);
assert(c > 0);
});
it("should compare on y if x is equal", function() {
let c = new Vector(5, 15).compare(new Vector(5, 25), false);
assert(c < 0);
c = new Vector(5, 45).compare(new Vector(5, 25), false);
assert(c > 0);
});
it("should compare on x by default", function() {
let c = new Vector(5, 19).compare(new Vector(10, 16));
assert(c < 0);
c = new Vector(5, 15).compare(new Vector(1, 25));
assert(c > 0);
});
it("should compare on y", function() {
let c = new Vector(5, 19).compare(new Vector(10, 16), true);
assert(c > 0);
c = new Vector(5, 15).compare(new Vector(1, 25), true);
assert(c < 0);
});
it("should compare on x if y is equal", function() {
let c = new Vector(5, 19).compare(new Vector(10, 19), true);
assert(c < 0);
c = new Vector(5, 15).compare(new Vector(1, 15), true);
assert(c > 0);
});
it("should return zero if both x and y are equal", function() {
let c = new Vector(5, 19).compare(new Vector(5, 19), true);
assert(c === 0);
c = new Vector(5, 45).compare(new Vector(5, 45), false);
assert(c === 0);
});
});
================================================
FILE: wasm/.gitignore
================================================
/target
**/*.rs.bk
Cargo.lock
bin/
pkg/
wasm-pack.log
================================================
FILE: wasm/Cargo.toml
================================================
[package]
name = "wasm"
version = "0.1.0"
authors = ["Anoop Elias "]
edition = "2018"
[lib]
crate-type = ["cdylib", "rlib"]
[features]
default = ["console_error_panic_hook"]
[dependencies]
cfg-if = "0.1.2"
wasm-bindgen = "0.2.84"
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
console_error_panic_hook = { version = "0.1.7", optional = true }
[dev-dependencies]
wasm-bindgen-test = "0.3.34"
[dependencies.web-sys]
version = "0.3"
features = [
"console",
]
[profile.release]
# Tell `rustc` to optimize for small code size.
opt-level = "s"
================================================
FILE: wasm/src/lib.rs
================================================
extern crate wasm_bindgen;
extern crate web_sys;
mod utils;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
pub struct Boid {
pos_x: f64,
pos_y: f64,
vel_x: f64,
vel_y: f64,
}
#[wasm_bindgen]
pub struct Boids {
boids: Vec,
}
impl Boid {
fn new() -> Boid {
Boid {
pos_x: 20.5,
pos_y: 40.2,
vel_x: 50.9,
vel_y: 51.9,
}
}
}
#[wasm_bindgen]
impl Boids {
pub fn new() -> Boids {
let boids = vec![Boid::new(), Boid::new()];
Boids { boids }
}
pub fn get_boids(&self) -> *const Boid {
self.boids.as_ptr()
}
}
================================================
FILE: wasm/src/utils.rs
================================================
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
// we will get better error messages if our code ever panics.
//
// For more details see
// https://github.com/rustwasm/console_error_panic_hook#readme
#[cfg(feature = "console_error_panic_hook")]
console_error_panic_hook::set_once();
}
================================================
FILE: wasm/tests/web.rs
================================================
//! Test suite for the Web and headless browsers.
#![cfg(target_arch = "wasm32")]
extern crate wasm_bindgen_test;
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn pass() {
assert_eq!(1 + 1, 2);
}
================================================
FILE: webpack.config.babel.js
================================================
import path from 'path';
import { fileURLToPath } from 'url'; // Import fileURLToPath
import HtmlWebpackPlugin from 'html-webpack-plugin';
// Derive __dirname using import.meta.url
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export default {
entry: {
bundle: './js/bootstrap.js',
browserBenchmark: './js/browser-benchmark.js',
},
experiments: {
asyncWebAssembly: true,
},
mode: 'development',
module: {
rules: [
{
test: /\.js$/,
// Use the derived __dirname
include: path.resolve(__dirname, 'js'),
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', { targets: "defaults" }]
]
}
}
}
]
},
output: {
clean: true,
},
plugins: [
new HtmlWebpackPlugin({
template: 'index.html',
favicon: 'favicon.ico',
chunks: ['bundle'],
}),
new HtmlWebpackPlugin({
filename: 'benchmark.html',
template: 'benchmark.html',
chunks: ['browserBenchmark'],
}),
]
};