1#![allow(clippy::needless_range_loop)]
2
3use std::array;
4
5use super::{Face, Region, Side};
6
7#[derive(Debug, Clone, Copy)]
11pub struct IndexSpace<const N: usize> {
12 size: [usize; N],
13}
14
15impl<const N: usize> IndexSpace<N> {
16 pub const fn new(size: [usize; N]) -> Self {
18 Self { size }
19 }
20
21 pub fn index_count(&self) -> usize {
23 let mut result = 1;
24
25 for i in 0..N {
26 result *= self.size[i]
27 }
28
29 result
30 }
31
32 pub fn size(self) -> [usize; N] {
34 self.size
35 }
36
37 pub fn cartesian_from_linear(self, mut linear: usize) -> [usize; N] {
42 debug_assert!(linear < self.size.iter().product());
43
44 let mut result = [0; N];
45
46 for i in 0..N {
47 result[i] = linear % self.size[i];
48 linear /= self.size[i];
49 }
50
51 result
52 }
53
54 pub fn linear_from_cartesian(self, cartesian: [usize; N]) -> usize {
56 for axis in 0..N {
57 debug_assert!(cartesian[axis] < self.size[axis]);
58 }
59
60 let mut result = 0;
61 let mut stride = 1;
62
63 for i in 0..N {
64 result += stride * cartesian[i];
65 stride *= self.size[i];
66 }
67
68 result
69 }
70
71 pub const fn iter(self) -> CartesianIter<N> {
73 CartesianIter {
74 size: self.size,
75 cursor: [0; N],
76 }
77 }
78
79 pub fn window(self) -> IndexWindow<N> {
81 IndexWindow {
82 origin: [0; N],
83 size: self.size,
84 }
85 }
86
87 pub fn plane_window(self, axis: usize, intercept: usize) -> IndexWindow<N> {
89 debug_assert!(intercept < self.size[axis]);
90
91 let mut origin = [0; N];
92 origin[axis] = intercept;
93
94 let mut size = self.size;
95 size[axis] = 1;
96
97 IndexWindow::new(origin, size)
98 }
99
100 pub fn face_window(self, face: Face<N>) -> IndexWindow<N> {
102 let intercept = if face.side {
103 self.size[face.axis] - 1
104 } else {
105 0
106 };
107 self.plane_window(face.axis, intercept)
108 }
109
110 pub fn region_adjacent_window(self, region: Region<N>) -> IndexWindow<N> {
112 let origin = array::from_fn(|axis| match region.side(axis) {
113 Side::Left | Side::Middle => 0,
114 Side::Right => self.size[axis] - 1,
115 });
116
117 let size = array::from_fn(|axis| match region.side(axis) {
118 Side::Left | Side::Right => 1,
119 Side::Middle => self.size[axis],
120 });
121
122 IndexWindow { origin, size }
123 }
124}
125
126impl<const N: usize> IntoIterator for IndexSpace<N> {
127 type IntoIter = CartesianIter<N>;
128 type Item = [usize; N];
129
130 fn into_iter(self) -> Self::IntoIter {
131 self.iter()
132 }
133}
134
135#[derive(Debug, Clone, Copy)]
137pub struct IndexWindow<const N: usize> {
138 pub origin: [usize; N],
140 pub size: [usize; N],
142}
143
144impl<const N: usize> IndexWindow<N> {
145 pub fn new(origin: [usize; N], size: [usize; N]) -> Self {
147 Self { origin, size }
148 }
149
150 pub fn iter(&self) -> CartesianWindowIter<N> {
152 CartesianWindowIter {
153 origin: self.origin,
154 inner: IndexSpace::new(self.size).iter(),
155 }
156 }
157}
158
159impl<const N: usize> IntoIterator for IndexWindow<N> {
160 type IntoIter = CartesianWindowIter<N>;
161 type Item = [usize; N];
162
163 fn into_iter(self) -> Self::IntoIter {
164 self.iter()
165 }
166}
167
168#[derive(Debug, Clone)]
169pub struct CartesianIter<const N: usize> {
171 size: [usize; N],
172 cursor: [usize; N],
173}
174
175impl<const N: usize> Iterator for CartesianIter<N> {
176 type Item = [usize; N];
177
178 fn next(&mut self) -> Option<Self::Item> {
179 if self.cursor[N - 1] == self.size[N - 1] {
181 return None;
182 }
183
184 let result = self.cursor;
186
187 for i in 0..N {
188 if self.size[i] == 0 {
189 return None;
191 }
192
193 self.cursor[i] += 1;
195 if self.cursor[i] == self.size[i] && i < N - 1 {
200 self.cursor[i] = 0;
201 continue;
203 }
204
205 break;
206 }
207
208 Some(result)
209 }
210}
211
212#[derive(Debug, Clone)]
213pub struct CartesianWindowIter<const N: usize> {
215 origin: [usize; N],
216 inner: CartesianIter<N>,
217}
218
219impl<const N: usize> Iterator for CartesianWindowIter<N> {
220 type Item = [usize; N];
221
222 fn next(&mut self) -> Option<Self::Item> {
223 let offset = self.inner.next()?;
224 Some(array::from_fn(|i| self.origin[i] + offset[i]))
225 }
226}
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231
232 #[test]
233 fn index_iteration() {
234 let space = IndexSpace::new([3, 2]);
235 let mut indices = space.iter();
236
237 assert_eq!(indices.next(), Some([0, 0]));
238 assert_eq!(indices.next(), Some([1, 0]));
239 assert_eq!(indices.next(), Some([2, 0]));
240 assert_eq!(indices.next(), Some([0, 1]));
241 assert_eq!(indices.next(), Some([1, 1]));
242 assert_eq!(indices.next(), Some([2, 1]));
243 assert_eq!(indices.next(), None);
244
245 let space = IndexSpace::new([0, 10]);
246 let mut indices = space.iter();
247
248 assert_eq!(indices.next(), None);
249
250 let space = IndexSpace::new([2, 3, 10]);
251 let mut plane = space.plane_window(2, 5).iter();
252
253 assert_eq!(plane.next(), Some([0, 0, 5]));
254 assert_eq!(plane.next(), Some([1, 0, 5]));
255 assert_eq!(plane.next(), Some([0, 1, 5]));
256 assert_eq!(plane.next(), Some([1, 1, 5]));
257 assert_eq!(plane.next(), Some([0, 2, 5]));
258 assert_eq!(plane.next(), Some([1, 2, 5]));
259 assert_eq!(plane.next(), None);
260
261 let space = IndexSpace::new([2, 3, 4]);
262 for (i, index) in space.iter().enumerate() {
263 assert_eq!(i, space.linear_from_cartesian(index));
264 }
265 }
266
267 #[test]
268 fn index_conversion() {
269 let space = IndexSpace::new([2, 4, 3]);
270
271 assert_eq!(space.linear_from_cartesian([1, 0, 0]), 1);
272 assert_eq!(space.linear_from_cartesian([0, 1, 0]), 2);
273 assert_eq!(space.linear_from_cartesian([0, 0, 2]), 8 * 2);
274 assert_eq!(space.linear_from_cartesian([1, 1, 2]), 8 * 2 + 2 + 1);
275
276 assert_eq!(space.cartesian_from_linear(1), [1, 0, 0]);
277 assert_eq!(space.cartesian_from_linear(2), [0, 1, 0]);
278 assert_eq!(space.cartesian_from_linear(8 * 2), [0, 0, 2]);
279 assert_eq!(space.cartesian_from_linear(8 * 2 + 2 + 1), [1, 1, 2]);
280 }
281}