Skip to main content

geometry_rtree/
indexable.rs

1//! The [`Indexable`] trait — what can be stored in an [`Rtree`].
2//!
3//! Mirrors `boost/geometry/index/indexable.hpp`: an indexable value is
4//! anything from which the index can read an axis-aligned bounding box.
5//! Boost's `indexable` function object maps points, boxes, and segments
6//! to their bounds; the port makes that a trait with impls for the same
7//! kinds.
8//!
9//! [`Rtree`]: crate::Rtree
10
11use geometry_cs::CoordinateSystem;
12use geometry_model::Point2D;
13use geometry_trait::Point;
14
15use crate::bounds::Bounds;
16
17/// A value the [`Rtree`](crate::Rtree) can index — one that exposes an
18/// axis-aligned bounding box.
19///
20/// Mirrors `boost::geometry::index::indexable<Value>`
21/// (`index/indexable.hpp`). Implemented for the kernel's point type and
22/// for [`Bounds`] itself; wrap any other payload in a `(Bounds, T)`
23/// pair, which is also `Indexable`, to index it by a precomputed box.
24///
25/// # Examples
26///
27/// ```
28/// use geometry_cs::Cartesian;
29/// use geometry_model::Point2D;
30/// use geometry_rtree::indexable::Indexable;
31///
32/// let p = Point2D::<f64, Cartesian>::new(3.0, 4.0);
33/// let b = p.bounds();
34/// assert_eq!(b.min, [3.0, 4.0]);
35/// assert_eq!(b.max, [3.0, 4.0]);
36/// ```
37pub trait Indexable {
38    /// The value's axis-aligned bounding box.
39    fn bounds(&self) -> Bounds;
40}
41
42impl<Cs: CoordinateSystem> Indexable for Point2D<f64, Cs> {
43    fn bounds(&self) -> Bounds {
44        Bounds::point([self.get::<0>(), self.get::<1>()])
45    }
46}
47
48impl Indexable for Bounds {
49    fn bounds(&self) -> Bounds {
50        *self
51    }
52}
53
54/// Index an arbitrary payload `T` by a precomputed box. The tree stores
55/// the pair and reads the box from `.0`.
56impl<T> Indexable for (Bounds, T) {
57    fn bounds(&self) -> Bounds {
58        self.0
59    }
60}
61
62#[cfg(test)]
63#[allow(clippy::float_cmp, reason = "exact integer-valued coordinate literals")]
64mod tests {
65    use super::Indexable;
66    use crate::bounds::Bounds;
67    use geometry_cs::Cartesian;
68    use geometry_model::Point2D;
69
70    #[test]
71    fn point_bounds_is_degenerate() {
72        let p = Point2D::<f64, Cartesian>::new(1.0, 2.0);
73        let b = p.bounds();
74        assert_eq!(b.min, [1.0, 2.0]);
75        assert_eq!(b.max, [1.0, 2.0]);
76    }
77
78    #[test]
79    fn box_is_its_own_bounds() {
80        let b = Bounds::new([0.0, 0.0], [5.0, 5.0]);
81        assert_eq!(b.bounds(), b);
82    }
83
84    #[test]
85    fn payload_pair_indexes_by_box() {
86        let value = (Bounds::new([1.0, 1.0], [2.0, 2.0]), "label");
87        assert_eq!(value.bounds(), Bounds::new([1.0, 1.0], [2.0, 2.0]));
88    }
89}