mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2026-11-01 12:17:18 +00:00
Migration to new Rust-based engine (#401)
* wasm first steps * parsing start * parsing continued * progress * progress * progress * progress * progress * progress * progress * progress * fixes * reorganize * change type * fix * progress * refactoring * progress * progress * progress * keep style info * progress * progress * remove (copy) in name * progress * merge methods into 1 * splice * progress * progress * progress * cleaning * progress * progress * progress * visibility * progress * progress * progress * progress * progress * fixes * progress * fix markers * fix * progress * progress * progress * progress * progress * progress * small fix * use engine info * fix popups * small fix * progress * progress * progress * clean tool * handle self closing elements * reduce data exchanges across boundary * copy on wasm side to avoid any data corruption * speed fix * routing * refactoring * reverse * reverse * crop * split * merge * progress * time tool * fix bounds for waypoints * reduce * fix reactivity * handle corrupt time data * export * progress * remove gpx js package * remove todo * update readme * cleaning * progress * fix link fields * remove unused fields * refactoring and tests * import and export of routes, ref #192 * per-chunk categories storage * cross-tab sync * outdated state * refactoring * reorder methods * refactoring * fix * improve flow * renaming * progress * progress * fix * progress * per file history * keep original flag * use history variant * fix * progress * file explorer * progress * fix * sticky folders * improved sticky folders * fix sortable * fix sortable * fix * fix tab size * no file delete on desktop * fix routing on other file * fix sorting * remember language * open links in browser * commit missing changes * avoid selecting on resize * clarify filename * explorer actions * fix folder styling * drop anywhere in folder * cleaning * fixes * remove focus-ring * full screen * small fix * consistent highlight color * improved preview * performance improvements * sparser data transfers * sparser data transfers * show popup once data is loaded * fix routing control size * fix * sparse desktop build * remove custom ign styles * use new endpoint for openhikingmap * strip native menu * reorder context menu items * route full support * route - track conversion * create route file action * route export, filter out anchors at high zoom level * clarify name handling * move hide & center to view * routes continued * update docs * fix power format, closes #378 * lint fixes * fix display of additional data with gaps, closes #361 * fix linting issues * fix import modules * fix multidrag * change base to resolve * fix type errors * fix layer typings * fix last type error * each keys * improve typings * deploy to cloudflare --------- Co-authored-by: vcoppe <vcoppe@hexaly.com>
This commit is contained in:
1 parent
6a0d434371
commit
8fe7d8d64d
484 files changed
+53949
-41152
No files matched your search
@@ -0,0 +1,162 @@
|
||||
use crate::{FileId, Files, Selection, TrackSegmentId, TrackSegmentRevisionId};
|
||||
|
||||
/// What the routing tool needs about the selected segments (see
|
||||
/// [`Selection::segment_locations`]): where their anchors are among their trackpoints.
|
||||
///
|
||||
/// Trackpoints are numbered like in the [`crate::StatisticsBuffer`], over all the selected
|
||||
/// segments one after the other, except that the segments that are not covered by the routing
|
||||
/// tool (waypoints) are left out.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct RoutingBuffer {
|
||||
/// The index of each anchor in the selection, in order. The first and last trackpoints of a
|
||||
/// segment always are anchors.
|
||||
pub anchor_indices: Vec<u32>,
|
||||
/// The lowest map zoom level at which each anchor is shown.
|
||||
pub anchor_zooms: Vec<u8>,
|
||||
/// Index in the selection of the first trackpoint of each selected segment that has some,
|
||||
/// which tells which segment an anchor or a trackpoint belongs to.
|
||||
pub segment_starts: Vec<u32>,
|
||||
/// Id of each selected segment that has trackpoints, as `segment_starts`.
|
||||
pub segment_ids: Vec<TrackSegmentId>,
|
||||
/// Changes when the selected segments, or their trackpoints, change. Commands that refer to
|
||||
/// trackpoints by their index in the selection carry it, to be refused if the selection is
|
||||
/// not the one the indices were taken from anymore.
|
||||
pub revision: u32,
|
||||
/// What the revision stands for.
|
||||
signature: Vec<(TrackSegmentId, TrackSegmentRevisionId)>,
|
||||
}
|
||||
|
||||
impl RoutingBuffer {
|
||||
pub fn update(&mut self, files: Option<&Files>, selection: &Selection, order: &[FileId]) {
|
||||
self.anchor_indices.clear();
|
||||
self.anchor_zooms.clear();
|
||||
self.segment_starts.clear();
|
||||
self.segment_ids.clear();
|
||||
|
||||
let mut signature = vec![];
|
||||
let mut index = 0;
|
||||
for location in files
|
||||
.map(|files| selection.segment_locations(files, order))
|
||||
.unwrap_or_default()
|
||||
{
|
||||
let segment = location.segment(files.unwrap());
|
||||
signature.push((segment.id, segment.rev_id));
|
||||
if segment.is_empty() {
|
||||
continue;
|
||||
}
|
||||
self.segment_starts.push(index as u32);
|
||||
self.segment_ids.push(segment.id);
|
||||
for trkpt in segment.iter() {
|
||||
if let Some(zoom) = trkpt.anchor {
|
||||
self.anchor_indices.push(index as u32);
|
||||
self.anchor_zooms.push(zoom);
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if signature != self.signature {
|
||||
self.revision = self.revision.wrapping_add(1);
|
||||
self.signature = signature;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{collections::HashSet, rc::Rc};
|
||||
|
||||
use crate::parse;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn loaded() -> (Files, FileId) {
|
||||
let data = std::fs::read("data/with_tracks_and_segments.gpx").unwrap();
|
||||
let file = parse(&data, &mut Default::default()).unwrap();
|
||||
let id = file.id;
|
||||
let mut files = Files::default();
|
||||
files.insert(id, Rc::new(file));
|
||||
(files, id)
|
||||
}
|
||||
|
||||
fn file_selection(id: FileId) -> Selection {
|
||||
Selection::File {
|
||||
file_ids: HashSet::from([id]),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_anchors_and_starts_follow_the_selected_segments() {
|
||||
let (files, id) = loaded();
|
||||
let lengths: Vec<usize> = files[&id]
|
||||
.trk
|
||||
.iter()
|
||||
.flat_map(|trk| trk.trkseg.iter().map(|segment| segment.len()))
|
||||
.collect();
|
||||
let mut buffer = RoutingBuffer::default();
|
||||
buffer.update(Some(&files), &file_selection(id), &[id]);
|
||||
|
||||
// an empty segment has no start
|
||||
assert_eq!(buffer.segment_ids.len(), buffer.segment_starts.len());
|
||||
let mut start = 0;
|
||||
let expected: Vec<u32> = lengths
|
||||
.iter()
|
||||
.filter(|len| **len > 0)
|
||||
.map(|len| {
|
||||
start += len;
|
||||
(start - len) as u32
|
||||
})
|
||||
.collect();
|
||||
assert_eq!(buffer.segment_starts, expected);
|
||||
// the ends of the segments are always anchors, shown at every zoom level
|
||||
assert_eq!(buffer.anchor_indices.len(), buffer.anchor_zooms.len());
|
||||
assert!(buffer.anchor_indices.windows(2).all(|w| w[0] < w[1]));
|
||||
let total: usize = lengths.iter().sum();
|
||||
let ends = expected
|
||||
.iter()
|
||||
.copied()
|
||||
.chain(expected.iter().skip(1).map(|start| start - 1))
|
||||
.chain([total as u32 - 1]);
|
||||
for end in ends {
|
||||
let position = buffer.anchor_indices.iter().position(|i| *i == end);
|
||||
assert_eq!(position.map(|p| buffer.anchor_zooms[p]), Some(0), "{end}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nothing_without_segments() {
|
||||
let (files, id) = loaded();
|
||||
let mut buffer = RoutingBuffer::default();
|
||||
buffer.update(Some(&files), &Selection::Empty, &[id]);
|
||||
assert!(buffer.anchor_indices.is_empty() && buffer.segment_starts.is_empty());
|
||||
// waypoints are not covered by the routing tool
|
||||
buffer.update(Some(&files), &Selection::Waypoints { file_id: id }, &[id]);
|
||||
assert!(buffer.anchor_indices.is_empty() && buffer.segment_starts.is_empty());
|
||||
buffer.update(None, &file_selection(id), &[id]);
|
||||
assert!(buffer.anchor_indices.is_empty() && buffer.segment_starts.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_revision_changes_with_the_selected_segments() {
|
||||
let (mut files, id) = loaded();
|
||||
let mut buffer = RoutingBuffer::default();
|
||||
buffer.update(Some(&files), &file_selection(id), &[id]);
|
||||
let first = buffer.revision;
|
||||
|
||||
// the same segments: the indices still mean the same trackpoints
|
||||
buffer.update(Some(&files), &file_selection(id), &[id]);
|
||||
assert_eq!(buffer.revision, first);
|
||||
|
||||
// other trackpoints in the same segment
|
||||
let mut file = (*files[&id]).clone();
|
||||
file.trk[0].trkseg[0].rev_id = Default::default();
|
||||
files.insert(id, Rc::new(file));
|
||||
buffer.update(Some(&files), &file_selection(id), &[id]);
|
||||
assert_ne!(buffer.revision, first);
|
||||
let second = buffer.revision;
|
||||
|
||||
// another selection
|
||||
buffer.update(Some(&files), &Selection::Empty, &[id]);
|
||||
assert_ne!(buffer.revision, second);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user