aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib.rs
blob: 80d089a4419e1cf1d9a1b89488d15d6f9d07a0da (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
use std::cell::RefCell;
use std::sync::{Mutex};
use std::rc::Rc;

use log::{Level, info, debug};
use wasm_bindgen::prelude::*;

use state::State;

mod line;
mod point;
mod state;

struct RenderLoop {
    inner: Rc<RefCell<Option<Closure<dyn FnMut(f64)>>>>,
}

impl RenderLoop {
    fn new<T: FnMut(f64) -> bool + 'static>(mut fun: T) -> Self {
        let inner = Rc::new(RefCell::new(None));
        let rloop = inner.clone();

        *inner.borrow_mut() = Some(Closure::new(move |t| {
            if fun(t) {
                request_animation_frame(rloop.borrow().as_ref().expect("can borrow rloop"));
            }
        }));
        Self { inner }
    }

    fn start(&self) -> Result<(), JsValue> {
        request_animation_frame(self.inner.borrow().as_ref().ok_or("closure exists")?);
        Ok(())
    }
}

impl Clone for RenderLoop {
    fn clone(&self) -> Self  {
        Self { inner: self.inner.clone() }
    }
}

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>()?)
}

fn fps() -> Result<web_sys::HtmlElement, JsValue> {
    let x = document()
        .query_selector("#fps")?
        .expect("fps counter exists");
    Ok(x.dyn_into::<web_sys::HtmlElement>()?)
}

fn go() -> Result<web_sys::HtmlElement, JsValue> {
    let x = document()
        .query_selector("button")?
        .expect("go button exists");
    Ok(x.dyn_into::<web_sys::HtmlElement>()?)
}

fn request_animation_frame(f: &Closure<dyn FnMut(f64)>) {
    window()
        .request_animation_frame(f.as_ref().unchecked_ref())
        .expect("should register `requestAnimationFrame` OK");
}

#[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 paused = Rc::new(Mutex::new(true));
    let p1 = paused.clone();

    let mut s = State::new(canvas()?, fps()?)?;
    s.render_frame(0.0)?;

    let render_loop = RenderLoop::new(move |t| {
        s.render_frame(t).expect("should render frame");
        !*paused.lock().unwrap()
    });
    render_loop.start()?;

    let a = Closure::<dyn FnMut()>::new(move || {
        let mut p = p1.lock().unwrap();
        debug!("go clicked: {}", *p);
        *p = !*p;
        let text = if *p {
            "go"
        } else {
            render_loop.start().expect("start render loop");
            "pause"
        };
        go().expect("go button works").set_text_content(Some(text));
    });
    go()?.set_onclick(Some(a.as_ref().unchecked_ref()));
    // otherwise it gets dropped after we're done, invalidating the
    // handler.
    a.forget();

    Ok(())
}