diff --git a/Cargo.toml b/Cargo.toml index 1a082ff..6a1d011 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "quadtree_rs" version = "0.1.3" authors = ["James Adam Buckland "] -edition = "2018" +edition = "2021" description = "Point/region Quadtree with support for overlapping regions." publish = true @@ -26,9 +26,9 @@ license = "Apache-2.0" maintenance = { status = "actively-developed" } [dependencies] -num = "0.2" -derive_builder = "0.7" -serde = { version = "1.0.152", features = ["derive"], optional=true} +num = "0.4" +derive_builder = "0.12" +serde = { version = "1", features = ["derive"], optional = true } [features] serde = ["dep:serde"] @@ -39,5 +39,9 @@ serde = ["dep:serde"] [dev-dependencies.cargo-husky] version = "1" default-features = false # Disable features which are enabled by default -features = ["precommit-hook", "run-cargo-test", "run-cargo-clippy", "run-cargo-fmt"] - +features = [ + "precommit-hook", + "run-cargo-test", + "run-cargo-clippy", + "run-cargo-fmt", +] diff --git a/README.md b/README.md index 331e126..fa76ba4 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ badge](https://img.shields.io/crates/v/quadtree_rs.svg)](https://crates.io/crate badge](https://docs.rs/quadtree_rs/badge.svg)](https://docs.rs/quadtree_rs) [![license](https://img.shields.io/crates/l/quadtree_rs.svg)](https://github.com/ambuc/quadtree/blob/master/LICENSE) -[Point/region Quadtree](https://en.wikipedia.org/wiki/Quadtree) with support for +[Point/region Quadtree](https://en.wikipedia.org/wiki/Quadtree) with support for overlapping regions. For documentation, see [docs.rs/quadtree_rs](https://docs.rs/quadtree_rs/). @@ -16,9 +16,9 @@ For documentation, see [docs.rs/quadtree_rs](https://docs.rs/quadtree_rs/). ```rust use quadtree_rs::{area::AreaBuilder, point::Point, Quadtree}; -// Instantiate a new quadtree which associates String values with u64 +// Instantiate a new quadtree which associates String values with u64 // coordinates. -let mut qt = Quadtree::::new(/*depth=*/4); +let mut qt = Quadtree::new(/*depth=*/4); // A depth of four means a square with width (and height) 2^4. assert_eq!(qt.width(), 16); @@ -37,7 +37,7 @@ let region_b = AreaBuilder::default() .build().unwrap(); let mut query = qt.query(region_b); -// The query region (region_b) intersects the region "foo" is associated with +// The query region (region_b) intersects the region "foo" is associated with // (region_a), so the query iterator returns "foo" by reference. assert_eq!(query.next().unwrap().value_ref(), "foo"); ``` @@ -60,7 +60,7 @@ This project is licensed under the Apache 2.0 license. # Disclaimer -This is not an official Google product. +This is not an official Google product. # TODO - [ ] Pretty-print quadtree function which plots a density map diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000..bf867e0 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +nightly diff --git a/src/area.rs b/src/area.rs index a93e1a5..b3b2335 100644 --- a/src/area.rs +++ b/src/area.rs @@ -14,13 +14,11 @@ //! A rectangular region in the tree. +use crate::point; +use num::PrimInt; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use { - crate::point, - num::PrimInt, - std::{cmp::PartialOrd, default::Default, fmt::Debug}, -}; +use std::{cmp::PartialOrd, default::Default, fmt::Debug}; /// A rectangular region in 2d space. /// diff --git a/src/entry.rs b/src/entry.rs index c84263e..b55d7af 100644 --- a/src/entry.rs +++ b/src/entry.rs @@ -15,13 +15,11 @@ //! A view into a single entry in the Quadtree. // Influenced by https://doc.rust-lang.org/std/collections/hash_map/enum.Entry.html. +use crate::{area::Area, point::Point}; +use num::PrimInt; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use { - crate::{area::Area, point::Point}, - num::PrimInt, - std::default::Default, -}; +use std::default::Default; /// A region/value association in the [`Quadtree`]. /// @@ -34,10 +32,10 @@ use { /// ``` /// use quadtree_rs::{ /// area::AreaBuilder, -/// Quadtree, +/// HashQuadtree as Quadtree, /// }; /// -/// let mut qt = Quadtree::::new(4); +/// let mut qt = Quadtree::new(4); /// let region_a = AreaBuilder::default() /// .anchor((1, 1).into()) /// .dimensions((3, 2)) diff --git a/src/handle_iter.rs b/src/handle_iter.rs index 701c956..7cdbf44 100644 --- a/src/handle_iter.rs +++ b/src/handle_iter.rs @@ -12,16 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -use { - crate::{area::Area, qtinner::QTInner, traversal::Traversal}, - num::PrimInt, - std::{collections::HashSet, default::Default, iter::FusedIterator}, -}; +use crate::{area::Area, qtinner::QTInner, traversal::Traversal}; +use num::PrimInt; +use std::{collections::HashSet, default::Default, iter::FusedIterator}; #[derive(Clone, Debug)] pub(crate) struct HandleIter<'a, U> where - U: PrimInt + Default, + U: PrimInt + Default + 'static, { search_area: Area, handle_stack: Vec, diff --git a/src/iter.rs b/src/iter.rs index 345002e..d065c81 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -12,14 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use { - crate::{ - area::Area, entry::Entry, handle_iter::HandleIter, qtinner::QTInner, traversal::Traversal, - types::StoreType, - }, - num::PrimInt, - std::iter::FusedIterator, +use crate::{ + area::Area, entry::Entry, handle_iter::HandleIter, map::Map, qtinner::QTInner, + traversal::Traversal, }; +use num::PrimInt; +use std::{iter::FusedIterator, marker::PhantomData}; /// An iterator over all regions and values of a [`Quadtree`]. /// @@ -28,29 +26,35 @@ use { /// [`iter`]: ../struct.Quadtree.html#method.iter /// [`Quadtree`]: ../struct.Quadtree.html #[derive(Clone, Debug)] -pub struct Iter<'a, U, V> +pub struct Iter<'a, U, V, M> where - U: PrimInt + Default, + M: Map, + U: PrimInt + Default + 'static, { - store: &'a StoreType, + store: &'a M, handle_iter: HandleIter<'a, U>, + _v: PhantomData, } -impl<'a, U, V> Iter<'a, U, V> +impl<'a, U, V, M> Iter<'a, U, V, M> where - U: PrimInt + Default, + M: Map, + U: PrimInt + Default + 'static, { - pub(crate) fn new(qt: &'a QTInner, store: &'a StoreType) -> Iter<'a, U, V> { + pub(crate) fn new(qt: &'a QTInner, store: &'a M) -> Self { Iter { store, handle_iter: HandleIter::new(qt, qt.region()), + _v: Default::default(), } } } -impl<'a, U, V> Iterator for Iter<'a, U, V> +impl<'a, U, V, M> Iterator for Iter<'a, U, V, M> where - U: PrimInt + Default, + M: Map, + U: PrimInt + Default + 'static, + V: 'a, { type Item = &'a Entry; @@ -59,7 +63,7 @@ where match self.handle_iter.next() { Some(handle) => Some( self.store - .get(&handle) + .get(handle) .expect("Shouldn't have an handle in the tree which isn't in the store."), ), None => None, @@ -72,7 +76,13 @@ where } } -impl FusedIterator for Iter<'_, U, V> where U: PrimInt + Default {} +impl<'a, U, V, M> FusedIterator for Iter<'a, U, V, M> +where + M: Map, + U: PrimInt + Default + 'static, + V: 'a, +{ +} /// A consuming iterator over all region/value associations held in a [`Quadtree`]. /// @@ -114,26 +124,27 @@ impl FusedIterator for IntoIter where U: PrimInt + Default {} /// [`query`]: ../struct.Quadtree.html#method.query /// [`Quadtree`]: ../struct.Quadtree.html #[derive(Clone, Debug)] -pub struct Query<'a, U, V> +pub struct Query<'a, U, V, M> where - U: PrimInt + Default, + U: PrimInt + Default + 'static, { query_region: Area, handle_iter: HandleIter<'a, U>, - store: &'a StoreType, + store: &'a M, traversal_method: Traversal, + _v: PhantomData, } -impl<'a, U, V> Query<'a, U, V> +impl<'a, U, V, M> Query<'a, U, V, M> where U: PrimInt + Default, { pub(crate) fn new( query_region: Area, qt: &'a QTInner, - store: &'a StoreType, + store: &'a M, traversal_method: Traversal, - ) -> Query<'a, U, V> + ) -> Self where U: PrimInt + Default, { @@ -150,19 +161,22 @@ where handle_iter, store, traversal_method, + _v: Default::default(), } } } -impl<'a, U, V> Iterator for Query<'a, U, V> +impl<'a, U, V, M> Iterator for Query<'a, U, V, M> where - U: PrimInt + Default, + M: Map, + U: PrimInt + Default + 'static, + V: 'a, { type Item = &'a Entry; #[inline] fn next(&mut self) -> Option { for handle in self.handle_iter.by_ref() { - if let Some(entry) = self.store.get(&handle) { + if let Some(entry) = self.store.get(handle) { if self.traversal_method.eval(entry.area(), self.query_region) { return Some(entry); } @@ -177,7 +191,13 @@ where } } -impl FusedIterator for Query<'_, U, V> where U: PrimInt + Default {} +impl<'a, U, V, M> FusedIterator for Query<'a, U, V, M> +where + M: Map, + U: PrimInt + Default + 'static, + V: 'a, +{ +} /// An iterator over the values held within a [`Quadtree`]. /// @@ -186,16 +206,19 @@ impl FusedIterator for Query<'_, U, V> where U: PrimInt + Default {} /// [`values`]: ../struct.Quadtree.html#method.values /// [`Quadtree`]: ../struct.Quadtree.html #[derive(Clone, Debug)] -pub struct Values<'a, U, V> +pub struct Values<'a, U, V, M> where - U: PrimInt + Default, + M: Map, + U: PrimInt + Default + 'static, { - pub(crate) inner: Iter<'a, U, V>, + pub(crate) inner: Iter<'a, U, V, M>, } -impl<'a, U, V> Iterator for Values<'a, U, V> +impl<'a, U, V, M> Iterator for Values<'a, U, V, M> where - U: PrimInt + Default, + M: Map, + U: PrimInt + Default + 'static, + V: 'a, { type Item = &'a V; @@ -210,7 +233,13 @@ where } } -impl FusedIterator for Values<'_, U, V> where U: PrimInt + Default {} +impl<'a, U, V, M> FusedIterator for Values<'a, U, V, M> +where + M: Map, + U: PrimInt + Default + 'static, + V: 'a, +{ +} /// An iterator over the regions held within a [`Quadtree`]. /// @@ -219,16 +248,19 @@ impl FusedIterator for Values<'_, U, V> where U: PrimInt + Default {} /// [`regions`]: ../struct.Quadtree.html#method.regions /// [`Quadtree`]: ../struct.Quadtree.html #[derive(Clone, Debug)] -pub struct Regions<'a, U, V> +pub struct Regions<'a, U, V, M> where - U: PrimInt + Default, + M: Map, + U: PrimInt + Default + 'static, { - pub(crate) inner: Iter<'a, U, V>, + pub(crate) inner: Iter<'a, U, V, M>, } -impl<'a, U, V> Iterator for Regions<'a, U, V> +impl<'a, U, V, M> Iterator for Regions<'a, U, V, M> where - U: PrimInt + Default, + M: Map, + U: PrimInt + Default + 'static, + V: 'a, { type Item = Area; @@ -243,4 +275,10 @@ where } } -impl FusedIterator for Regions<'_, U, V> where U: PrimInt + Default {} +impl<'a, U, V, M> FusedIterator for Regions<'a, U, V, M> +where + M: Map, + U: PrimInt + Default + 'static, + V: 'a, +{ +} diff --git a/src/lib.rs b/src/lib.rs index 7073e23..5fb7fc3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,10 +17,10 @@ //! //! # Quick Start //! ``` -//! use quadtree_rs::{area::AreaBuilder, point::Point, Quadtree}; +//! use quadtree_rs::{area::AreaBuilder, point::Point, HashQuadtree as Quadtree}; //! //! // Instantiate a new quadtree which associates String values with u64 coordinates. -//! let mut qt = Quadtree::::new(/*depth=*/4); +//! let mut qt = Quadtree::new(/*depth=*/4); //! //! // A depth of four means a square with width (and height) 2^4. //! assert_eq!(qt.width(), 16); @@ -45,9 +45,9 @@ //! //! # Implementation //! ``` -//! use quadtree_rs::{area::AreaBuilder, point::Point, Quadtree}; +//! use quadtree_rs::{area::AreaBuilder, point::Point, HashQuadtree as Quadtree}; //! -//! let mut qt = Quadtree::::new(2); +//! let mut qt = Quadtree::new(2); //! //! // In a quadtree, every region is (lazily) subdivided into subqudrants. //! @@ -123,613 +123,17 @@ pub mod iter; pub mod point; mod handle_iter; +mod map; mod qtinner; +mod qtree; mod traversal; -mod types; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -use { - crate::{ - area::{Area, AreaBuilder}, - entry::Entry, - handle_iter::HandleIter, - iter::{IntoIter, Iter, Query, Regions, Values}, - point::Point, - qtinner::QTInner, - traversal::Traversal, - types::StoreType, - }, - num::PrimInt, - std::{ - collections::{HashMap, HashSet}, - default::Default, - hash::Hash, - }, -}; +use entry::Entry; +use std::collections::{BTreeMap, HashMap}; -/// A data structure for storing and accessing data in 2d space. -/// -/// For historical context, other implementations, and potential uses of a -/// quadtree, see the [quadtree](https://en.wikipedia.org/wiki/Quadtree) -/// article on Wikipedia. -/// -/// ## Parameterization -/// -/// `Quadtree` is parameterized over -/// - `U`, the type of the coordinate, and -/// - `V`, the value being stored. -/// -/// `U` must implement `num::PrimInt` and a set of arithmetic operations necessary for coordinate -/// insertion and comparison. `U` must also implement `std::default` for [`derive_builder`] -/// semantics. -/// -/// ## Strictness -/// -/// Some methods ([`.query()`], [`.modify()`], and [`.delete()`]) have strict variants. While the -/// default behavior is for any operation to apply to all regions which _intersect_ some -/// operational region, the strict behavior is for the operation to apply only to those regions -/// which are _totally contained by_ the operational region. -/// -/// [`derive_builder`]: https://docs.rs/derive_builder/0.7.0/derive_builder/ -/// [`.query()`]: #method.query -/// [`.modify()`]: #method.modify -/// [`.delete()`]: #method.delete -// TODO(ambuc): Implement `.delete_by(anchor, dimensions, fn)`: `.retain()` is the inverse. -// TODO(ambuc): Implement `FromIterator<(K, V)>` for `Quadtree`. -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, PartialEq, Eq)] -pub struct Quadtree -where - U: PrimInt + Default, -{ - inner: QTInner, - store: StoreType, -} +pub type HashQuadtree = Quadtree>>; +pub type BQuadtree = Quadtree>>; -impl Quadtree -where - U: PrimInt + Default, -{ - // pub - - /// Creates a new, empty quadtree with some depth. - /// A quadtree with depth `n` will accept coordinates in the range `[0, 2^n]`. - /// ``` - /// use quadtree_rs::{point::Point, Quadtree}; - /// - /// let qt = Quadtree::::new(/*depth=*/ 2); - /// - /// // The anchor of a rectangular region is its top-left coordinate. - /// // By default, quadtrees are anchored at (0, 0). - /// assert_eq!(qt.anchor(), Point {x: 0, y: 0}); - /// assert_eq!(qt.depth(), 2); - /// assert_eq!(qt.width(), 4); - /// assert_eq!(qt.height(), 4); - /// ``` - pub fn new(depth: usize) -> Self { - Self::new_with_anchor( - point::Point { - x: U::zero(), - y: U::zero(), - }, - depth, - ) - } - - /// Creates a new, empty quadtree with some depth and an explicit anchor. - /// - /// The anchor of a rectangular region is its upper-left coordinate. The - /// anchor argument is of type [`point::Point`], and can either be - /// explicit (`Point {x: 2, y: 4}`) or implicit (`(2, 4).into()`). - /// - /// [`point::Point`]: point/struct.Point.html - /// ``` - /// use quadtree_rs::{point::Point, Quadtree}; - /// - /// let anchor = Point {x: 2, y: 4}; - /// let depth = 3_usize; - /// let qt = Quadtree::::new_with_anchor(anchor, depth); - /// - /// assert_eq!(qt.depth(), 3); - /// assert_eq!(qt.anchor(), Point {x: 2, y: 4}); - /// assert_eq!(qt.width(), 8); - /// assert_eq!(qt.height(), 8); - /// ``` - pub fn new_with_anchor(anchor: point::Point, depth: usize) -> Self { - Self { - inner: QTInner::new(anchor, depth), - store: HashMap::new(), - } - } - - /// The top-left corner (anchor) of the region which this quadtree represents. - pub fn anchor(&self) -> point::Point { - self.inner.region().anchor() - } - - /// The width of the region which this quadtree represents. - pub fn width(&self) -> usize { - self.inner.region().width().to_usize().unwrap() - } - - /// The height of the region which this quadtree represents. - pub fn height(&self) -> usize { - self.inner.region().height().to_usize().unwrap() - } - - /// The depth of the quadtree. - pub fn depth(&self) -> usize { - self.inner.depth() - } - - /// The number of elements in the quadtree. - pub fn len(&self) -> usize { - self.store.len() - } - - /// Whether or not the quadtree is empty. - pub fn is_empty(&self) -> bool { - self.store.is_empty() - } - - /// Whether or not some trial region could fit in the region which this quadtree represents. - pub fn contains(&self, area: Area) -> bool { - self.inner.region().contains(area) - } - - /// Associate some value with a region in the quadtree. - /// - /// If insertion is successful, returns a unique handle to the value. - /// - /// If the region is too large for, or doesn't overlap with, the region which this quadtree - /// represents, returns `None`. - /// ``` - /// use quadtree_rs::{area::AreaBuilder, point::Point, Quadtree}; - /// - /// let mut qt = Quadtree::::new(8); - /// - /// let region = AreaBuilder::default() - /// .anchor(Point {x: 4, y: 5}) - /// .dimensions((2,3)) - /// .build().unwrap(); - /// - /// let handle_a_1 = qt.insert(region, 5).unwrap(); - /// let handle_a_2 = qt.insert(region, 5).unwrap(); - /// - /// // Even though we inserted 5 at the same point in the quadtree, the - /// // two handles returned were not the same. - /// assert_ne!(handle_a_1, handle_a_2); - /// ``` - pub fn insert(&mut self, region: Area, val: V) -> Option { - if self.contains(region) { - return Some( - self.inner - .insert_val_at_region(region, val, &mut self.store), - ); - } - None - } - - /// Alias for [`.insert()`] which expects a [`Point`] instead of an [`Area`]. - /// - /// (An [`Area`] is really just a [`Point`] with dimensions `(1, 1)`, so - /// the point still has to fit within the region.) - /// - /// ``` - /// use quadtree_rs::{point::Point, Quadtree}; - /// - /// let mut qt = Quadtree::::new(2); - /// - /// assert!(qt.insert_pt(Point { x: 1, y: 2 }, 5_i8).is_some()); - /// ``` - /// - /// [`.insert()`]: #method.insert - /// [`Area`]: area/struct.Area.html - /// [`Point`]: point/struct.Point.html - pub fn insert_pt(&mut self, point: Point, val: V) -> Option { - if let Ok(area) = AreaBuilder::default().anchor(point).build() { - return self.insert(area, val); - } - None - } - - /// Given the handle from an [`.insert()`] operation, provides read-only - /// access to the associated [`Entry`] struct. - /// - /// Handles are unique and never re-used, so lookup of a handle to a now-deleted entry can - /// fail and return `None`. - /// - /// ``` - /// use quadtree_rs::{area::AreaBuilder, point::Point, Quadtree}; - /// - /// let mut qt = Quadtree::::new(4); - /// - /// let region = AreaBuilder::default() - /// .anchor(Point {x: 0, y: 1}) - /// .dimensions((2, 3)) - /// .build().unwrap(); - /// let handle = qt.insert(region, 9.87).unwrap(); - /// - /// let entry = qt.get(handle).unwrap(); - /// assert_eq!(entry.value_ref(), &9.87); - /// ``` - /// - /// [`.insert()`]: #method.insert - /// [`Entry`]: entry/struct.Entry.html - pub fn get(&self, handle: u64) -> Option<&Entry> { - self.store.get(&handle) - } - - /// A mutable variant of [`.get()`] which provides mutable access to the - /// associated [`Entry`] struct. - /// - /// ``` - /// use quadtree_rs::{area::AreaBuilder, point::Point, Quadtree}; - /// - /// let mut qt = Quadtree::::new(4); - /// - /// let region = AreaBuilder::default() - /// .anchor(Point {x: 0, y: 1}) - /// .dimensions((2, 3)) - /// .build().unwrap(); - /// let handle: u64 = qt.insert(region, 9.87).unwrap(); - /// - /// if let Some(entry) = qt.get_mut(handle) { - /// *entry.value_mut() += 1.0; - /// } - /// - /// assert_eq!(qt.get(handle).unwrap().value_ref(), &10.87); - /// - /// ``` - /// - /// [`.get()`]: #method.get - /// [`Entry`]: entry/struct.Entry.html - pub fn get_mut(&mut self, handle: u64) -> Option<&mut Entry> { - self.store.get_mut(&handle) - } - - /// Returns an iterator over [`&Entry`] structs representing values - /// within the query region. - /// ``` - /// use quadtree_rs::{area::AreaBuilder, Quadtree}; - /// - /// // 0123456 - /// // 0 ░░░░░░░ - /// // 1 ░░▒▒▒░░ (2,1)->3x2 - /// // 2 ░░▒▒▒░░ - /// // 3 ░░░░░░░ - /// // 4 ░▒▒▒░░░ (1,4)->3x1 - /// // 5 ░░░░░░░ - /// let mut qt = Quadtree::::new(4); - /// - /// let region_a = AreaBuilder::default() - /// .anchor((2, 1).into()) - /// .dimensions((3, 2)) - /// .build().unwrap(); - /// qt.insert(region_a, 'a'); - /// - /// let region_b = AreaBuilder::default() - /// .anchor((1, 4).into()) - /// .dimensions((3, 1)) - /// .build().unwrap(); - /// qt.insert(region_b, 'b'); - /// - /// // 0123456 - /// // 0 ░░░░░░░ - /// // 1 ░░▓▒▒░░ <-- Query over the region - /// // 2 ░░▒▒▒░░ (2,1)->1x1 - /// // 3 ░░░░░░░ - /// // 4 ░▒▒▒░░░ - /// // 5 ░░░░░░░ - /// let region_c = AreaBuilder::default() - /// .anchor((2, 1).into()).build().unwrap(); - /// let mut query_a = qt.query(region_c); - /// - /// // We can use the Entry API to destructure the result. - /// let entry = query_a.next().unwrap(); - /// assert_eq!(entry.area().height(), 2); - /// assert_eq!(entry.value_ref(), &'a'); - /// - /// // But that was the only result. - /// assert!(query_a.next().is_none()); - /// - /// // 0123456 - /// // 0 ░░░░░░░ - /// // 1 ░▒▓▓▓▒░ <-- query over the region - /// // 2 ░▒▓▓▓▒░ (0,0)->6x6. - /// // 3 ░▒▒▒▒▒░ - /// // 4 ░▓▓▓▒▒░ - /// // 5 ░░░░░░░ - /// let region_d = AreaBuilder::default() - /// .anchor((1, 1).into()) - /// .dimensions((4, 4)) - /// .build().unwrap(); - /// let query_b = qt.query(region_d); - /// - /// // It's unspecified what order the regions should - /// // return in, but there will be two of them. - /// assert_eq!(query_b.count(), 2); - /// ``` - /// - /// [`&Entry`]: entry/struct.Entry.html - /// [`.query()`]: #method.query - // TODO(ambuc): Settle on a stable return order to avoid breaking callers. - pub fn query(&self, area: Area) -> Query { - Query::new(area, &self.inner, &self.store, Traversal::Overlapping) - } - - /// A strict variant of [`.query()`]. - /// - /// [`.query()`]: #method.query - pub fn query_strict(&self, area: Area) -> Query { - Query::new(area, &self.inner, &self.store, Traversal::Strict) - } - - /// Accepts a modification lambda and applies it to all elements in the - /// quadtree which intersecting the described region. - /// - /// ``` - /// use quadtree_rs::{area::AreaBuilder, Quadtree}; - /// - /// let mut qt = Quadtree::::new(3); - /// - /// let region_a = AreaBuilder::default() - /// .anchor((0, 0).into()) - /// .build().unwrap(); - /// let handle = qt.insert(region_a, true).unwrap(); - /// - /// // Run a modification lambda over all values in region_a... - /// qt.modify(region_a, |i| *i = false); - /// - /// // ...and verify that the value was applied. - /// assert_eq!(qt.get(handle).unwrap().value_ref(), &false); - /// ``` - pub fn modify(&mut self, area: Area, f: F) - where - F: Fn(&mut V) + Copy, - { - self.modify_region(|a| a.intersects(area), f); - } - - /// A strict variant of [`.modify()`]. - /// - /// [`.modify()`]: #method.modify - pub fn modify_strict(&mut self, area: Area, f: F) - where - F: Fn(&mut V) + Copy, - { - self.modify_region(|a| area.contains(a), f); - } - - /// Alias for [`.modify()`] which runs over the entire - /// quadtree. - /// - /// [`.modify()`]: #method.modify - pub fn modify_all(&mut self, f: F) - where - F: Fn(&mut V) + Copy, - { - for entry in self.store.values_mut() { - f(entry.value_mut()); - } - } - - /// Resets the quadtree to a totally empty state. - pub fn reset(&mut self) { - self.store.clear(); - self.inner.reset(); - } - - /// Deletes all value associations which overlap a region in the tree. - /// - /// Along the way, consumed [`Entry`] entries are collected and returned in an iterator - /// [`IntoIter`]. - /// ``` - /// use quadtree_rs::{area::AreaBuilder, Quadtree}; - /// - /// let mut qt = Quadtree::::new(4); - /// - /// let region_a = AreaBuilder::default() - /// .anchor((0, 0).into()) - /// .dimensions((2, 2)) - /// .build().unwrap(); - /// qt.insert(region_a, 1.23); - /// - /// let region_b = AreaBuilder::default() - /// .anchor((1, 1).into()) - /// .dimensions((3, 2)) - /// .build().unwrap(); - /// qt.insert(region_b, 4.56); - /// - /// // 0123 - /// // 0 ░░ - /// // 1 ░▓╳░ <-- ╳ is the deletion region - /// // 2 ░░░ - /// - /// let region_c = AreaBuilder::default() - /// .anchor((2, 1).into()).build().unwrap(); - /// let mut returned_entries = qt.delete(region_c); - /// - /// // We've removed one object from the quadtree. - /// assert_eq!(returned_entries.next().unwrap().value_ref(), - /// &4.56); - /// - /// // And left one behind. - /// assert_eq!(qt.len(), 1); - /// ``` - /// - /// [`IntoIter`]: iter/struct.IntoIter.html - /// [`Entry`]: entry/struct.Entry.html - /// [`.delete()`]: #method.delete - pub fn delete(&mut self, area: Area) -> IntoIter { - self.delete_handles_and_return(self.query(area).map(|e| e.handle()).collect()) - } - - /// A strict variant of [`.delete()`]. - /// - /// [`.delete()`]: #method.delete - pub fn delete_strict(&mut self, area: Area) -> IntoIter { - self.delete_handles_and_return(self.query_strict(area).map(|e| e.handle()).collect()) - } - - #[allow(clippy::needless_pass_by_value)] - fn delete_handles_and_return(&mut self, handles: HashSet) -> IntoIter { - let error: &'static str = "I tried to look up an handle in the store which I found in the tree, but it wasn't there!"; - - let mut entries: Vec> = vec![]; - - handles.iter().for_each(|u| { - // We were just passed a hashset of handles taken from this quadtree, so it is safe to - // assume they all still exist. - entries.push(self.store.remove(u).expect(error)); - }); - - IntoIter { entries } - } - - /// Given an handle, deletes a single item from the - /// Quadtree. If that handle was found, - /// `delete_by_handle()` returns an `Entry` - /// containing its former region and value. Otherwise, - /// returns `None`. - pub fn delete_by_handle(&mut self, handle: u64) -> Option> { - // Pop the Entry out of the @store, - if let Some(entry) = self.store.remove(&handle) { - // Use the now-known region to descend into the tree efficiently, - self.inner.delete_by_handle(handle, entry.area()); - // And return the Entry. - return Some(entry); - } - // If the handle wasn't in the @store, we don't need to perform a descent. - None - } - - // TODO(ambuc): Test this fn. - /// Retains only the elements specified by the predicate. - /// - /// In other words, remove all items such that `f(&mut v)` returns `false`. - pub fn retain(&mut self, mut f: F) -> IntoIter - where - F: FnMut(&mut V) -> bool, - U: Hash, - { - // TODO(ambuc): I think this is technically correct but it seems to be interweaving three - // routines. Is there a way to simplify this? - let mut doomed: HashSet<(u64, Area)> = HashSet::new(); - for (handle, entry) in &mut self.store { - if f(entry.value_mut()) { - doomed.insert((*handle, entry.area())); - } - } - // TODO(ambuc): There is an optimization here to do one traversal with many matches, over - // many traversals i.e. one per match. - let mut entries: Vec> = vec![]; - for (handle, region) in doomed { - entries.push(self.store.remove(&handle).unwrap()); - self.inner.delete_by_handle(handle, region); - } - - IntoIter { entries } - } - // TODO(ambuc): retain_within - - /// Returns an iterator ([`Iter`]) over all [`&'a Entry`] - /// region/value associations in the Quadtree. - /// - /// [`Iter`]: iter/struct.Iter.html - /// [`&'a Entry`]: entry/struct.Entry.html - pub fn iter(&self) -> Iter { - Iter::new(&self.inner, &self.store) - } - - /// Returns an iterator ([`Regions`]) over all [`Area`] regions - /// in the Quadtree. - /// - /// [`Regions`]: iter/struct.Regions.html - /// [`Area`]: area/struct.Area.html - pub fn regions(&self) -> Regions { - Regions { - inner: Iter::new(&self.inner, &self.store), - } - } - - /// Returns an iterator ([`Values`]) over all `&'a V` values in the - /// Quadtree. - /// - /// [`Values`]: iter/struct.Values.html - pub fn values(&self) -> Values { - Values { - inner: Iter::new(&self.inner, &self.store), - } - } - - // fn - - fn modify_region(&mut self, filter: F, modify: M) - where - F: Fn(Area) -> bool, - M: Fn(&mut V) + Copy, - { - let relevant_handles: Vec = - HandleIter::new(&self.inner, self.inner.region()).collect(); - for i in relevant_handles { - if let Some(entry) = self.store.get_mut(&i) { - if filter(entry.area()) { - modify(entry.value_mut()); - } - } - } - } -} - -/// `Extend<((U, U), V)>` will silently drop values whose coordinates do not fit in the region -/// represented by the Quadtree. It is the responsibility of the callsite to ensure these points -/// fit. -impl Extend<((U, U), V)> for Quadtree -where - U: PrimInt + Default, -{ - fn extend(&mut self, iter: T) - where - T: IntoIterator, - { - for ((x, y), val) in iter { - // Ignore errors. - self.insert( - AreaBuilder::default() - .anchor(point::Point { x, y }) - .build() - .unwrap(), - val, - ); - } - } -} - -// Immutable iterator for the Quadtree, returning by-reference. -impl<'a, U, V> IntoIterator for &'a Quadtree -where - U: PrimInt + Default, -{ - type Item = &'a Entry; - type IntoIter = Iter<'a, U, V>; - - fn into_iter(self) -> Iter<'a, U, V> { - Iter::new(&self.inner, &self.store) - } -} - -impl IntoIterator for Quadtree -where - U: PrimInt + Default, -{ - type Item = Entry; - type IntoIter = IntoIter; - - fn into_iter(self) -> IntoIter { - IntoIter { - entries: self.store.into_values().collect(), - } - } -} +pub use area::{Area, AreaBuilder}; +pub use point::Point; +pub use qtree::Quadtree; diff --git a/src/map.rs b/src/map.rs new file mode 100644 index 0000000..e959314 --- /dev/null +++ b/src/map.rs @@ -0,0 +1,93 @@ +use std::collections::{BTreeMap, HashMap}; + +use num::PrimInt; + +use crate::entry::Entry; + +pub trait Map +where + U: PrimInt + Default + 'static, +{ + fn len(&self) -> usize; + fn is_empty(&self) -> bool; + fn get(&self, k: u64) -> Option<&Entry>; + fn get_mut(&mut self, k: u64) -> Option<&mut Entry>; + fn clear(&mut self); + fn insert(&mut self, k: u64, v: Entry) -> Option>; + fn remove(&mut self, k: u64) -> Option>; + fn values_mut<'a>(&'a mut self) -> impl Iterator> + where + V: 'a; + fn into_values(self) -> impl Iterator>; + fn iter<'a>(&'a self) -> impl Iterator)> + where + V: 'a; + fn iter_mut<'a>(&'a mut self) -> impl Iterator)> + where + V: 'a; +} + +macro_rules! impl_map { + ($ty:ty) => { + impl Map for $ty + where + U: PrimInt + Default + 'static, + { + fn len(&self) -> usize { + self.len() + } + + fn is_empty(&self) -> bool { + self.is_empty() + } + + fn get(&self, k: u64) -> Option<&Entry> { + self.get(&k) + } + + fn get_mut(&mut self, k: u64) -> Option<&mut Entry> { + self.get_mut(&k) + } + + fn clear(&mut self) { + self.clear() + } + + fn insert(&mut self, k: u64, v: Entry) -> Option> { + self.insert(k, v) + } + + fn remove(&mut self, k: u64) -> Option> { + self.remove(&k) + } + + fn values_mut<'a>(&'a mut self) -> impl Iterator> + where + V: 'a, + { + self.values_mut() + } + + fn into_values(self) -> impl Iterator> { + self.into_values() + } + + fn iter<'a>(&'a self) -> impl Iterator)> + where + V: 'a, + { + self.iter() + } + + fn iter_mut<'a>(&'a mut self) -> impl Iterator)> + where + V: 'a, + { + self.iter_mut() + } + } + }; +} + +impl_map!(BTreeMap>); +impl_map!(HashMap>); diff --git a/src/point.rs b/src/point.rs index 237a4ae..567b833 100644 --- a/src/point.rs +++ b/src/point.rs @@ -16,12 +16,11 @@ #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use { - num::PrimInt, - std::{ - fmt::Debug, - ops::{Add, Sub}, - }, + +use num::PrimInt; +use std::{ + fmt::Debug, + ops::{Add, Sub}, }; // Transparent alias. In docs and user-facing APIs, this resolves to (U, U). diff --git a/src/qtinner.rs b/src/qtinner.rs index 778c99a..6c6824f 100644 --- a/src/qtinner.rs +++ b/src/qtinner.rs @@ -14,12 +14,13 @@ #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; + +use crate::map::Map; use { crate::{ area::{Area, AreaBuilder}, entry::Entry, point::Point, - types::StoreType, }, num::PrimInt, std::{default::Default, fmt::Debug}, @@ -51,7 +52,7 @@ where impl Debug for QTInner where - U: PrimInt + Default + Debug, + U: PrimInt + Default + Debug + 'static, { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { if self.subquadrants.is_some() { @@ -70,7 +71,7 @@ where impl QTInner where - U: PrimInt + Default, + U: PrimInt + Default + 'static, { // pub @@ -112,16 +113,15 @@ where // Attempts to insert the value at the requested region. Returns false if the region was too // large. - pub fn insert_val_at_region( - &mut self, - req: Area, - val: V, - store: &mut StoreType, - ) -> u64 { + pub fn insert_val_at_region<'a, V, M>(&mut self, req: Area, val: V, store: &'a mut M) -> u64 + where + M: Map + Default, + V: 'a, + { let handle = self.handle_counter; self.handle_counter += 1; store.insert(handle, Entry::new((req, val), handle)); - self.insert_handle_at_region(req, handle, store); + self.insert_handle_at_region::(req, handle, store); handle } @@ -153,12 +153,7 @@ where // Attempts to insert the value at the requested region. Returns false if the region was too // large. - fn insert_handle_at_region( - &mut self, - req: Area, - handle: u64, - _store: &mut StoreType, - ) { + fn insert_handle_at_region(&mut self, req: Area, handle: u64, _store: &mut M) { // If we're at the bottom depth, it had better fit. if self.depth == 0 { self.kept_handles.push(handle); @@ -184,7 +179,7 @@ where if let Some(sqs) = self.subquadrants.as_mut() { for sq in sqs.iter_mut() { if sq.region.intersects(req) { - sq.insert_handle_at_region(req, handle, _store); + sq.insert_handle_at_region::(req, handle, _store); } } } diff --git a/src/qtree.rs b/src/qtree.rs new file mode 100644 index 0000000..17a6e82 --- /dev/null +++ b/src/qtree.rs @@ -0,0 +1,611 @@ +use crate::{ + area::{Area, AreaBuilder}, + entry::Entry, + handle_iter::HandleIter, + iter::{IntoIter, Iter, Query, Regions, Values}, + map::Map, + point::Point, + qtinner::QTInner, + traversal::Traversal, +}; + +use num::PrimInt; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; +use std::{collections::HashSet, default::Default, hash::Hash, marker::PhantomData}; + +/// A data structure for storing and accessing data in 2d space. +/// +/// For historical context, other implementations, and potential uses of a +/// quadtree, see the [quadtree](https://en.wikipedia.org/wiki/Quadtree) +/// article on Wikipedia. +/// +/// ## Parameterization +/// +/// `Quadtree` is parameterized over +/// - `U`, the type of the coordinate, and +/// - `V`, the value being stored. +/// +/// `U` must implement `num::PrimInt` and a set of arithmetic operations necessary for coordinate +/// insertion and comparison. `U` must also implement `std::default` for [`derive_builder`] +/// semantics. +/// +/// ## Strictness +/// +/// Some methods ([`.query()`], [`.modify()`], and [`.delete()`]) have strict variants. While the +/// default behavior is for any operation to apply to all regions which _intersect_ some +/// operational region, the strict behavior is for the operation to apply only to those regions +/// which are _totally contained by_ the operational region. +/// +/// [`derive_builder`]: https://docs.rs/derive_builder/0.7.0/derive_builder/ +/// [`.query()`]: #method.query +/// [`.modify()`]: #method.modify +/// [`.delete()`]: #method.delete +// TODO(ambuc): Implement `.delete_by(anchor, dimensions, fn)`: `.retain()` is the inverse. +// TODO(ambuc): Implement `FromIterator<(K, V)>` for `Quadtree`. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialEq, Eq)] +pub struct Quadtree +where + M: Map + Default, + U: PrimInt + Default + 'static, +{ + pub(crate) inner: QTInner, + pub(crate) store: M, + _v: PhantomData, +} + +impl Quadtree +where + M: Map + Default, + U: PrimInt + Default + 'static, +{ + // pub + + /// Creates a new, empty quadtree with some depth. + /// A quadtree with depth `n` will accept coordinates in the range `[0, 2^n]`. + /// ``` + /// use quadtree_rs::{point::Point, HashQuadtree as Quadtree}; + /// + /// let qt: Quadtree = Quadtree::new(/*depth=*/ 2); + /// + /// // The anchor of a rectangular region is its top-left coordinate. + /// // By default, quadtrees are anchored at (0, 0). + /// assert_eq!(qt.anchor(), Point {x: 0, y: 0}); + /// assert_eq!(qt.depth(), 2); + /// assert_eq!(qt.width(), 4); + /// assert_eq!(qt.height(), 4); + /// ``` + pub fn new(depth: usize) -> Self { + Self::new_with_anchor( + Point { + x: U::zero(), + y: U::zero(), + }, + depth, + ) + } + + /// Creates a new, empty quadtree with some depth and an explicit anchor. + /// + /// The anchor of a rectangular region is its upper-left coordinate. The + /// anchor argument is of type [`point::Point`], and can either be + /// explicit (`Point {x: 2, y: 4}`) or implicit (`(2, 4).into()`). + /// + /// [`point::Point`]: point/struct.Point.html + /// ``` + /// use quadtree_rs::{point::Point, HashQuadtree as Quadtree}; + /// + /// let anchor = Point {x: 2, y: 4}; + /// let depth = 3_usize; + /// let qt: Quadtree = Quadtree::new_with_anchor(anchor, depth); + /// + /// assert_eq!(qt.depth(), 3); + /// assert_eq!(qt.anchor(), Point {x: 2, y: 4}); + /// assert_eq!(qt.width(), 8); + /// assert_eq!(qt.height(), 8); + /// ``` + pub fn new_with_anchor(anchor: Point, depth: usize) -> Self { + Self { + inner: QTInner::new(anchor, depth), + store: Default::default(), + _v: Default::default(), + } + } + + /// The top-left corner (anchor) of the region which this quadtree represents. + pub fn anchor(&self) -> Point { + self.inner.region().anchor() + } + + /// The width of the region which this quadtree represents. + pub fn width(&self) -> usize { + self.inner.region().width().to_usize().unwrap() + } + + /// The height of the region which this quadtree represents. + pub fn height(&self) -> usize { + self.inner.region().height().to_usize().unwrap() + } + + /// The depth of the quadtree. + pub fn depth(&self) -> usize { + self.inner.depth() + } + + /// The number of elements in the quadtree. + pub fn len(&self) -> usize { + self.store.len() + } + + /// Whether or not the quadtree is empty. + pub fn is_empty(&self) -> bool { + self.store.is_empty() + } + + /// Whether or not some trial region could fit in the region which this quadtree represents. + pub fn contains(&self, area: Area) -> bool { + self.inner.region().contains(area) + } + + /// Associate some value with a region in the quadtree. + /// + /// If insertion is successful, returns a unique handle to the value. + /// + /// If the region is too large for, or doesn't overlap with, the region which this quadtree + /// represents, returns `None`. + /// ``` + /// use quadtree_rs::{area::AreaBuilder, point::Point, HashQuadtree as Quadtree}; + /// + /// let mut qt = Quadtree::new(8); + /// + /// let region = AreaBuilder::default() + /// .anchor(Point {x: 4, y: 5}) + /// .dimensions((2,3)) + /// .build().unwrap(); + /// + /// let handle_a_1 = qt.insert(region, 5).unwrap(); + /// let handle_a_2 = qt.insert(region, 5).unwrap(); + /// + /// // Even though we inserted 5 at the same point in the quadtree, the + /// // two handles returned were not the same. + /// assert_ne!(handle_a_1, handle_a_2); + /// ``` + pub fn insert(&mut self, region: Area, val: V) -> Option { + if self.contains(region) { + return Some( + self.inner + .insert_val_at_region(region, val, &mut self.store), + ); + } + None + } + + /// Alias for [`.insert()`] which expects a [`Point`] instead of an [`Area`]. + /// + /// (An [`Area`] is really just a [`Point`] with dimensions `(1, 1)`, so + /// the point still has to fit within the region.) + /// + /// ``` + /// use quadtree_rs::{point::Point, HashQuadtree as Quadtree}; + /// + /// let mut qt: Quadtree = Quadtree::new(2); + /// + /// assert!(qt.insert_pt(Point { x: 1, y: 2 }, 5).is_some()); + /// ``` + /// + /// [`.insert()`]: #method.insert + /// [`Area`]: area/struct.Area.html + /// [`Point`]: point/struct.Point.html + pub fn insert_pt(&mut self, point: Point, val: V) -> Option { + if let Ok(area) = AreaBuilder::default().anchor(point).build() { + return self.insert(area, val); + } + None + } + + /// Given the handle from an [`.insert()`] operation, provides read-only + /// access to the associated [`Entry`] struct. + /// + /// Handles are unique and never re-used, so lookup of a handle to a now-deleted entry can + /// fail and return `None`. + /// + /// ``` + /// use quadtree_rs::{area::AreaBuilder, point::Point, HashQuadtree as Quadtree}; + /// + /// let mut qt = Quadtree::new(4); + /// + /// let region = AreaBuilder::default() + /// .anchor(Point {x: 0, y: 1}) + /// .dimensions((2, 3)) + /// .build().unwrap(); + /// let handle = qt.insert(region, 9.87).unwrap(); + /// + /// let entry = qt.get(handle).unwrap(); + /// assert_eq!(entry.value_ref(), &9.87); + /// ``` + /// + /// [`.insert()`]: #method.insert + /// [`Entry`]: entry/struct.Entry.html + pub fn get(&self, handle: u64) -> Option<&Entry> + where + U: 'static, + { + self.store.get(handle) + } + + /// A mutable variant of [`.get()`] which provides mutable access to the + /// associated [`Entry`] struct. + /// + /// ``` + /// use quadtree_rs::{area::AreaBuilder, point::Point, HashQuadtree as Quadtree}; + /// + /// let mut qt: Quadtree = Quadtree::new(4); + /// + /// let region = AreaBuilder::default() + /// .anchor(Point {x: 0, y: 1}) + /// .dimensions((2, 3)) + /// .build().unwrap(); + /// let handle: u64 = qt.insert(region, 9.87).unwrap(); + /// + /// if let Some(entry) = qt.get_mut(handle) { + /// *entry.value_mut() += 1.0; + /// } + /// + /// assert_eq!(qt.get(handle).unwrap().value_ref(), &10.87); + /// + /// ``` + /// + /// [`.get()`]: #method.get + /// [`Entry`]: entry/struct.Entry.html + pub fn get_mut(&mut self, handle: u64) -> Option<&mut Entry> { + self.store.get_mut(handle) + } + + /// Returns an iterator over [`&Entry`] structs representing values + /// within the query region. + /// ``` + /// use quadtree_rs::{area::AreaBuilder, HashQuadtree as Quadtree}; + /// + /// // 0123456 + /// // 0 ░░░░░░░ + /// // 1 ░░▒▒▒░░ (2,1)->3x2 + /// // 2 ░░▒▒▒░░ + /// // 3 ░░░░░░░ + /// // 4 ░▒▒▒░░░ (1,4)->3x1 + /// // 5 ░░░░░░░ + /// let mut qt = Quadtree::new(4); + /// + /// let region_a = AreaBuilder::default() + /// .anchor((2, 1).into()) + /// .dimensions((3, 2)) + /// .build().unwrap(); + /// qt.insert(region_a, 'a'); + /// + /// let region_b = AreaBuilder::default() + /// .anchor((1, 4).into()) + /// .dimensions((3, 1)) + /// .build().unwrap(); + /// qt.insert(region_b, 'b'); + /// + /// // 0123456 + /// // 0 ░░░░░░░ + /// // 1 ░░▓▒▒░░ <-- Query over the region + /// // 2 ░░▒▒▒░░ (2,1)->1x1 + /// // 3 ░░░░░░░ + /// // 4 ░▒▒▒░░░ + /// // 5 ░░░░░░░ + /// let region_c = AreaBuilder::default() + /// .anchor((2, 1).into()).build().unwrap(); + /// let mut query_a = qt.query(region_c); + /// + /// // We can use the Entry API to destructure the result. + /// let entry = query_a.next().unwrap(); + /// assert_eq!(entry.area().height(), 2); + /// assert_eq!(entry.value_ref(), &'a'); + /// + /// // But that was the only result. + /// assert!(query_a.next().is_none()); + /// + /// // 0123456 + /// // 0 ░░░░░░░ + /// // 1 ░▒▓▓▓▒░ <-- query over the region + /// // 2 ░▒▓▓▓▒░ (0,0)->6x6. + /// // 3 ░▒▒▒▒▒░ + /// // 4 ░▓▓▓▒▒░ + /// // 5 ░░░░░░░ + /// let region_d = AreaBuilder::default() + /// .anchor((1, 1).into()) + /// .dimensions((4, 4)) + /// .build().unwrap(); + /// let query_b = qt.query(region_d); + /// + /// // It's unspecified what order the regions should + /// // return in, but there will be two of them. + /// assert_eq!(query_b.count(), 2); + /// ``` + /// + /// [`&Entry`]: entry/struct.Entry.html + /// [`.query()`]: #method.query + // TODO(ambuc): Settle on a stable return order to avoid breaking callers. + pub fn query(&self, area: Area) -> Query { + Query::new(area, &self.inner, &self.store, Traversal::Overlapping) + } + + /// A strict variant of [`.query()`]. + /// + /// [`.query()`]: #method.query + pub fn query_strict(&self, area: Area) -> Query { + Query::new(area, &self.inner, &self.store, Traversal::Strict) + } + + /// Accepts a modification lambda and applies it to all elements in the + /// quadtree which intersecting the described region. + /// + /// ``` + /// use quadtree_rs::{area::AreaBuilder, HashQuadtree as Quadtree}; + /// + /// let mut qt: Quadtree = Quadtree::new(3); + /// + /// let region_a = AreaBuilder::default() + /// .anchor((0, 0).into()) + /// .build().unwrap(); + /// let handle = qt.insert(region_a, true).unwrap(); + /// + /// // Run a modification lambda over all values in region_a... + /// qt.modify(region_a, |i| *i = false); + /// + /// // ...and verify that the value was applied. + /// assert_eq!(qt.get(handle).unwrap().value_ref(), &false); + /// ``` + pub fn modify(&mut self, area: Area, f: F) + where + F: Fn(&mut V) + Copy, + { + self.modify_region(|a| a.intersects(area), f); + } + + /// A strict variant of [`.modify()`]. + /// + /// [`.modify()`]: #method.modify + pub fn modify_strict(&mut self, area: Area, f: F) + where + F: Fn(&mut V) + Copy, + { + self.modify_region(|a| area.contains(a), f); + } + + /// Alias for [`.modify()`] which runs over the entire + /// quadtree. + /// + /// [`.modify()`]: #method.modify + pub fn modify_all(&mut self, f: F) + where + F: Fn(&mut V) + Copy, + { + for entry in self.store.values_mut() { + f(entry.value_mut()); + } + } + + /// Resets the quadtree to a totally empty state. + pub fn reset(&mut self) { + self.store.clear(); + self.inner.reset(); + } + + /// Deletes all value associations which overlap a region in the tree. + /// + /// Along the way, consumed [`Entry`] entries are collected and returned in an iterator + /// [`IntoIter`]. + /// ``` + /// use quadtree_rs::{area::AreaBuilder, HashQuadtree as Quadtree}; + /// + /// let mut qt = Quadtree::new(4); + /// + /// let region_a = AreaBuilder::default() + /// .anchor((0, 0).into()) + /// .dimensions((2, 2)) + /// .build().unwrap(); + /// qt.insert(region_a, 1.23); + /// + /// let region_b = AreaBuilder::default() + /// .anchor((1, 1).into()) + /// .dimensions((3, 2)) + /// .build().unwrap(); + /// qt.insert(region_b, 4.56); + /// + /// // 0123 + /// // 0 ░░ + /// // 1 ░▓╳░ <-- ╳ is the deletion region + /// // 2 ░░░ + /// + /// let region_c = AreaBuilder::default() + /// .anchor((2, 1).into()).build().unwrap(); + /// let mut returned_entries = qt.delete(region_c); + /// + /// // We've removed one object from the quadtree. + /// assert_eq!(returned_entries.next().unwrap().value_ref(), + /// &4.56); + /// + /// // And left one behind. + /// assert_eq!(qt.len(), 1); + /// ``` + /// + /// [`IntoIter`]: iter/struct.IntoIter.html + /// [`Entry`]: entry/struct.Entry.html + /// [`.delete()`]: #method.delete + pub fn delete(&mut self, area: Area) -> IntoIter { + self.delete_handles_and_return(self.query(area).map(|e| e.handle()).collect()) + } + + /// A strict variant of [`.delete()`]. + /// + /// [`.delete()`]: #method.delete + pub fn delete_strict(&mut self, area: Area) -> IntoIter { + self.delete_handles_and_return(self.query_strict(area).map(|e| e.handle()).collect()) + } + + #[allow(clippy::needless_pass_by_value)] + pub(crate) fn delete_handles_and_return(&mut self, handles: HashSet) -> IntoIter { + let error: &'static str = "I tried to look up an handle in the store which I found in the tree, but it wasn't there!"; + + let mut entries: Vec> = vec![]; + + handles.iter().for_each(|u| { + // We were just passed a hashset of handles taken from this quadtree, so it is safe to + // assume they all still exist. + entries.push(self.store.remove(*u).expect(error)); + }); + + IntoIter { entries } + } + + /// Given an handle, deletes a single item from the + /// Quadtree. If that handle was found, + /// `delete_by_handle()` returns an `Entry` + /// containing its former region and value. Otherwise, + /// returns `None`. + pub fn delete_by_handle(&mut self, handle: u64) -> Option> { + // Pop the Entry out of the @store, + if let Some(entry) = self.store.remove(handle) { + // Use the now-known region to descend into the tree efficiently, + self.inner.delete_by_handle(handle, entry.area()); + // And return the Entry. + return Some(entry); + } + // If the handle wasn't in the @store, we don't need to perform a descent. + None + } + + // TODO(ambuc): Test this fn. + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all items such that `f(&mut v)` returns `false`. + pub fn retain(&mut self, mut f: F) -> IntoIter + where + F: FnMut(&mut V) -> bool, + U: Hash, + { + // TODO(ambuc): I think this is technically correct but it seems to be interweaving three + // routines. Is there a way to simplify this? + let mut doomed: HashSet<(u64, Area)> = HashSet::new(); + for (handle, entry) in self.store.iter_mut() { + if f(entry.value_mut()) { + doomed.insert((*handle, entry.area())); + } + } + // TODO(ambuc): There is an optimization here to do one traversal with many matches, over + // many traversals i.e. one per match. + let mut entries: Vec> = vec![]; + for (handle, region) in doomed { + entries.push(self.store.remove(handle).unwrap()); + self.inner.delete_by_handle(handle, region); + } + + IntoIter { entries } + } + // TODO(ambuc): retain_within + + /// Returns an iterator ([`Iter`]) over all [`&'a Entry`] + /// region/value associations in the Quadtree. + /// + /// [`Iter`]: iter/struct.Iter.html + /// [`&'a Entry`]: entry/struct.Entry.html + pub fn iter(&self) -> Iter { + Iter::new(&self.inner, &self.store) + } + + /// Returns an iterator ([`Regions`]) over all [`Area`] regions + /// in the Quadtree. + /// + /// [`Regions`]: iter/struct.Regions.html + /// [`Area`]: area/struct.Area.html + pub fn regions(&self) -> Regions { + Regions { + inner: Iter::new(&self.inner, &self.store), + } + } + + /// Returns an iterator ([`Values`]) over all `&'a V` values in the + /// Quadtree. + /// + /// [`Values`]: iter/struct.Values.html + pub fn values(&self) -> Values { + Values { + inner: Iter::new(&self.inner, &self.store), + } + } + + // fn + + pub(crate) fn modify_region(&mut self, filter: F, modify: MF) + where + F: Fn(Area) -> bool, + MF: Fn(&mut V) + Copy, + { + let relevant_handles: Vec = + HandleIter::new(&self.inner, self.inner.region()).collect(); + for i in relevant_handles { + if let Some(entry) = self.store.get_mut(i) { + if filter(entry.area()) { + modify(entry.value_mut()); + } + } + } + } +} + +/// `Extend<((U, U), V)>` will silently drop values whose coordinates do not fit in the region +/// represented by the Quadtree. It is the responsibility of the callsite to ensure these points +/// fit. +impl Extend<((U, U), V)> for Quadtree +where + M: Map + Default, + U: PrimInt + Default + 'static, +{ + fn extend(&mut self, iter: T) + where + T: IntoIterator, + { + for ((x, y), val) in iter { + // Ignore errors. + self.insert( + AreaBuilder::default() + .anchor(Point { x, y }) + .build() + .unwrap(), + val, + ); + } + } +} + +// Immutable iterator for the Quadtree, returning by-reference. +impl<'a, U, V, M> IntoIterator for &'a Quadtree +where + M: Map + Default, + U: PrimInt + Default + 'static, +{ + type Item = &'a Entry; + type IntoIter = Iter<'a, U, V, M>; + + fn into_iter(self) -> Iter<'a, U, V, M> { + Iter::new(&self.inner, &self.store) + } +} + +impl IntoIterator for Quadtree +where + M: Map + Default, + U: PrimInt + Default + 'static, +{ + type Item = Entry; + type IntoIter = IntoIter; + + fn into_iter(self) -> IntoIter { + IntoIter { + entries: self.store.into_values().collect(), + } + } +} diff --git a/src/traversal.rs b/src/traversal.rs index 01f83e0..c61b315 100644 --- a/src/traversal.rs +++ b/src/traversal.rs @@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -use {crate::area::Area, num::PrimInt, std::default::Default}; +use crate::area::Area; +use num::PrimInt; +use std::default::Default; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum Traversal { diff --git a/src/types.rs b/src/types.rs deleted file mode 100644 index 638ecd9..0000000 --- a/src/types.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2019 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// The hashmap storage type for qtinners. Made explicit here for brevity in other files. -pub(crate) type StoreType = std::collections::HashMap>; diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 5b90dfd..a6e6aa0 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -14,7 +14,7 @@ mod util; // For unordered_elements_are. -use quadtree_rs::{area::AreaBuilder, Quadtree}; +use quadtree_rs::{AreaBuilder, HashQuadtree as Quadtree}; mod new { use super::*; @@ -22,48 +22,53 @@ mod new { #[test] fn new_with_depth() { // None of these should crash. - let _q0 = Quadtree::::new(0); - let _q1 = Quadtree::::new(1); - let _q2 = Quadtree::::new(2); + let _q0: Quadtree = Quadtree::new(0); + let _q1: Quadtree = Quadtree::new(1); + let _q2: Quadtree = Quadtree::new(2); } #[test] fn new_with_anchor() { // None of these should crash. - let _q0 = Quadtree::::new_with_anchor((1, 1).into(), 0); - let _q1 = Quadtree::::new_with_anchor((0, 510123).into(), 1); - let _q2 = Quadtree::::new_with_anchor((4009, 4009).into(), 2); + let _q0: Quadtree = Quadtree::new_with_anchor((1, 1).into(), 0); + let _q1: Quadtree = Quadtree::new_with_anchor((0, 510123).into(), 1); + let _q2: Quadtree = Quadtree::new_with_anchor((4009, 4009).into(), 2); } } #[test] fn anchor() { - debug_assert_eq!(Quadtree::::new(0).anchor(), (0, 0).into()); - debug_assert_eq!(Quadtree::::new(1).anchor(), (0, 0).into()); - debug_assert_eq!(Quadtree::::new(2).anchor(), (0, 0).into()); + let q0: Quadtree = Quadtree::new(0); + let q1: Quadtree = Quadtree::new(0); + let q2: Quadtree = Quadtree::new(0); + debug_assert_eq!(q0.anchor(), (0, 0).into()); + debug_assert_eq!(q1.anchor(), (0, 0).into()); + debug_assert_eq!(q2.anchor(), (0, 0).into()); for x in [20, 49, 2013, 1, 0].iter() { for y in [10, 399, 20, 4, 397].iter() { - debug_assert_eq!( - Quadtree::::new_with_anchor((*x, *y).into(), 2).anchor(), - (*x, *y).into() - ); + let q: Quadtree = Quadtree::new_with_anchor((*x, *y).into(), 2); + debug_assert_eq!(q.anchor(), (*x, *y).into()); } } } #[test] fn width_and_height() { - debug_assert_eq!(Quadtree::::new(0).width(), 1); - debug_assert_eq!(Quadtree::::new(0).height(), 1); + let q0: Quadtree = Quadtree::new(0); + debug_assert_eq!(q0.width(), 1); + debug_assert_eq!(q0.height(), 1); - debug_assert_eq!(Quadtree::::new(1).width(), 2); - debug_assert_eq!(Quadtree::::new(1).height(), 2); + let q1: Quadtree = Quadtree::new(1); + debug_assert_eq!(q1.width(), 2); + debug_assert_eq!(q1.height(), 2); - debug_assert_eq!(Quadtree::::new(2).width(), 4); - debug_assert_eq!(Quadtree::::new(2).height(), 4); + let q2: Quadtree = Quadtree::new(2); + debug_assert_eq!(q2.width(), 4); + debug_assert_eq!(q2.height(), 4); - debug_assert_eq!(Quadtree::::new(3).width(), 8); - debug_assert_eq!(Quadtree::::new(3).height(), 8); + let q3: Quadtree = Quadtree::new(3); + debug_assert_eq!(q3.width(), 8); + debug_assert_eq!(q3.height(), 8); } mod insert { @@ -71,7 +76,7 @@ mod insert { #[test] fn insert_successful() { - let mut qt = Quadtree::::new(2); + let mut qt = Quadtree::new(2); assert!(qt .insert( AreaBuilder::default() @@ -117,7 +122,7 @@ mod insert { #[test] fn insert_unsucessful() { - let mut qt = Quadtree::::new(2); + let mut qt = Quadtree::new(2); // At (0, 0) and too large. assert!(qt .insert( @@ -142,7 +147,7 @@ mod insert { .is_none()); // Since the region overlaps, insertion fails. - let mut qt = Quadtree::::new_with_anchor((2, 2).into(), 2); + let mut qt = Quadtree::new_with_anchor((2, 2).into(), 2); assert!(qt .insert( AreaBuilder::default() @@ -157,7 +162,7 @@ mod insert { #[test] fn len() { - let mut qt = Quadtree::::new(4); + let mut qt = Quadtree::new(4); debug_assert_eq!(qt.len(), 0); assert!(qt .insert( @@ -195,7 +200,7 @@ fn len() { #[test] fn fill_quadrant() { - let mut qt = Quadtree::::new(2); + let mut qt = Quadtree::new(2); debug_assert!(qt.is_empty()); assert!(qt @@ -228,7 +233,7 @@ fn fill_quadrant() { #[test] fn is_empty() { - let mut qt = Quadtree::::new(2); + let mut qt = Quadtree::new(2); debug_assert!(qt.is_empty()); // Insert region @@ -244,7 +249,7 @@ fn is_empty() { .is_some()); debug_assert!(!qt.is_empty()); - let mut q2 = Quadtree::::new(4); + let mut q2 = Quadtree::new(4); debug_assert!(q2.is_empty()); // Insert point @@ -262,7 +267,7 @@ fn is_empty() { #[test] fn reset() { - let mut qt = Quadtree::::new(4); + let mut qt = Quadtree::new(4); debug_assert!(qt.is_empty()); assert!(qt @@ -287,7 +292,7 @@ mod string { #[test] fn quadtree_string() { - let mut qt = Quadtree::::new(4); + let mut qt = Quadtree::new(4); assert!(qt .insert( AreaBuilder::default() @@ -309,7 +314,7 @@ mod string { #[test] fn quadtree_mut_string() { - let mut qt = Quadtree::::new(4); + let mut qt = Quadtree::new(4); assert!(qt .insert( AreaBuilder::default() @@ -355,7 +360,7 @@ fn quadtree_struct() { baz: "baz".to_string(), }; - let mut qt = Quadtree::::new(4); + let mut qt = Quadtree::new(4); assert!(qt .insert( @@ -389,7 +394,7 @@ mod extend { #[test] fn extend_with_just_points() { - let mut qt = Quadtree::::new(4); + let mut qt = Quadtree::new(4); assert!(qt.is_empty()); qt.extend(vec![((0, 0), 0), ((2, 3), 5)]); @@ -431,7 +436,7 @@ mod extend { #[test] fn extend_with_points_and_regions() { - let mut qt = Quadtree::::new(3); + let mut qt = Quadtree::new(3); assert!(qt.is_empty()); assert!(qt @@ -488,7 +493,7 @@ mod delete { #[test] fn delete_by_handle() { - let mut qt = Quadtree::::new(4); + let mut qt = Quadtree::new(4); // We don't know the indices for any of these. qt.extend(vec![((0, 0), 0), ((2, 3), 5), ((2, 2), 7), ((1, 2), 9)]); debug_assert_eq!(qt.len(), 4); @@ -534,7 +539,7 @@ mod delete { #[test] #[ignore] fn debug() { - let mut qt = Quadtree::::new(2); + let mut qt = Quadtree::new(2); assert!(qt .insert( AreaBuilder::default() @@ -582,7 +587,7 @@ fn debug() { fn test_print_quadtree() { use crate::util::print_quadtree; - let mut qt = Quadtree::::new(3); + let mut qt = Quadtree::new(3); assert!(qt .insert( AreaBuilder::default() diff --git a/tests/iterator_tests.rs b/tests/iterator_tests.rs index 2e16a4c..2219927 100644 --- a/tests/iterator_tests.rs +++ b/tests/iterator_tests.rs @@ -16,13 +16,11 @@ mod util; // For unordered_elements_are. // For testing .iter(), .iter_mut(), .regions(), .values(), .values_mut(). mod iterator_tests { - use { - crate::util::unordered_elements_are, - quadtree_rs::{area::AreaBuilder, entry::Entry, Quadtree}, - }; + use crate::util::unordered_elements_are; + use quadtree_rs::{area::AreaBuilder, entry::Entry, HashQuadtree as Quadtree}; fn mk_quadtree_for_iter_tests() -> Quadtree { - let mut qt = Quadtree::::new_with_anchor((-35, -35).into(), 8); + let mut qt = Quadtree::new_with_anchor((-35, -35).into(), 8); qt.extend(vec![((0, -5), 10), ((-15, 20), -25), ((30, -35), 40)]); qt } diff --git a/tests/query_tests.rs b/tests/query_tests.rs index 420b9c3..2e3c90d 100644 --- a/tests/query_tests.rs +++ b/tests/query_tests.rs @@ -16,14 +16,12 @@ mod util; // For unordered_elements_are. // For testing .query(), .modify(). mod query_tests { - use { - crate::util::unordered_elements_are, - quadtree_rs::{area::AreaBuilder, Quadtree}, - }; + use crate::util::unordered_elements_are; + use quadtree_rs::{AreaBuilder, HashQuadtree as Quadtree}; #[test] fn query_empty() { - let qt = Quadtree::::new(2); + let qt: Quadtree = Quadtree::new(2); let mut iter = qt.query( AreaBuilder::default() .anchor((0, 0).into()) @@ -36,7 +34,7 @@ mod query_tests { #[test] fn query_on_point() { - let mut qt = Quadtree::::new(1); + let mut qt = Quadtree::new(1); assert!(qt .insert( AreaBuilder::default() @@ -93,7 +91,7 @@ mod query_tests { #[test] fn query_in_region() { - let mut qt = Quadtree::::new(4); + let mut qt = Quadtree::new(4); // 0 1 2 3 4 5 6 // 0 +--+--+--+--+--+--+ // | | | | | | | @@ -326,7 +324,7 @@ mod query_tests { #[test] fn query_strict_in_region() { - let mut qt = Quadtree::::new(4); + let mut qt = Quadtree::new(4); // 0 1 2 3 4 5 6 // 0 +--+--+--+--+--+--+ // | | | | | | | @@ -658,21 +656,21 @@ mod query_tests { #[test] fn modify_empty() { // Modification shouldn't change the emptiness. - let mut qt = Quadtree::::new(2); + let mut qt = Quadtree::new(2); qt.modify( AreaBuilder::default() .anchor((0, 0).into()) .dimensions((4, 4)) .build() .unwrap(), - |v| *v *= 2, + |v: &mut u32| *v *= 2, ); debug_assert!(qt.is_empty()); } #[test] fn modify() { - let mut qt = Quadtree::::new(3); + let mut qt = Quadtree::new(3); // Insert #49 at (0, 0)->1x1. assert!(qt diff --git a/tests/util.rs b/tests/util.rs index 106d81d..003557d 100644 --- a/tests/util.rs +++ b/tests/util.rs @@ -1,8 +1,6 @@ -use { - num::{cast::FromPrimitive, PrimInt}, - quadtree_rs::area::AreaBuilder, - std::{collections::HashSet, default::Default, fmt::Debug, hash::Hash, iter::FromIterator}, -}; +use num::{cast::FromPrimitive, PrimInt}; +use quadtree_rs::{AreaBuilder, HashQuadtree as Quadtree}; +use std::{collections::HashSet, default::Default, fmt::Debug, hash::Hash, iter::FromIterator}; // Inspired by google/googletest's UnorderedElementsAre(). // https://github.com/google/googletest/blob/master/googlemock/docs/CheatSheet.md#container-matchers @@ -22,7 +20,7 @@ where } #[allow(dead_code)] -pub fn print_quadtree(qt: &quadtree_rs::Quadtree) +pub fn print_quadtree(qt: &Quadtree) where U: PrimInt + Default + FromPrimitive + Debug, V: Debug,