use macaw::Vec3;
use crate::SignedDistance;
pub type Index3 = [usize; 3];
pub struct Grid3<T = f32> {
size: Index3,
data: Vec<T>,
}
impl<T> Grid3<T> {
pub fn data(&self) -> &[T] {
&self.data
}
pub fn size(&self) -> Index3 {
self.size
}
}
impl<T> std::ops::Index<Index3> for Grid3<T> {
type Output = T;
#[inline]
fn index(&self, p: Index3) -> &Self::Output {
debug_assert!(p[0] < self.size[0]);
debug_assert!(p[1] < self.size[1]);
debug_assert!(p[2] < self.size[2]);
&self.data[p[0] + self.size[0] * (p[1] + self.size[1] * p[2])]
}
}
impl<T> std::ops::IndexMut<Index3> for Grid3<T> {
#[inline]
fn index_mut(&mut self, p: Index3) -> &mut Self::Output {
debug_assert!(p[0] < self.size[0]);
debug_assert!(p[1] < self.size[1]);
debug_assert!(p[2] < self.size[2]);
&mut self.data[p[0] + self.size[0] * (p[1] + self.size[1] * p[2])]
}
}
impl<T: std::cmp::PartialEq> std::cmp::PartialEq for Grid3<T> {
fn eq(&self, other: &Self) -> bool {
self.size == other.size && self.data == other.data
}
}
impl<T: SignedDistance + Copy + Clone + Default> Grid3<T> {
pub fn new(size: Index3) -> Self {
Self {
size,
data: vec![T::default(); size[0] * size[1] * size[2]],
}
}
pub fn set(&mut self, mut f: impl FnMut(Index3) -> T) {
let mut index = 0;
for z in 0..self.size[2] {
for y in 0..self.size[1] {
for x in 0..self.size[0] {
self.data[index] = f([x, y, z]);
index += 1;
}
}
}
}
}
impl<T> Grid3<T>
where
T: SignedDistance,
{
#[inline]
pub fn gradient_clamped(&self, p: Index3) -> Vec3 {
let p = [
p[0].clamp(1, self.size[0] - 2),
p[1].clamp(1, self.size[1] - 2),
p[2].clamp(1, self.size[2] - 2),
];
let dx = self[[p[0] + 1, p[1], p[2]]].distance() - self[[p[0] - 1, p[1], p[2]]].distance();
let dy = self[[p[0], p[1] + 1, p[2]]].distance() - self[[p[0], p[1] - 1, p[2]]].distance();
let dz = self[[p[0], p[1], p[2] + 1]].distance() - self[[p[0], p[1], p[2] - 1]].distance();
Vec3::new(dx, dy, dz) / 2.0
}
#[inline]
pub fn fast_gradient(
&self,
x: usize,
y: usize,
z: usize,
i: usize, ys: usize, zs: usize, ) -> Vec3 {
let sx = self.size[0];
let sy = self.size[1];
let sz = self.size[2];
let x1 = if x < sx - 1 { i + 1 } else { i };
let x2 = if x > 0 { i - 1 } else { i };
let y1 = if y < sy - 1 { i + ys } else { i };
let y2 = if y > 0 { i - ys } else { i };
let z1 = if z < sz - 1 { i + zs } else { i };
let z2 = if z > 0 { i - zs } else { i };
let dx = self.data[x1].distance() - self.data[x2].distance();
let dy = self.data[y1].distance() - self.data[y2].distance();
let dz = self.data[z1].distance() - self.data[z2].distance();
Vec3::new(dx, dy, dz) }
fn set_truncated_span(
x_slice: &mut [T],
y: usize,
z: usize,
sdf: impl Fn(Index3) -> T + Send + Sync,
truncate_dist: f32,
) {
let w = x_slice.len();
let mut x = 0;
while x < w {
let distance = sdf([x, y, z]);
let abs_distance = distance.distance().abs();
x_slice[x] = distance;
x += 1;
let mut distance_bound = abs_distance - 1.0;
while distance_bound > truncate_dist && x < w {
x_slice[x] = distance;
x += 1;
distance_bound -= 1.0;
}
}
}
pub fn set_truncated_sync(
&mut self,
sdf: impl Fn(Index3) -> T + Send + Sync,
truncate_dist: f32,
) {
puffin::profile_function!();
let _d = self.size[2];
let h = self.size[1];
let w = self.size[0];
self.data
.chunks_mut(w * h)
.enumerate()
.for_each(|(z, xy_plane)| {
xy_plane.chunks_mut(w).enumerate().for_each(|(y, x_slice)| {
Self::set_truncated_span(x_slice, y, z, &sdf, truncate_dist);
});
});
}
#[cfg(not(feature = "with_rayon"))]
pub fn set_truncated(&mut self, sdf: impl Fn(Index3) -> T + Send + Sync, truncate_dist: f32) {
self.set_truncated_sync(sdf, truncate_dist);
}
#[cfg(feature = "with_rayon")]
pub fn set_truncated(&mut self, sdf: impl Fn(Index3) -> T + Send + Sync, truncate_dist: f32)
where
T: Send,
{
puffin::profile_function!();
let _d = self.size[2];
let h = self.size[1];
let w = self.size[0];
use rayon::prelude::*;
self.data
.par_chunks_mut(w * h)
.enumerate()
.for_each(|(z, xy_plane)| {
xy_plane
.par_chunks_mut(w)
.enumerate()
.for_each(|(y, x_slice)| {
Self::set_truncated_span(x_slice, y, z, &sdf, truncate_dist);
});
});
}
}