From de324daadaea2d076b709f8b3264c82c59935967 Mon Sep 17 00:00:00 2001 From: Tsuki Date: Tue, 12 Mar 2024 18:38:54 +0800 Subject: [PATCH] sync --- Cargo.lock | 50 ++++++++- Cargo.toml | 1 + src/components/monitor/messages.rs | 4 +- src/components/monitor/monitor.rs | 118 ++++++++++++--------- src/map_tile.rs | 165 +++++++++++++++++++++++++---- src/widgets/render/imp.rs | 6 +- src/widgets/render/mod.rs | 7 +- 7 files changed, 270 insertions(+), 81 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b48f084..d19f865 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -92,6 +92,19 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.0.2" @@ -525,6 +538,7 @@ dependencies = [ "proj", "proj-sys", "quadtree_rs", + "quick_cache", "radarg_plugin_interface", "rayon", "regex", @@ -1023,9 +1037,9 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" @@ -3272,6 +3286,18 @@ dependencies = [ "serde", ] +[[package]] +name = "quick_cache" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c20af3800cee5134b79a3bd4a3d4b583c16ccfa5f53338f46400851a5b3819" +dependencies = [ + "ahash", + "equivalent", + "hashbrown", + "parking_lot", +] + [[package]] name = "quote" version = "0.6.13" @@ -5131,6 +5157,26 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", +] + [[package]] name = "zip" version = "0.6.6" diff --git a/Cargo.toml b/Cargo.toml index b982c84..8662f69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,6 +93,7 @@ rgb = "0.8.37" slippy-map-tiles = "0.16.0" reqwest = "0.11.25" url = "2.5.0" +quick_cache = "0.4.1" [build-dependencies] diff --git a/src/components/monitor/messages.rs b/src/components/monitor/messages.rs index 52aa099..c24252a 100644 --- a/src/components/monitor/messages.rs +++ b/src/components/monitor/messages.rs @@ -7,12 +7,12 @@ use crate::{ }; pub enum MonitorInputMsg { - NewElement((ElementKey, ElementID)), AddWidget(Box), RemoveWidget, AddMetaItem(HashMap), ClearMetaItems, UpdateMetaItem(HashMap), + RefreshTiles, RefreshLayerList, SetRenderRange(f64, f64, f64, f64), ChangeZoom(u8), @@ -22,10 +22,10 @@ pub enum MonitorInputMsg { impl Debug for MonitorInputMsg { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { + MonitorInputMsg::RefreshTiles => write!(f, "MonitorInputMsg::RefreshTiles"), MonitorInputMsg::ChangeZoom(_) => write!(f, "MonitorInputMsg::ChangeZoom"), MonitorInputMsg::SetRenderRange(_, _, _, _) => write!(f, "MonitorInputMsg::SetRenderRange"), MonitorInputMsg::RefreshLayerList => write!(f, "MonitorInputMsg::RefreshLayerList"), - MonitorInputMsg::NewElement(_) => write!(f, "MonitorInputMsg::NewElement"), MonitorInputMsg::None => write!(f, "MonitorInputMsg::None"), MonitorInputMsg::AddWidget(_) => write!(f, "MonitorInputMsg::AddWidget"), MonitorInputMsg::RemoveWidget => write!(f, "MonitorInputMsg::RemoveWidget"), diff --git a/src/components/monitor/monitor.rs b/src/components/monitor/monitor.rs index 453cb4b..e6c1d15 100644 --- a/src/components/monitor/monitor.rs +++ b/src/components/monitor/monitor.rs @@ -26,6 +26,7 @@ use crate::map_tile_utils::lat_lon_to_zoom; use crate::pipeline::element::Target; use adw::prelude::*; use femtovg::ImageId; +use quick_cache::unsync::Cache; use relm4::{component::Component, *}; use slippy_map_tiles::Tile; use tokio::task; @@ -44,7 +45,9 @@ pub struct MonitorModel { sidebar_width: i32, zoom: u8, #[do_not_track] - map_tile_getter: Arc, + tile_cache: Cache, + #[do_not_track] + map_tile_getter: Arc>, #[do_not_track] tiles: Rc>>>, new_layer: i8, @@ -102,41 +105,15 @@ impl Component for MonitorModel { }, set_tiles: model.tiles.clone(), connect_range_changing_notify[sender] => move |r| { - let map_tile_getter = map_tile_getter.clone(); - let ((x1 , x2), (y1,y2))= r.render_range(); - let new_tiles = map_tile_getter.new_tiles(model.zoom ,(y1 as f32, y2 as f32),(x1 as f32,x2 as f32)); - let mut tiles = (*tiles).borrow_mut(); - let new_tiles = new_tiles.filter(|x| !tiles.contains_key(x)).collect::>(); - tiles.extend(new_tiles.iter().map(|x| (*x, None))); - - - if new_tiles.len() > 0 { - let new_sender = sender.clone(); - sender.oneshot_command(async move { - for tile in new_tiles { - let map_tile_getter = map_tile_getter.clone(); - let sender = new_sender.clone(); - task::spawn(async move { - info!("Loading tile {:?}", tile); - let result = map_tile_getter.get_tile(&tile).await; - if let Ok(result) = result { - let origin = tile.nw_corner(); - let rb = tile.se_corner(); - let bounds = ((origin.lon() as f64..rb.lon() as f64).into(), (origin.lat() as f64..rb.lat() as f64).into()); - sender.command_sender().emit(MonitorCommand::LoadTile(tile, result,(origin.lon() as f64, origin.lat() as f64), bounds)); - } - }).await; - } - MonitorCommand::None - }); - } + sender.input(MonitorInputMsg::RefreshTiles); }, connect_scale_notify[sender] => move |r| { - // let ((x1 , x2), (y1,y2))= r.render_range(); - // let w = r.width() as f32; - // let h = r.height() as f32; - // let zoom = lat_lon_to_zoom((y1,y2), (x1,x2),w, h); - // sender.input(MonitorInputMsg::ChangeZoom(zoom)); + let scale_factor = r.scale(); + let zoom = scale_factor.log2().round() as u8; + let new_zoom = model.zoom + zoom; + if model.zoom != new_zoom { + sender.input(MonitorInputMsg::ChangeZoom(new_zoom)); + } }, set_interior_layers: model.layers.clone(), }, @@ -183,6 +160,10 @@ impl Component for MonitorModel { self.sidebar.emit(SideBarInputMsg::ClearMetaItems); self.sidebar.emit(SideBarInputMsg::AddMetaItems(map)) } + MonitorInputMsg::RefreshTiles => { + let ((x1 , x2), (y1,y2)) = widgets.renderer.render_range(); + // Self::load_cache_tile(&mut self.tile_cache,&sender, &self.map_tile_getter, self.zoom, ((y1 as f32, y2 as f32),(x1 as f32,x2 as f32))); + } MonitorInputMsg::AddWidget(widget) => match widget.widget_type() { WidgetType::Cairo => { let frame = WidgetFrame::new(); @@ -195,14 +176,7 @@ impl Component for MonitorModel { MonitorInputMsg::ChangeZoom(zoom) => { if self.zoom != zoom { self.zoom = zoom; - - let mut ts = self.tiles.borrow_mut(); - - for (k,v) in ts.drain() { - if let Some(v) = v { - widgets.renderer.delete_img(v); - } - } + sender.input(MonitorInputMsg::RefreshTiles); } } MonitorInputMsg::RemoveWidget => {} @@ -239,24 +213,19 @@ impl Component for MonitorModel { widgets: vec![], tiles: Rc::new(RefCell::new(HashMap::new())), render_cfg, + tile_cache: Cache::new(32), sidebar_open: true, sidebar_width: 400, layers: init, zoom: 4, - map_tile_getter: Arc::new(MapTile::default()), + map_tile_getter: Arc::new(tokio::sync::Mutex::new( MapTile::default())), sidebar, tracker: 0, }; let map_tile_getter = model.map_tile_getter.clone(); let tiles = model.tiles.clone(); - let widgets = view_output! {}; - - // let (h, w) = (widgets.renderer.height(), widgets.renderer.width()); - // let zoom = lat_lon_to_zoom((4.0, 53.3), (73.3, 135.0), w as f32, h as f32); - // model.zoom = zoom; - ComponentParts { model, widgets } } @@ -274,13 +243,56 @@ impl Component for MonitorModel { .renderer .load_img_mem(&tile, origin, (256f32, 256f32), bounds) .unwrap(); - // self.tiles.borrow_mut().insert(key, target); - // *self.tiles.borrow_mut().get_mut(&key) = Some(target); - self.tiles.borrow_mut().entry(key).and_modify(|x| *x = Some(target)); - widgets.render.queue_draw(); + let mut binding = self.tiles.borrow_mut(); + if !binding.contains_key(&key) { + return ; + } + binding.entry(key).and_modify(|x| *x = Some(target)); + widgets.renderer.queue_draw(); } _ => {} } self.update_view(widgets, sender); } } + + +impl MonitorModel { + fn load_tile(tiles: &Rc>>> ,sender: &ComponentSender, map_tile_getter: &Arc, zoom:u8, range:((f32,f32), (f32,f32))) { + + let map_tile_getter = map_tile_getter.clone(); + let new_tiles = map_tile_getter.new_tiles(zoom ,range.0, range.1); + let mut tiles = (*tiles).borrow_mut(); + let new_tiles = new_tiles.filter(|x| !tiles.contains_key(x)).collect::>(); + tiles.extend(new_tiles.iter().map(|x| (*x, None))); + + if new_tiles.len() > 0 { + let new_sender = sender.clone(); + sender.oneshot_command(async move { + for tile in new_tiles { + let map_tile_getter = map_tile_getter.clone(); + let sender = new_sender.clone(); + task::spawn(async move { + let result = map_tile_getter.get_tile(&tile).await; + if let Ok(result) = result { + let origin = tile.nw_corner(); + let rb = tile.se_corner(); + let bounds = ((origin.lon() as f64..rb.lon() as f64).into(), (origin.lat() as f64..rb.lat() as f64).into()); + sender.command_sender().emit(MonitorCommand::LoadTile(tile, result,(origin.lon() as f64, origin.lat() as f64), bounds)); + } + }).await; + } + MonitorCommand::None + }); + } + } + + fn load_tiles(&self, sender: &ComponentSender,range:((f32,f32), (f32,f32))) { + + // sender.oneshot_command(async move { + // MapTile::load_tiles(self.map_tile_getter.clone(), self.zoom, range).await; + // MonitorCommand::None + // }); + } + +} \ No newline at end of file diff --git a/src/map_tile.rs b/src/map_tile.rs index 493ba68..a663dab 100644 --- a/src/map_tile.rs +++ b/src/map_tile.rs @@ -1,41 +1,78 @@ -use crate::map_tile_utils::lat_lon_to_zoom; -use femtovg::ImageSource; -use reqwest::{Client, Error, Url}; -use slippy_map_tiles::{BBox, merc_location_to_tile_coords, Tile}; use crate::coords::Range; +use crate::map_tile_utils::lat_lon_to_zoom; +use crate::pipeline::element::{Target, TargetType}; +use femtovg::ImageSource; +use futures::future::BoxFuture; +use gtk::ffi::gtk_about_dialog_add_credit_section; +use quick_cache::sync::Cache; +use reqwest::{Client, Error, Url}; +use slippy_map_tiles::{merc_location_to_tile_coords, BBox, Tile}; +use smallvec::SmallVec; +use sorted_vec::SortedSet; +use std::collections::HashSet; +use std::sync::Arc; +use relm4::{ComponentSender, Sender}; +use tokio::sync::Mutex; +use tokio::task; +use tracing::{debug, info}; +use crate::components::messages::MonitorInputMsg; +use crate::components::MonitorModel; pub struct MapTile { server: String, api_key: String, style: String, client: Client, + onloading: HashSet, + cache: Arc>>>, } -impl Default for MapTile{ +impl Default for MapTile { fn default() -> Self { - Self{ - server: "https://tiles.stadiamaps.com/tiles/".to_string(), - api_key: "06f1aeed-5d91-48e3-9ce5-1e99063f7f73".to_string(), - style: "stamen_terrain/".to_string(), - client: Client::new(), - } + Self { + server: "https://tiles.stadiamaps.com/tiles/".to_string(), + api_key: "06f1aeed-5d91-48e3-9ce5-1e99063f7f73".to_string(), + style: "stamen_terrain/".to_string(), + client: Client::new(), + onloading: HashSet::new(), + cache: Arc::new(std::sync::Mutex::new(Cache::new(32))), + } } } impl MapTile { - - pub fn new_tiles(&self, zoom: u8, lat_range: (f32, f32), lon_range: (f32, f32)) -> impl Iterator + Sized { - let bbox= BBox::new(lat_range.1 , lon_range.0, lat_range.0, lon_range.1).unwrap(); + pub fn new_tiles( + &self, + zoom: u8, + lat_range: (f32, f32), + lon_range: (f32, f32), + ) -> impl Iterator + Sized { + let bbox = BBox::new(lat_range.1, lon_range.0, lat_range.0, lon_range.1).unwrap(); let tiles = bbox.tiles_for_zoom(zoom); tiles } - pub async fn get_tile( - &self, - tile: &Tile - ) -> Result, Error> - { + + fn get_tile_task(&self, tile: &Tile) -> BoxFuture<'static, Result, Error>> { + let base_url = Url::parse(&self.server).unwrap(); + let mut request_url = base_url + .join(&self.style) + .unwrap() + .join(&format!("{}/{}/{}.png", tile.zoom(), tile.x(), tile.y())) + .unwrap(); + let client = self.client.clone(); + let key = self.api_key.clone(); + Box::pin(async move { + let result = client + .get(request_url) + .query(&[("api_key", &key)]) + .send() + .await?; + let bytes = result.bytes().await?; + Ok(bytes.to_vec()) + }) + } + pub async fn get_tile(&self, tile: &Tile) -> Result, Error> { let base_url = Url::parse(&self.server).unwrap(); - // let zoom = lat_lon_to_zoom(lat_range, lat_range, w, h); let mut request_url = base_url .join(&self.style) .unwrap() @@ -51,4 +88,92 @@ impl MapTile { let bytes = result.bytes().await?; Ok(bytes.to_vec()) } + + pub fn load_tiles( + &self, + zoom: u8, + range: ((f32, f32), (f32, f32)), + sender: ComponentSender, + ) -> BoxFuture<'static, ()> { + let new_tiles = self.new_tiles(zoom, range.0, range.1); + let cache = (*self.cache).lock().unwrap(); + let new_tiles = new_tiles + .filter(|x| cache.peek(x).is_none() && !self.onloading.contains(x)) + .collect::>(); + + if new_tiles.len() > 0 { + let tasks = new_tiles.into_iter().map(move |tile| { + let _task = self.get_tile_task(&tile); + let sender = sender.clone(); + let cache = self.cache.clone(); + task::spawn(async move { + let result = _task.await; + if let Ok(result) = result { + let origin = tile.nw_corner(); + let rb = tile.se_corner(); + let bounds = ( + (origin.lon() as f64..rb.lon() as f64).into(), + (origin.lat() as f64..rb.lat() as f64).into(), + ); + let result = Target::new( + TargetType::Mem(result), + 256.0, + 256.0, + bounds, + None, + None + ); + cache.lock().unwrap().insert(tile, Arc::new(result)); + sender.input(MonitorInputMsg::RefreshTiles); + } + }) + }); + Box::pin(async move { + for task in tasks { + task.await.unwrap(); + } + }); + } else { + debug!("No new tiles to load"); + } + + Box::pin(async move {}) + } + + pub fn current_tiles(&self, zoom: u8, range: ((f32, f32), (f32, f32))) -> Vec> { + let current = self.new_tiles(zoom, range.0, range.1); + let mut total_len = 0; + let mut results = Vec::new(); + let mut cached = Vec::new(); + let cache = self.cache.lock().unwrap(); + for tile in current { + total_len += 1; + if let Some(target) = cache.get(&tile) { + results.push(target); + } else { + debug!("Tile {:?} is not in cache", tile); + let center = tile.center_point(); + let mut start_zoom = zoom - 1; + while start_zoom > 0 { + let (tile, (x, y)) = merc_location_to_tile_coords( + center.lon() as f64, + center.lat() as f64, + start_zoom, + ); + let tile = Tile::new(start_zoom, x, y).unwrap(); + if cached.contains(&tile) { + break; + } else { + cached.push(tile); + } + if let Some(target) = cache.get(&tile) { + results.insert(0, target); + break; + } + start_zoom -= 1; + } + } + } + results + } } diff --git a/src/widgets/render/imp.rs b/src/widgets/render/imp.rs index 1db6581..de49cc0 100644 --- a/src/widgets/render/imp.rs +++ b/src/widgets/render/imp.rs @@ -14,6 +14,7 @@ use std::num::NonZeroU32; use std::rc::Rc; use std::sync::{Arc, Mutex}; use slippy_map_tiles::Tile; +use tracing::info; #[derive(Debug, Default, Clone, Copy, PartialEq)] pub struct RenderConfig { @@ -43,6 +44,7 @@ pub struct RenderStatus { pub(super) translate: Option<(f32, f32)>, pub(super) view_range: Option<((f64, f64), (f64, f64))>, translation: Option<(f64, f64)>, + init_scale_rate: f64, init_translation: (f64, f64), } @@ -187,6 +189,7 @@ impl GLAreaImpl for Render { let scale = ((lat1 - lat2).abs() / h as f64).max((lon1 - lon2).abs() / w as f64); status.scale_rate.replace(scale); + status.init_scale_rate = scale; status.view_range = None; } else { @@ -206,6 +209,7 @@ impl GLAreaImpl for Render { RenderMotion::Scale => { let scale_rate = status.scale_rate.unwrap(); let scale_flag = status.scale as f64; + let initial = status.init_scale_rate; let step = scale_rate * 0.1; let (tx, ty) = status.translation.unwrap(); @@ -213,7 +217,7 @@ impl GLAreaImpl for Render { let scaled = scale_rate + scale_flag * step; status.scale_rate = Some(scaled); - self.obj().set_scale(scaled as f64); + self.obj().set_scale(initial / scaled); let sx = scale_flag * step * px as f64; let sy = scale_flag * step * py as f64; status.translation = Some((tx - sx, ty - sy)); diff --git a/src/widgets/render/mod.rs b/src/widgets/render/mod.rs index f74ef4e..2c0b26e 100644 --- a/src/widgets/render/mod.rs +++ b/src/widgets/render/mod.rs @@ -69,7 +69,6 @@ impl Render { status.scale = y as f32; status.motion = RenderMotion::Scale; }); - r.set_range_changing(0.0); r.queue_render(); Inhibit(false) } @@ -233,10 +232,12 @@ impl Render { pub fn delete_img(&self, img: Target) { let mut canvas = self.get_canvas(); let cvs = canvas.as_mut().unwrap(); - // cvs.delete_image() - if let TargetType::ImageId(id) = img.target { cvs.delete_image(id); } } + + pub fn get_scale(&self) -> f32 { + self.imp().status.borrow().scale + } }