use crate::distance::*;
use crate::errors::*;
use crate::gate::*;
use crate::gradient::*;
use crate::mesh::*;
use crate::mesh_25d::full_25d::*;
use crate::mesh_3d::Index3D;
use crate::mesh_3d::Shape3D;
use crate::mesh_source::*;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Compact25D {
width: usize,
height: usize,
depth: usize,
full_to_compact: Vec<usize>,
compact_to_full: Vec<usize>,
successors_map: Vec<Vec<Gate>>,
}
impl Compact25D {
pub fn new_full(width: usize, height: usize, depth: usize) -> Self {
let size = width * height * depth;
let mut full_to_compact = Vec::with_capacity(size);
let mut successors_map = Vec::with_capacity(size);
for i in 0..size {
full_to_compact.push(i);
let successors = Full25D::successors(width, height, depth, i, false).collect();
successors_map.push(successors);
}
let compact_to_full = full_to_compact.clone();
Self {
width,
height,
depth,
full_to_compact,
compact_to_full,
successors_map,
}
}
pub fn from_source(source: &impl Source3D) -> Result<Self> {
let width = source.width();
let height = source.height();
let depth = source.depth();
let size = width * height * depth;
let mut full_to_compact = Vec::with_capacity(size);
let mut compact_to_full = Vec::with_capacity(size);
let mut current_full = 0;
let mut current_compact = 0;
for z in 0..depth {
for y in 0..height {
for x in 0..width {
let can_go = source.get(x, y, z)?.can_go();
if can_go {
compact_to_full.push(current_full);
full_to_compact.push(current_compact);
current_full += 1;
current_compact += 1;
} else {
full_to_compact.push(size);
current_full += 1;
}
}
}
}
let mut successors_map = Vec::with_capacity(compact_to_full.len());
for &full_index in &compact_to_full {
let possible_successors = Full25D::successors(width, height, depth, full_index, false);
let successors = possible_successors
.filter_map(|s| {
let (x, y, z) = Full25D::index_to_xyz(width, height, depth, s.target()).ok()?;
if source.get(x, y, z).ok()?.can_go() {
Some(s)
} else {
None
}
})
.map(|s| Gate {
distance: s.distance,
target: full_to_compact[s.target()] as u32,
})
.collect();
successors_map.push(successors);
}
Ok(Self {
width,
height,
depth,
full_to_compact,
compact_to_full,
successors_map,
})
}
pub fn unique(self) -> Self {
if self.compact_to_full.is_empty() {
return self;
}
let size = self.width * self.height * self.depth;
let mut gradient = Gradient::from_mesh(&self);
gradient.set_distance(0, 0.0);
gradient.spread(&self);
let reachable: Vec<usize> = (0..self.len())
.filter(|&i| gradient.get_distance(i) < DISTANCE_MAX)
.collect();
if reachable.len() == self.len() {
return self;
}
let mut new_full_to_compact = vec![size; size];
let mut new_compact_to_full = Vec::with_capacity(reachable.len());
let mut old_to_new_compact: Vec<usize> = vec![size; self.len()];
for (new_idx, &old_idx) in reachable.iter().enumerate() {
let full_idx = self.compact_to_full[old_idx];
new_compact_to_full.push(full_idx);
new_full_to_compact[full_idx] = new_idx;
old_to_new_compact[old_idx] = new_idx;
}
let mut new_successors_map = Vec::with_capacity(reachable.len());
for &old_idx in &reachable {
let old_successors = &self.successors_map[old_idx];
let new_successors: Vec<Gate> = old_successors
.iter()
.filter(|s| old_to_new_compact[s.target()] < size)
.map(|s| Gate {
distance: s.distance,
target: old_to_new_compact[s.target()] as u32,
})
.collect();
new_successors_map.push(new_successors);
}
Self {
width: self.width,
height: self.height,
depth: self.depth,
full_to_compact: new_full_to_compact,
compact_to_full: new_compact_to_full,
successors_map: new_successors_map,
}
}
pub fn from_text(input: &str) -> Result<Self> {
let source = Source3DFromText::from_text(input);
Self::from_source(&source)
}
pub fn from_vec_str(input: &Vec<&str>) -> Result<Self> {
let source = Source3DFromText::from_slice(input);
Self::from_source(&source)
}
pub fn from_vec_string(input: &[String]) -> Result<Self> {
let source = Source3DFromText::from_strings(input);
Self::from_source(&source)
}
pub fn from_iter_str<'a, I>(input: I) -> Result<Self>
where
I: Iterator<Item = &'a str>,
{
let source = Source3DFromText::from_lines(input);
Self::from_source(&source)
}
#[cfg(feature = "image")]
pub fn from_paths<P: AsRef<std::path::Path>>(paths: &[P]) -> Result<Self> {
let source = Source3DFromImage::from_paths(paths, Source3DFromImage::DEFAULT_THRESHOLD)?;
Self::from_source(&source)
}
#[cfg(feature = "image")]
pub fn from_paths_with_threshold<P: AsRef<std::path::Path>>(
paths: &[P],
threshold: f32,
) -> Result<Self> {
let source = Source3DFromImage::from_paths(paths, threshold)?;
Self::from_source(&source)
}
#[cfg(feature = "image")]
pub fn from_images_with_threshold(
images: &[image::DynamicImage],
threshold: f32,
) -> Result<Self> {
let source = Source3DFromImage::from_images(images, threshold)?;
Self::from_source(&source)
}
#[cfg(feature = "image")]
pub fn from_images(images: &[image::DynamicImage]) -> Result<Self> {
Self::from_images_with_threshold(images, Source3DFromImage::DEFAULT_THRESHOLD)
}
fn compact_to_full_index(&self, compact_index: usize) -> Result<usize> {
if compact_index >= self.compact_to_full.len() {
return Err(Error::invalid_index(compact_index));
}
let full_index = self.compact_to_full[compact_index];
if full_index >= self.full_to_compact.len() {
return Err(Error::invalid_index(full_index));
}
Ok(full_index)
}
fn full_to_compact_index(&self, full_index: usize) -> Result<usize> {
if full_index >= self.full_to_compact.len() {
return Err(Error::invalid_index(full_index));
}
let compact_index = self.full_to_compact[full_index];
if compact_index >= self.compact_to_full.len() {
return Err(Error::invalid_index(compact_index));
}
Ok(compact_index)
}
}
impl Mesh for Compact25D {
type IntoIter = std::vec::IntoIter<Gate>;
fn successors(&self, from: usize, backward: bool) -> std::vec::IntoIter<Gate> {
let mut peers = self.successors_map[from].clone();
if backward {
peers.reverse();
}
peers.into_iter()
}
fn len(&self) -> usize {
self.compact_to_full.len()
}
}
impl Index3D for Compact25D {
fn index_to_xyz(&self, index: usize) -> Result<(usize, usize, usize)> {
let full_index = self.compact_to_full_index(index)?;
Full25D::index_to_xyz(self.width, self.height, self.depth, full_index)
}
fn xyz_to_index(&self, x: usize, y: usize, z: usize) -> Result<usize> {
let full_index = Full25D::xyz_to_index(self.width, self.height, self.width, x, y, z)?;
self.full_to_compact_index(full_index)
}
}
impl Shape3D for Compact25D {
fn shape(&self) -> (usize, usize, usize) {
(self.width, self.height, self.depth)
}
}
impl std::str::FromStr for Compact25D {
type Err = Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Self::from_text(s)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mesh_3d::repr_mesh_with_gradient_3d;
#[test]
fn test_compact_3d_full_basic() {
let mesh = Compact25D::new_full(4, 3, 2);
assert_eq!((4, 3, 2), mesh.shape());
assert_eq!(24, mesh.len());
let mut grad = Gradient::from_mesh(&mesh);
grad.set_distance(0, 0.0);
grad.spread(&mesh);
assert_eq!(
String::from("0123\n1123\n2234\n----\n1234\n2234\n3345\n----\n"),
repr_mesh_with_gradient_3d(&mesh, &grad)
);
}
#[test]
fn test_compact_3d_with_str_small() {
let mesh = Compact25D::from_text(" ## \n \n# #\n====\n####\n\n\n====").unwrap();
assert_eq!((4, 3, 2), mesh.shape());
assert_eq!(16, mesh.len());
let mut grad = Gradient::from_mesh(&mesh);
assert_eq!(
String::from("?##?\n????\n#??#\n----\n####\n????\n????\n----\n"),
repr_mesh_with_gradient_3d(&mesh, &grad)
);
grad.set_distance(0, 0.0);
grad.spread(&mesh);
assert_eq!(
String::from("0##4\n1123\n#23#\n----\n####\n2234\n3345\n----\n"),
repr_mesh_with_gradient_3d(&mesh, &grad)
);
}
#[test]
fn test_compact_25d_with_str_multiple_zones() {
let mesh_all = Compact25D::from_text(
"##########\n# # #\n# # #\n##########\n====\n##########\n# # #\n# # #\n##########\n====",
)
.unwrap();
assert_eq!((10, 4, 2), mesh_all.shape());
assert_eq!(28, mesh_all.len());
let mesh: Compact25D = mesh_all.unique();
assert_eq!((10, 4, 2), mesh.shape()); assert_eq!(16, mesh.len()); let mut grad = Gradient::from_mesh(&mesh);
assert_eq!(
String::from(
"##########\n#????#####\n#????#####\n##########\n----------\n##########\n#????#####\n#????#####\n##########\n----------\n"
),
repr_mesh_with_gradient_3d(&mesh, &grad)
);
grad.set_distance(0, 0.0);
grad.spread(&mesh);
}
#[test]
fn test_compact_25d_empty_string() {
let mesh = Compact25D::from_text("").unwrap();
assert_eq!((0, 0, 0), mesh.shape());
assert_eq!(0, mesh.len());
assert!(mesh.is_empty());
}
#[test]
fn test_compact_25d_all_walls() {
let mesh = Compact25D::from_text("#").unwrap();
assert_eq!((1, 1, 1), mesh.shape());
assert_eq!(0, mesh.len());
assert!(mesh.is_empty());
let mesh = Compact25D::from_text("###\n###\n---\n###\n###").unwrap();
assert_eq!((3, 2, 2), mesh.shape());
assert_eq!(0, mesh.len());
assert!(mesh.is_empty());
}
#[test]
#[cfg(feature = "serde")]
fn test_serde() {
let mesh = Compact25D::from_text("...\n.#.\n...\n---\n...\n...\n...").unwrap();
let json = serde_json::to_string(&mesh).unwrap();
let deserialized: Compact25D = serde_json::from_str(&json).unwrap();
assert_eq!(mesh.len(), deserialized.len());
assert_eq!(mesh.shape(), deserialized.shape());
}
}