Skip to main content

geometry_trait/
boxg.rs

1//! The [`Box`] concept and the [`box_min`] / [`box_max`] materialiser
2//! helpers. The module is named `boxg` because `box` is a Rust
3//! keyword; the trait itself keeps the C++ name.
4//!
5//! Mirrors `doc/concept/box.qbk` and the model in
6//! `boost/geometry/geometries/box.hpp`. Boxes are axis-aligned and
7//! described by a minimum and a maximum corner point; the two-axis
8//! `(corner, dimension)` access is provided by
9//! [`crate::IndexedAccess`].
10//!
11//! Design notes:
12//! * The corner indices match Boost's
13//!   `boost::geometry::min_corner` / `max_corner` constants
14//!   (`boost/geometry/core/access.hpp:36-39`) and are re-exposed
15//!   through [`crate::corner::MIN`] / [`crate::corner::MAX`].
16//! * The `min_corner()` / `max_corner()` accessors on
17//!   `model::box` (`boost/geometry/geometries/box.hpp:138-171`) are
18//!   replaced by the free helpers [`box_min`] / [`box_max`], which
19//!   reconstruct a [`crate::Point`] value via `Default::default()`
20//!   followed by per-dimension `set::<D>` writes — the same shared
21//!   materialiser used by `segment_start` / `segment_end`.
22
23use crate::geometry::Geometry;
24use crate::indexed_access::{IndexedAccess, corner};
25use crate::point::PointMut;
26use crate::segment::materialise;
27use geometry_tag::BoxTag;
28
29/// An axis-aligned bounding box.
30///
31/// Models `doc/concept/box.qbk` — equivalent in C++ to the trait
32/// specialisations on `model::box` in
33/// `boost/geometry/geometries/box.hpp:187-247`. Implementers provide
34/// [`IndexedAccess`] for `I` ∈ {[`corner::MIN`], [`corner::MAX`]}.
35/// The two corner points can but need not be materialised as `Point`
36/// values; [`box_min`] / [`box_max`] reconstruct them from the
37/// indexed accessors on demand.
38///
39/// # Examples
40///
41/// ```
42/// use geometry_trait::Box;
43/// // A generic helper that accepts any `Box` impl.
44/// fn _accepts_box<B: Box>(_b: &B) {}
45/// ```
46pub trait Box: Geometry<Kind = BoxTag> + IndexedAccess {}
47
48/// Materialise the minimum corner of a box as a [`crate::Point`]
49/// value.
50///
51/// Mirrors `b.min_corner()` on `model::box`
52/// (`boost/geometry/geometries/box.hpp:138-144`). The point is built
53/// via [`Default::default`] and one `set::<D>` call per dimension,
54/// with each value sourced from
55/// `b.get_indexed::<{ corner::MIN }, D>()`.
56///
57/// # Examples
58///
59/// ```
60/// // See `geometry_model::Box::min_corner` and the integration tests
61/// // under `crates/geometry-model/` for a fully-built example.
62/// use geometry_trait::{box_min, Box, PointMut};
63/// fn _ex<B: Box>(b: &B) where B::Point: Default + PointMut { let _ = box_min(b); }
64/// ```
65pub fn box_min<B: Box>(b: &B) -> B::Point
66where
67    B::Point: Default + PointMut,
68{
69    materialise::<B, { corner::MIN }>(b)
70}
71
72/// Materialise the maximum corner of a box as a [`crate::Point`]
73/// value.
74///
75/// Mirrors `b.max_corner()` on `model::box`
76/// (`boost/geometry/geometries/box.hpp:149-155`). The point is built
77/// via [`Default::default`] and one `set::<D>` call per dimension,
78/// with each value sourced from
79/// `b.get_indexed::<{ corner::MAX }, D>()`.
80///
81/// # Examples
82///
83/// ```
84/// use geometry_trait::{box_max, Box, PointMut};
85/// fn _ex<B: Box>(b: &B) where B::Point: Default + PointMut { let _ = box_max(b); }
86/// ```
87pub fn box_max<B: Box>(b: &B) -> B::Point
88where
89    B::Point: Default + PointMut,
90{
91    materialise::<B, { corner::MAX }>(b)
92}
93
94#[cfg(test)]
95mod tests {
96    //! Synthetic Box round-trip — mirrors
97    //! `boost/geometry/test/core/access.cpp:55-86` for the box case.
98    //! The `MyBox` here is *not* `geometry-model::box`; that lands
99    //! in T17.
100
101    use super::*;
102    use crate::point::Point;
103    use geometry_cs::Cartesian;
104    use geometry_tag::PointTag;
105    // `PointMut` is already in scope via the module-level `use`.
106
107    #[derive(Default)]
108    struct Xy {
109        x: f64,
110        y: f64,
111    }
112
113    impl Geometry for Xy {
114        type Kind = PointTag;
115        type Point = Self;
116    }
117
118    impl Point for Xy {
119        type Scalar = f64;
120        type Cs = Cartesian;
121        const DIM: usize = 2;
122
123        fn get<const D: usize>(&self) -> f64 {
124            if D == 0 { self.x } else { self.y }
125        }
126    }
127
128    impl PointMut for Xy {
129        fn set<const D: usize>(&mut self, v: f64) {
130            if D == 0 {
131                self.x = v;
132            } else {
133                self.y = v;
134            }
135        }
136    }
137
138    struct MyBox {
139        c: [[f64; 2]; 2],
140    }
141
142    impl Geometry for MyBox {
143        type Kind = BoxTag;
144        type Point = Xy;
145    }
146
147    impl IndexedAccess for MyBox {
148        fn get_indexed<const I: usize, const D: usize>(&self) -> f64 {
149            self.c[I][D]
150        }
151        fn set_indexed<const I: usize, const D: usize>(&mut self, v: f64) {
152            self.c[I][D] = v;
153        }
154    }
155
156    impl Box for MyBox {}
157
158    #[test]
159    fn box_indexed_round_trip() {
160        let mut b = MyBox { c: [[0.0; 2]; 2] };
161        b.set_indexed::<{ corner::MIN }, 0>(1.0);
162        b.set_indexed::<{ corner::MIN }, 1>(2.0);
163        b.set_indexed::<{ corner::MAX }, 0>(3.0);
164        b.set_indexed::<{ corner::MAX }, 1>(4.0);
165        assert_eq!(
166            b.get_indexed::<{ corner::MIN }, 0>().to_bits(),
167            1.0_f64.to_bits()
168        );
169        assert_eq!(
170            b.get_indexed::<{ corner::MAX }, 1>().to_bits(),
171            4.0_f64.to_bits()
172        );
173    }
174
175    #[test]
176    fn box_corners_materialise() {
177        let mut b = MyBox { c: [[0.0; 2]; 2] };
178        b.set_indexed::<{ corner::MIN }, 0>(1.0);
179        b.set_indexed::<{ corner::MIN }, 1>(2.0);
180        b.set_indexed::<{ corner::MAX }, 0>(3.0);
181        b.set_indexed::<{ corner::MAX }, 1>(4.0);
182
183        let lo = box_min(&b);
184        let hi = box_max(&b);
185        assert_eq!(lo.get::<0>().to_bits(), 1.0_f64.to_bits());
186        assert_eq!(lo.get::<1>().to_bits(), 2.0_f64.to_bits());
187        assert_eq!(hi.get::<0>().to_bits(), 3.0_f64.to_bits());
188        assert_eq!(hi.get::<1>().to_bits(), 4.0_f64.to_bits());
189    }
190}