Skip to main content

aeon_tk/geometry/
face.rs

1use std::{
2    array::from_fn,
3    fmt::{Display, Write},
4    ops::{Index, IndexMut},
5};
6
7use crate::array::ArrayWrap;
8
9use super::{Region, Side, Split, index::IndexWindow};
10
11/// A face of a rectangular prism in `N` dimensional space.
12/// If `face.side` is true, than this face points in the positive direction along
13/// `face.axis`, otherwise it points along the negative direction.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
15pub struct Face<const N: usize> {
16    pub axis: usize,
17    pub side: bool,
18}
19
20impl<const N: usize> Face<N> {
21    pub fn iterate() -> FaceIter<N> {
22        FaceIter {
23            axis: 0,
24            side: false,
25        }
26    }
27
28    /// Face on negative side of axis.
29    pub fn negative(axis: usize) -> Self {
30        debug_assert!(axis < N);
31        Self { axis, side: false }
32    }
33
34    /// Face on positive side of axis.
35    pub fn positive(axis: usize) -> Self {
36        debug_assert!(axis < N);
37        Self { axis, side: true }
38    }
39
40    /// Reverses the direction of the face along its axis.
41    pub fn reversed(self) -> Self {
42        Self {
43            axis: self.axis,
44            side: !self.side,
45        }
46    }
47
48    /// Transforms a face into a linear index.
49    pub fn to_linear(self) -> usize {
50        2 * self.axis + self.side as usize
51    }
52
53    /// Constructs a face from a linear index.
54    pub fn from_linear(linear: usize) -> Self {
55        assert!(linear < 2 * N);
56
57        Self {
58            axis: linear / 2,
59            side: linear % 2 == 1,
60        }
61    }
62
63    /// Finds a split adjacent to the given face (all other axes default to negative).
64    pub fn adjacent_split(self) -> Split<N> {
65        let mut result = Split::empty();
66        result.set_to(self.axis, self.side);
67        result
68    }
69
70    /// Iterates over all splits adjacent to the given face.
71    pub fn adjacent_splits(self) -> impl Iterator<Item = Split<N>> {
72        Split::<N>::enumerate().filter(move |split| split.is_set(self.axis) == self.side)
73    }
74}
75
76const AXIS_NAMES: [char; 4] = ['x', 'y', 'z', 'w'];
77
78impl<const N: usize> Display for Face<N> {
79    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80        if self.side {
81            f.write_char('+')?;
82        } else {
83            f.write_char('-')?;
84        }
85
86        if N < 4 {
87            f.write_char(AXIS_NAMES[self.axis])
88        } else {
89            f.write_str(&self.axis.to_string())
90        }
91    }
92}
93
94/// Iterator over all faces in a given number of dimensions.
95#[derive(Debug)]
96pub struct FaceIter<const N: usize> {
97    axis: usize,
98    side: bool,
99}
100
101impl<const N: usize> Iterator for FaceIter<N> {
102    type Item = Face<N>;
103
104    fn next(&mut self) -> Option<Self::Item> {
105        if self.axis >= N {
106            return None;
107        }
108
109        let result = Face {
110            axis: self.axis,
111            side: self.side,
112        };
113
114        self.axis += self.side as usize;
115        self.side = !self.side;
116
117        Some(result)
118    }
119
120    fn size_hint(&self) -> (usize, Option<usize>) {
121        (2 * N, Some(2 * N))
122    }
123}
124
125impl<const N: usize> ExactSizeIterator for FaceIter<N> {
126    fn len(&self) -> usize {
127        2 * N
128    }
129}
130
131/// An array storing a value for each `Face<N>` in a N-dimensional space.
132#[derive(Clone, Copy, Debug, PartialEq, Eq)]
133pub struct FaceArray<const N: usize, T>([[T; 2]; N]);
134
135impl<const N: usize, T> FaceArray<N, T> {
136    /// Constructs a `FaceArray<N>` by calling `f` for each `Face<N>`.
137    pub fn from_fn<F: FnMut(Face<N>) -> T>(mut f: F) -> Self {
138        Self(core::array::from_fn(|axis| {
139            [f(Face::negative(axis)), f(Face::positive(axis))]
140        }))
141    }
142
143    /// Retrieves the inner representation of `FaceArray`, i.e. an array of type
144    /// `[[T; 2]; N]` where the first index is axis and the second index is size.
145    pub fn into_inner(self) -> [[T; 2]; N] {
146        self.0
147    }
148}
149
150impl<const N: usize, T: Clone> FaceArray<N, T> {
151    /// Constructs a `FaceArray` by filling the whole array with `value`.
152    pub fn splat(value: T) -> Self {
153        Self::from_fn(|_| value.clone())
154    }
155
156    pub fn from_sides(negative: [T; N], positive: [T; N]) -> Self {
157        Self::from_fn(|face| match face.side {
158            true => positive[face.axis].clone(),
159            false => negative[face.axis].clone(),
160        })
161    }
162}
163
164impl<const N: usize, T> From<[[T; 2]; N]> for FaceArray<N, T> {
165    fn from(value: [[T; 2]; N]) -> Self {
166        Self(value)
167    }
168}
169
170impl<const N: usize, T> From<[(T, T); N]> for FaceArray<N, T> {
171    fn from(value: [(T, T); N]) -> Self {
172        Self(value.map(|(l, r)| [l, r]))
173    }
174}
175
176impl<const N: usize, T: serde::Serialize + Clone> serde::Serialize for FaceArray<N, T> {
177    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
178    where
179        S: serde::Serializer,
180    {
181        ArrayWrap(self.0.clone()).serialize(serializer)
182    }
183}
184
185impl<'de, const N: usize, T: serde::de::Deserialize<'de>> serde::de::Deserialize<'de>
186    for FaceArray<N, T>
187where
188    T: serde::de::Deserialize<'de>,
189{
190    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
191    where
192        D: serde::Deserializer<'de>,
193    {
194        Ok(FaceArray(ArrayWrap::deserialize(deserializer)?.0))
195    }
196}
197
198impl<const N: usize, T: Default> Default for FaceArray<N, T> {
199    fn default() -> Self {
200        Self::from_fn(|_| T::default())
201    }
202}
203
204impl<const N: usize, T> Index<Face<N>> for FaceArray<N, T> {
205    type Output = T;
206    fn index(&self, index: Face<N>) -> &Self::Output {
207        &self.0[index.axis][index.side as usize]
208    }
209}
210
211impl<const N: usize, T> IndexMut<Face<N>> for FaceArray<N, T> {
212    fn index_mut(&mut self, index: Face<N>) -> &mut Self::Output {
213        &mut self.0[index.axis][index.side as usize]
214    }
215}
216
217/// Stores a boolean flag for each face of a rectangular prism.
218#[derive(Clone, Copy, Debug, PartialEq, Eq)]
219pub struct FaceMask<const N: usize>([[bool; 2]; N]);
220
221impl<const N: usize> FaceMask<N> {
222    pub fn from_fn<F: FnMut(Face<N>) -> bool>(mut f: F) -> Self {
223        Self(core::array::from_fn(|axis| {
224            [f(Face::negative(axis)), f(Face::positive(axis))]
225        }))
226    }
227
228    pub fn pack(bits: [[bool; 2]; N]) -> Self {
229        Self(bits)
230    }
231
232    /// Constructs a mask where all flags have been set to false.
233    pub fn empty() -> Self {
234        Self([[false; 2]; N])
235    }
236
237    /// Constructs a mask where all flags have been set to true.
238    pub fn full() -> Self {
239        Self([[true; 2]; N])
240    }
241
242    pub fn is_set(&self, face: Face<N>) -> bool {
243        self.0[face.axis][face.side as usize]
244    }
245
246    pub fn set(&mut self, face: Face<N>) {
247        self.0[face.axis][face.side as usize] = true;
248    }
249
250    pub fn clear(&mut self, face: Face<N>) {
251        self.0[face.axis][face.side as usize] = false;
252    }
253
254    pub fn set_to(&mut self, face: Face<N>, val: bool) {
255        self.0[face.axis][face.side as usize] = val;
256    }
257
258    /// Iterates over regions adjacent to flagged faces.
259    pub fn adjacent_regions(&self) -> impl Iterator<Item = Region<N>> {
260        let mut window = IndexWindow::new([1; N], [1; N]);
261
262        for axis in 0..N {
263            if self.is_set(Face::negative(axis)) {
264                window.origin[axis] -= 1;
265                window.size[axis] += 1;
266            }
267
268            if self.is_set(Face::positive(axis)) {
269                window.size[axis] += 1;
270            }
271        }
272
273        window
274            .iter()
275            .map(|index| Region::new(from_fn(|axis| Side::from_value(index[axis] as u8))))
276    }
277}
278
279impl<const N: usize> Default for FaceMask<N> {
280    fn default() -> Self {
281        Self::from_fn(|_| false)
282    }
283}
284
285#[cfg(test)]
286mod tests {
287    use super::*;
288
289    #[test]
290    fn face_iteration() {
291        let mut list = Face::<3>::iterate();
292        assert_eq!(list.next(), Some(Face::negative(0)));
293        assert_eq!(list.next(), Some(Face::positive(0)));
294        assert_eq!(list.next(), Some(Face::negative(1)));
295        assert_eq!(list.next(), Some(Face::positive(1)));
296        assert_eq!(list.next(), Some(Face::negative(2)));
297        assert_eq!(list.next(), Some(Face::positive(2)));
298        assert_eq!(list.next(), None);
299
300        assert_eq!(Face::<4>::negative(1).to_linear(), 2);
301        assert_eq!(Face::<4>::positive(3).to_linear(), 7);
302        assert_eq!(Face::<4>::positive(3), Face::<4>::from_linear(7));
303    }
304
305    #[test]
306    fn adjacent_regions() {
307        let mut mask = FaceMask::<2>::empty();
308        mask.set(Face::positive(0));
309        mask.set(Face::negative(1));
310
311        let mut regions = mask.adjacent_regions();
312
313        assert_eq!(
314            regions.next(),
315            Some(Region::new([Side::Middle, Side::Left]))
316        );
317        assert_eq!(regions.next(), Some(Region::new([Side::Right, Side::Left])));
318        assert_eq!(
319            regions.next(),
320            Some(Region::new([Side::Middle, Side::Middle]))
321        );
322        assert_eq!(
323            regions.next(),
324            Some(Region::new([Side::Right, Side::Middle]))
325        );
326        assert_eq!(regions.next(), None);
327    }
328}