use crate::distance::*;
use crate::errors::*;
use crate::gate::*;
use crate::gradient::*;
use crate::mesh::*;
use crate::mesh_2d::full_2d::*;
use crate::mesh_2d::index_2d::*;
use crate::mesh_2d::shape_2d::*;
use crate::mesh_source::*;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Compact2D {
width: usize,
height: usize,
full_to_compact: Vec<usize>,
compact_to_full: Vec<usize>,
successors_map: Vec<Vec<Gate>>,
}
impl Compact2D {
pub fn new_full(width: usize, height: usize) -> Self {
let size = width * height;
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 = Full2D::successors(width, height, i, false).collect();
successors_map.push(successors);
}
let compact_to_full = full_to_compact.clone();
Self {
width,
height,
full_to_compact,
compact_to_full,
successors_map,
}
}
pub fn from_source(source: &impl Source2D) -> Result<Self> {
let width = source.width();
let height = source.height();
let size = width * height;
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 y in 0..height {
for x in 0..width {
let can_go = source.get(x, y)?.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 = Full2D::successors(width, height, full_index, false);
let successors = possible_successors
.filter_map(|s| {
let (x, y) = Full2D::index_to_xy(width, height, s.target()).ok()?;
if source.get(x, y).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,
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;
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,
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 = Source2DFromText::from_text(input);
Self::from_source(&source)
}
pub fn from_vec_str(input: &Vec<&str>) -> Result<Self> {
let source = Source2DFromText::from_slice(input);
Self::from_source(&source)
}
pub fn from_vec_string(input: &[String]) -> Result<Self> {
let source = Source2DFromText::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 = Source2DFromText::from_lines(input);
Self::from_source(&source)
}
#[cfg(feature = "image")]
pub fn from_path<P: AsRef<std::path::Path>>(path: P) -> Result<Self> {
let source = Source2DFromImage::from_path(path)?;
Self::from_source(&source)
}
#[cfg(feature = "image")]
pub fn from_path_with_threshold<P: AsRef<std::path::Path>>(
path: P,
threshold: f32,
) -> Result<Self> {
let source = Source2DFromImage::from_path_with_threshold(path, threshold)?;
Self::from_source(&source)
}
#[cfg(feature = "image")]
pub fn from_image_with_threshold(image: &image::DynamicImage, threshold: f32) -> Result<Self> {
let source = Source2DFromImage::from_image(image, threshold)?;
Self::from_source(&source)
}
#[cfg(feature = "image")]
pub fn from_image(image: &image::DynamicImage) -> Result<Self> {
Self::from_image_with_threshold(image, Source2DFromImage::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 Compact2D {
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 Index2D for Compact2D {
fn index_to_xy(&self, index: usize) -> Result<(usize, usize)> {
let full_index = self.compact_to_full_index(index)?;
Full2D::index_to_xy(self.width, self.height, full_index)
}
fn xy_to_index(&self, x: usize, y: usize) -> Result<usize> {
let full_index = Full2D::xy_to_index(self.width, self.height, x, y)?;
self.full_to_compact_index(full_index)
}
}
impl Shape2D for Compact2D {
fn shape(&self) -> (usize, usize) {
(self.width, self.height)
}
}
impl std::str::FromStr for Compact2D {
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_2d::repr_2d::*;
#[test]
fn test_compact_2d_full_basic() {
let mesh = Compact2D::new_full(3, 2);
assert_eq!((3, 2), mesh.shape());
assert_eq!(6, mesh.len());
let mut grad = Gradient::from_mesh(&mesh);
grad.set_distance(0, 0.0);
grad.spread(&mesh);
assert_eq!(
String::from("012\n112\n"),
repr_mesh_with_gradient_2d(&mesh, &grad)
);
}
#[test]
fn test_compact_2d_with_str_small() {
let mesh = Compact2D::from_text(" # \n \n").unwrap();
assert_eq!((3, 2), mesh.shape());
assert_eq!(5, mesh.len());
let mut grad = Gradient::from_mesh(&mesh);
assert_eq!(
String::from("?#?\n???\n"),
repr_mesh_with_gradient_2d(&mesh, &grad)
);
grad.set_distance(0, 0.0);
grad.spread(&mesh);
assert_eq!(
String::from("0#3\n112\n"),
repr_mesh_with_gradient_2d(&mesh, &grad)
);
}
#[test]
fn test_compact_2d_with_str_medium() {
let mesh =
Compact2D::from_text(" # \n\n\n ####\n\n##\n\n\n #####\n\n")
.unwrap();
assert_eq!((20, 10), mesh.shape());
assert_eq!(188, 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_2d(&mesh, &grad)
);
grad.set_distance(0, 0.0);
grad.spread(&mesh);
assert_eq!(
String::from( "01#45678901234567890\n11234567890123456789\n22345678901234567890\n3####678901234567890\n44567789012345678901\n##678899012345678901\n87778900012345678901\n98889011123456789012\n09990#####3456789012\n10001123454567890123\n"),
repr_mesh_with_gradient_2d(&mesh, &grad)
);
}
#[test]
fn test_compact_2d_with_str_multiple_zones() {
let mesh_all =
Compact2D::from_text("##########\n# # #\n# # #\n##########\n").unwrap();
assert_eq!((10, 4), mesh_all.shape());
assert_eq!(14, mesh_all.len());
let mesh: Compact2D = mesh_all.unique();
assert_eq!((10, 4), mesh.shape()); assert_eq!(8, mesh.len()); let mut grad = Gradient::from_mesh(&mesh);
assert_eq!(
String::from("##########\n#????#####\n#????#####\n##########\n"),
repr_mesh_with_gradient_2d(&mesh, &grad)
);
grad.set_distance(0, 0.0);
grad.spread(&mesh);
}
#[test]
fn test_compact_2d_empty_string() {
let mesh = Compact2D::from_text("").unwrap();
assert_eq!((0, 0), mesh.shape());
assert_eq!(0, mesh.len());
assert!(mesh.is_empty());
}
#[test]
fn test_compact_2d_all_walls() {
let mesh = Compact2D::from_text("#").unwrap();
assert_eq!((1, 1), mesh.shape());
assert_eq!(0, mesh.len());
assert!(mesh.is_empty());
let mesh = Compact2D::from_text("###\n###").unwrap();
assert_eq!((3, 2), mesh.shape());
assert_eq!(0, mesh.len());
assert!(mesh.is_empty());
}
#[test]
#[cfg(feature = "serde")]
fn test_serde() {
let mesh = Compact2D::from_text("...\n.#.\n...").unwrap();
let json = serde_json::to_string(&mesh).unwrap();
let deserialized: Compact2D = serde_json::from_str(&json).unwrap();
assert_eq!(mesh.len(), deserialized.len());
assert_eq!(mesh.shape(), deserialized.shape());
}
#[test]
#[cfg(feature = "serde")]
fn test_serde_with_gradient_workflow() {
use crate::Gradient;
let mesh = Compact2D::from_text(".....\n.###.\n.....\n").unwrap();
let mut gradient = Gradient::from_mesh(&mesh);
gradient.set_distance(6, 0.0);
gradient.spread(&mesh);
let mesh_json = serde_json::to_string(&mesh).unwrap();
let gradient_json = serde_json::to_string(&gradient).unwrap();
let deserialized_mesh: Compact2D = serde_json::from_str(&mesh_json).unwrap();
let deserialized_gradient: Gradient = serde_json::from_str(&gradient_json).unwrap();
assert_eq!(mesh.len(), deserialized_mesh.len());
assert_eq!(
gradient.get_distance(0),
deserialized_gradient.get_distance(0)
);
assert_eq!(gradient.get_target(0), deserialized_gradient.get_target(0));
}
}