1#![warn(missing_docs)]
6
7use std::cell::RefCell;
8use std::rc::Rc;
9use wasm_bindgen::{JsCast, prelude::*};
10use web_sys::{Document, HtmlCanvasElement, Window};
11
12use crate::render::RenderEngine;
13use crate::ui::Ui;
14use crate::waterfall::Waterfall;
15use crate::waterfall_interaction::WaterfallInteraction;
16use crate::websocket::WebSocketClient;
17
18pub mod array_view;
19pub mod colormap;
20pub mod pointer;
21pub mod render;
22pub mod ui;
23pub mod version;
24pub mod waterfall;
25pub mod waterfall_interaction;
26pub mod websocket;
27
28#[wasm_bindgen(start)]
35pub fn start() -> Result<(), JsValue> {
36 std::panic::set_hook(Box::new(console_error_panic_hook::hook));
37 Ok(())
38}
39
40#[wasm_bindgen]
46pub fn maia_wasm_start() -> Result<(), JsValue> {
47 let (window, document) = get_window_and_document()?;
48 let canvas = Rc::new(
49 document
50 .get_element_by_id("canvas")
51 .ok_or("unable to get #canvas element")?
52 .dyn_into::<web_sys::HtmlCanvasElement>()?,
53 );
54
55 let (render_engine, waterfall, mut waterfall_interaction) =
56 new_waterfall(&window, &document, &canvas)?;
57 WebSocketClient::start(&window, Rc::clone(&waterfall))?;
58 let ui = Ui::new(
59 Rc::clone(&window),
60 Rc::clone(&document),
61 Rc::clone(&render_engine),
62 Rc::clone(&waterfall),
63 )?;
64 waterfall_interaction.set_ui(ui);
65
66 setup_render_loop(render_engine, waterfall);
67
68 Ok(())
69}
70
71pub fn get_window_and_document() -> Result<(Rc<Window>, Rc<Document>), JsValue> {
75 let window = Rc::new(web_sys::window().ok_or("unable to get window")?);
76 let document = Rc::new(window.document().ok_or("unable to get document")?);
77 Ok((window, document))
78}
79
80#[allow(clippy::type_complexity)]
86pub fn new_waterfall(
87 window: &Rc<Window>,
88 document: &Document,
89 canvas: &Rc<HtmlCanvasElement>,
90) -> Result<
91 (
92 Rc<RefCell<RenderEngine>>,
93 Rc<RefCell<Waterfall>>,
94 WaterfallInteraction,
95 ),
96 JsValue,
97> {
98 let render_engine = Rc::new(RefCell::new(RenderEngine::new(
99 Rc::clone(canvas),
100 Rc::clone(window),
101 document,
102 )?));
103 let waterfall = Rc::new(RefCell::new(Waterfall::new(
104 &mut render_engine.borrow_mut(),
105 window.performance().ok_or("unable to get performance")?,
106 )?));
107 let waterfall_interaction = WaterfallInteraction::new(
108 Rc::clone(window),
109 Rc::clone(canvas),
110 Rc::clone(&render_engine),
111 Rc::clone(&waterfall),
112 )?;
113 Ok((render_engine, waterfall, waterfall_interaction))
114}
115
116pub fn setup_render_loop(
123 render_engine: Rc<RefCell<RenderEngine>>,
124 waterfall: Rc<RefCell<Waterfall>>,
125) {
126 let f = Rc::new(RefCell::new(None));
127 let g = f.clone();
128 *g.borrow_mut() = Some(Closure::new(move |dt| {
129 let mut render_engine = render_engine.borrow_mut();
130 if let Err(e) = waterfall
131 .borrow_mut()
132 .prepare_render(&mut render_engine, dt)
133 {
134 web_sys::console::error_1(&e);
135 return;
136 }
137 if let Err(e) = render_engine.render() {
138 web_sys::console::error_1(&e);
139 return;
140 }
141 request_animation_frame(f.borrow().as_ref().unwrap());
143 }));
144 request_animation_frame(g.borrow().as_ref().unwrap());
146}
147
148fn request_animation_frame(f: &Closure<dyn FnMut(f32)>) {
149 web_sys::window()
150 .unwrap()
151 .request_animation_frame(f.as_ref().unchecked_ref())
152 .unwrap();
153}