aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock78
-rw-r--r--Cargo.toml3
-rw-r--r--site/index.html3
-rw-r--r--site/pure.mjs (renamed from site/main.mjs)3
-rw-r--r--site/wasm.mjs7
-rw-r--r--src/lib.rs62
-rw-r--r--src/line.rs22
-rw-r--r--src/point.rs50
-rw-r--r--src/state.rs190
9 files changed, 371 insertions, 47 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 1b38286..0051145 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4,15 +4,15 @@ version = 4
[[package]]
name = "bumpalo"
-version = "3.19.0"
+version = "3.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
+checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
[[package]]
name = "cfg-if"
-version = "1.0.1"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "console_log"
@@ -25,10 +25,16 @@ dependencies = [
]
[[package]]
+name = "fastrand"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
+
+[[package]]
name = "js-sys"
-version = "0.3.77"
+version = "0.3.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
+checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8"
dependencies = [
"once_cell",
"wasm-bindgen",
@@ -36,9 +42,9 @@ dependencies = [
[[package]]
name = "log"
-version = "0.4.27"
+version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
+checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "once_cell"
@@ -51,6 +57,7 @@ name = "polyring"
version = "0.1.0"
dependencies = [
"console_log",
+ "fastrand",
"log",
"wasm-bindgen",
"web-sys",
@@ -58,33 +65,33 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.95"
+version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
+checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.40"
+version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
+checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustversion"
-version = "1.0.21"
+version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "syn"
-version = "2.0.104"
+version = "2.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
+checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
dependencies = [
"proc-macro2",
"quote",
@@ -93,41 +100,28 @@ dependencies = [
[[package]]
name = "unicode-ident"
-version = "1.0.18"
+version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
+checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "wasm-bindgen"
-version = "0.2.100"
+version = "0.2.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
+checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd"
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"
+version = "0.2.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
+checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -135,31 +129,31 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.100"
+version = "0.2.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
+checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40"
dependencies = [
+ "bumpalo",
"proc-macro2",
"quote",
"syn",
- "wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.100"
+version = "0.2.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
+checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4"
dependencies = [
"unicode-ident",
]
[[package]]
name = "web-sys"
-version = "0.3.77"
+version = "0.3.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
+checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac"
dependencies = [
"js-sys",
"wasm-bindgen",
diff --git a/Cargo.toml b/Cargo.toml
index c17179b..84380f9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,7 +12,8 @@ crate-type = ["cdylib", "rlib"]
console_log = "1.0"
log = "0.4"
wasm-bindgen = "0.2"
+fastrand = "2.3"
[dependencies.web-sys]
-features = ["Document", "Element", "HtmlElement", "Node", "Window"]
+features = ["Document", "DocumentTimeline", "Element", "HtmlElement", "HtmlCanvasElement", "CanvasRenderingContext2d", "Node", "Window"]
version = "0.3"
diff --git a/site/index.html b/site/index.html
index 012e301..bdb2f5d 100644
--- a/site/index.html
+++ b/site/index.html
@@ -16,7 +16,8 @@
<br>
<canvas width='500' height='500'></canvas>
- <script src='./main.mjs' type='module'></script>
+ <!-- <script src='./pure.mjs' type='module'></script> -->
+ <script src='./wasm.mjs' type='module'></script>
<footer>
<address><a href='https://git.spork.org/polyring.git'>src</a></address>
diff --git a/site/main.mjs b/site/pure.mjs
index dc67d73..43f7213 100644
--- a/site/main.mjs
+++ b/site/pure.mjs
@@ -112,7 +112,7 @@ function findPoly3(points) {
let res = [points[0]];
do {
// last result is always a point on the edge.
- const p1 = res[res.length-1];
+ // const p1 = res[res.length-1];
// console.debug(`-- checking lines from ${p1}`);
let last = res[res.length-1];
let minTheta = TAU;
@@ -233,6 +233,7 @@ async function loaded() {
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
+
renderPoints(ctx, points);
if (bouncePoints(points)) {
//goButton.onclick({ target: goButton });
diff --git a/site/wasm.mjs b/site/wasm.mjs
new file mode 100644
index 0000000..29a2741
--- /dev/null
+++ b/site/wasm.mjs
@@ -0,0 +1,7 @@
+import init from './wasm/polyring.js';
+
+async function loaded() {
+ await init();
+}
+
+document.addEventListener('DOMContentLoaded', loaded);
diff --git a/src/lib.rs b/src/lib.rs
index 66bafe7..223e28a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,11 +1,69 @@
-use log::{Level, error, info};
+use std::rc::Rc;
+use std::cell::RefCell;
+
+use log::{Level, info};
use wasm_bindgen::prelude::*;
-use web_sys::js_sys;
+
+use state::State;
+
+mod line;
+mod point;
+mod state;
+
+struct RenderLoop {
+ animation_id: Option<i32>,
+ pub closure: Option<Closure<dyn FnMut(f64)>>,
+}
+
+impl RenderLoop {
+ pub fn new() -> Self {
+ Self {
+ animation_id: None,
+ closure: None,
+ }
+ }
+}
+
+fn window() -> web_sys::Window {
+ web_sys::window().expect("no window")
+}
#[wasm_bindgen(start)]
pub fn init() -> Result<(), JsValue> {
console_log::init_with_level(Level::Debug).expect("couldn't init console log");
info!("wasm init");
+ let mut s = State::new()?;
+ s.render_frame(0.0)?;
+
+ //
+ // from https://users.rust-lang.org/t/wasm-web-sys-how-to-use-window-request-animation-frame-resolved/20882
+ //
+
+ let render_loop: Rc<RefCell<RenderLoop>> = Rc::new(RefCell::new(RenderLoop::new()));
+ {
+ let closure: Closure<dyn FnMut(f64)> = {
+ let render_loop = render_loop.clone();
+ Closure::wrap(Box::new(move |t| {
+ s.render_frame(t).expect("should render frame");
+ if !s.paused {
+ let mut render_loop = render_loop.borrow_mut();
+ render_loop.animation_id = if let Some(ref closure) = render_loop.closure {
+ Some(window().request_animation_frame(closure.as_ref().unchecked_ref()).expect("req anim frame"))
+ } else {
+ None
+ }
+ }
+ }))
+ };
+ let mut render_loop = render_loop.borrow_mut();
+ render_loop.animation_id = Some(window().request_animation_frame(closure.as_ref().unchecked_ref()).expect("req anim frame"));
+ render_loop.closure = Some(closure);
+ }
+
+ //
+ //
+ //
+
Ok(())
}
diff --git a/src/line.rs b/src/line.rs
new file mode 100644
index 0000000..07a1f03
--- /dev/null
+++ b/src/line.rs
@@ -0,0 +1,22 @@
+use crate::point::Point;
+
+pub struct Line {
+ p1: Point,
+ p2: Point,
+}
+
+impl Line {
+ pub fn new(p1: Point, p2: Point) -> Self {
+ Self { p1, p2 }
+ }
+
+ pub fn vec(&self) -> Point {
+ Point::new(self.p2.x - self.p1.x, self.p2.y - self.p1.y)
+ }
+
+ pub fn angle_between(&self, other: &Self) -> f64 {
+ let v1 = self.vec();
+ let v2 = other.vec();
+ (v1.dot(&v2) / (v1.mag() * v2.mag())).acos()
+ }
+}
diff --git a/src/point.rs b/src/point.rs
new file mode 100644
index 0000000..76f9ced
--- /dev/null
+++ b/src/point.rs
@@ -0,0 +1,50 @@
+use crate::state::{MAX_SPEED, TAU};
+
+const POINT_RADIUS: f64 = 0.01;
+
+fn rand_color() -> String {
+ format!("rgb({} {} {})", fastrand::u8(0..255), fastrand::u8(0..255), fastrand::u8(0..255))
+}
+
+#[derive(Clone, Debug)]
+pub struct Point {
+ pub x: f64,
+ pub y: f64,
+ pub radius: f64,
+ pub heading: f64,
+ pub speed: f64,
+ pub color: String,
+}
+
+impl Point {
+ pub fn new(x: f64, y: f64) -> Self {
+ Self {
+ x,
+ y,
+ radius: POINT_RADIUS,
+ heading: fastrand::f64() * TAU,
+ speed: fastrand::f64() * MAX_SPEED,
+ color: rand_color(),
+ }
+ }
+
+ pub fn equal_pos(&self, other: &Self) -> bool {
+ self.x == other.x && self.y == other.y
+ }
+
+ pub fn dot(&self, other: &Self) -> f64 {
+ self.x * other.x + self.y * other.y
+ }
+
+ pub fn mag(&self) -> f64 {
+ (self.x.powi(2) + self.y.powi(2)).sqrt()
+ }
+
+ pub fn speed_x(&self) -> f64 {
+ self.speed * self.heading.cos()
+ }
+
+ pub fn speed_y(&self) -> f64 {
+ self.speed * self.heading.sin()
+ }
+}
diff --git a/src/state.rs b/src/state.rs
new file mode 100644
index 0000000..8941a3b
--- /dev/null
+++ b/src/state.rs
@@ -0,0 +1,190 @@
+use std::f64::consts::PI;
+
+use log::debug;
+use wasm_bindgen::prelude::*;
+use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement, HtmlElement};
+
+use crate::line::Line;
+use crate::point::Point;
+
+pub const TAU: f64 = PI * 2.0;
+pub const MAX_SPEED: f64 = 0.01;
+
+const NUM_POINTS: usize = 40;
+const VELVEC_SCALE: f64 = 3.0;
+
+fn window() -> web_sys::Window {
+ web_sys::window().expect("no window")
+}
+
+fn document() -> web_sys::Document {
+ window().document().expect("no document")
+}
+
+fn canvas() -> Result<web_sys::HtmlCanvasElement, JsValue> {
+ let x = document()
+ .query_selector("canvas")?
+ .expect("canvas element");
+ Ok(x.dyn_into::<web_sys::HtmlCanvasElement>()?)
+}
+
+#[derive(Debug)]
+pub struct State {
+ canvas: HtmlCanvasElement,
+ ctx: CanvasRenderingContext2d,
+ fps: HtmlElement,
+ points: Vec<Point>,
+ pub paused: bool,
+ last_time: Option<f64>,
+ inter_count: u16,
+}
+
+impl State {
+ pub fn new() -> Result<Self, JsValue> {
+ debug!("State::new()");
+ let canvas = canvas()?;
+ let ctx: CanvasRenderingContext2d = canvas
+ .get_context("2d")?
+ .expect("2d context on canvas")
+ .dyn_into()?;
+ ctx.scale(canvas.width().into(), canvas.height().into())?;
+ let fps = document().query_selector("#fps")?.expect("fps counter exists").dyn_into::<HtmlElement>().expect("is html element");
+
+ let points = (0..NUM_POINTS)
+ .map(|_| Point::new(fastrand::f64(), fastrand::f64()))
+ .collect();
+
+ Ok(Self {
+ canvas,
+ ctx,
+ fps,
+ points,
+ paused: false,
+ last_time: document().timeline().current_time(),
+ inter_count: 1,
+ })
+ }
+
+ pub fn render_frame(&mut self, t: f64) -> Result<(), JsValue> {
+ if let Some(last_time) = self.last_time {
+ if t > last_time {
+ let ic: f64 = self.inter_count.into();
+ let val: f64 = (1_000.0 * ic / (t - last_time)).trunc();
+ self.fps.set_text_content(Some(val.to_string().as_str()));
+ self.last_time = Some(t);
+ self.inter_count = 1;
+ } else {
+ self.inter_count += 1;
+ }
+ }
+
+ self.ctx.clear_rect(
+ 0.0,
+ 0.0,
+ self.canvas.width().into(),
+ self.canvas.height().into(),
+ );
+ self.render_points()?;
+ if self.bounce_points() {
+ //debug!("point bounced");
+ }
+ self.move_points();
+
+ // poly finding assumes sorted
+ self.points.sort_by(|a, b| {
+ if a.y > b.y {
+ std::cmp::Ordering::Greater
+ } else if a.y == b.y {
+ std::cmp::Ordering::Equal
+ } else {
+ std::cmp::Ordering::Less
+ }
+ });
+ let poly_points = self.find_poly();
+
+ self.ctx.set_line_width(0.005);
+ self.ctx.begin_path();
+ self.ctx.move_to(poly_points[0].x, poly_points[0].y);
+ for p in poly_points {
+ self.ctx.line_to(p.x, p.y);
+ }
+ self.ctx.set_stroke_style_str("blue");
+ self.ctx.stroke();
+
+ Ok(())
+ }
+
+ fn render_points(&self) -> Result<(), JsValue> {
+ for p in &self.points {
+ self.ctx.set_fill_style_str(&p.color);
+ self.ctx.begin_path();
+ self.ctx.arc(p.x, p.y, p.radius, 0.0, TAU)?;
+ self.ctx.fill();
+
+ self.ctx.set_line_width(0.005);
+ self.ctx.set_stroke_style_str(&p.color);
+ self.ctx.begin_path();
+ self.ctx.move_to(p.x, p.y);
+ self.ctx.line_to(
+ p.x + p.speed_x() * VELVEC_SCALE,
+ p.y + p.speed_y() * VELVEC_SCALE,
+ );
+ self.ctx.stroke();
+ }
+ Ok(())
+ }
+
+ fn find_poly(&self) -> Vec<Point> {
+ let mut last_line = Line::new(Point::new(0.0, 0.0), Point::new(1.0, 0.0));
+ let mut xxx = 100;
+ let mut res = vec![self.points[0].clone()];
+ loop {
+ let last = res.last().expect("something in res");
+ let mut min_theta = TAU;
+ let mut min_p = &self.points[0];
+ for p in &self.points {
+ if !last.equal_pos(p) {
+ let l2 = Line::new(last.clone(), p.clone());
+ let theta = last_line.angle_between(&l2);
+ if theta < min_theta {
+ min_theta = theta;
+ min_p = p;
+ }
+ }
+ }
+ last_line = Line::new(res.last().expect("something in res").clone(), min_p.clone());
+ res.push(min_p.clone());
+
+ xxx -= 1;
+ if xxx == 0 || res[0].equal_pos(res.last().expect("something in res")) {
+ break;
+ }
+ }
+
+ res
+ }
+
+ fn bounce_points(&mut self) -> bool {
+ let mut did_bounce = false;
+ for p in &mut self.points {
+ let x = p.x + p.speed_x();
+ let y = p.y + p.speed_y();
+
+ if x < 0.0 || x >= 1.0 {
+ p.heading = PI - p.heading;
+ did_bounce = true;
+ } else if y < 0.0 || y >= 1.0 {
+ p.heading = -p.heading;
+ did_bounce = true;
+ }
+ }
+ did_bounce
+ }
+
+ fn move_points(&mut self) {
+ for p in &mut self.points {
+ p.x += p.speed_x();
+ p.y += p.speed_y();
+ }
+ }
+}