devela/num/geom/metric/
extent.rs

1// devela::num::geom::metric::extent
2//
3//! A geometrical extent.
4//
5
6use crate::{_impl_metric, cfor, is};
7
8#[doc = crate::TAG_GEOM!()]
9/// An orthogonal extension in `D`-space without a coordinate position.
10///
11/// Represents the lengths of each dimension in a multi-dimensional space,
12/// providing an origin-agnostic shape with the implied form of an orthotope
13/// (generalized rectangle or box).
14#[must_use]
15#[repr(transparent)]
16pub struct Extent<T, const D: usize> {
17    /// The size values in `D`-dimensional space.
18    pub dim: [T; D],
19}
20
21_impl_metric![common_methods: Extent];
22_impl_metric![common_traits: Extent];
23
24/* manual impls for specific dimensionalities */
25
26#[rustfmt::skip]
27impl<T> Extent<T, 2> {
28    /// Returns a copy of the first dimension `x`.
29    #[must_use]
30    pub const fn x(self) -> T where T: Copy { self.dim[0] }
31    /// Returns a copy of the second dimension `y`.
32    #[must_use]
33    pub const fn y(self) -> T where T: Copy { self.dim[1] }
34
35    /// Returns a shared reference to the first dimension `x`.
36    #[must_use]
37    pub const fn x_ref(&self) -> &T { &self.dim[0] }
38    /// Returns a shared reference to the second dimension `y`.
39    #[must_use]
40    pub const fn y_ref(&self) -> &T { &self.dim[1] }
41
42    /// Returns an exclusive reference to the first dimension `x`.
43    #[must_use]
44    pub fn x_mut(&mut self) -> &mut T { &mut self.dim[0] }
45    /// Returns an exclusive reference to the second dimension `y`.
46    #[must_use]
47    pub fn y_mut(&mut self) -> &mut T { &mut self.dim[1] }
48
49    /// Returns `true` if the 2 dimensions of the extent are equal.
50    #[must_use]
51    pub fn is_uniform(&self) -> bool where T: PartialEq {
52        self.dim[0] == self.dim[1]
53    }
54}
55
56#[rustfmt::skip]
57impl<T> Extent<T, 3> {
58    /// Returns a copy of the first dimension `x`.
59    #[must_use]
60    pub const fn x(self) -> T where T: Copy { self.dim[0] }
61    /// Returns a copy of the second dimension `y`.
62    #[must_use]
63    pub const fn y(self) -> T where T: Copy { self.dim[1] }
64    /// Returns a copy of the third dimension `z`.
65    #[must_use]
66    pub const fn z(self) -> T where T: Copy { self.dim[2] }
67
68    /// Returns a shared reference to the first dimension `x`.
69    #[must_use]
70    pub const fn x_ref(&self) -> &T { &self.dim[0] }
71    /// Returns a shared reference to the second dimension `y`.
72    #[must_use]
73    pub const fn y_ref(&self) -> &T { &self.dim[1] }
74    /// Returns a shared reference to the third dimension `z`.
75    #[must_use]
76    pub const fn z_ref(&self) -> &T { &self.dim[2] }
77
78    /// Returns an exclusive reference to the first dimension `x`.
79    #[must_use]
80    pub fn x_mut(&mut self) -> &mut T { &mut self.dim[0] }
81    /// Returns an exclusive reference to the second dimension `y`.
82    #[must_use]
83    pub fn y_mut(&mut self) -> &mut T { &mut self.dim[1] }
84    /// Returns an exclusive reference to the third dimension `z`.
85    #[must_use]
86    pub fn z_mut(&mut self) -> &mut T { &mut self.dim[2] }
87
88    /// Returns `true` if the 3 dimensions of the extent are equal.
89    #[must_use]
90    pub fn is_uniform_3d(&self) -> bool where T: PartialEq {
91        self.dim[0] == self.dim[1] && self.dim[0] == self.dim[2]
92    }
93}
94
95/// Implement `Extent` methods for all primitives.
96macro_rules! impl_extent {
97    () => {
98        impl_extent![sint i8, i16, i32, i64, i128, isize];
99        impl_extent![uint u8, u16, u32, u64, u128, usize];
100        impl_extent![float f32, f64];
101    };
102    // integers common methods
103    //
104    // $t: the inner integer primitive type
105    (int $($t:ty),+) => { $( impl_extent![@int $t]; )+ };
106    (@int $t:ty) => {
107        impl<const D: usize> Extent<$t, D> {
108            /// Returns the internal measure, the product of the extents.
109            ///
110            /// It's equivalent to length, area, and volume in 1, 2 and 3 dimensions.
111            pub const fn c_measure(self) -> $t {
112                let mut measure = 1;
113                cfor!(i in 0..D => {
114                    measure *= self.dim[i];
115                });
116                measure
117            }
118            /// Returns the external boundary, the sum of the extents.
119            ///
120            /// It's equivalent to 2, perimeter and surface area in 1, 2 and 3 dimensions.
121            pub const fn c_boundary(self) -> $t {
122                let mut boundary = 0;
123                cfor!(i in 0..D => {
124                    let mut face_measure = 1;
125                    cfor!(j in 0..D => {
126                        is![i != j; face_measure *= self.dim[j]];
127                    });
128                    boundary += face_measure;
129                });
130                2 * boundary // Each dimension's contribution is counted twice
131            }
132        }
133
134        impl Extent<$t, 1> {
135            /// The length of the 1d extent.
136            #[must_use]
137            pub const fn c_length(self) -> $t { self.dim[0] }
138        }
139        impl Extent<$t, 2> {
140            /// The area of the 2d extent.
141            #[must_use]
142            pub const fn c_area(self) -> $t { self.dim[0] * self.dim[1] }
143
144            /// The perimeter of the 2d extent.
145            #[must_use]
146            pub const fn c_perimeter(self) -> $t { 2 * (self.dim[0] + self.dim[1]) }
147
148        }
149        impl Extent<$t, 3> {
150            /// The volume of the 3d extent.
151            #[must_use]
152            pub const fn c_volume(self) -> $t {
153                self.dim[0] * self.dim[1] * self.dim[2]
154            }
155
156            /// The surface area of the 3d extent.
157            #[must_use]
158            pub const fn c_surface_area(self) -> $t {
159                2 * (self.dim[0] * self.dim[1]
160                    + self.dim[1] * self.dim[2]
161                    + self.dim[2] * self.dim[0])
162            }
163        }
164    };
165
166    (sint $($t:ty),+) => { $( impl_extent![@sint $t]; )+ };
167    (@sint $t:ty ) => {
168        impl_extent![int $t];
169    };
170    (uint $($t:ty),+) => { $( impl_extent![@uint $t]; )+ };
171    (@uint $t:ty ) => {
172        impl_extent![int $t];
173    };
174
175    // $f: the inner floating-point primitive type
176    (float $($f:ty),+) => { $( impl_extent![@float $f]; )+ };
177    (@float $f:ty) => {
178        impl<const D: usize> Extent<$f, D> {
179            /// Returns the internal measure, the product of the extents.
180            ///
181            /// It's equivalent to length, area, and volume in 1, 2 and 3 dimensions.
182            #[must_use]
183            pub const fn measure(self) -> $f {
184                let mut measure = 1.0;
185                cfor!(i in 0..D => {
186                    measure *= self.dim[i];
187                });
188                measure
189            }
190            /// Returns the external boundary, the sum of the extents.
191            ///
192            /// It's equivalent to 2, perimeter and surface area in 1, 2 and 3 dimensions.
193            #[must_use]
194            pub const fn boundary(self) -> $f {
195                let mut boundary = 0.0;
196                cfor!(i in 0..D => {
197                    let mut face_measure = 1.0;
198                    cfor!(j in 0..D => {
199                        is![i != j; face_measure *= self.dim[j]];
200                    });
201                    boundary += face_measure;
202                });
203                2.0 * boundary // Each dimension's contribution is counted twice
204            }
205        }
206
207        impl Extent<$f, 1> {
208            /// The length of the 1d extent.
209            #[must_use]
210            pub const fn length(self) -> $f { self.dim[0] }
211        }
212        impl Extent<$f, 2> {
213            /// The area of the 2d extent.
214            #[must_use]
215            pub const fn area(self) -> $f { self.dim[0] * self.dim[1] }
216
217            /// The perimeter of the 2d extent.
218            #[must_use]
219            pub const fn perimeter(self) -> $f { 2.0 * (self.dim[0] + self.dim[1]) }
220
221        }
222        impl Extent<$f, 3> {
223            /// The volume of the 3d extent.
224            #[must_use]
225            pub const fn volume(self) -> $f {
226                self.dim[0] * self.dim[1] * self.dim[2]
227            }
228
229            /// The surface area of the 3d extent.
230            #[must_use]
231            pub const fn surface_area(self) -> $f {
232                2.0 * (self.dim[0] * self.dim[1]
233                    + self.dim[1] * self.dim[2]
234                    + self.dim[2] * self.dim[0])
235            }
236        }
237    };
238}
239impl_extent![];