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}