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/src/area.rs b/src/area.rs index a93e1a5..baaf112 100644 --- a/src/area.rs +++ b/src/area.rs @@ -14,13 +14,11 @@ //! A rectangular region in the tree. +use crate::point::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. /// @@ -37,7 +35,7 @@ pub struct Area where U: PrimInt + Default + PartialOrd, { - anchor: point::Point, + anchor: Point, #[builder(default = "(U::one(), U::one())")] dimensions: (U, U), } @@ -101,7 +99,7 @@ where U: PrimInt + Default, { /// The top-left coordinate (anchor) of the region. - pub fn anchor(&self) -> point::Point { + pub fn anchor(&self) -> Point { self.anchor } @@ -152,7 +150,7 @@ where } /// Whether or not an area contains a point. - pub fn contains_pt(self, pt: point::Point) -> bool { + pub fn contains_pt(self, pt: Point) -> bool { self.contains( AreaBuilder::default() .anchor(pt) @@ -164,9 +162,9 @@ where // NB: The center point is an integer and thus rounded, i.e. a 2x2 region at (0,0) has a center // at (0,0), when in reality the center would be at (0.5, 0.5). - pub(crate) fn center_pt(&self) -> point::Point { + pub(crate) fn center_pt(&self) -> Point { self.anchor() - + point::Point { + + Point { x: self.width() / Self::two(), y: self.height() / Self::two(), } diff --git a/src/entry.rs b/src/entry.rs index c84263e..b7c9ba3 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`]. /// @@ -80,8 +78,6 @@ impl Entry where U: PrimInt + Default, { - // pub - /// The returned region. pub fn area(&self) -> Area { self.region @@ -112,8 +108,6 @@ where &self.value } - // pub(crate) - pub(crate) fn new((region, value): (Area, V), handle: u64) -> Self { Self { region, diff --git a/src/handle_iter.rs b/src/handle_iter.rs index 701c956..f42b31a 100644 --- a/src/handle_iter.rs +++ b/src/handle_iter.rs @@ -12,11 +12,9 @@ // 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> @@ -33,7 +31,7 @@ impl<'a, U> HandleIter<'a, U> where U: PrimInt + Default, { - pub(crate) fn new(qt: &'a QTInner, search_area: Area) -> HandleIter<'a, U> { + pub(crate) fn new(qt: &'a QTInner, search_area: Area) -> Self { HandleIter { search_area, handle_stack: vec![], @@ -114,7 +112,7 @@ where if let Some(qt) = self.qt_stack.pop() { // Push my sub quadrants onto the qt_stack too. if let Some(sub_quadrants) = qt.subquadrants().as_ref() { - for sub_quadrant in sub_quadrants { + for sub_quadrant in &**sub_quadrants { if sub_quadrant.region().intersects(self.search_area) { self.qt_stack.push(sub_quadrant) } diff --git a/src/iter.rs b/src/iter.rs index 345002e..38380b9 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, qtinner::QTInner, traversal::Traversal, + StoreType, }; +use num::PrimInt; +use std::iter::FusedIterator; /// An iterator over all regions and values of a [`Quadtree`]. /// @@ -40,7 +38,7 @@ impl<'a, U, V> Iter<'a, U, V> where U: PrimInt + Default, { - pub(crate) fn new(qt: &'a QTInner, store: &'a StoreType) -> Iter<'a, U, V> { + pub(crate) fn new(qt: &'a QTInner, store: &'a StoreType) -> Self { Iter { store, handle_iter: HandleIter::new(qt, qt.region()), @@ -133,7 +131,7 @@ where qt: &'a QTInner, store: &'a StoreType, traversal_method: Traversal, - ) -> Query<'a, U, V> + ) -> Self where U: PrimInt + Default, { diff --git a/src/lib.rs b/src/lib.rs index 7073e23..0c140b9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -124,612 +124,12 @@ pub mod point; mod handle_iter; 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, - }, -}; +// The hashmap storage type for qtinners. Made explicit here for brevity in other files. +pub(crate) type StoreType = std::collections::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, -} - -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/point.rs b/src/point.rs index 237a4ae..c6034e8 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). @@ -101,8 +100,6 @@ impl Point where U: PrimInt, { - // pub - /// The x-coordinate of the point. pub fn x(&self) -> U { self.x diff --git a/src/qtinner.rs b/src/qtinner.rs index 778c99a..0102ca2 100644 --- a/src/qtinner.rs +++ b/src/qtinner.rs @@ -12,18 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::{ + area::{Area, AreaBuilder}, + entry::Entry, + point::Point, + StoreType, +}; +use num::PrimInt; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use { - crate::{ - area::{Area, AreaBuilder}, - entry::Entry, - point::Point, - types::StoreType, - }, - num::PrimInt, - std::{default::Default, fmt::Debug}, -}; +use std::{default::Default, fmt::Debug}; #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, PartialEq, Eq)] @@ -43,7 +41,7 @@ where // The subquadrants under this cell. [ne, nw, se, sw]. If there are no subquadrants, this // entire list could be None. - subquadrants: Option<[Box>; 4]>, + subquadrants: Option; 4]>>, // The last-inserted handle. This is a monotonically increasing counter. handle_counter: u64, @@ -72,8 +70,6 @@ impl QTInner where U: PrimInt + Default, { - // pub - pub fn new(anchor: Point, depth: usize) -> Self { #[allow(clippy::cast_possible_truncation)] let width: U = Self::two().pow(depth as u32); @@ -100,7 +96,7 @@ where &self.kept_handles } - pub fn subquadrants(&self) -> &Option<[Box; 4]> { + pub fn subquadrants(&self) -> &Option> { &self.subquadrants } @@ -121,7 +117,7 @@ where 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); handle } @@ -153,12 +149,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) { // If we're at the bottom depth, it had better fit. if self.depth == 0 { self.kept_handles.push(handle); @@ -184,7 +175,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); } } } @@ -198,28 +189,28 @@ where fn expand_subquadrants_by_pt(&mut self, p: Point) { assert!(self.region.contains_pt(p)); - self.subquadrants = Some([ + self.subquadrants = Some(Box::new([ // Northeast - Box::new(Self::new( + Self::new( Point { x: p.x(), y: self.region.anchor().y(), }, self.depth - 1, - )), + ), // Northwest - Box::new(Self::new(self.region.anchor(), self.depth - 1)), + Self::new(self.region.anchor(), self.depth - 1), // Southeast - Box::new(Self::new(p, self.depth - 1)), + Self::new(p, self.depth - 1), // Southwest - Box::new(Self::new( + Self::new( Point { x: self.region.anchor().x(), y: p.y(), }, self.depth - 1, - )), - ]); + ), + ])); } // Strongly-typed alias for U::one() + U::One() diff --git a/src/qtree.rs b/src/qtree.rs new file mode 100644 index 0000000..6b84f84 --- /dev/null +++ b/src/qtree.rs @@ -0,0 +1,603 @@ +use crate::{ + area::{Area, AreaBuilder}, + entry::Entry, + handle_iter::HandleIter, + iter::{IntoIter, Iter, Query, Regions, Values}, + point::Point, + qtinner::QTInner, + traversal::Traversal, + StoreType, +}; + +use num::PrimInt; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; +use std::{ + collections::{HashMap, HashSet}, + default::Default, + hash::Hash, +}; + +/// 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, +{ + pub(crate) inner: QTInner, + pub(crate) store: StoreType, +} + +impl Quadtree +where + U: PrimInt + Default, +{ + /// 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 { + 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, 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 { + 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)] + 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 &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 + + pub(crate) 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 { 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(), + } + } +} 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..d4f73cc 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, Quadtree}; mod new { use super::*; diff --git a/tests/iterator_tests.rs b/tests/iterator_tests.rs index 2e16a4c..d99922a 100644 --- a/tests/iterator_tests.rs +++ b/tests/iterator_tests.rs @@ -16,10 +16,8 @@ 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, Quadtree}; fn mk_quadtree_for_iter_tests() -> Quadtree { let mut qt = Quadtree::::new_with_anchor((-35, -35).into(), 8); diff --git a/tests/query_tests.rs b/tests/query_tests.rs index 420b9c3..235c320 100644 --- a/tests/query_tests.rs +++ b/tests/query_tests.rs @@ -16,10 +16,8 @@ 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, Quadtree}; #[test] fn query_empty() { diff --git a/tests/util.rs b/tests/util.rs index 106d81d..ae3797c 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, 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,