aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbrian cully <bjc@spork.org>2025-12-26 12:04:14 -0500
committerbrian cully <bjc@spork.org>2025-12-26 12:04:14 -0500
commitfa3067a16f8ba06ee756eb2b2293be4cb728681d (patch)
tree41b5ab1e76e12be764d4f6481febe49862bf8ea1
downloadpolyring-fa3067a16f8ba06ee756eb2b2293be4cb728681d.tar.gz
polyring-fa3067a16f8ba06ee756eb2b2293be4cb728681d.zip
add bouncing particles
-rw-r--r--.dir-locals.el10
-rw-r--r--.envrc1
-rw-r--r--.gitignore2
-rw-r--r--Cargo.lock166
-rw-r--r--Cargo.toml18
-rw-r--r--LICENSE5
-rw-r--r--Makefile13
-rw-r--r--shell.nix31
-rw-r--r--site/favicon.icobin0 -> 1258 bytes
-rw-r--r--site/index.html25
-rw-r--r--site/main.css11
-rw-r--r--site/main.mjs140
-rw-r--r--src/lib.rs11
13 files changed, 433 insertions, 0 deletions
diff --git a/.dir-locals.el b/.dir-locals.el
new file mode 100644
index 0000000..058923b
--- /dev/null
+++ b/.dir-locals.el
@@ -0,0 +1,10 @@
+;;; Directory Local Variables -*- no-byte-compile: t -*-
+;;; For more information see (info "(emacs) Directory Variables")
+
+((nil . ((compile-command . "make serve")
+ ;; https://rust-analyzer.github.io/book/configuration.html
+ (eglot-workspace-configuration
+ . (:rust-analyzer ( :typing (:triggerChars ".=()<>"))))))
+ (js-base-mode . ((js-indent-level . 4)
+ (js-chain-indent . t)
+ (js-indent-first-init . dynamic))))
diff --git a/.envrc b/.envrc
new file mode 100644
index 0000000..1d953f4
--- /dev/null
+++ b/.envrc
@@ -0,0 +1 @@
+use nix
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ce39741
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/target
+/site/wasm
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..1b38286
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,166 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "bumpalo"
+version = "3.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
+
+[[package]]
+name = "console_log"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be8aed40e4edbf4d3b4431ab260b63fdc40f5780a4766824329ea0f1eefe3c0f"
+dependencies = [
+ "log",
+ "web-sys",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
+dependencies = [
+ "once_cell",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "log"
+version = "0.4.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
+
+[[package]]
+name = "once_cell"
+version = "1.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
+
+[[package]]
+name = "polyring"
+version = "0.1.0"
+dependencies = [
+ "console_log",
+ "log",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
+
+[[package]]
+name = "syn"
+version = "2.0.104"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
+dependencies = [
+ "bumpalo",
+ "log",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..c17179b
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "polyring"
+version = "0.1.0"
+edition = "2024"
+license-file = "LICENSE"
+
+[lib]
+# cdylib for wasm, rlib for integration tests
+crate-type = ["cdylib", "rlib"]
+
+[dependencies]
+console_log = "1.0"
+log = "0.4"
+wasm-bindgen = "0.2"
+
+[dependencies.web-sys]
+features = ["Document", "Element", "HtmlElement", "Node", "Window"]
+version = "0.3"
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..944f969
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,5 @@
+this work is granted to the public domain wherever possible. where it
+is not possible, this work is licensed under the terms of the GNU
+Affero General Public License version 3¹
+
+¹ https://www.gnu.org/licenses/agpl-3.0.html
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..5d17ff0
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,13 @@
+BUILDDIR=site
+HOST=coleridge:public_html/automathon
+
+# the --target is currently necessary or firefox will return
+# "disallowed mime type" -bjc 7-aug-2025
+build:
+ wasm-pack build --target web --out-dir $(BUILDDIR)/wasm
+
+serve: build
+ (cd $(BUILDDIR) && python -m http.server 8118)
+
+deploy: build
+ rsync -avz --delete --exclude='*~' "$(BUILDDIR)"/ "$(HOST)"
diff --git a/shell.nix b/shell.nix
new file mode 100644
index 0000000..3d960fa
--- /dev/null
+++ b/shell.nix
@@ -0,0 +1,31 @@
+{ pkgs ? import <nixpkgs> {} }:
+
+pkgs.mkShellNoCC {
+ packages = with pkgs; [
+ rustc
+ clang # yes, it's necessary or ‘cc’ can't be found. -bjc 2025-aug-7
+ lld # ibid.
+ cargo
+ wasm-pack
+ rust-analyzer
+ clippy
+ rustfmt
+ # the only thing better than needing cargo's infinite dependencies
+ # is needing npm's as well, just so we can use a bundler built for
+ # another, wildly different, registry.
+ nodePackages.npm
+
+ typescript-language-server
+ vscode-langservers-extracted
+
+ # for http.server
+ python3
+
+ # testing scheme wasm stuff
+ guile
+ guile-hoot
+ ];
+
+ TMPDIR = "/tmp"; # javascript lsp needs it
+ CARGO_HOME = "/data/bjc/cargo";
+}
diff --git a/site/favicon.ico b/site/favicon.ico
new file mode 100644
index 0000000..73de524
--- /dev/null
+++ b/site/favicon.ico
Binary files differ
diff --git a/site/index.html b/site/index.html
new file mode 100644
index 0000000..012e301
--- /dev/null
+++ b/site/index.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html lang='en-US'>
+ <head>
+ <meta charset='utf-8'>
+ <title>polyring</title>
+ <link rel='stylesheet' href='main.css'>
+ <meta name='viewport' content='width=device-width'>
+ </head>
+
+ <body>
+ <h1>polyring</h1>
+ <p>benchmarking maximal convex polygon finding</p>
+
+ <button>go</button>
+ <span>fps: <span id='fps'>n/a</span></span>
+ <br>
+ <canvas width='500' height='500'></canvas>
+
+ <script src='./main.mjs' type='module'></script>
+
+ <footer>
+ <address><a href='https://git.spork.org/polyring.git'>src</a></address>
+ </footer>
+ </body>
+</html>
diff --git a/site/main.css b/site/main.css
new file mode 100644
index 0000000..f875232
--- /dev/null
+++ b/site/main.css
@@ -0,0 +1,11 @@
+html {
+ box-sizing: border-box;
+}
+*, *::before, *::after {
+ box-sizing: inherit;
+}
+
+canvas {
+ border: 1px solid orangered;
+ background-color: rgb(200 200 200);
+}
diff --git a/site/main.mjs b/site/main.mjs
new file mode 100644
index 0000000..0c81b41
--- /dev/null
+++ b/site/main.mjs
@@ -0,0 +1,140 @@
+const NUM_POINTS = 5;
+
+// thanks vihart
+const TAU = 2 * Math.PI;
+
+// no more than 1% of world size per tick.
+const MAX_SPEED = 0.01;
+
+// size of rendered points in proportion to canvas.
+const POINT_RADIUS = 0.01;
+
+class Point {
+ x = Math.random();
+ y = Math.random();
+ #heading = Math.random() * TAU;
+ speed = Math.random() * MAX_SPEED;
+ color = randColor();
+
+ #speedX;
+ get speedX() {
+ if (this.#speedX === undefined) {
+ this.#speedX = this.speed * Math.cos(this.heading);
+ }
+ return this.#speedX;
+ }
+
+ #speedY;
+ get speedY() {
+ if (this.#speedY === undefined) {
+ this.#speedY = this.speed * Math.sin(this.heading);
+ }
+ return this.#speedY;
+ }
+
+ get heading() {
+ return this.#heading;
+ }
+
+ set heading(val) {
+ this.#heading = val;
+ this.#speedX = this.#speedY = undefined;
+ }
+}
+
+const points = [...Array(NUM_POINTS)].map(_ => new Point());
+
+function randColor() {
+ const [r, g, b] = [...Array(3)].map(_ => Math.random() * 255);
+ return `rgb(${r} ${g} ${b})`
+}
+
+function renderPoints(ctx, points) {
+ points.forEach(p => {
+ ctx.fillStyle = p.color;
+ ctx.beginPath();
+ ctx.arc(p.x, p.y, POINT_RADIUS, 0, TAU);
+ ctx.fill();
+
+ ctx.lineWidth = 0.005;
+ ctx.beginPath();
+ ctx.moveTo(p.x, p.y);
+ ctx.lineTo(p.x + p.speedX * 3, p.y + p.speedY * 3);
+ ctx.stroke();
+ });
+}
+
+function movePoints(points) {
+ points.forEach(p => {
+ p.x += p.speedX;
+ p.y += p.speedY;
+ });
+}
+
+function bouncePoints(points) {
+ let didBounce = false;
+ points.forEach(p => {
+ const x = p.x + p.speedX;
+ const y = p.y + p.speedY;
+
+ if (x < 0) {
+ p.heading = Math.PI - p.heading;
+ didBounce = true;
+ } else if (x >= 1) {
+ p.heading = Math.PI - p.heading;
+ didBounce = true;
+ } else if (y < 0) {
+ p.heading = -1 * p.heading;
+ didBounce = true;
+ } else if (y >= 1) {
+ p.heading = -1 * p.heading;
+ didBounce = true;
+ }
+ });
+ return didBounce;
+}
+
+async function loaded() {
+ const canvas = document.querySelector('canvas');
+ const ctx = canvas.getContext('2d');
+ console.debug('canvas:', canvas, 'ctx', ctx, 'points:', points);
+ ctx.scale(canvas.width, canvas.height);
+
+ const fps = document.querySelector('#fps');
+
+ const goButton = document.querySelector('button');
+ let paused = true;
+ goButton.onclick = e => {
+ paused = !paused;
+ if (paused) {
+ e.target.textContent = 'go';
+ } else {
+ e.target.textContent = 'pause';
+ self.requestAnimationFrame(render);
+ }
+ };
+
+ let lastTime = document.timeline.currentTime;
+ function render(t) {
+ if (t > lastTime) {
+ fps.textContent = Math.floor(1_000 / (t - lastTime));
+ lastTime = t;
+ }
+
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ renderPoints(ctx, points);
+ if (bouncePoints(points)) {
+ //goButton.onclick({ target: goButton });
+ }
+ movePoints(points);
+
+ if (!paused) {
+ self.requestAnimationFrame(render);
+ }
+ }
+
+ //goButton.onclick({ target: goButton });
+ render(document.timeline.currentTime);
+}
+
+document.addEventListener('DOMContentLoaded', loaded);
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..66bafe7
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,11 @@
+use log::{Level, error, info};
+use wasm_bindgen::prelude::*;
+use web_sys::js_sys;
+
+#[wasm_bindgen(start)]
+pub fn init() -> Result<(), JsValue> {
+ console_log::init_with_level(Level::Debug).expect("couldn't init console log");
+ info!("wasm init");
+
+ Ok(())
+}