mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2026-10-17 08:42:28 +00:00
wasm first steps
This commit is contained in:
1 parent
86f4fc3394
commit
ed6264444f
11 files changed
+398
-3
No files matched your search
@@ -0,0 +1,27 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use crate::{stack::Stack, types::GPXFile};
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct Controller {
|
||||
stack: Stack,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl Controller {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
stack: Stack::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn create_file(&mut self, name: &str) {
|
||||
let id = self.stack.get_new_file_id();
|
||||
let file = Rc::new(GPXFile::new(id, name));
|
||||
self.stack.update(&[file]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
mod controller;
|
||||
mod stack;
|
||||
mod types;
|
||||
@@ -0,0 +1,101 @@
|
||||
use std::{collections::HashMap, rc::Rc};
|
||||
|
||||
use crate::types::{GPXFile, GPXFileId};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Stack {
|
||||
entries: Vec<StackEntry>,
|
||||
index: Option<usize>,
|
||||
}
|
||||
|
||||
impl Stack {
|
||||
pub fn current(&self) -> Option<&StackEntry> {
|
||||
match self.index {
|
||||
Some(i) => Some(&self.entries[i]),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_new_file_id(&self) -> GPXFileId {
|
||||
if let Some(current) = self.current() {
|
||||
for id in 0..current.len() {
|
||||
if !current.contains_key(&id) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
current.len() as GPXFileId
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, files: &[Rc<GPXFile>]) {
|
||||
let mut next = match self.current() {
|
||||
Some(current) => current.clone(),
|
||||
None => StackEntry::default(),
|
||||
};
|
||||
for file in files {
|
||||
next.insert(file.id, file.clone());
|
||||
}
|
||||
self.push(next);
|
||||
}
|
||||
|
||||
pub fn delete(&mut self, files: &[GPXFileId]) {
|
||||
if let Some(current) = self.current() {
|
||||
let mut next = current.clone();
|
||||
for file in files {
|
||||
next.remove(file);
|
||||
}
|
||||
self.push(next);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_undo(&self) -> bool {
|
||||
self.index.is_some()
|
||||
}
|
||||
|
||||
pub fn can_redo(&self) -> bool {
|
||||
match self.index {
|
||||
Some(i) => i + 1 < self.entries.len(),
|
||||
None => !self.entries.is_empty(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn undo(&mut self) {
|
||||
if let Some(i) = self.index {
|
||||
if i == 0 {
|
||||
self.index = None;
|
||||
} else {
|
||||
self.index = Some(i - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn redo(&mut self) {
|
||||
match self.index {
|
||||
Some(i) => {
|
||||
if i + 1 < self.entries.len() {
|
||||
self.index = Some(i + 1);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
if !self.entries.is_empty() {
|
||||
self.index = Some(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn push(&mut self, entry: StackEntry) {
|
||||
if let Some(i) = self.index {
|
||||
if i + 1 < self.entries.len() {
|
||||
self.entries.truncate(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
self.entries.push(entry);
|
||||
self.index = Some(self.entries.len() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
pub type StackEntry = HashMap<GPXFileId, Rc<GPXFile>>;
|
||||
@@ -0,0 +1,106 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
pub type GPXFileId = usize;
|
||||
|
||||
pub struct GPXFile {
|
||||
pub id: GPXFileId,
|
||||
pub info: Rc<GPXFileInfo>,
|
||||
pub trk: Vec<Track>,
|
||||
pub wpt: Vec<Rc<WaypointChunk>>,
|
||||
}
|
||||
|
||||
impl GPXFile {
|
||||
pub fn new(id: GPXFileId, name: &str) -> Self {
|
||||
Self {
|
||||
id,
|
||||
info: Rc::new(GPXFileInfo::new(name)),
|
||||
trk: Vec::new(),
|
||||
wpt: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GPXFileInfo {
|
||||
pub name: String,
|
||||
pub desc: Option<String>,
|
||||
pub author: Option<Author>,
|
||||
pub link: Option<Link>,
|
||||
pub time: Option<i64>,
|
||||
}
|
||||
|
||||
impl GPXFileInfo {
|
||||
pub fn new(name: &str) -> Self {
|
||||
Self {
|
||||
name: name.to_owned(),
|
||||
desc: None,
|
||||
author: None,
|
||||
link: None,
|
||||
time: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Author {
|
||||
pub name: Option<String>,
|
||||
pub email: Option<String>,
|
||||
pub link: Option<Link>,
|
||||
}
|
||||
|
||||
pub struct Link {
|
||||
pub href: String,
|
||||
pub text: Option<String>,
|
||||
pub type_: Option<String>,
|
||||
}
|
||||
|
||||
pub struct Track {
|
||||
pub info: Rc<TrackInfo>,
|
||||
pub trkseg: Vec<TrackSegment>,
|
||||
}
|
||||
|
||||
pub struct TrackInfo {
|
||||
name: Option<String>,
|
||||
cmt: Option<String>,
|
||||
desc: Option<String>,
|
||||
src: Option<String>,
|
||||
link: Option<Link>,
|
||||
type_: Option<String>,
|
||||
color: Option<String>,
|
||||
opacity: Option<f64>,
|
||||
width: Option<f64>,
|
||||
}
|
||||
|
||||
pub struct TrackSegment {
|
||||
pub trkpt: Vec<Rc<TrackPointChunk>>,
|
||||
}
|
||||
|
||||
pub type TrackPointChunk = Vec<TrackPoint>;
|
||||
|
||||
pub struct TrackPoint {
|
||||
pub coordinates: LngLat,
|
||||
pub ele: f64,
|
||||
pub time: Option<i64>,
|
||||
pub hr: Option<u16>,
|
||||
pub cad: Option<u16>,
|
||||
pub power: Option<u16>,
|
||||
pub atemp: Option<i16>,
|
||||
// TODO OSM data? or store intervals at a higher level?
|
||||
}
|
||||
|
||||
pub type WaypointChunk = Vec<Waypoint>;
|
||||
|
||||
pub struct Waypoint {
|
||||
pub coordinates: LngLat,
|
||||
pub ele: f64,
|
||||
pub time: Option<i64>,
|
||||
pub name: Option<String>,
|
||||
pub cmt: Option<String>,
|
||||
pub desc: Option<String>,
|
||||
pub link: Option<Link>,
|
||||
pub sym: Option<String>,
|
||||
pub type_: Option<String>,
|
||||
}
|
||||
|
||||
pub struct LngLat {
|
||||
pub lng: f64,
|
||||
pub lat: f64,
|
||||
}
|
||||
Reference in new issue
Block a user