Progress towards getting web version working

This commit is contained in:
Matthew Gordon 2025-05-19 13:40:36 -03:00
parent 1d9a771986
commit a15eedcd1b
9 changed files with 471 additions and 61 deletions

310
Cargo.lock generated
View File

@ -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"

View File

@ -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]

View File

@ -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<Model> for App {
eprintln!("Using {}", adapter.get_info().name);
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor {
.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<Model> 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<Model> for App {
}
}
fn open_test_file(file_path: PathBuf, model: Rc<Model>) -> Rc<Model> {
let dem = Some(Rc::new(raster::Dem::load_from_image(&file_path)));
fn open_test_file(file: Box<dyn File>, model: Rc<Model>) -> Rc<Model> {
let dem = Some(Rc::new(raster::Dem::load_from_image(file)));
Rc::new(Model { dem, ..*model })
}

View File

@ -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<dyn File>) -> 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 {

View File

@ -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
}

View File

@ -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<Vec<u8>> {}
pub enum Event {
MouseButtonPressed,
OpenTestFile(PathBuf),
OpenTestFile(Box<dyn File>),
}
#[derive(Debug, Clone, Copy)]

View File

@ -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<dyn File>;
self.model = block_on(
self.app
.update(self.model.clone(), mvu::Event::OpenTestFile(file_path)),
.update(self.model.clone(), mvu::Event::OpenTestFile(file)),
)
}
_ => (),

View File

@ -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<A, M>(mut app: A, model: Rc<M>)
where
A: MvuApp<M> + '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();
#[wasm_bindgen]
#[derive(Clone)]
pub struct PteropusCanvas {
app: Rc<Mutex<App>>,
model: Rc<Model>,
}
/*struct ArrayFile {
data: Vec<u8>,
}
impl std::io::Read for ArrayFile {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
todo!()
}
}
impl std::io::Seek for ArrayFile {
fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
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).unwrap();
let surface = instance
.create_surface(surface_target)
.expect("create graphics surface for canvas");
app.init(&instance, surface, size).await;
app.view(model).await.unwrap();
self.app
.lock()
.expect("get app mutex")
.init(&instance, surface, size)
.await;
}
pub fn run<A, M>(app: A, model: Rc<M>)
where
A: MvuApp<M> + 'static,
M: 'static,
{
#[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<Model>) -> 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
}

View File

@ -4,8 +4,8 @@
<title>Pteropus</title>
<style>
#pteropus-canvas {
height: 100vh;
width: 100vh;
height: 100%;
width: 100%;
display: block;
}
</style>
@ -13,10 +13,38 @@
<body>
<canvas id="pteropus-canvas"></canvas>
<script type="module">
import init from '../pkg/pteropus.js';
import init, {init_pteropus} from '../pkg/pteropus.js';
function fileDragOverHandler(event) {
event.preventDefault();
}
async function run() {
await init();
let pteropus = await init_pteropus();
let test_file_data = null
let mainCanvas = document.getElementById("pteropus-canvas");
mainCanvas.addEventListener("drop", async (event) => {
event.preventDefault();
test_file_data = event.dataTransfer.files[0]
});
mainCanvas.addEventListener("dragover", fileDragOverHandler);
while(true)
{
await pteropus.render();
// TODO: I really want to do this asynchronously, with the drop
// event listener calling pteropus_load_file directly, but I need
// to figure out how to get that to work.
if(test_file_data) {
await pteropus.load_file(test_file_data);
test_file_data = null
}
await new Promise(requestAnimationFrame);
}
}
run();