1
//! Very Generic Data Structures
2
//!
3
//! Crater provides KD Trees ([`kdtree::KdTree`]), minmax heaps ([`mmheap::MmHeap`]), and intrusive Fibonacci heaps ([`fheap::FibHeap`]) with as much flexibility as possible.
4
//! For KD Trees, no restrictions are placed on the data besides that points
5
//! have well defined distance to regions/other points, and regions can be expanded/split.
6
//! Similarly, minmax heaps accept any comparison function.
7
//!
8
//! To get started quickly, look at [`cuboid::CuPoint`] and [`cuboid::CuRegion`] and the tests in [`cuboid::tests::pointcloud`].
9
//! This will provide an overview of how to use KD trees with the prebuilt Cartesian points/regions.
10
//! To associate custom data to points, create structs wrapping [`cuboid::CuPoint`] and [`cuboid::CuRegion`] which delegate to their [`KdPoint`]
11
//! and [`KdRegion`] implementations.  To implement [`KdPoint`] and [`KdRegion`] for arbitrary custom types,
12
//! continue reading their trait documentation here.
13
#![feature(split_at_checked, allocator_api, non_null_convenience, slice_ptr_get)]
14

            
15
pub mod mmheap;
16
pub mod cuboid;
17
pub mod kdtree;
18
pub mod fheap;
19

            
20
use std::cmp::Ordering;
21

            
22
use num_traits::Zero;
23

            
24

            
25
/// A point in the tree.  Types implementing this can contain arbitrary data,
26
/// to be accessed when the tree is queried by a point + distance, point + count,
27
/// region, etc.
28
/// NB: If `T: KdPoint`, `<T as Eq>::eq` can be implemented by comparing `T::sqdist` to `T::Distance::zero()` 
29
pub trait KdPoint: Sized + Eq {
30
    /// The type used by sqdist and related KdRegion functions to represent (squared) distance.
31
    /// Must be totally ordered, where greater distances mean points are farther apart.
32
    /// Also must be Clone and have a meaningful minimum value (0)
33
    type Distance: Ord + Clone + Zero;
34
    /// The squared distance is more computationally convenient than the proper distance
35
    /// in many cases.  The distance function only has to be topologically consistent
36
    /// and totally ordered.  See KdRegion::min_sqdist for more info.  The return value should be >= Distance::zero().
37
    fn sqdist(&self, other: &Self) -> Self::Distance;
38
    /// Compare two points in some layer of the tree.  This generalizes splitting
39
    /// different layers of the tree in different dimensions and is tied to KdRegion::split.
40
    /// Traditionally, points in a KD tree are compared by their 1st coordinate in layer 0,
41
    /// their 2nd in layer 1, ..., onto their Kth in layer k - 1, and then in layer K it wraps
42
    /// around to 0 and repeats.  So for a 3D KD tree, layer 0 would compare x coordinates
43
    /// with the root having the median x coordinate, all points in the first subtree having
44
    /// x coordinate <=, and all points in the second subtree having x coordinate >=.
45
    /// This method allows for more general strategies, but pushes some of the burden of
46
    /// layer-dependent behavior onto the point type.  It's still possible to just implement
47
    /// a by-coordinate cmp function of course, and this is what the prebuilt CuPoint does.
48
    fn cmp(&self, other: &Self, layer: usize) -> Ordering;
49
}
50

            
51
/// A region in the tree, or in space in general.  A type implementing this will be tightly
52
/// coupled to some type implementing [`KdPoint`].  For example, the prebuilt [`cuboid::CuPoint`] struct
53
/// represents a point in Cartesian space with Euclidean distance, and the prebuilt [`cuboid::CuRegion`]
54
/// struct represents cuboid regions of space (rectangles in 2D, rectangular prisms in 3D, etc).
55
/// Regions often represent infinitely many points in space (how many points are in your
56
/// typical rectangle?).  Regions should be able to represent a single point, but the ability
57
/// to represent an empty region isn't necessary.
58
pub trait KdRegion: Sized + Clone {
59
    type Point: KdPoint;
60
    /// Given a point `p` in this region `A` and a layer `l`, split `A` into two subregions `B` and `C` so that:
61
    /// - Any point `q` in `A` is in `B` or `C`, that is, if `A.min_sqdist(q) == 0`, then either `B.min_sqdist(q) == 0`
62
    ///   or `C.min_sqdist(q) == 0` (possibly both).  Note that strictly speaking this only *needs* to be
63
    ///   true over all points in the tree, not all points in space, but in practice it is almost always
64
    ///   much easier to make this true for all points in space, not least because this trait and
65
    ///   method have no reference to any particular KD tree.
66
    /// - Any point `q` in `B` is `<= p` in layer `l`, using [`KdPoint::cmp`], and any point `q` in `C` is `>= p`
67
    /// - Any point not in `A` should hopefully be farther from `B` than from `C` (or visa versa) in at least
68
    ///   some layers (note that distance doesn't depend on layer, but split direction does).
69
    ///   That is, if `A.min_dist(q) == d where d > 0`, then `B.min_sqdist(q), C.min_sqdist(q) >= d` (are both at least `d`),
70
    ///   and ideally one should be significantly more in at least some layers.
71
    /// Note how the basic instance of regions, cuboid regions, obviously obey all these properties
72
    fn split(&self, point: &Self::Point, layer: usize) -> (Self, Self);
73
    /// Given a region and a point possibly not in the region, extend the region to include the point if
74
    /// necessary.  The concrete requirements this places on the implementation are that
75
    /// `self.min_sqdist(q)` can only decrease or remain the same for any fixed `q`, and in particular `self.extend(q)`
76
    /// should cause `self.min_sqdist(q)` to be `0` if it wasn't already
77
    fn extend(&mut self, point: &Self::Point);
78
    /// Create a region consisting of a single point.  For cuboid regions for example, this is represented as
79
    /// a cuboid whose inclusive "start" and "end" points are both the same.  Types implementing this trait
80
    /// should be able to represent single points fairly well, but because of the conservative nature of
81
    /// everything, it is acceptable to fudge it by having a very small region containing the point.
82
    /// It's not necessary for types to be able to represent an empty region well or even at all.
83
    fn single_point(point: &Self::Point) -> Self;
84
    /// Return the minimal squared distance any point in this region could have to a given point.
85
    /// The return value must be `<= KdPoint::sqdist` between the given point and any point within this region.
86
    /// It's safe to return a smaller value, or even always return `Distance::zero()`, but this degrades performance because
87
    /// we can't prune subtrees from the search.
88
    /// If `B` is a subregion of `A` and `p` is a point not in `A`, then `B.min_sqdist(p) >= A.min_sqdist(p)`
89
    fn min_sqdist(&self, point: &Self::Point) -> <Self::Point as KdPoint>::Distance;
90
    /// Return the maximal squared distance any point in this region could have to a given point, or `None` if infinite.
91
    /// The return value must be `>= KdPoint::sqdist` between the given point and any point within this region.
92
    /// `None` is considered infinitely far away.  It's safe to return a larger value, or even always return `None`,
93
    /// but this may degrade performace for some queries that cull based on minimal distance.
94
    /// Currently, this only happens for [`kdtree::KdTree::k_closest`] where [`kdtree::QueryOptions::lower_bound`] is [`kdtree::QueryBound::SqDist`].
95
    /// If `B` is a subregion of `A` and `p` is a point not in `A`, then `B.max_sqdist(p) <= A.max_sqdist(p)`.
96
    fn max_sqdist(&self, point: &Self::Point) -> Option<<Self::Point as KdPoint>::Distance>;
97
    /// Return true if this region and another region might overlap, or false if they are definitely disjoint.
98
    /// Conservative implementors can always return true.
99
    /// Currently only used by [`kdtree::KdTree::k_closest`] if [`kdtree::QueryOptions::outer_bound`] is [`kdtree::QueryBound::Region`].
100
    fn might_overlap(&self, other: &Self) -> bool;
101
    /// Return true if this region is DEFINITELY a superset of another region, or false if it is not.
102
    /// `A` may be a superset of `B` even if `B` is internally tangent to `A` or `B` is `A`.
103
    /// May return false even if self is a superset of other, if it would be expensive or difficult to compute correctly.
104
    /// Currently only used by [`kdtree::KdTree::k_closest`] if [`kdtree::QueryOptions::inner_bound`] is [`kdtree::QueryBound::Region`]
105
    fn is_superset(&self, other: &Self) -> bool;
106
}
107

            
108

            
109
/// Tree traversal control flow, similar to Rust's builtin [`std::ops::ControlFlow`] enum.
110
pub enum WalkDecision {
111
    Continue,
112
    SkipChildren,
113
    Stop
114
}
115

            
116

            
117
/// Get the bounding box of a set of points.  For [`cuboid::CuRegion`] and [`cuboid::CuPoint`], this will be an
118
/// AABB (Axis Aligned Bounding Box).  For general [`KdRegion`], the bounds are not required to be tight
119
10
pub fn get_bounds<'a, R: KdRegion>(points: impl IntoIterator<Item = &'a R::Point>) -> Option<R> where R: 'a {
120
10
    let mut it = points.into_iter();
121
10
    let mut res = R::single_point(it.next()?);
122
9990
    it.for_each(|p|res.extend(p));
123
10
    Some(res)
124
10
}
125