#[cfg(feature = "serde")]
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use crate::point::Point;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum SnapResult {
Inside([usize; 3]),
Outside([usize; 3]),
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(bound = "V: Serialize + DeserializeOwned"))]
pub struct Grid<V: Point> {
first_cell: V,
cell_size: V,
cell_count: [usize; 3],
}
impl<V: Point> Grid<V> {
pub fn new(first_cell: V, cell_size: V, cell_count: [usize; 3]) -> Self {
Self {
first_cell,
cell_size,
cell_count,
}
}
pub fn from_bounding_box(bbox_min: &V, bbox_max: &V, cell_count: [usize; 3]) -> Self {
let fcell_count = V::new(
cell_count[0] as f32,
cell_count[1] as f32,
cell_count[2] as f32,
);
let cell_size = bbox_max.sub(bbox_min).comp_div(&fcell_count);
let first_cell = bbox_min.add(&cell_size.fmul(0.5));
Self {
first_cell,
cell_size,
cell_count,
}
}
pub fn get_first_cell(&self) -> V {
self.first_cell
}
pub fn get_last_cell(&self) -> V {
V::new(
self.first_cell.x() + self.cell_count[0] as f32 * self.cell_size.x(),
self.first_cell.y() + self.cell_count[1] as f32 * self.cell_size.y(),
self.first_cell.z() + self.cell_count[2] as f32 * self.cell_size.z(),
)
}
pub fn get_cell_size(&self) -> V {
self.cell_size
}
pub fn get_cell_count(&self) -> [usize; 3] {
self.cell_count
}
pub fn get_total_cell_count(&self) -> usize {
self.cell_count[0] * self.cell_count[1] * self.cell_count[2]
}
pub fn get_bounding_box(&self) -> (V, V) {
let min = self.first_cell.sub(&self.cell_size.fmul(0.5));
let max = V::new(
min.x() + self.cell_count[0] as f32 * self.cell_size.x(),
min.y() + self.cell_count[1] as f32 * self.cell_size.y(),
min.z() + self.cell_count[2] as f32 * self.cell_size.z(),
);
(min, max)
}
pub fn get_cell_idx(&self, cell: &[usize; 3]) -> usize {
cell[2] + cell[1] * self.cell_count[2] + cell[0] * self.cell_count[1] * self.cell_count[2]
}
pub fn get_cell_integer_coordinates(&self, cell_idx: usize) -> [usize; 3] {
let z = cell_idx % self.cell_count[2];
let y = (cell_idx / self.cell_count[2]) % self.cell_count[1];
let x = cell_idx / (self.cell_count[1] * self.cell_count[2]);
[x, y, z]
}
pub fn get_cell_center(&self, cell: &[usize; 3]) -> V {
V::new(
self.first_cell.x() + cell[0] as f32 * self.cell_size.x(),
self.first_cell.y() + cell[1] as f32 * self.cell_size.y(),
self.first_cell.z() + cell[2] as f32 * self.cell_size.z(),
)
}
pub fn snap_point_to_grid(&self, point: &V) -> SnapResult {
let cell = point
.sub(&self.get_bounding_box().0)
.comp_div(&self.cell_size);
let cell = [
cell.x().floor() as isize,
cell.y().floor() as isize,
cell.z().floor() as isize,
];
let ires = [
cell[0].clamp(0, self.cell_count[0] as isize - 1),
cell[1].clamp(0, self.cell_count[1] as isize - 1),
cell[2].clamp(0, self.cell_count[2] as isize - 1),
];
let res = [ires[0] as usize, ires[1] as usize, ires[2] as usize];
if ires != cell {
SnapResult::Outside(res)
} else {
SnapResult::Inside(res)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new() {
let first_cell = [0.1, 0.2, 0.3];
let cell_size = [1.1, 1.2, 1.3];
let cell_count = [11, 12, 13];
let grid = Grid::new(first_cell, cell_size, cell_count);
assert_eq!(grid.first_cell, [0.1, 0.2, 0.3]);
assert_eq!(grid.cell_size, [1.1, 1.2, 1.3]);
assert_eq!(grid.cell_count, [11, 12, 13]);
}
#[test]
fn test_first_last_cells() {
let first_cell = [0.0, 1.0, 2.0];
let cell_size = [1.0, 2.0, 3.0];
let cell_count = [10, 20, 30];
let grid = Grid::new(first_cell, cell_size, cell_count);
assert_eq!(grid.get_first_cell(), [0.0, 1.0, 2.0]);
assert_eq!(grid.get_last_cell(), [10.0, 41.0, 92.0]);
}
#[test]
fn test_from_bounding_box() {
let min_cell = [-1.0, 0.0, 1.0];
let max_cell = [0.0, 2.0, 5.0];
let cell_count = [2, 2, 2];
let grid = Grid::from_bounding_box(&min_cell, &max_cell, cell_count);
assert_eq!(grid.first_cell, [-0.75, 0.5, 2.]);
assert_eq!(grid.cell_size, [0.5, 1., 2.]);
assert_eq!(grid.cell_count, [2, 2, 2]);
assert_eq!(grid.get_bounding_box(), (min_cell, max_cell));
}
#[test]
fn test_snap_point_to_grid() {
let min_cell = [0.0, 0.0, 0.0];
let max_cell = [1.0, 1.0, 1.0];
let cell_count = [2, 2, 2];
let grid = Grid::from_bounding_box(&min_cell, &max_cell, cell_count);
assert_eq!(
grid.snap_point_to_grid(&[0.4, 0.8, 0.1]),
SnapResult::Inside([0, 1, 0])
);
assert_eq!(
grid.snap_point_to_grid(&[-0.5, 0.8, 0.8]),
SnapResult::Outside([0, 1, 1])
);
assert_eq!(
grid.snap_point_to_grid(&[0.8, 0.8, 0.8]),
SnapResult::Inside([1, 1, 1])
);
assert_eq!(
grid.snap_point_to_grid(&[0.8, 1.5, 0.8]),
SnapResult::Outside([1, 1, 1])
);
}
#[test]
fn test_get_cell_idx() {
let min_cell = [0.0, 0.0, 0.0];
let max_cell = [1.0, 1.0, 1.0];
let cell_count = [2, 3, 4];
let grid = Grid::from_bounding_box(&min_cell, &max_cell, cell_count);
assert_eq!(grid.get_cell_idx(&[0, 0, 0]), 0);
assert_eq!(grid.get_cell_idx(&[0, 0, 1]), 1);
assert_eq!(grid.get_cell_idx(&[0, 1, 0]), 4);
assert_eq!(grid.get_cell_idx(&[0, 1, 1]), 5);
assert_eq!(grid.get_cell_idx(&[1, 0, 0]), 12);
assert_eq!(grid.get_cell_idx(&[1, 0, 1]), 13);
assert_eq!(grid.get_cell_idx(&[1, 1, 0]), 16);
assert_eq!(grid.get_cell_idx(&[1, 1, 1]), 17);
}
#[test]
fn test_get_cell_integer_coordinates() {
let min_cell = [0.0, 0.0, 0.0];
let max_cell = [1.0, 1.0, 1.0];
let cell_count = [5, 10, 15];
let grid = Grid::from_bounding_box(&min_cell, &max_cell, cell_count);
for i in 0..1000 {
let cell = grid.get_cell_integer_coordinates(i);
let idx = grid.get_cell_idx(&cell);
assert_eq!(i, idx);
}
for x in 0..5 {
for y in 0..10 {
for z in 0..15 {
let i = grid.get_cell_idx(&[x, y, z]);
let cell = grid.get_cell_integer_coordinates(i);
assert_eq!([x, y, z], cell);
}
}
}
}
#[test]
fn test_get_cell_center() {
let min_cell = [0.0, 0.0, 0.0];
let max_cell = [1.0, 1.0, 1.0];
let cell_count = [2, 2, 2];
let grid = Grid::from_bounding_box(&min_cell, &max_cell, cell_count);
assert_eq!(grid.get_cell_center(&[0, 0, 0]), [0.25, 0.25, 0.25]);
assert_eq!(grid.get_cell_center(&[0, 0, 1]), [0.25, 0.25, 0.75]);
assert_eq!(grid.get_cell_center(&[0, 1, 0]), [0.25, 0.75, 0.25]);
assert_eq!(grid.get_cell_center(&[0, 1, 1]), [0.25, 0.75, 0.75]);
assert_eq!(grid.get_cell_center(&[1, 0, 0]), [0.75, 0.25, 0.25]);
assert_eq!(grid.get_cell_center(&[1, 0, 1]), [0.75, 0.25, 0.75]);
assert_eq!(grid.get_cell_center(&[1, 1, 0]), [0.75, 0.75, 0.25]);
assert_eq!(grid.get_cell_center(&[1, 1, 1]), [0.75, 0.75, 0.75]);
}
}