use crate::prelude::{BuilderError, CMap2, DartIdType, Vector2, Vertex2};
use crate::{attributes::AttrStorageManager, geometry::CoordsFloat};
#[derive(Default, Clone)]
pub struct GridDescriptor<T: CoordsFloat> {
pub(crate) origin: Vertex2<T>,
pub(crate) n_cells: Option<[usize; 3]>,
pub(crate) len_per_cell: Option<[T; 3]>,
pub(crate) lens: Option<[T; 3]>,
pub(crate) split_quads: bool,
}
macro_rules! setters {
($fld: ident, $fldx: ident, $fldy: ident, $fldz: ident, $zero: expr, $fldty: ty) => {
#[must_use = "unused builder object"]
pub fn $fld(mut self, $fld: [$fldty; 3]) -> Self {
self.$fld = Some($fld);
self
}
#[must_use = "unused builder object"]
pub fn $fldx(mut self, $fld: $fldty) -> Self {
if let Some([ptr, _, _]) = &mut self.$fld {
*ptr = $fld;
} else {
self.$fld = Some([$fld, $zero, $zero]);
}
self
}
#[must_use = "unused builder object"]
pub fn $fldy(mut self, $fld: $fldty) -> Self {
if let Some([_, ptr, _]) = &mut self.$fld {
*ptr = $fld;
} else {
self.$fld = Some([$zero, $fld, $zero]);
}
self
}
#[must_use = "unused builder object"]
pub fn $fldz(mut self, $fld: $fldty) -> Self {
if let Some([_, _, ptr]) = &mut self.$fld {
*ptr = $fld;
} else {
self.$fld = Some([$zero, $zero, $fld]);
}
self
}
};
}
impl<T: CoordsFloat> GridDescriptor<T> {
setters!(n_cells, n_cells_x, n_cells_y, n_cells_z, 0, usize);
setters!(
len_per_cell,
len_per_cell_x,
len_per_cell_y,
len_per_cell_z,
T::zero(),
T
);
setters!(lens, lens_x, lens_y, lens_z, T::zero(), T);
#[must_use = "unused builder object"]
pub fn origin(mut self, origin: Vertex2<T>) -> Self {
self.origin = origin;
self
}
#[must_use = "unused builder object"]
pub fn split_quads(mut self, split: bool) -> Self {
self.split_quads = split;
self
}
}
macro_rules! check_parameters {
($id: ident, $msg: expr) => {
if $id.is_sign_negative() | $id.is_zero() {
return Err(BuilderError::InvalidGridParameters($msg));
}
};
}
impl<T: CoordsFloat> GridDescriptor<T> {
#[allow(clippy::type_complexity)]
pub(crate) fn parse_2d(self) -> Result<(Vertex2<T>, [usize; 2], [T; 2]), BuilderError> {
match (self.n_cells, self.len_per_cell, self.lens) {
(Some([nx, ny, _]), Some([lpx, lpy, _]), lens) => {
if lens.is_some() {
eprintln!("W: All three grid parameters were specified, total lengths will be ignored");
}
#[rustfmt::skip]
check_parameters!(lpx, "length per x cell is null or negative");
#[rustfmt::skip]
check_parameters!(lpy, "length per y cell is null or negative");
Ok((self.origin, [nx, ny], [lpx, lpy]))
}
(Some([nx, ny, _]), None, Some([lx, ly, _])) => {
#[rustfmt::skip]
check_parameters!(lx, "grid length along x is null or negative");
#[rustfmt::skip]
check_parameters!(ly, "grid length along y is null or negative");
Ok((
self.origin,
[nx, ny],
[lx / T::from(nx).unwrap(), ly / T::from(ny).unwrap()],
))
}
(None, Some([lpx, lpy, _]), Some([lx, ly, _])) => {
#[rustfmt::skip]
check_parameters!(lpx, "length per x cell is null or negative");
#[rustfmt::skip]
check_parameters!(lpy, "length per y cell is null or negative");
#[rustfmt::skip]
check_parameters!(lx, "grid length along x is null or negative");
#[rustfmt::skip]
check_parameters!(ly, "grid length along y is null or negative");
Ok((
self.origin,
[
(lx / lpx).ceil().to_usize().unwrap(),
(ly / lpy).ceil().to_usize().unwrap(),
],
[lpx, lpy],
))
}
(_, _, _) => Err(BuilderError::MissingGridParameters),
}
}
}
#[allow(clippy::too_many_lines)]
pub fn build_2d_grid<T: CoordsFloat>(
origin: Vertex2<T>,
[n_square_x, n_square_y]: [usize; 2],
[len_per_x, len_per_y]: [T; 2],
manager: AttrStorageManager,
) -> CMap2<T> {
let map: CMap2<T> = CMap2::new_with_undefined_attributes(4 * n_square_x * n_square_y, manager);
(1..=(4 * n_square_x * n_square_y) as DartIdType)
.zip(generate_square_beta_values(n_square_x, n_square_y))
.for_each(|(dart, images)| {
map.set_betas(dart, images);
});
(0..n_square_y)
.flat_map(|y_idx| (0..n_square_x).map(move |x_idx| (y_idx, x_idx)))
.for_each(|(y_idx, x_idx)| {
let vertex_id = map.vertex_id((1 + x_idx * 4 + y_idx * 4 * n_square_x) as DartIdType);
map.force_write_vertex(
vertex_id,
origin
+ Vector2(
T::from(x_idx).unwrap() * len_per_x,
T::from(y_idx).unwrap() * len_per_y,
),
);
});
(0..n_square_x).for_each(|x_idx| {
let y_idx = n_square_y - 1;
let vertex_id = map.vertex_id((4 + x_idx * 4 + y_idx * 4 * n_square_x) as DartIdType);
map.force_write_vertex(
vertex_id,
origin
+ Vector2(
T::from(x_idx).unwrap() * len_per_x,
T::from(y_idx + 1).unwrap() * len_per_y,
),
);
});
(0..n_square_y).for_each(|y_idx| {
let x_idx = n_square_x - 1;
let vertex_id = map.vertex_id((2 + x_idx * 4 + y_idx * 4 * n_square_x) as DartIdType);
map.force_write_vertex(
vertex_id,
origin
+ Vector2(
T::from(x_idx + 1).unwrap() * len_per_x,
T::from(y_idx).unwrap() * len_per_y,
),
);
});
{
let (x_idx, y_idx) = (n_square_x - 1, n_square_y - 1);
let vertex_id = map.vertex_id((3 + x_idx * 4 + y_idx * 4 * n_square_x) as DartIdType); map.force_write_vertex(
vertex_id,
origin
+ Vector2(
T::from(x_idx + 1).unwrap() * len_per_x,
T::from(y_idx + 1).unwrap() * len_per_y,
),
);
}
debug_assert_eq!(map.iter_faces().count(), n_square_x * n_square_y);
map
}
#[allow(clippy::inline_always)]
#[rustfmt::skip]
#[inline(always)]
fn generate_square_beta_values(n_x: usize, n_y: usize) -> impl Iterator<Item = [DartIdType; 3]> {
(0..n_y).flat_map(move |iy| {
(0..n_x).flat_map(move |ix| {
let d1 = (1 + 4 * ix + n_x * 4 * iy) as DartIdType;
let (d2, d3, d4) = (d1 + 1, d1 + 2, d1 + 3);
[
[ d4, d2, if iy == 0 { 0 } else { d3 - 4 * n_x as DartIdType } ],
[ d1, d3, if ix == n_x-1 { 0 } else { d2 + 6 } ],
[ d2, d4, if iy == n_y-1 { 0 } else { d1 + 4 * n_x as DartIdType } ],
[ d3, d1, if ix == 0 { 0 } else { d4 - 6 } ],
]
.into_iter()
})
})
}
#[allow(clippy::too_many_lines)]
pub fn build_2d_splitgrid<T: CoordsFloat>(
origin: Vertex2<T>,
[n_square_x, n_square_y]: [usize; 2],
[len_per_x, len_per_y]: [T; 2],
manager: AttrStorageManager,
) -> CMap2<T> {
let map: CMap2<T> = CMap2::new_with_undefined_attributes(6 * n_square_x * n_square_y, manager);
(1..=(6 * n_square_x * n_square_y) as DartIdType)
.zip(generate_tris_beta_values(n_square_x, n_square_y))
.for_each(|(dart, images)| {
map.set_betas(dart, images);
});
(0..n_square_y)
.flat_map(|y_idx| (0..n_square_x).map(move |x_idx| (y_idx, x_idx)))
.for_each(|(y_idx, x_idx)| {
let vertex_id = map.vertex_id((1 + x_idx * 6 + y_idx * 6 * n_square_x) as DartIdType);
map.force_write_vertex(
vertex_id,
origin
+ Vector2(
T::from(x_idx).unwrap() * len_per_x,
T::from(y_idx).unwrap() * len_per_y,
),
);
});
(0..n_square_x).for_each(|x_idx| {
let y_idx = n_square_y - 1;
let vertex_id = map.vertex_id((4 + x_idx * 6 + y_idx * 6 * n_square_x) as DartIdType);
map.force_write_vertex(
vertex_id,
origin
+ Vector2(
T::from(x_idx).unwrap() * len_per_x,
T::from(y_idx + 1).unwrap() * len_per_y,
),
);
});
(0..n_square_y).for_each(|y_idx| {
let x_idx = n_square_x - 1;
let vertex_id = map.vertex_id((2 + x_idx * 6 + y_idx * 6 * n_square_x) as DartIdType);
map.force_write_vertex(
vertex_id,
origin
+ Vector2(
T::from(x_idx + 1).unwrap() * len_per_x,
T::from(y_idx).unwrap() * len_per_y,
),
);
});
{
let (x_idx, y_idx) = (n_square_x - 1, n_square_y - 1);
let vertex_id = map.vertex_id((6 + x_idx * 6 + y_idx * 6 * n_square_x) as DartIdType); map.force_write_vertex(
vertex_id,
origin
+ Vector2(
T::from(x_idx + 1).unwrap() * len_per_x,
T::from(y_idx + 1).unwrap() * len_per_y,
),
);
}
debug_assert_eq!(map.iter_faces().count(), 2 * n_square_x * n_square_y);
map
}
#[allow(clippy::inline_always)]
#[rustfmt::skip]
#[inline(always)]
fn generate_tris_beta_values(n_x: usize, n_y: usize) -> impl Iterator<Item = [DartIdType; 3]> {
(0..n_y).flat_map(move |iy| {
(0..n_x).flat_map(move |ix| {
let d1 = (1 + 6 * ix + n_x * 6 * iy) as DartIdType;
let (d2, d3, d4, d5, d6) = (d1 + 1, d1 + 2, d1 + 3, d1 + 4, d1 + 5);
[
[ d3, d2, if iy == 0 { 0 } else { d6 - 6 * n_x as DartIdType } ],
[ d1, d3, d4 ],
[ d2, d1, if ix == 0 { 0 } else { d5 - 6 } ],
[ d6, d5, d2 ],
[ d4, d6, if ix == n_x-1 { 0 } else { d3 + 6 } ],
[ d5, d4, if iy == n_y-1 { 0 } else { d1 + 6 * n_x as DartIdType } ],
]
.into_iter()
})
})
}