use crate::mesh_2d::*;
use crate::mesh_3d::*;
pub fn xy_to_closest_index(mesh: &impl Index2D, x: isize, y: isize, backward: bool) -> Option<usize> {
let (width, height) = mesh.shape();
if width == 0 || height == 0 {
return None;
}
let width_i = width as isize;
let height_i = height as isize;
let max_radius = width.max(height) + x.unsigned_abs().max(y.unsigned_abs());
for radius in 0..=max_radius {
let radius_i = radius as isize;
let y_range: Box<dyn Iterator<Item = isize>> = if backward {
Box::new((-radius_i..=radius_i).rev())
} else {
Box::new(-radius_i..=radius_i)
};
for dy in y_range {
let x_range: Box<dyn Iterator<Item = isize>> = if backward {
Box::new((-radius_i..=radius_i).rev())
} else {
Box::new(-radius_i..=radius_i)
};
for dx in x_range {
if dx.abs().max(dy.abs()) != radius_i {
continue;
}
let cx = x + dx;
let cy = y + dy;
if cx < 0 || cy < 0 || cx >= width_i || cy >= height_i {
continue;
}
if let Ok(index) = mesh.xy_to_index(cx as usize, cy as usize) {
return Some(index);
}
}
}
}
None
}
pub fn xyz_to_closest_index(
mesh: &impl Index3D,
x: isize,
y: isize,
z: isize,
backward: bool,
) -> Option<usize> {
let (width, height, depth) = mesh.shape();
if width == 0 || height == 0 || depth == 0 {
return None;
}
let width_i = width as isize;
let height_i = height as isize;
let depth_i = depth as isize;
let max_radius = width
.max(height)
.max(depth)
+ x.unsigned_abs().max(y.unsigned_abs()).max(z.unsigned_abs());
for radius in 0..=max_radius {
let radius_i = radius as isize;
let z_range: Box<dyn Iterator<Item = isize>> = if backward {
Box::new((-radius_i..=radius_i).rev())
} else {
Box::new(-radius_i..=radius_i)
};
for dz in z_range {
let y_range: Box<dyn Iterator<Item = isize>> = if backward {
Box::new((-radius_i..=radius_i).rev())
} else {
Box::new(-radius_i..=radius_i)
};
for dy in y_range {
let x_range: Box<dyn Iterator<Item = isize>> = if backward {
Box::new((-radius_i..=radius_i).rev())
} else {
Box::new(-radius_i..=radius_i)
};
for dx in x_range {
if dx.abs().max(dy.abs()).max(dz.abs()) != radius_i {
continue;
}
let cx = x + dx;
let cy = y + dy;
let cz = z + dz;
if cx < 0
|| cy < 0
|| cz < 0
|| cx >= width_i
|| cy >= height_i
|| cz >= depth_i
{
continue;
}
if let Ok(index) = mesh.xyz_to_index(cx as usize, cy as usize, cz as usize) {
return Some(index);
}
}
}
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_xy_to_closest_index_exact() {
let mesh = Compact2D::from_text("...\n...\n...").unwrap();
let index = xy_to_closest_index(&mesh, 1, 1, false).unwrap();
assert_eq!(mesh.index_to_xy(index).unwrap(), (1, 1));
}
#[test]
fn test_xy_to_closest_index_wall() {
let mesh = Compact2D::from_text("...\n.#.\n...").unwrap();
let index = xy_to_closest_index(&mesh, 1, 1, false).unwrap();
assert_eq!(mesh.index_to_xy(index).unwrap(), (0, 0));
let index = xy_to_closest_index(&mesh, 1, 1, true).unwrap();
assert_eq!(mesh.index_to_xy(index).unwrap(), (2, 2));
}
#[test]
fn test_xy_to_closest_index_outside() {
let mesh = Full2D::new(5, 5);
let index = xy_to_closest_index(&mesh, -1, -1, false).unwrap();
assert_eq!(mesh.index_to_xy(index).unwrap(), (0, 0));
let index = xy_to_closest_index(&mesh, 10, 10, false).unwrap();
assert_eq!(mesh.index_to_xy(index).unwrap(), (4, 4));
}
#[test]
fn test_xy_to_closest_index_empty() {
let mesh = Full2D::new(0, 0);
assert_eq!(xy_to_closest_index(&mesh, 0, 0, false), None);
}
#[test]
fn test_xy_to_closest_index_all_walls() {
let mesh = Compact2D::from_text("###\n###\n###").unwrap();
assert_eq!(xy_to_closest_index(&mesh, 1, 1, false), None);
}
#[test]
fn test_xyz_to_closest_index_exact() {
let mesh = Full3D::new(5, 5, 5);
let index = xyz_to_closest_index(&mesh, 2, 2, 2, false).unwrap();
assert_eq!(mesh.index_to_xyz(index).unwrap(), (2, 2, 2));
}
#[test]
fn test_xyz_to_closest_index_outside() {
let mesh = Full3D::new(5, 5, 5);
let index = xyz_to_closest_index(&mesh, -1, 0, 0, false).unwrap();
assert_eq!(mesh.index_to_xyz(index).unwrap(), (0, 0, 0));
}
#[test]
fn test_xyz_to_closest_index_empty() {
let mesh = Full3D::new(0, 0, 0);
assert_eq!(xyz_to_closest_index(&mesh, 0, 0, 0, false), None);
}
}