1use crate::attributes::AttrStorageManager;
2use crate::cmap::{BuilderError, CMap2, DartIdType};
3use crate::geometry::{CoordsFloat, Vector2, Vertex2};
4
5#[derive(Default, Clone)]
19pub struct GridDescriptor<T: CoordsFloat> {
20 pub(crate) origin: Vertex2<T>,
21 pub(crate) n_cells: Option<[usize; 3]>,
22 pub(crate) len_per_cell: Option<[T; 3]>,
23 pub(crate) lens: Option<[T; 3]>,
24 pub(crate) split_quads: bool,
25}
26
27macro_rules! setters {
28 ($fld: ident, $fldx: ident, $fldy: ident, $fldz: ident, $zero: expr, $fldty: ty) => {
29 #[must_use = "unused builder object"]
31 pub fn $fld(mut self, $fld: [$fldty; 3]) -> Self {
32 self.$fld = Some($fld);
33 self
34 }
35
36 #[must_use = "unused builder object"]
38 pub fn $fldx(mut self, $fld: $fldty) -> Self {
39 if let Some([ptr, _, _]) = &mut self.$fld {
40 *ptr = $fld;
41 } else {
42 self.$fld = Some([$fld, $zero, $zero]);
43 }
44 self
45 }
46
47 #[must_use = "unused builder object"]
49 pub fn $fldy(mut self, $fld: $fldty) -> Self {
50 if let Some([_, ptr, _]) = &mut self.$fld {
51 *ptr = $fld;
52 } else {
53 self.$fld = Some([$zero, $fld, $zero]);
54 }
55 self
56 }
57
58 #[must_use = "unused builder object"]
60 pub fn $fldz(mut self, $fld: $fldty) -> Self {
61 if let Some([_, _, ptr]) = &mut self.$fld {
62 *ptr = $fld;
63 } else {
64 self.$fld = Some([$zero, $zero, $fld]);
65 }
66 self
67 }
68 };
69}
70
71impl<T: CoordsFloat> GridDescriptor<T> {
72 setters!(n_cells, n_cells_x, n_cells_y, n_cells_z, 0, usize);
74
75 setters!(
77 len_per_cell,
78 len_per_cell_x,
79 len_per_cell_y,
80 len_per_cell_z,
81 T::zero(),
82 T
83 );
84
85 setters!(lens, lens_x, lens_y, lens_z, T::zero(), T);
87
88 #[must_use = "unused builder object"]
90 pub fn origin(mut self, origin: Vertex2<T>) -> Self {
91 self.origin = origin;
92 self
93 }
94
95 #[must_use = "unused builder object"]
97 pub fn split_quads(mut self, split: bool) -> Self {
98 self.split_quads = split;
99 self
100 }
101}
102
103macro_rules! check_parameters {
106 ($id: ident, $msg: expr) => {
107 if $id.is_sign_negative() | $id.is_zero() {
108 return Err(BuilderError::InvalidGridParameters($msg));
109 }
110 };
111}
112
113impl<T: CoordsFloat> GridDescriptor<T> {
114 #[allow(clippy::type_complexity)]
116 pub(crate) fn parse_2d(self) -> Result<(Vertex2<T>, [usize; 2], [T; 2]), BuilderError> {
117 match (self.n_cells, self.len_per_cell, self.lens) {
118 (Some([nx, ny, _]), Some([lpx, lpy, _]), lens) => {
120 if lens.is_some() {
121 eprintln!("W: All three grid parameters were specified, total lengths will be ignored");
122 }
123 #[rustfmt::skip]
124 check_parameters!(lpx, "length per x cell is null or negative");
125 #[rustfmt::skip]
126 check_parameters!(lpy, "length per y cell is null or negative");
127 Ok((self.origin, [nx, ny], [lpx, lpy]))
128 }
129 (Some([nx, ny, _]), None, Some([lx, ly, _])) => {
131 #[rustfmt::skip]
132 check_parameters!(lx, "grid length along x is null or negative");
133 #[rustfmt::skip]
134 check_parameters!(ly, "grid length along y is null or negative");
135 Ok((
136 self.origin,
137 [nx, ny],
138 [lx / T::from(nx).unwrap(), ly / T::from(ny).unwrap()],
139 ))
140 }
141 (None, Some([lpx, lpy, _]), Some([lx, ly, _])) => {
143 #[rustfmt::skip]
144 check_parameters!(lpx, "length per x cell is null or negative");
145 #[rustfmt::skip]
146 check_parameters!(lpy, "length per y cell is null or negative");
147 #[rustfmt::skip]
148 check_parameters!(lx, "grid length along x is null or negative");
149 #[rustfmt::skip]
150 check_parameters!(ly, "grid length along y is null or negative");
151 Ok((
152 self.origin,
153 [
154 (lx / lpx).ceil().to_usize().unwrap(),
155 (ly / lpy).ceil().to_usize().unwrap(),
156 ],
157 [lpx, lpy],
158 ))
159 }
160 (_, _, _) => Err(BuilderError::MissingGridParameters),
161 }
162 }
163}
164
165#[allow(clippy::too_many_lines)]
169pub fn build_2d_grid<T: CoordsFloat>(
170 origin: Vertex2<T>,
171 [n_square_x, n_square_y]: [usize; 2],
172 [len_per_x, len_per_y]: [T; 2],
173 manager: AttrStorageManager,
174) -> CMap2<T> {
175 let map: CMap2<T> = CMap2::new_with_undefined_attributes(4 * n_square_x * n_square_y, manager);
176
177 (1..=(4 * n_square_x * n_square_y) as DartIdType)
179 .zip(generate_square_beta_values(n_square_x, n_square_y))
180 .for_each(|(dart, images)| {
181 map.set_betas(dart, images);
182 });
183
184 (0..n_square_y)
188 .flat_map(|y_idx| (0..n_square_x).map(move |x_idx| (y_idx, x_idx)))
190 .for_each(|(y_idx, x_idx)| {
191 let vertex_id = map.vertex_id((1 + x_idx * 4 + y_idx * 4 * n_square_x) as DartIdType);
192 map.force_write_vertex(
193 vertex_id,
194 origin
195 + Vector2(
196 T::from(x_idx).unwrap() * len_per_x,
197 T::from(y_idx).unwrap() * len_per_y,
198 ),
199 );
200 });
201
202 (0..n_square_x).for_each(|x_idx| {
204 let y_idx = n_square_y - 1;
205 let vertex_id = map.vertex_id((4 + x_idx * 4 + y_idx * 4 * n_square_x) as DartIdType);
206 map.force_write_vertex(
207 vertex_id,
208 origin
209 + Vector2(
210 T::from(x_idx).unwrap() * len_per_x,
211 T::from(y_idx + 1).unwrap() * len_per_y,
212 ),
213 );
214 });
215
216 (0..n_square_y).for_each(|y_idx| {
218 let x_idx = n_square_x - 1;
219 let vertex_id = map.vertex_id((2 + x_idx * 4 + y_idx * 4 * n_square_x) as DartIdType);
220 map.force_write_vertex(
221 vertex_id,
222 origin
223 + Vector2(
224 T::from(x_idx + 1).unwrap() * len_per_x,
225 T::from(y_idx).unwrap() * len_per_y,
226 ),
227 );
228 });
229
230 {
232 let (x_idx, y_idx) = (n_square_x - 1, n_square_y - 1);
233 let vertex_id = map.vertex_id((3 + x_idx * 4 + y_idx * 4 * n_square_x) as DartIdType); map.force_write_vertex(
235 vertex_id,
236 origin
237 + Vector2(
238 T::from(x_idx + 1).unwrap() * len_per_x,
239 T::from(y_idx + 1).unwrap() * len_per_y,
240 ),
241 );
242 }
243
244 debug_assert_eq!(map.iter_faces().count(), n_square_x * n_square_y);
248
249 map
250}
251
252#[allow(clippy::inline_always)]
253#[rustfmt::skip]
254#[inline(always)]
255fn generate_square_beta_values(n_x: usize, n_y: usize) -> impl Iterator<Item = [DartIdType; 3]> {
256 (0..n_y).flat_map(move |iy| {
259 (0..n_x).flat_map(move |ix| {
260 let d1 = (1 + 4 * ix + n_x * 4 * iy) as DartIdType;
261 let (d2, d3, d4) = (d1 + 1, d1 + 2, d1 + 3);
262 [
264 [ d4, d2, if iy == 0 { 0 } else { d3 - 4 * n_x as DartIdType } ],
265 [ d1, d3, if ix == n_x-1 { 0 } else { d2 + 6 } ],
266 [ d2, d4, if iy == n_y-1 { 0 } else { d1 + 4 * n_x as DartIdType } ],
267 [ d3, d1, if ix == 0 { 0 } else { d4 - 6 } ],
268 ]
269 .into_iter()
270 })
271 })
272}
273
274#[allow(clippy::too_many_lines)]
276pub fn build_2d_splitgrid<T: CoordsFloat>(
277 origin: Vertex2<T>,
278 [n_square_x, n_square_y]: [usize; 2],
279 [len_per_x, len_per_y]: [T; 2],
280 manager: AttrStorageManager,
281) -> CMap2<T> {
282 let map: CMap2<T> = CMap2::new_with_undefined_attributes(6 * n_square_x * n_square_y, manager);
283
284 (1..=(6 * n_square_x * n_square_y) as DartIdType)
286 .zip(generate_tris_beta_values(n_square_x, n_square_y))
287 .for_each(|(dart, images)| {
288 map.set_betas(dart, images);
289 });
290
291 (0..n_square_y)
295 .flat_map(|y_idx| (0..n_square_x).map(move |x_idx| (y_idx, x_idx)))
297 .for_each(|(y_idx, x_idx)| {
298 let vertex_id = map.vertex_id((1 + x_idx * 6 + y_idx * 6 * n_square_x) as DartIdType);
299 map.force_write_vertex(
300 vertex_id,
301 origin
302 + Vector2(
303 T::from(x_idx).unwrap() * len_per_x,
304 T::from(y_idx).unwrap() * len_per_y,
305 ),
306 );
307 });
308
309 (0..n_square_x).for_each(|x_idx| {
311 let y_idx = n_square_y - 1;
312 let vertex_id = map.vertex_id((4 + x_idx * 6 + y_idx * 6 * n_square_x) as DartIdType);
313 map.force_write_vertex(
314 vertex_id,
315 origin
316 + Vector2(
317 T::from(x_idx).unwrap() * len_per_x,
318 T::from(y_idx + 1).unwrap() * len_per_y,
319 ),
320 );
321 });
322
323 (0..n_square_y).for_each(|y_idx| {
325 let x_idx = n_square_x - 1;
326 let vertex_id = map.vertex_id((2 + x_idx * 6 + y_idx * 6 * n_square_x) as DartIdType);
327 map.force_write_vertex(
328 vertex_id,
329 origin
330 + Vector2(
331 T::from(x_idx + 1).unwrap() * len_per_x,
332 T::from(y_idx).unwrap() * len_per_y,
333 ),
334 );
335 });
336
337 {
339 let (x_idx, y_idx) = (n_square_x - 1, n_square_y - 1);
340 let vertex_id = map.vertex_id((6 + x_idx * 6 + y_idx * 6 * n_square_x) as DartIdType); map.force_write_vertex(
342 vertex_id,
343 origin
344 + Vector2(
345 T::from(x_idx + 1).unwrap() * len_per_x,
346 T::from(y_idx + 1).unwrap() * len_per_y,
347 ),
348 );
349 }
350
351 debug_assert_eq!(map.iter_faces().count(), 2 * n_square_x * n_square_y);
355
356 map
357}
358
359#[allow(clippy::inline_always)]
360#[rustfmt::skip]
361#[inline(always)]
362fn generate_tris_beta_values(n_x: usize, n_y: usize) -> impl Iterator<Item = [DartIdType; 3]> {
363 (0..n_y).flat_map(move |iy| {
366 (0..n_x).flat_map(move |ix| {
367 let d1 = (1 + 6 * ix + n_x * 6 * iy) as DartIdType;
368 let (d2, d3, d4, d5, d6) = (d1 + 1, d1 + 2, d1 + 3, d1 + 4, d1 + 5);
369 [
371 [ d3, d2, if iy == 0 { 0 } else { d6 - 6 * n_x as DartIdType } ],
372 [ d1, d3, d4 ],
373 [ d2, d1, if ix == 0 { 0 } else { d5 - 6 } ],
374 [ d6, d5, d2 ],
375 [ d4, d6, if ix == n_x-1 { 0 } else { d3 + 6 } ],
376 [ d5, d4, if iy == n_y-1 { 0 } else { d1 + 6 * n_x as DartIdType } ],
377 ]
378 .into_iter()
379 })
380 })
381}