diff --git a/Cargo.lock b/Cargo.lock index c7558c8..f661fe9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -171,6 +171,15 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bit-set" version = "0.8.0" @@ -556,6 +565,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "futures" version = "0.3.31" @@ -662,8 +680,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -695,6 +715,190 @@ version = "0.29.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee" +[[package]] +name = "gloo" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d15282ece24eaf4bd338d73ef580c6714c8615155c4190c781290ee3fa0fd372" +dependencies = [ + "gloo-console", + "gloo-dialogs", + "gloo-events", + "gloo-file", + "gloo-history", + "gloo-net", + "gloo-render", + "gloo-storage", + "gloo-timers", + "gloo-utils", + "gloo-worker", +] + +[[package]] +name = "gloo-console" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a17868f56b4a24f677b17c8cb69958385102fa879418052d60b50bc1727e261" +dependencies = [ + "gloo-utils", + "js-sys", + "serde", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-dialogs" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4748e10122b01435750ff530095b1217cf6546173459448b83913ebe7815df" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-events" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27c26fb45f7c385ba980f5fa87ac677e363949e065a083722697ef1b2cc91e41" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-file" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97563d71863fb2824b2e974e754a81d19c4a7ec47b09ced8a0e6656b6d54bd1f" +dependencies = [ + "futures-channel", + "gloo-events", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-history" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "903f432be5ba34427eac5e16048ef65604a82061fe93789f2212afc73d8617d6" +dependencies = [ + "getrandom 0.2.16", + "gloo-events", + "gloo-utils", + "serde", + "serde-wasm-bindgen", + "serde_urlencoded", + "thiserror 1.0.69", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-net" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "http", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror 1.0.69", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-render" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56008b6744713a8e8d98ac3dcb7d06543d5662358c9c805b4ce2167ad4649833" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-storage" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc8031e8c92758af912f9bc08fbbadd3c6f3cfcbf6b64cdf3d6a81f0139277a" +dependencies = [ + "gloo-utils", + "js-sys", + "serde", + "serde_json", + "thiserror 1.0.69", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-worker" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "085f262d7604911c8150162529cefab3782e91adb20202e8658f7275d2aefe5d" +dependencies = [ + "bincode", + "futures", + "gloo-utils", + "gloo-worker-macros", + "js-sys", + "pinned", + "serde", + "thiserror 1.0.69", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-worker-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956caa58d4857bc9941749d55e4bd3000032d8212762586fa5705632967140e7" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "glow" version = "0.16.0" @@ -805,6 +1009,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "indexmap" version = "2.9.0" @@ -821,6 +1036,12 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + [[package]] name = "jiff" version = "0.2.13" @@ -1130,7 +1351,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.3.0", "proc-macro2", "quote", "syn", @@ -1448,6 +1669,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pinned" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a829027bd95e54cfe13e3e258a1ae7b645960553fb82b75ff852c29688ee595b" +dependencies = [ + "futures", + "rustversion", + "thiserror 1.0.69", +] + [[package]] name = "pkg-config" version = "0.3.32" @@ -1499,13 +1731,23 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + [[package]] name = "proc-macro-crate" version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ - "toml_edit", + "toml_edit 0.22.26", ] [[package]] @@ -1553,6 +1795,7 @@ dependencies = [ "env_logger", "futures", "glam", + "gloo", "log", "proptest", "tiff", @@ -1749,6 +1992,12 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + [[package]] name = "same-file" version = "1.0.6" @@ -1792,6 +2041,17 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-wasm-bindgen" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + [[package]] name = "serde_derive" version = "1.0.219" @@ -1803,6 +2063,30 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "shlex" version = "1.3.0" @@ -2025,6 +2309,17 @@ version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow 0.5.40", +] + [[package]] name = "toml_edit" version = "0.22.26" @@ -2033,7 +2328,7 @@ checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap", "toml_datetime", - "winnow", + "winnow 0.7.10", ] [[package]] @@ -2856,6 +3151,15 @@ dependencies = [ "xkbcommon-dl", ] +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + [[package]] name = "winnow" version = "0.7.10" diff --git a/Cargo.toml b/Cargo.toml index 8df4f03..0695164 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,10 +25,12 @@ wasm-bindgen = "0.2.84" wasm-bindgen-futures = "0.4.42" console_log = "1.0" console_error_panic_hook = "0.1.7" +gloo = { version = "0.11.0", features=["file", "futures"] } web-sys = { version = "0.3", features = [ "Document", "Window", "Element", + "File" ]} [dev-dependencies] diff --git a/src/app/mod.rs b/src/app/mod.rs index 3dbbc98..7ddcd1e 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1,9 +1,9 @@ use std::rc::Rc; -use crate::mvu::{Event, MvuApp, Size2i}; +use crate::mvu::{Event, File, MvuApp, Size2i}; use { log::info, - std::{borrow::Cow, path::PathBuf}, + std::borrow::Cow, wgpu::{Device, Instance, Queue, RenderPipeline, Surface, SurfaceConfiguration, Trace}, }; @@ -55,16 +55,14 @@ impl MvuApp for App { eprintln!("Using {}", adapter.get_info().name); let (device, queue) = adapter - .request_device( - &wgpu::DeviceDescriptor { - label: None, - required_features: wgpu::Features::empty(), - required_limits: wgpu::Limits::downlevel_webgl2_defaults() - .using_resolution(adapter.limits()), - memory_hints: wgpu::MemoryHints::MemoryUsage, - trace: Trace::Off, - }, - ) + .request_device(&wgpu::DeviceDescriptor { + label: None, + required_features: wgpu::Features::empty(), + required_limits: wgpu::Limits::downlevel_webgl2_defaults() + .using_resolution(adapter.limits()), + memory_hints: wgpu::MemoryHints::MemoryUsage, + trace: Trace::Off, + }) .await .expect("Failed to create device"); @@ -106,7 +104,7 @@ impl MvuApp for App { let mut config = surface .get_default_config(&adapter, size.width, size.height) - .unwrap(); + .expect("get default graphics surface"); config.present_mode = wgpu::PresentMode::AutoVsync; config.view_formats.push(config.format); surface.configure(&device, &config); @@ -199,7 +197,7 @@ impl MvuApp for App { } } -fn open_test_file(file_path: PathBuf, model: Rc) -> Rc { - let dem = Some(Rc::new(raster::Dem::load_from_image(&file_path))); +fn open_test_file(file: Box, model: Rc) -> Rc { + let dem = Some(Rc::new(raster::Dem::load_from_image(file))); Rc::new(Model { dem, ..*model }) } diff --git a/src/app/raster/dem.rs b/src/app/raster/dem.rs index f6033ed..1dc3874 100644 --- a/src/app/raster/dem.rs +++ b/src/app/raster/dem.rs @@ -1,4 +1,4 @@ -use std::{fs::File, path::PathBuf}; +use crate::mvu::File; #[derive(Clone)] pub struct Dem { @@ -168,8 +168,7 @@ fn normalized_f32_to_u16(value: f32) -> u16 { } impl Dem { - pub fn load_from_image(file_path: &PathBuf) -> Dem { - let file = File::open(file_path).unwrap(); + pub fn load_from_image(file: Box) -> Dem { let mut tiff = tiff::decoder::Decoder::new(file).unwrap(); let (num_cells_x, num_cells_y) = tiff.dimensions().unwrap(); match tiff.read_image().unwrap() { @@ -180,13 +179,13 @@ impl Dem { let y_max = 500.0; let (z_min, z_max) = f32_values[1..] .iter() - .map(|z| z * 0.1) + .map(|z| z * 0.6) .fold((f32_values[0], f32_values[0]), |(min, max), elem| { (min.min(elem), max.max(elem)) }); let grid = f32_values .iter() - .map(|z| z * 0.1) + .map(|z| z * 0.6) .map(|v| normalized_f32_to_u16(normalize_f32(v, z_min, z_max))) .collect(); Dem { diff --git a/src/lib.rs b/src/lib.rs index 66f9858..f7359f0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,13 @@ mod app; mod mvu; use app::{App, Model}; -#[cfg_attr(target_arch = "wasm32", wasm_bindgen(start))] +#[cfg(target_arch = "x86_64")] pub fn main() { run(App::new(), Rc::new(Model::new())); } + +#[cfg(target_arch = "wasm32")] +#[wasm_bindgen] +pub async fn init_pteropus() -> wasm::PteropusCanvas { + run(App::new(), Rc::new(Model::new())).await +} diff --git a/src/mvu/mod.rs b/src/mvu/mod.rs index ac3b059..5bce9e7 100644 --- a/src/mvu/mod.rs +++ b/src/mvu/mod.rs @@ -1,11 +1,20 @@ use { - std::{path::PathBuf, rc::Rc}, + std::{ + io::{Read, Seek}, + rc::Rc, + }, wgpu::{Instance, Surface}, }; +pub trait File: Seek + Read {} + +impl File for std::fs::File {} + +impl File for std::io::Cursor> {} + pub enum Event { MouseButtonPressed, - OpenTestFile(PathBuf), + OpenTestFile(Box), } #[derive(Debug, Clone, Copy)] diff --git a/src/native/mod.rs b/src/native/mod.rs index 75a4583..846e019 100644 --- a/src/native/mod.rs +++ b/src/native/mod.rs @@ -1,6 +1,6 @@ use std::{rc::Rc, sync::Arc}; -use crate::mvu::{self, MvuApp, Size2i}; +use crate::mvu::{self, File, MvuApp, Size2i}; use { futures::executor::block_on, winit::{ @@ -63,9 +63,10 @@ where self.window.as_ref().unwrap().request_redraw(); } WindowEvent::DroppedFile(file_path) => { + let file = Box::new(std::fs::File::open(file_path).unwrap()) as Box; self.model = block_on( self.app - .update(self.model.clone(), mvu::Event::OpenTestFile(file_path)), + .update(self.model.clone(), mvu::Event::OpenTestFile(file)), ) } _ => (), diff --git a/src/wasm/mod.rs b/src/wasm/mod.rs index 726443e..699b297 100644 --- a/src/wasm/mod.rs +++ b/src/wasm/mod.rs @@ -1,38 +1,101 @@ use { - std::rc::Rc, wasm_bindgen::prelude::*, wasm_bindgen_futures::spawn_local, - web_sys::HtmlCanvasElement, wgpu::SurfaceTarget, + log::info, + std::{rc::Rc, sync::Mutex}, + wasm_bindgen::prelude::*, + web_sys::HtmlCanvasElement, + wgpu::SurfaceTarget, }; -use crate::mvu::{MvuApp, Size2i}; +use crate::{ + app::{App, Model}, + mvu::{Event, File, MvuApp, Size2i}, +}; -async fn run_async(mut app: A, model: Rc) -where - A: MvuApp + 'static, - M: 'static, -{ - let window = web_sys::window().unwrap(); - let document = window.document().unwrap(); - let canvas_element = document.get_element_by_id("pteropus-canvas").unwrap(); - let html_canvas: HtmlCanvasElement = canvas_element.dyn_into().unwrap(); - let size = Size2i { - width: html_canvas.width(), - height: html_canvas.height(), - }; - let instance = wgpu::Instance::default(); - let surface_target = SurfaceTarget::Canvas(html_canvas); - let surface = instance.create_surface(surface_target).unwrap(); - - app.init(&instance, surface, size).await; - app.view(model).await.unwrap(); +#[wasm_bindgen] +#[derive(Clone)] +pub struct PteropusCanvas { + app: Rc>, + model: Rc, } -pub fn run(app: A, model: Rc) -where - A: MvuApp + 'static, - M: 'static, -{ +/*struct ArrayFile { + data: Vec, +} + +impl std::io::Read for ArrayFile { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + todo!() + } +} + +impl std::io::Seek for ArrayFile { + fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result { + todo!() + } +} + +impl File for ArrayFile {}*/ + +#[wasm_bindgen] +impl PteropusCanvas { + async fn init(&self) { + let window = web_sys::window().expect("get window"); + let document = window.document().expect("get HTML document"); + let canvas_element = document + .get_element_by_id("pteropus-canvas") + .expect("document contains element with id \"pteropus-canvas\""); + let html_canvas: HtmlCanvasElement = canvas_element + .dyn_into() + .expect("pteropus-canvas element is a canvas"); + let size = Size2i { + width: html_canvas.width(), + height: html_canvas.height(), + }; + let instance = wgpu::Instance::default(); + let surface_target = SurfaceTarget::Canvas(html_canvas); + let surface = instance + .create_surface(surface_target) + .expect("create graphics surface for canvas"); + + self.app + .lock() + .expect("get app mutex") + .init(&instance, surface, size) + .await; + } + + #[wasm_bindgen] + pub async fn render(&self) { + self.app + .lock() + .expect("get app mutex") + .view(Rc::clone(&self.model)) + .await + .expect("view succeeds"); + } + + #[wasm_bindgen] + pub async fn load_file(&mut self, file: web_sys::File) { + let data = gloo::file::futures::read_as_bytes(&file.into()) + .await + .expect("read data from dragged file"); + self.model = self + .app + .lock() + .expect("get app mutex") + .update( + Rc::clone(&self.model), + Event::OpenTestFile(Box::new(std::io::Cursor::new(data))), + ) + .await; + } +} + +pub async fn run(app: App, model: Rc) -> PteropusCanvas { + let app = Rc::new(Mutex::new(app)); std::panic::set_hook(Box::new(console_error_panic_hook::hook)); console_log::init_with_level(log::Level::Info).expect("Couldn't initialize logger"); - - spawn_local(run_async(app, model)); + let pteropus_canvas = PteropusCanvas { app, model }; + pteropus_canvas.init().await; + pteropus_canvas } diff --git a/web/index.html b/web/index.html index 502f4c7..5861cdc 100644 --- a/web/index.html +++ b/web/index.html @@ -4,8 +4,8 @@ Pteropus @@ -13,10 +13,38 @@