aeon_tk/geometry/region.rs
1use std::{
2 array::from_fn,
3 cmp::Ordering,
4 fmt::{Display, Write},
5};
6
7use super::{index::IndexWindow, Split, CartesianIter, Face, IndexSpace};
8
9/// Denotes where the region falls on a certain axis.
10#[repr(u8)]
11#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
12pub enum Side {
13 Left = 0,
14 Middle = 1,
15 Right = 2,
16}
17
18impl Side {
19 pub fn reverse(self) -> Self {
20 match self {
21 Self::Left => Self::Right,
22 Self::Right => Self::Left,
23 Self::Middle => Self::Middle,
24 }
25 }
26
27 pub fn from_value(val: u8) -> Self {
28 assert!(val < 3);
29 // Safety. We have specified the memory representation of the
30 // enum and checked the value, so this should be safe.
31 unsafe { std::mem::transmute(val) }
32 }
33}
34
35#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
36pub struct Region<const N: usize> {
37 #[serde(with = "crate::array")]
38 sides: [Side; N],
39}
40
41impl<const N: usize> Region<N> {
42 /// Number of different regions in a given number of dimensions.
43 pub const COUNT: usize = 3usize.pow(N as u32);
44 /// The default "central" region.
45 pub const CENTRAL: Self = Self::new([Side::Middle; N]);
46
47 // Builds a new region from the given sides.
48 pub const fn new(sides: [Side; N]) -> Self {
49 Self { sides }
50 }
51
52 pub const fn sides(&self) -> [Side; N] {
53 self.sides
54 }
55
56 pub const fn side(&self, axis: usize) -> Side {
57 self.sides[axis]
58 }
59
60 pub fn set_side(&mut self, axis: usize, side: Side) {
61 self.sides[axis] = side
62 }
63
64 /// Reverse every side in the region.
65 pub fn reverse(&self) -> Self {
66 let mut result = [Side::Left; N];
67
68 for axis in 0..N {
69 result[axis] = self.sides[axis].reverse();
70 }
71
72 Self::new(result)
73 }
74
75 /// Returns number of axes that are not `Side::Middle`.
76 pub fn adjacency(&self) -> usize {
77 self.sides
78 .into_iter()
79 .filter(|&s| s != Side::Middle)
80 .count()
81 }
82
83 /// Iterates over all faces one would have to move over to get to the region.
84 pub fn adjacent_faces(&self) -> impl Iterator<Item = Face<N>> + '_ {
85 (0..N)
86 .filter(|&axis| self.side(axis) != Side::Middle)
87 .map(|axis| Face {
88 axis,
89 side: self.side(axis) == Side::Right,
90 })
91 }
92
93 /// Iterates over all splits that can touch this region.
94 pub fn adjacent_splits(self) -> impl Iterator<Item = Split<N>> {
95 let origin: [_; N] = from_fn(|axis| match self.side(axis) {
96 Side::Left | Side::Middle => 0,
97 Side::Right => 1,
98 });
99
100 let size: [_; N] = from_fn(|axis| match self.side(axis) {
101 Side::Middle => 2,
102 _ => 1,
103 });
104
105 IndexWindow::new(origin, size)
106 .iter()
107 .map(|index| Split::pack(from_fn(|axis| index[axis] != 0)))
108 }
109
110 /// Computes a split which touches the given region.
111 pub fn adjacent_split(&self) -> Split<N> {
112 let mut result = Split::empty();
113 for axis in 0..N {
114 result.set_to(axis, self.side(axis) == Side::Right)
115 }
116 result
117 }
118
119 /// Checks whether a given split is adjacent to the region.
120 pub fn is_split_adjacent(&self, split: Split<N>) -> bool {
121 for axis in 0..N {
122 match (self.side(axis), split.is_set(axis)) {
123 (Side::Left, true) => return false,
124 (Side::Right, false) => return false,
125 _ => {}
126 }
127 }
128
129 true
130 }
131
132 // /// Returns an index space with the same size as the region.
133 // pub fn index_space(&self, support: usize, block: [usize; N]) -> IndexSpace<N> {
134 // let mut size = [0; N];
135
136 // for axis in 0..N {
137 // if self.sides[axis] != Side::Middle {
138 // size[axis] = support;
139 // } else {
140 // size[axis] = block[axis]
141 // }
142 // }
143
144 // IndexSpace::new(size)
145 // }
146
147 // /// Iterates nodes in the given region (including ghost nodes and nodes
148 // /// on faces).
149 // pub fn nodes(&self, support: usize, block: [usize; N]) -> RegionNodeIter<N> {
150 // RegionNodeIter {
151 // inner: self.index_space(support, block).iter(),
152 // block,
153 // sides: self.sides,
154 // }
155 // }
156
157 // pub fn face_vertices(&self, block: [usize; N]) -> RegionFaceVertexIter<N> {
158 // let mut size = [0; N];
159
160 // for axis in 0..N {
161 // size[axis] = match self.sides[axis] {
162 // Side::Left | Side::Right => 1,
163 // Side::Middle => block[axis],
164 // }
165 // }
166
167 // RegionFaceVertexIter {
168 // inner: IndexSpace::new(size).iter(),
169 // block,
170 // sides: self.sides,
171 // }
172 // }
173
174 // pub fn offset_nodes(&self, support: usize) -> RegionOffsetNodeIter<N> {
175 // let size = self.sides.map(|side| match side {
176 // Side::Left | Side::Right => support,
177 // Side::Middle => 1,
178 // });
179
180 // RegionOffsetNodeIter {
181 // inner: IndexSpace::new(size).iter(),
182 // sides: self.sides,
183 // }
184 // }
185
186 // pub fn offset_dir(&self) -> [isize; N] {
187 // self.sides.map(|side| match side {
188 // Side::Left => -1,
189 // Side::Right => 1,
190 // Side::Middle => 0,
191 // })
192 // }
193
194 // /// Returns a mask for which a given axis is set if and only if `self.sides[axis] != Side::Middle`.
195 // pub fn to_mask(&self) -> AxisMask<N> {
196 // let mut result = AxisMask::empty();
197
198 // for axis in 0..N {
199 // result.set_to(axis, self.sides[axis] != Side::Middle);
200 // }
201
202 // result
203 // }
204
205 // pub fn masked(&self, mask: AxisMask<N>) -> Self {
206 // let mut sides = self.sides;
207
208 // for i in 0..N {
209 // if !mask.is_set(i) {
210 // sides[i] = Side::Middle;
211 // }
212 // }
213
214 // Self::new(sides)
215 // }
216
217 // pub fn masked_by_split(&self, split: AxisMask<N>) -> Self {
218 // let mut mask = AxisMask::empty();
219
220 // for axis in 0..N {
221 // match self.sides[axis] {
222 // Side::Left => mask.set_to(axis, !split.is_set(axis)),
223 // Side::Middle => mask.clear(axis),
224 // Side::Right => mask.set_to(axis, split.is_set(axis)),
225 // }
226 // }
227
228 // self.masked(mask)
229 // }
230
231 /// Converts the region into an integer value.
232 pub fn to_linear(&self) -> usize {
233 let space = IndexSpace::new([3; N]);
234 let index = from_fn(|axis| self.side(axis) as usize);
235 space.linear_from_cartesian(index)
236 }
237
238 /// Converts an integer value into a region.
239 pub fn from_linear(val: usize) -> Self {
240 let space = IndexSpace::new([3; N]);
241 let index = space.cartesian_from_linear(val);
242 Self::new(from_fn(|axis| Side::from_value(index[axis] as u8)))
243 }
244}
245
246impl<const N: usize> Ord for Region<N> {
247 fn cmp(&self, other: &Self) -> Ordering {
248 let space = IndexSpace::new([3; N]);
249 let index = from_fn(|axis| self.side(axis) as usize);
250 let oindex = from_fn(|axis| other.side(axis) as usize);
251
252 space
253 .linear_from_cartesian(index)
254 .cmp(&space.linear_from_cartesian(oindex))
255 }
256}
257
258impl<const N: usize> PartialOrd for Region<N> {
259 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
260 Some(self.cmp(other))
261 }
262}
263
264impl<const N: usize> Display for Region<N> {
265 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
266 for axis in 0..N {
267 match self.side(axis) {
268 Side::Left => f.write_char('-'),
269 Side::Middle => f.write_char('='),
270 Side::Right => f.write_char('+'),
271 }?;
272 }
273 Ok(())
274 }
275}
276
277// /// Allows iterating the nodes in a region.
278// pub struct RegionNodeIter<const N: usize> {
279// inner: CartesianIter<N>,
280// block: [usize; N],
281// sides: [Side; N],
282// }
283
284// impl<const N: usize> Iterator for RegionNodeIter<N> {
285// type Item = [isize; N];
286
287// fn next(&mut self) -> Option<Self::Item> {
288// let cart = self.inner.next()?;
289
290// let mut result = [0isize; N];
291
292// for axis in 0..N {
293// result[axis] = match self.sides[axis] {
294// Side::Left => -(cart[axis] as isize),
295// Side::Right => (self.block[axis] + cart[axis]) as isize,
296// Side::Middle => cart[axis] as isize,
297// }
298// }
299
300// Some(result)
301// }
302// }
303
304// /// Allows iterating the vertices on the face of a region.
305// pub struct RegionFaceVertexIter<const N: usize> {
306// inner: CartesianIter<N>,
307// block: [usize; N],
308// sides: [Side; N],
309// }
310
311// impl<const N: usize> Iterator for RegionFaceVertexIter<N> {
312// type Item = [usize; N];
313
314// fn next(&mut self) -> Option<Self::Item> {
315// let cart = self.inner.next()?;
316
317// let mut result = [0; N];
318
319// for axis in 0..N {
320// result[axis] = match self.sides[axis] {
321// Side::Left => 0,
322// Side::Right => self.block[axis] - 1,
323// Side::Middle => cart[axis],
324// }
325// }
326
327// Some(result)
328// }
329// }
330
331// pub struct RegionOffsetNodeIter<const N: usize> {
332// inner: CartesianIter<N>,
333// sides: [Side; N],
334// }
335
336// impl<const N: usize> Iterator for RegionOffsetNodeIter<N> {
337// type Item = [isize; N];
338
339// fn next(&mut self) -> Option<Self::Item> {
340// let cart = self.inner.next()?;
341
342// let mut result = [0isize; N];
343
344// for axis in 0..N {
345// result[axis] = match self.sides[axis] {
346// Side::Left => -(cart[axis] as isize),
347// Side::Right => cart[axis] as isize,
348// Side::Middle => 0,
349// }
350// }
351
352// Some(result)
353// }
354// }
355
356pub struct RegionIter<const N: usize> {
357 inner: CartesianIter<N>,
358}
359
360impl<const N: usize> Iterator for RegionIter<N> {
361 type Item = Region<N>;
362
363 fn next(&mut self) -> Option<Self::Item> {
364 Some(Region::new(self.inner.next()?.map(|idx| match idx {
365 0 => Side::Left,
366 1 => Side::Middle,
367 2 => Side::Right,
368 _ => unreachable!(),
369 })))
370 }
371}
372
373impl<const N: usize> ExactSizeIterator for RegionIter<N> {
374 fn len(&self) -> usize {
375 Region::<N>::COUNT
376 }
377}
378
379/// Iterates over all regions in an N-dimensional space.
380pub fn regions<const N: usize>() -> RegionIter<N> {
381 RegionIter {
382 inner: IndexSpace::new([3; N]).iter(),
383 }
384}
385
386#[cfg(test)]
387mod tests {
388 use crate::geometry::{Split, Face};
389
390 use super::{regions, Region, Side};
391
392 #[test]
393 fn region_iteration() {
394 let comparison = [
395 [Side::Left, Side::Left],
396 [Side::Middle, Side::Left],
397 [Side::Right, Side::Left],
398 [Side::Left, Side::Middle],
399 [Side::Middle, Side::Middle],
400 [Side::Right, Side::Middle],
401 [Side::Left, Side::Right],
402 [Side::Middle, Side::Right],
403 [Side::Right, Side::Right],
404 ];
405
406 for (region, compare) in regions().zip(comparison.into_iter()) {
407 assert_eq!(region, Region::new(compare));
408 }
409 }
410
411 #[test]
412 fn adjacency() {
413 let region = Region::new([Side::Left, Side::Right]);
414 assert_eq!(region.adjacency(), 2);
415
416 let mut faces = region.adjacent_faces();
417 assert_eq!(faces.next(), Some(Face::negative(0)));
418 assert_eq!(faces.next(), Some(Face::positive(1)));
419 assert_eq!(faces.next(), None);
420
421 let mut splits = region.adjacent_splits();
422 assert_eq!(splits.next(), Some(Split::pack([false, true])));
423 assert_eq!(splits.next(), None);
424
425 assert_eq!(region.adjacent_split(), Split::pack([false, true]));
426 }
427}