Skip to main content

devela/geom/metric/
extent.rs

1// devela::geom::metric::extent
2//
3//! Defines [`Extent`][1|2|3].
4//!
5//! > A geometrical extent.
6//
7// TOC
8// - struct Extent, type aliases.
9// - implementations
10// - tests
11//
12// IMPROVE: use TBD NumConst::ONE and unify methods for int and floats.
13
14use crate::{is, whilst};
15
16#[doc = crate::_tags!(geom)]
17/// An orthogonal extension in `D`-space without a coordinate position.
18#[doc = crate::_doc_meta!{location("geom/metric")}]
19///
20/// Represents the lengths of each dimension in a multi-dimensional space,
21/// providing an origin-agnostic shape with the implied form of an orthotope
22/// (generalized rectangle or box).
23///
24/// See also [`Extent1`], [`Extent2`], [`Extent3`], [`ext!`][crate::ext].
25#[must_use]
26#[repr(transparent)]
27#[doc(alias = "Size")]
28pub struct Extent<T, const D: usize> {
29    /// The size values in `D`-dimensional space.
30    pub dim: [T; D],
31}
32
33#[doc = crate::_tags!(geom)]
34/// A 1-dimensional [`Extent`].
35#[doc = crate::_doc_meta!{location("geom/metric")}]
36#[doc(alias = "Size")]
37pub type Extent1<T> = Extent<T, 1>;
38
39#[doc = crate::_tags!(geom)]
40/// A 2-dimensional [`Extent`].
41#[doc = crate::_doc_meta!{location("geom/metric")}]
42#[doc(alias = "Size")]
43pub type Extent2<T> = Extent<T, 2>;
44
45#[doc = crate::_tags!(geom)]
46/// A 3-dimensional [`Extent`].
47#[doc = crate::_doc_meta!{location("geom/metric")}]
48#[doc(alias = "Size")]
49pub type Extent3<T> = Extent<T, 3>;
50
51crate::_geom_dim_impl_common![common_methods: Extent];
52crate::_geom_dim_impl_common![common_traits: Extent];
53
54/// Implement `Extent` methods for all primitives.
55macro_rules! impl_extent {
56    () => {
57        impl_extent![sint i8, i16, i32, i64, i128, isize];
58        impl_extent![uint u8, u16, u32, u64, u128, usize];
59        impl_extent![float f32, f64];
60    };
61    // integers common methods
62    //
63    // $t: the inner integer primitive type
64    (int $($t:ty),+) => { $( impl_extent![@int $t]; )+ };
65    (@int $t:ty) => {
66        impl<const D: usize> Extent<$t, D> {
67            #[must_use]
68            /// Returns the internal measure, the product of the extents.
69            ///
70            /// It's equivalent to length, area, and volume in 1, 2 and 3 dimensions.
71            pub const fn measure(self) -> $t {
72                let mut measure = 1;
73                whilst!(i in 0..D; measure *= self.dim[i]);
74                measure
75            }
76            #[must_use]
77            /// Returns the external boundary, the sum of the extents.
78            ///
79            /// It's equivalent to 2, perimeter and surface area in 1, 2 and 3 dimensions.
80            pub const fn boundary(self) -> $t {
81                let mut boundary = 0;
82                whilst!(i in 0..D; {
83                    let mut face_measure = 1;
84                    whilst!(j in 0..D; is![i != j, face_measure *= self.dim[j]]);
85                    boundary += face_measure;
86                });
87                2 * boundary // Each dimension's contribution is counted twice
88            }
89        }
90
91        // impl Extent<$t, 1> {}
92        impl Extent2<$t> {
93            #[must_use]
94            /// The area of the 2d extent.
95            pub const fn area(self) -> $t { self.dim[0] * self.dim[1] }
96            #[must_use]
97            /// The perimeter of the 2d extent.
98            pub const fn perimeter(self) -> $t { 2 * (self.dim[0] + self.dim[1]) }
99        }
100        impl Extent3<$t> {
101            #[must_use]
102            /// The volume of the 3d extent.
103            pub const fn volume(self) -> $t {
104                self.dim[0] * self.dim[1] * self.dim[2]
105            }
106            #[must_use]
107            /// The surface area of the 3d extent.
108            pub const fn surface_area(self) -> $t {
109                2 * (self.dim[0] * self.dim[1]
110                    + self.dim[1] * self.dim[2]
111                    + self.dim[2] * self.dim[0])
112            }
113        }
114    };
115
116    (sint $($t:ty),+) => { $( impl_extent![@sint $t]; )+ };
117    (@sint $t:ty ) => {
118        impl_extent![int $t];
119    };
120    (uint $($t:ty),+) => { $( impl_extent![@uint $t]; )+ };
121    (@uint $t:ty ) => {
122        impl_extent![int $t];
123    };
124
125    // $f: the inner floating-point primitive type
126    (float $($f:ty),+) => { $( impl_extent![@float $f]; )+ };
127    (@float $f:ty) => {
128        impl<const D: usize> Extent<$f, D> {
129            #[must_use]
130            /// Returns the internal measure, the product of the extents.
131            ///
132            /// It's equivalent to length, area, and volume in 1, 2 and 3 dimensions.
133            pub const fn measure(self) -> $f {
134                let mut measure = 1.0;
135                whilst!(i in 0..D; measure *= self.dim[i]);
136                measure
137            }
138            #[must_use]
139            /// Returns the external boundary, the sum of the extents.
140            ///
141            /// It's equivalent to 2, perimeter and surface area in 1, 2 and 3 dimensions.
142            pub const fn boundary(self) -> $f {
143                let mut boundary = 0.0;
144                whilst!(i in 0..D; {
145                    let mut face_measure = 1.0;
146                    whilst!(j in 0..D; is![i != j, face_measure *= self.dim[j]]);
147                    boundary += face_measure;
148                });
149                2.0 * boundary // Each dimension's contribution is counted twice
150            }
151        }
152
153        impl Extent2<$f> {
154            #[must_use]
155            /// Returns the area of the 2d extent.
156            pub const fn area(self) -> $f { self.dim[0] * self.dim[1] }
157            #[must_use]
158            /// Returns the perimeter of the 2d extent.
159            pub const fn perimeter(self) -> $f { 2.0 * (self.dim[0] + self.dim[1]) }
160        }
161        impl Extent3<$f> {
162            #[must_use]
163            /// Returns the volume of the 3d extent.
164            pub const fn volume(self) -> $f {
165                self.dim[0] * self.dim[1] * self.dim[2]
166            }
167            #[must_use]
168            /// The surface area of the 3d extent.
169            pub const fn surface_area(self) -> $f {
170                2.0 * (self.dim[0] * self.dim[1]
171                    + self.dim[1] * self.dim[2]
172                    + self.dim[2] * self.dim[0])
173            }
174        }
175    };
176}
177impl_extent![];
178
179#[rustfmt::skip]
180/// 1D accessors
181impl<T: Copy> Extent1<T> {
182    #[must_use]
183    /// Returns a copy of the first dimension.
184    pub const fn length(self) -> T { self.dim[0] }
185    /// Returns a copy of the first dimension.
186    pub const fn l(self) -> T { self.dim[0] }
187}
188
189#[rustfmt::skip]
190/// 2D Accessors
191impl<T: Copy> Extent2<T> {
192    #[must_use]
193    /// Returns a copy of the horizontal dimension (X-axis).
194    pub const fn width(self) -> T { self.dim[0] }
195    #[must_use]
196    /// Returns a copy of the horizontal dimension (X-axis).
197    pub const fn w(self) -> T { self.dim[0] }
198    #[must_use]
199    /// Returns a copy of the vertical dimension (Y-axis).
200    pub const fn height(self) -> T { self.dim[1] }
201    #[must_use]
202    /// Returns a copy of the vertical dimension (Y-axis).
203    pub const fn h(self) -> T { self.dim[1] }
204
205    #[must_use]
206    /// Returns a copy of the horizontal dimension (X-axis) (width).
207    pub const fn length(self) -> T { self.dim[0] }
208    #[must_use]
209    /// Returns a copy of the horizontal dimension (X-axis) (width).
210    pub const fn l(self) -> T { self.dim[0] }
211    #[must_use]
212    /// Returns a copy of the vertical dimension (Y-axis) (width).
213    pub const fn breadth(self) -> T { self.dim[1] }
214    #[must_use]
215    /// Returns a copy of the vertical dimension (Y-axis) (width).
216    pub const fn b(self) -> T { self.dim[1] }
217}
218
219#[rustfmt::skip]
220/// 3D Accessors
221impl<T: Copy> Extent3<T> {
222    #[must_use]
223    /// Returns a copy of the horizontal dimension (X-axis) (width).
224    pub const fn width(self) -> T { self.dim[0] }
225    #[must_use]
226    /// Returns a copy of the horizontal dimension (X-axis) (width).
227    pub const fn w(self) -> T { self.dim[0] }
228    #[must_use]
229    /// Returns a copy of the vertical dimension (Y-axis).
230    pub const fn height(self) -> T { self.dim[1] }
231    #[must_use]
232    /// Returns a copy of the vertical dimension (Y-axis).
233    pub const fn h(self) -> T { self.dim[1] }
234    #[must_use]
235    /// Returns a copy of the depth dimension (Z-axis).
236    pub const fn depth(self) -> T { self.dim[2] }
237    #[must_use]
238    /// Returns a copy of the depth dimension (Z-axis).
239    pub const fn d(self) -> T { self.dim[2] }
240}
241
242#[cfg(test)]
243mod tests {
244    use crate::{Extent2, ext};
245
246    #[test]
247    fn test_runtime_conversion_methods() {
248        let a = ext!(300_u16, 40_u16);
249
250        let _b: Extent2<u32> = a.map_into();
251        let _c: Result<Extent2<u8>, _> = a.try_map_into();
252        let _d = a.map(|x| x as f32 * 0.5);
253        let _e: Result<Extent2<u8>, _> = a.try_map(u8::try_from);
254    }
255}