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}