From 74f352462e7c92c2e0d6c14d8196030279d4f683 Mon Sep 17 00:00:00 2001 From: Maxim Date: Tue, 6 Feb 2024 14:44:58 +0300 Subject: [PATCH] Add methods to manipulate map layer list (#43) Add `LayerCollection` struct with some methods to change the layers list of a map. Also provides `as_any` method on layers to allow downcasting layers from the list into concrete types. Fixes #39 --- .github/workflows/check.yml | 2 +- galileo/Cargo.toml | 3 + galileo/src/galileo_map.rs | 18 +- galileo/src/layer/feature_layer/mod.rs | 39 +- galileo/src/layer/mod.rs | 40 +- galileo/src/layer/raster_tile.rs | 9 + galileo/src/layer/vector_tile_layer/mod.rs | 9 + galileo/src/map/layer_collection.rs | 593 +++++++++++++++++++++ galileo/src/map/mod.rs | 17 +- galileo/src/render/wgpu/mod.rs | 4 +- 10 files changed, 711 insertions(+), 23 deletions(-) create mode 100644 galileo/src/map/layer_collection.rs diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index f18e841..ab8f8e1 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -18,7 +18,7 @@ jobs: - name: Build run: cargo build --verbose --all - name: Tests - run: cargo test --verbose + run: cargo test --features _tests --verbose fmt: name: Rustfmt diff --git a/galileo/Cargo.toml b/galileo/Cargo.toml index ef48b15..a4af854 100644 --- a/galileo/Cargo.toml +++ b/galileo/Cargo.toml @@ -21,6 +21,9 @@ default = ["wgpu", "serde", "winit"] wgpu = ["dep:wgpu", "raw-window-handle"] geojson = ["dep:geojson", "galileo-types/geojson"] +# Used to provide some fixtures for doctests +_tests = [] + [dependencies] cfg-if = "1" async-trait = "0.1.68" diff --git a/galileo/src/galileo_map.rs b/galileo/src/galileo_map.rs index e564169..69de9f5 100644 --- a/galileo/src/galileo_map.rs +++ b/galileo/src/galileo_map.rs @@ -289,19 +289,25 @@ impl MapBuilder { self } + pub fn create_raster_tile_layer( + tile_source: impl UrlSource + 'static, + tile_scheme: TileSchema, + ) -> RasterTileLayer> { + let cache_controller = Some(FileCacheController::new(".tile_cache")); + + let tile_provider = UrlImageProvider::new(tile_source, cache_controller); + RasterTileLayer::new(tile_scheme, tile_provider, None) + } + #[cfg(not(target_arch = "wasm32"))] pub fn with_raster_tiles( mut self, tile_source: impl UrlSource + 'static, tile_scheme: TileSchema, ) -> Self { - let cache_controller = Some(FileCacheController::new(".tile_cache")); - - let tile_provider = UrlImageProvider::new(tile_source, cache_controller); - self.layers.push(Box::new(RasterTileLayer::new( + self.layers.push(Box::new(Self::create_raster_tile_layer( + tile_source, tile_scheme, - tile_provider, - None, ))); self } diff --git a/galileo/src/layer/feature_layer/mod.rs b/galileo/src/layer/feature_layer/mod.rs index ce1927f..434b637 100644 --- a/galileo/src/layer/feature_layer/mod.rs +++ b/galileo/src/layer/feature_layer/mod.rs @@ -20,6 +20,7 @@ use galileo_types::geometry::{CartesianGeometry2d, Geom, Geometry}; use galileo_types::geometry_type::{CartesianSpace2d, CartesianSpace3d, GeoSpace2d}; use maybe_sync::{MaybeSend, MaybeSync}; use num_traits::AsPrimitive; +use std::any::Any; use std::marker::PhantomData; use std::sync::{Arc, RwLock}; @@ -283,9 +284,9 @@ where impl Layer for FeatureLayer where P: NewGeoPoint + 'static, - F: Feature + MaybeSend + MaybeSync, + F: Feature + MaybeSend + MaybeSync + 'static, F::Geom: Geometry, - S: Symbol + MaybeSend + MaybeSync, + S: Symbol + MaybeSend + MaybeSync + 'static, { fn render(&self, view: &MapView, canvas: &mut dyn Canvas) { if self.features.is_empty() { @@ -336,14 +337,22 @@ where fn set_messenger(&mut self, messenger: Box) { *self.messenger.write().unwrap() = Some(messenger); } + + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } } impl Layer for FeatureLayer where P: NewCartesianPoint2d + Clone + 'static, - F: Feature + MaybeSend + MaybeSync, + F: Feature + MaybeSend + MaybeSync + 'static, F::Geom: Geometry, - S: Symbol + MaybeSend + MaybeSync, + S: Symbol + MaybeSend + MaybeSync + 'static, { fn render(&self, view: &MapView, canvas: &mut dyn Canvas) { let lod = self.select_lod(view.resolution()); @@ -401,15 +410,23 @@ where fn set_messenger(&mut self, messenger: Box) { *self.messenger.write().unwrap() = Some(messenger); } + + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } } impl Layer for FeatureLayer where - P: NewCartesianPoint3d, + P: NewCartesianPoint3d + 'static, P::Num: AsPrimitive, - F: Feature + MaybeSend + MaybeSync, + F: Feature + MaybeSend + MaybeSync + 'static, F::Geom: Geometry, - S: Symbol + MaybeSend + MaybeSync, + S: Symbol + MaybeSend + MaybeSync + 'static, { fn render(&self, view: &MapView, canvas: &mut dyn Canvas) { if view.crs() != &self.crs { @@ -461,4 +478,12 @@ where fn set_messenger(&mut self, messenger: Box) { *self.messenger.write().unwrap() = Some(messenger); } + + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } } diff --git a/galileo/src/layer/mod.rs b/galileo/src/layer/mod.rs index b2e01df..df5a763 100644 --- a/galileo/src/layer/mod.rs +++ b/galileo/src/layer/mod.rs @@ -2,6 +2,7 @@ use crate::messenger::Messenger; use crate::render::{Canvas, Renderer}; use crate::view::MapView; use maybe_sync::{MaybeSend, MaybeSync}; +use std::any::Any; use std::sync::{Arc, RwLock}; pub mod data_provider; @@ -18,9 +19,11 @@ pub trait Layer: MaybeSend + MaybeSync { fn render(&self, view: &MapView, canvas: &mut dyn Canvas); fn prepare(&self, view: &MapView, renderer: &Arc>); fn set_messenger(&mut self, messenger: Box); + fn as_any(&self) -> &dyn Any; + fn as_any_mut(&mut self) -> &mut dyn Any; } -impl Layer for Arc> { +impl Layer for Arc> { fn render(&self, position: &MapView, canvas: &mut dyn Canvas) { self.read().unwrap().render(position, canvas) } @@ -32,4 +35,39 @@ impl Layer for Arc> { fn set_messenger(&mut self, messenger: Box) { self.write().unwrap().set_messenger(messenger) } + + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } +} + +#[cfg(feature = "_tests")] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct TestLayer(pub &'static str); + +#[cfg(feature = "_tests")] +impl Layer for TestLayer { + fn render(&self, _view: &MapView, _canvas: &mut dyn Canvas) { + unimplemented!() + } + + fn prepare(&self, _view: &MapView, _renderer: &Arc>) { + unimplemented!() + } + + fn set_messenger(&mut self, _messenger: Box) { + unimplemented!() + } + + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } } diff --git a/galileo/src/layer/raster_tile.rs b/galileo/src/layer/raster_tile.rs index 84ec9cb..d85851f 100644 --- a/galileo/src/layer/raster_tile.rs +++ b/galileo/src/layer/raster_tile.rs @@ -7,6 +7,7 @@ use crate::tile_scheme::{TileIndex, TileSchema}; use crate::view::MapView; use maybe_sync::{MaybeSend, MaybeSync, Mutex}; use quick_cache::sync::Cache; +use std::any::Any; use std::collections::HashSet; use std::sync::{Arc, RwLock}; use web_time::{Duration, SystemTime}; @@ -335,4 +336,12 @@ where fn set_messenger(&mut self, messenger: Box) { self.messenger = Some(Arc::from(messenger)); } + + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } } diff --git a/galileo/src/layer/vector_tile_layer/mod.rs b/galileo/src/layer/vector_tile_layer/mod.rs index 35381f7..deae85f 100644 --- a/galileo/src/layer/vector_tile_layer/mod.rs +++ b/galileo/src/layer/vector_tile_layer/mod.rs @@ -4,6 +4,7 @@ use crate::render::{Canvas, PackedBundle, RenderOptions, Renderer}; use crate::tile_scheme::TileSchema; use crate::view::MapView; use nalgebra::Point2; +use std::any::Any; use std::collections::HashSet; use std::sync::{Arc, RwLock}; @@ -46,6 +47,14 @@ impl Layer for VectorTileLayer fn set_messenger(&mut self, messenger: Box) { self.tile_provider.set_messenger(messenger); } + + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } } impl VectorTileLayer { diff --git a/galileo/src/map/layer_collection.rs b/galileo/src/map/layer_collection.rs new file mode 100644 index 0000000..416342c --- /dev/null +++ b/galileo/src/map/layer_collection.rs @@ -0,0 +1,593 @@ +use crate::layer::Layer; +use std::ops::{Index, IndexMut, RangeBounds}; + +/// Collection of layers with some meta-information. +/// +/// When a map is rendered, it draws all visible layers in the order they are stored in the +/// collection. Any layer can be temporary hidden with the [`LayerCollection::hide`] or +/// [`LayerCollection::show_by`] methods. These layers will be ignored by the renderer, but +/// retain their place in the collection. +/// +/// Since a map should be able to render anything implementing the [`Layer`] trait, this +/// collection stores layers as trait objects. You can use downcasting through `Any` trait +/// to obtain a concrete layer type you work with. +/// +/// ```no_run +/// use galileo::galileo_map::VectorTileProvider; +/// use galileo::layer::{RasterTileLayer, VectorTileLayer}; +/// use galileo::map::layer_collection::LayerCollection; +/// use galileo::MapBuilder; +/// +/// let raster_tiles = MapBuilder::create_raster_tile_layer(|index| format!("url from {index:?}"), todo!()); +/// let vector_tiles = MapBuilder::create_vector_tile_layer(|index| format!("url from {index:?}"), todo!(), todo!()); +/// +/// let mut collection = LayerCollection::default(); +/// collection.push(raster_tiles); +/// collection.push(vector_tiles); +/// +/// assert!(collection[1].as_any().downcast_ref::>().is_some()); +/// ``` +#[derive(Default)] +pub struct LayerCollection(Vec); + +struct LayerEntry { + layer: Box, + is_hidden: bool, +} + +impl LayerCollection { + /// Shortens the collection, keeping the first `length` layers and dropping the rest. If + /// the length of the collection is less than `length` does nothing. + /// + /// # Examples + /// + /// ``` + /// use galileo::map::layer_collection::LayerCollection; + /// use galileo::layer::TestLayer; + /// + /// let mut collection = LayerCollection::from(vec![ + /// TestLayer("Layer A"), + /// TestLayer("Layer B"), + /// ]); + /// + /// collection.truncate(3); + /// assert_eq!(collection.len(), 2); + /// collection.truncate(1); + /// assert_eq!(collection.len(), 1); + /// assert_eq!(collection[0].as_any().downcast_ref(), Some(&TestLayer("Layer A"))); + /// ``` + pub fn truncate(&mut self, length: usize) { + self.0.truncate(length) + } + + /// Removes all layers from the collection. + /// + /// # Examples + /// + /// ``` + /// use galileo::map::layer_collection::LayerCollection; + /// use galileo::layer::TestLayer; + /// + /// let mut collection = LayerCollection::from(vec![ + /// TestLayer("Layer A"), + /// TestLayer("Layer B"), + /// ]); + /// + /// collection.clear(); + /// assert_eq!(collection.len(), 0); + /// ``` + pub fn clear(&mut self) { + self.0.clear() + } + + /// Removes a layer from the collection and returns it. The removed element is replaced by the + /// last layer in the collection. + /// + /// # Panics + /// + /// Panics if `index` equals or greater then collection length. + /// + /// # Examples + /// + /// ``` + /// use galileo::map::layer_collection::LayerCollection; + /// use galileo::layer::TestLayer; + /// + /// let mut collection = LayerCollection::from(vec![ + /// TestLayer("Layer A"), + /// TestLayer("Layer B"), + /// TestLayer("Layer C"), + /// ]); + /// + /// let removed = collection.swap_remove(0); + /// assert_eq!(removed.as_any().downcast_ref(), Some(&TestLayer("Layer A"))); + /// assert_eq!(collection[0].as_any().downcast_ref(), Some(&TestLayer("Layer C"))); + /// ``` + pub fn swap_remove(&mut self, index: usize) -> Box { + self.0.swap_remove(index).layer + } + + /// Inserts a layer at position `index`, shifting all layers after it to the right. + /// + /// # Panics + /// + /// Panics if `index > len` + /// + /// # Examples + /// + /// ``` + /// use galileo::map::layer_collection::LayerCollection; + /// use galileo::layer::TestLayer; + /// + /// let mut collection = LayerCollection::from(vec![ + /// TestLayer("Layer A"), + /// TestLayer("Layer B"), + /// ]); + /// + /// collection.insert(1, TestLayer("Layer C")); + /// assert_eq!(collection.len(), 3); + /// assert_eq!(collection[1].as_any().downcast_ref(), Some(&TestLayer("Layer C"))); + /// assert_eq!(collection[2].as_any().downcast_ref(), Some(&TestLayer("Layer B"))); + pub fn insert(&mut self, index: usize, layer: impl Layer + 'static) { + self.0.insert(index, layer.into()); + } + + /// Removes a layer at `index`, shifting all layers after it to the left and returning the + /// removed layer. + /// + /// # Panics + /// + /// Panics if `index` is out of bounds. + /// + /// # Examples + /// + /// ``` + /// use galileo::map::layer_collection::LayerCollection; + /// use galileo::layer::TestLayer; + /// + /// let mut collection = LayerCollection::from(vec![ + /// TestLayer("Layer A"), + /// TestLayer("Layer B"), + /// TestLayer("Layer C"), + /// ]); + /// + /// let removed = collection.remove(1); + /// assert_eq!(removed.as_any().downcast_ref(), Some(&TestLayer("Layer B"))); + /// assert_eq!(collection.len(), 2); + /// assert_eq!(collection[1].as_any().downcast_ref(), Some(&TestLayer("Layer C"))); + /// ``` + pub fn remove(&mut self, index: usize) -> Box { + self.0.remove(index).layer + } + + /// Retains only the layers specified by the predicate. In other words, remove all layers `l` + /// for which f(&l) returns false. + /// + /// # Examples + /// + /// ``` + /// use galileo::map::layer_collection::LayerCollection; + /// use galileo::layer::TestLayer; + /// + /// let mut collection = LayerCollection::from(vec![ + /// TestLayer("Layer A"), + /// TestLayer("Layer B"), + /// TestLayer("Layer C"), + /// ]); + /// + /// collection.retain(|layer| !layer.as_any().downcast_ref::().is_some_and(|l| l.0.ends_with("A"))); + /// + /// assert_eq!(collection.len(), 2); + /// assert_eq!(collection[0].as_any().downcast_ref(), Some(&TestLayer("Layer B"))); + /// assert_eq!(collection[1].as_any().downcast_ref(), Some(&TestLayer("Layer C"))); + /// ``` + pub fn retain(&mut self, mut f: F) + where + F: FnMut(&dyn Layer) -> bool, + { + self.0.retain(|entry| f(&*entry.layer)) + } + + /// Adds the layer to the end of the collection. + /// + /// # Examples + /// + /// ``` + /// use galileo::map::layer_collection::LayerCollection; + /// use galileo::layer::TestLayer; + /// + /// let mut collection = LayerCollection::from(vec![ + /// TestLayer("Layer A"), + /// TestLayer("Layer B"), + /// ]); + /// + /// collection.push(TestLayer("Layer C")); + /// + /// assert_eq!(collection.len(), 3); + /// assert_eq!(collection[2].as_any().downcast_ref(), Some(&TestLayer("Layer C"))); + /// ``` + pub fn push(&mut self, layer: impl Layer + 'static) { + self.0.push(layer.into()) + } + + /// Removes the last layer from the collection and returns it. Returns `None` if the collection + /// is empty. + /// + /// # Examples + /// + /// ``` + /// use galileo::map::layer_collection::LayerCollection; + /// use galileo::layer::TestLayer; + /// + /// let mut collection = LayerCollection::from(vec![ + /// TestLayer("Layer A"), + /// TestLayer("Layer B"), + /// TestLayer("Layer C"), + /// ]); + /// + /// let removed = collection.pop(); + /// + /// assert_eq!(collection.len(), 2); + /// assert_eq!(removed.unwrap().as_any().downcast_ref(), Some(&TestLayer("Layer C"))); + /// ``` + pub fn pop(&mut self) -> Option> { + self.0.pop().map(|entry| entry.layer) + } + + /// Removes the specified range of layers from the collection in bulk, returning all removed + /// layers in an iterator. If the iterator is dropped before being fully consumed, it drops + /// the remaining removed layers. + /// + /// # Panics + /// + /// Panics if the starting point is greater than the end point and if the end point is + /// greater that the length of the collection. + /// + /// # Examples + /// + /// ``` + /// use galileo::map::layer_collection::LayerCollection; + /// use galileo::layer::TestLayer; + /// + /// let mut collection = LayerCollection::from(vec![ + /// TestLayer("Layer A"), + /// TestLayer("Layer B"), + /// TestLayer("Layer C"), + /// ]); + /// + /// let drained: Vec<_> = collection.drain(0..2).collect(); + /// assert_eq!(drained.len(), 2); + /// assert_eq!(drained[1].as_any().downcast_ref(), Some(&TestLayer("Layer B"))); + /// + /// assert_eq!(collection.len(), 1); + /// assert_eq!(collection[0].as_any().downcast_ref(), Some(&TestLayer("Layer C"))); + /// ``` + pub fn drain(&mut self, range: R) -> impl Iterator> + '_ + where + R: RangeBounds, + { + self.0.drain(range).map(|entry| entry.layer) + } + + /// Returns the count of layers in the collection. + /// + /// # Examples + /// + /// ``` + /// use galileo::map::layer_collection::LayerCollection; + /// use galileo::layer::TestLayer; + /// + /// let collection = LayerCollection::from(vec![ + /// TestLayer("Layer A"), + /// TestLayer("Layer B"), + /// ]); + /// + /// assert_eq!(collection.len(), 2); + /// ``` + pub fn len(&self) -> usize { + self.0.len() + } + + /// Returns `true` if the collection contains zero layers. + /// + /// # Examples + /// + /// ``` + /// use galileo::map::layer_collection::LayerCollection; + /// use galileo::layer::TestLayer; + /// + /// let mut collection = LayerCollection::default(); + /// assert!(collection.is_empty()); + /// + /// collection.push(TestLayer("Layer A")); + /// assert!(!collection.is_empty()); + /// ``` + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Returns a layer at `index`, or `None` if index is out of bounds. + /// + /// # Examples + /// + /// ``` + /// use galileo::map::layer_collection::LayerCollection; + /// use galileo::layer::TestLayer; + /// + /// let collection = LayerCollection::from(vec![ + /// TestLayer("Layer A"), + /// TestLayer("Layer B"), + /// ]); + /// + /// assert_eq!(collection.get(1).and_then(|layer| layer.as_any().downcast_ref()), Some(&TestLayer("Layer B"))); + /// assert!(collection.get(2).is_none()); + /// ``` + pub fn get(&self, index: usize) -> Option<&dyn Layer> { + self.0.get(index).map(|entry| &*entry.layer) + } + + /// Returns a mutable reference to a layer at `index`, or `None` if index is out of bounds. + /// + /// # Examples + /// + /// ``` + /// use galileo::map::layer_collection::LayerCollection; + /// use galileo::layer::TestLayer; + /// + /// let mut collection = LayerCollection::from(vec![ + /// TestLayer("Layer A"), + /// TestLayer("Layer B"), + /// ]); + /// + /// assert_eq!(collection.get_mut(1).and_then(|layer| layer.as_any_mut().downcast_ref()), Some(&TestLayer("Layer B"))); + /// assert!(collection.get(2).is_none()); + /// ``` + pub fn get_mut(&mut self, index: usize) -> Option<&mut Box> { + self.0.get_mut(index).map(|entry| &mut entry.layer) + } + + /// Swaps two layers in the collection. + /// + /// # Panics + /// + /// Panics if `a` or `b` are out of bounds. + /// + /// # Examples + /// + /// ``` + /// use galileo::map::layer_collection::LayerCollection; + /// use galileo::layer::TestLayer; + /// + /// let mut collection = LayerCollection::from(vec![ + /// TestLayer("Layer A"), + /// TestLayer("Layer B"), + /// TestLayer("Layer C"), + /// ]); + /// + /// collection.swap(1, 2); + /// + /// assert_eq!(collection[1].as_any().downcast_ref(), Some(&TestLayer("Layer C"))); + /// assert_eq!(collection[2].as_any().downcast_ref(), Some(&TestLayer("Layer B"))); + /// ``` + pub fn swap(&mut self, a: usize, b: usize) { + self.0.swap(a, b) + } + + /// Iterates over all layers in the collection. + /// + /// ``` + /// use galileo::map::layer_collection::LayerCollection; + /// use galileo::layer::TestLayer; + /// + /// let collection = LayerCollection::from(vec![ + /// TestLayer("Layer A"), + /// TestLayer("Layer B"), + /// ]); + /// + /// let mut iterator = collection.iter(); + /// assert_eq!(iterator.next().and_then(|layer| layer.as_any().downcast_ref()), Some(&TestLayer("Layer A"))); + /// assert_eq!(iterator.next().and_then(|layer| layer.as_any().downcast_ref()), Some(&TestLayer("Layer B"))); + /// assert!(iterator.next().is_none()); + /// ``` + pub fn iter(&self) -> impl Iterator + '_ { + self.0.iter().map(|entry| &*entry.layer) + } + + /// Iterates over mutable references to all layers in the collection. + /// + /// ``` + /// use galileo::map::layer_collection::LayerCollection; + /// use galileo::layer::TestLayer; + /// + /// let mut collection = LayerCollection::from(vec![ + /// TestLayer("Layer A"), + /// TestLayer("Layer B"), + /// ]); + /// + /// let mut iterator = collection.iter_mut(); + /// assert_eq!(iterator.next().and_then(|layer| layer.as_any_mut().downcast_ref()), Some(&TestLayer("Layer A"))); + /// assert_eq!(iterator.next().and_then(|layer| layer.as_any_mut().downcast_ref()), Some(&TestLayer("Layer B"))); + /// assert!(iterator.next().is_none()); + /// ``` + pub fn iter_mut(&mut self) -> impl Iterator> + '_ { + self.0.iter_mut().map(|entry| &mut entry.layer) + } + + /// Sets the layer at `index` as invisible. The hidden layer can be later shown with + /// [`LayerCollection::show`]. + /// + /// Hidden layers are stored in the layer collection, but are not rendered to a map. + /// + /// # Panics + /// + /// Panics if `index` is out of bounds. + /// + /// # Examples + /// + /// ``` + /// use galileo::map::layer_collection::LayerCollection; + /// use galileo::layer::TestLayer; + /// + /// let mut collection = LayerCollection::from(vec![ + /// TestLayer("Layer A"), + /// TestLayer("Layer B"), + /// ]); + /// + /// collection.hide(1); + /// assert!(!collection.is_visible(1)); + /// ``` + pub fn hide(&mut self, index: usize) { + self.0[index].is_hidden = true; + } + + /// Sets the layer at `index` as visible. + /// + /// Hidden layers are stored in the layer collection, but are not rendered to a map. + /// + /// # Panics + /// + /// Panics if `index` is out of bounds. + /// + /// # Examples + /// + /// ``` + /// use galileo::map::layer_collection::LayerCollection; + /// use galileo::layer::TestLayer; + /// + /// let mut collection = LayerCollection::from(vec![ + /// TestLayer("Layer A"), + /// TestLayer("Layer B"), + /// ]); + /// + /// collection.hide(1); + /// collection.show(1); + /// assert!(collection.is_visible(1)); + /// ``` + pub fn show(&mut self, index: usize) { + self.0[index].is_hidden = false; + } + + /// Sets all layers for which the predicate returns true as visible. The rest of layers are set + /// as hidden. + /// + /// # Examples + /// + /// ``` + /// use galileo::map::layer_collection::LayerCollection; + /// use galileo::layer::TestLayer; + /// + /// let mut collection = LayerCollection::from(vec![ + /// TestLayer("Layer A"), + /// TestLayer("Layer B"), + /// TestLayer("Layer C"), + /// ]); + /// + /// collection.show_by(|layer| layer.as_any().downcast_ref::().unwrap().0.ends_with("B")); + /// + /// assert!(!collection.is_visible(0)); + /// assert!(collection.is_visible(1)); + /// assert!(!collection.is_visible(2)); + pub fn show_by(&mut self, mut f: F) + where + F: FnMut(&dyn Layer) -> bool, + { + for entry in &mut self.0 { + entry.is_hidden = !f(&*entry.layer); + } + } + + /// Returns true, if the layer at `index` is not hidden. + /// + /// Hidden layers are stored in the layer collection, but are not rendered to a map. + /// + /// # Panics + /// + /// Panics if `index` is out of bounds. + /// + /// # Examples + /// + /// ``` + /// use galileo::map::layer_collection::LayerCollection; + /// use galileo::layer::TestLayer; + /// + /// let mut collection = LayerCollection::from(vec![ + /// TestLayer("Layer A"), + /// TestLayer("Layer B"), + /// ]); + /// + /// assert!(collection.is_visible(1)); + /// collection.hide(1); + /// assert!(!collection.is_visible(1)); + /// collection.show(1); + /// assert!(collection.is_visible(1)); + /// ``` + pub fn is_visible(&self, index: usize) -> bool { + !self.0[index].is_hidden + } + + /// Iterates over all visible layers in the collection. + /// + /// # Examples + /// + /// ``` + /// use galileo::map::layer_collection::LayerCollection; + /// use galileo::layer::TestLayer; + /// + /// let mut collection = LayerCollection::from(vec![ + /// TestLayer("Layer A"), + /// TestLayer("Layer B"), + /// TestLayer("Layer C"), + /// ]); + /// + /// collection.hide(1); + /// + /// let mut iterator = collection.iter_visible(); + /// assert_eq!(iterator.next().and_then(|layer| layer.as_any().downcast_ref()), Some(&TestLayer("Layer A"))); + /// assert_eq!(iterator.next().and_then(|layer| layer.as_any().downcast_ref()), Some(&TestLayer("Layer C"))); + /// assert!(iterator.next().is_none()); + /// ``` + pub fn iter_visible(&self) -> impl Iterator + '_ { + self.0 + .iter() + .filter(|entry| !entry.is_hidden) + .map(|entry| &*entry.layer) + } +} + +impl Index for LayerCollection { + type Output = dyn Layer; + + fn index(&self, index: usize) -> &Self::Output { + &*self.0[index].layer + } +} + +impl IndexMut for LayerCollection { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut *self.0[index].layer + } +} + +impl, T: IntoIterator> From for LayerCollection { + fn from(value: T) -> Self { + Self(value.into_iter().map(|layer| layer.into()).collect()) + } +} + +impl From for LayerEntry { + fn from(value: T) -> Self { + Self { + layer: Box::new(value), + is_hidden: false, + } + } +} + +impl From> for LayerEntry { + fn from(value: Box) -> Self { + Self { + layer: value, + is_hidden: false, + } + } +} diff --git a/galileo/src/map/mod.rs b/galileo/src/map/mod.rs index b3e7a02..0c5acbf 100644 --- a/galileo/src/map/mod.rs +++ b/galileo/src/map/mod.rs @@ -1,4 +1,5 @@ use crate::layer::Layer; +use crate::map::layer_collection::LayerCollection; use crate::messenger::Messenger; use crate::render::Renderer; use crate::view::MapView; @@ -7,11 +8,13 @@ use std::sync::{Arc, RwLock}; use std::time::Duration; use web_time::SystemTime; +pub mod layer_collection; + const FRAME_DURATION: Duration = Duration::from_millis(16); pub struct Map { view: MapView, - layers: Vec>, + layers: LayerCollection, messenger: Option>, animation: Option, } @@ -36,7 +39,7 @@ impl Map { }; Self { view, - layers, + layers: layers.into(), messenger, animation: None, } @@ -46,12 +49,14 @@ impl Map { &self.view } - pub fn layers(&self) -> &[Box] { + /// Returns the list of map's layers. + pub fn layers(&self) -> &LayerCollection { &self.layers } - pub fn layer_mut(&mut self, index: usize) -> Option<&mut Box> { - self.layers.get_mut(index) + /// Returns a mutable reference to the list of map's layers. + pub fn layers_mut(&mut self) -> &mut LayerCollection { + &mut self.layers } pub(crate) fn set_view(&mut self, view: MapView) { @@ -62,7 +67,7 @@ impl Map { } pub fn load_layers(&self, renderer: &Arc>) { - for layer in &self.layers { + for layer in self.layers.iter_visible() { layer.prepare(&self.view, renderer); } } diff --git a/galileo/src/render/wgpu/mod.rs b/galileo/src/render/wgpu/mod.rs index 456a72e..2631c20 100644 --- a/galileo/src/render/wgpu/mod.rs +++ b/galileo/src/render/wgpu/mod.rs @@ -439,8 +439,8 @@ impl WgpuRenderer { fn render_map(&self, map: &Map, texture_view: &TextureView) { let view = map.view(); - for layer in map.layers() { - self.render_layer(&(**layer), view, texture_view); + for layer in map.layers().iter_visible() { + self.render_layer(layer, view, texture_view); } }