2026-10-01 18:42:31 +02:00
|
|
|
// TODO license in every file
|
|
|
|
|
|
2026-07-14 18:36:30 +02:00
|
|
|
use std::rc::Rc;
|
|
|
|
|
|
2026-10-02 18:06:45 +02:00
|
|
|
use js_sys::{Float64Array, Function, Int32Array};
|
2026-09-23 19:53:39 +02:00
|
|
|
use uuid::Uuid;
|
2026-07-14 18:36:30 +02:00
|
|
|
use wasm_bindgen::prelude::*;
|
|
|
|
|
|
2026-10-03 10:03:22 +02:00
|
|
|
use crate::{File, FileId, Selection, Stack, StackEntry, StatisticsBuffer, StatisticsCache, parse};
|
2026-09-23 19:53:39 +02:00
|
|
|
|
2026-10-02 18:06:45 +02:00
|
|
|
extern crate console_error_panic_hook;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Default)]
|
2026-07-14 18:36:30 +02:00
|
|
|
#[wasm_bindgen]
|
2026-09-25 12:09:58 +02:00
|
|
|
pub struct Engine {
|
2026-07-14 18:36:30 +02:00
|
|
|
stack: Stack,
|
2026-09-24 20:55:30 +02:00
|
|
|
selection: Selection,
|
2026-10-02 18:06:45 +02:00
|
|
|
statistics_cache: StatisticsCache,
|
|
|
|
|
statistics_buffer: StatisticsBuffer,
|
|
|
|
|
callback: Option<Function>,
|
2026-07-14 18:36:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[wasm_bindgen]
|
2026-09-25 12:09:58 +02:00
|
|
|
impl Engine {
|
2026-07-14 18:36:30 +02:00
|
|
|
#[wasm_bindgen(constructor)]
|
2026-09-25 12:09:58 +02:00
|
|
|
pub fn new(callback: Function) -> Self {
|
2026-10-02 18:06:45 +02:00
|
|
|
console_error_panic_hook::set_once();
|
2026-07-14 18:36:30 +02:00
|
|
|
Self {
|
2026-09-24 20:55:30 +02:00
|
|
|
stack: Default::default(),
|
|
|
|
|
selection: Default::default(),
|
2026-10-02 18:06:45 +02:00
|
|
|
statistics_cache: Default::default(),
|
|
|
|
|
statistics_buffer: Default::default(),
|
|
|
|
|
callback: Some(callback),
|
2026-07-14 18:36:30 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-02 18:06:45 +02:00
|
|
|
pub fn total_distance(&self) -> Float64Array {
|
|
|
|
|
unsafe { Float64Array::view(&self.statistics_buffer.total_distance) }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn moving_distance(&self) -> Float64Array {
|
|
|
|
|
unsafe { Float64Array::view(&self.statistics_buffer.moving_distance) }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn total_time(&self) -> Int32Array {
|
|
|
|
|
unsafe { Int32Array::view(&self.statistics_buffer.total_time) }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn moving_time(&self) -> Int32Array {
|
|
|
|
|
unsafe { Int32Array::view(&self.statistics_buffer.moving_time) }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn speed(&self) -> Float64Array {
|
|
|
|
|
unsafe { Float64Array::view(&self.statistics_buffer.speed) }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn elevation_gain(&self) -> Float64Array {
|
|
|
|
|
unsafe { Float64Array::view(&self.statistics_buffer.elevation_gain) }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn elevation_loss(&self) -> Float64Array {
|
|
|
|
|
unsafe { Float64Array::view(&self.statistics_buffer.elevation_loss) }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn slope(&self) -> Float64Array {
|
|
|
|
|
unsafe { Float64Array::view(&self.statistics_buffer.slope) }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn slope_segment_slope(&self) -> Float64Array {
|
|
|
|
|
unsafe { Float64Array::view(&self.statistics_buffer.slope_segment_slope) }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn slope_segment_distance(&self) -> Float64Array {
|
|
|
|
|
unsafe { Float64Array::view(&self.statistics_buffer.slope_segment_distance) }
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-23 19:53:39 +02:00
|
|
|
pub fn create_file(&mut self, name: &str) {
|
2026-10-02 18:06:45 +02:00
|
|
|
self.event_loop(|entry| {
|
|
|
|
|
let mut file = File::default();
|
|
|
|
|
file.info.name = name.to_string();
|
2026-09-23 19:53:39 +02:00
|
|
|
entry.insert(file.id, Rc::new(file));
|
|
|
|
|
true
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn load_file(&mut self, data: &[u8]) {
|
2026-10-02 18:06:45 +02:00
|
|
|
self.event_loop(|entry| {
|
|
|
|
|
if let Ok(file) = parse(data) {
|
2026-09-23 19:53:39 +02:00
|
|
|
entry.insert(file.id, Rc::new(file));
|
|
|
|
|
true
|
2026-10-02 18:06:45 +02:00
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
});
|
2026-09-23 19:53:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn delete_file(&mut self, id: &[u8]) {
|
2026-10-02 18:06:45 +02:00
|
|
|
self.event_loop(|entry| {
|
|
|
|
|
if let Ok(id) = Uuid::from_slice(id) {
|
|
|
|
|
let id = FileId(id);
|
|
|
|
|
entry.remove(&id).is_some()
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
});
|
2026-09-23 19:53:39 +02:00
|
|
|
}
|
2026-10-01 18:42:31 +02:00
|
|
|
|
2026-10-02 18:06:45 +02:00
|
|
|
fn event_loop<F>(&mut self, f: F)
|
|
|
|
|
where
|
|
|
|
|
F: FnOnce(&mut StackEntry) -> bool,
|
|
|
|
|
{
|
|
|
|
|
if let Some(diff) = self.stack.create_and_push_next(f) {
|
|
|
|
|
self.selection.select(diff.added[0]);
|
|
|
|
|
self.statistics_cache.update(self.stack.current());
|
|
|
|
|
self.statistics_buffer.update(
|
|
|
|
|
&self
|
|
|
|
|
.statistics_cache
|
|
|
|
|
.get(self.stack.current(), &self.selection),
|
|
|
|
|
);
|
|
|
|
|
if let Some(c) = &self.callback {
|
|
|
|
|
let _ = c.call0(&JsValue::NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use std::{fs::File, io::Read};
|
|
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_load_file() {
|
|
|
|
|
let mut engine = Engine::default();
|
|
|
|
|
|
|
|
|
|
let mut f = File::open("data/simple.gpx").unwrap();
|
|
|
|
|
let mut data = String::new();
|
|
|
|
|
let _ = f.read_to_string(&mut data);
|
|
|
|
|
|
|
|
|
|
engine.load_file(data.as_bytes());
|
|
|
|
|
|
|
|
|
|
assert_eq!(engine.statistics_buffer.total_distance.len(), 80);
|
2026-10-01 18:42:31 +02:00
|
|
|
}
|
2026-07-14 18:36:30 +02:00
|
|
|
}
|