use crate::distance::*;
use crate::errors::*;
use crate::gate::*;
use crate::mesh::*;
use crate::mesh_3d::index_3d::*;
use crate::mesh_3d::shape_3d::*;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Full3D {
width: usize,
height: usize,
depth: usize,
}
impl Full3D {
pub fn new(width: usize, height: usize, depth: usize) -> Self {
Self {
width,
height,
depth,
}
}
pub fn unique(self) -> Self {
self
}
#[inline]
pub(crate) fn successors(
width: usize,
height: usize,
depth: usize,
from: usize,
backward: bool,
) -> std::vec::IntoIter<Gate> {
let mut peers = Vec::<Gate>::with_capacity(26);
if let Ok(xyz) = Self::index_to_xyz(width, height, depth, from) {
let (x, y, z) = xyz;
let layer_size = width * height;
if x < width - 1 {
peers.push(Gate::new(from + 1, DISTANCE_STRAIGHT));
}
if x > 0 {
peers.push(Gate::new(from - 1, DISTANCE_STRAIGHT));
}
if y < height - 1 {
peers.push(Gate::new(from + width, DISTANCE_STRAIGHT));
}
if y > 0 {
peers.push(Gate::new(from - width, DISTANCE_STRAIGHT));
}
if x < width - 1 && y < height - 1 {
peers.push(Gate::new(from + 1 + width, DISTANCE_DIAGONAL));
}
if x > 0 && y < height - 1 {
peers.push(Gate::new(from - 1 + width, DISTANCE_DIAGONAL));
}
if x < width - 1 && y > 0 {
peers.push(Gate::new(from + 1 - width, DISTANCE_DIAGONAL));
}
if x > 0 && y > 0 {
peers.push(Gate::new(from - 1 - width, DISTANCE_DIAGONAL));
}
if z < depth - 1 {
let up_offset = layer_size;
peers.push(Gate::new(from + up_offset, DISTANCE_STRAIGHT));
if x < width - 1 {
peers.push(Gate::new(from + up_offset + 1, DISTANCE_DIAGONAL));
}
if x > 0 {
peers.push(Gate::new(from + up_offset - 1, DISTANCE_DIAGONAL));
}
if y < height - 1 {
peers.push(Gate::new(from + up_offset + width, DISTANCE_DIAGONAL));
}
if y > 0 {
peers.push(Gate::new(from + up_offset - width, DISTANCE_DIAGONAL));
}
if x < width - 1 && y < height - 1 {
peers.push(Gate::new(from + up_offset + 1 + width, DISTANCE_DIAGONAL_3D));
}
if x > 0 && y < height - 1 {
peers.push(Gate::new(from + up_offset - 1 + width, DISTANCE_DIAGONAL_3D));
}
if x < width - 1 && y > 0 {
peers.push(Gate::new(from + up_offset + 1 - width, DISTANCE_DIAGONAL_3D));
}
if x > 0 && y > 0 {
peers.push(Gate::new(from + up_offset - 1 - width, DISTANCE_DIAGONAL_3D));
}
}
if z > 0 {
let down_offset = layer_size;
peers.push(Gate::new(from - down_offset, DISTANCE_STRAIGHT));
if x < width - 1 {
peers.push(Gate::new(from - down_offset + 1, DISTANCE_DIAGONAL));
}
if x > 0 {
peers.push(Gate::new(from - down_offset - 1, DISTANCE_DIAGONAL));
}
if y < height - 1 {
peers.push(Gate::new(from - down_offset + width, DISTANCE_DIAGONAL));
}
if y > 0 {
peers.push(Gate::new(from - down_offset - width, DISTANCE_DIAGONAL));
}
if x < width - 1 && y < height - 1 {
peers.push(Gate::new(from - down_offset + 1 + width, DISTANCE_DIAGONAL_3D));
}
if x > 0 && y < height - 1 {
peers.push(Gate::new(from - down_offset - 1 + width, DISTANCE_DIAGONAL_3D));
}
if x < width - 1 && y > 0 {
peers.push(Gate::new(from - down_offset + 1 - width, DISTANCE_DIAGONAL_3D));
}
if x > 0 && y > 0 {
peers.push(Gate::new(from - down_offset - 1 - width, DISTANCE_DIAGONAL_3D));
}
}
}
if backward {
peers.reverse();
}
peers.into_iter()
}
#[inline]
pub(crate) fn len(width: usize, height: usize, depth: usize) -> usize {
width * height * depth
}
#[inline]
pub(crate) fn index_to_xyz(
width: usize,
height: usize,
depth: usize,
index: usize,
) -> Result<(usize, usize, usize)> {
if index >= width * height * depth {
return Err(Error::invalid_index(index));
}
Ok((
index % width,
(index / width) % height,
index / (height * width),
))
}
#[inline]
pub(crate) fn xyz_to_index(
width: usize,
height: usize,
depth: usize,
x: usize,
y: usize,
z: usize,
) -> Result<usize> {
if x >= width || y >= height || z >= depth {
return Err(Error::invalid_xyz(x, y, z));
}
Ok(z * height * width + y * width + x)
}
#[inline]
pub(crate) fn shape(width: usize, height: usize, depth: usize) -> (usize, usize, usize) {
(width, height, depth)
}
}
impl Mesh for Full3D {
type IntoIter = std::vec::IntoIter<Gate>;
fn successors(&self, from: usize, backward: bool) -> std::vec::IntoIter<Gate> {
Self::successors(self.width, self.height, self.depth, from, backward)
}
fn len(&self) -> usize {
Self::len(self.width, self.height, self.depth)
}
}
impl Index3D for Full3D {
fn index_to_xyz(&self, index: usize) -> Result<(usize, usize, usize)> {
Self::index_to_xyz(self.width, self.height, self.depth, index)
}
fn xyz_to_index(&self, x: usize, y: usize, z: usize) -> Result<usize> {
Self::xyz_to_index(self.width, self.height, self.depth, x, y, z)
}
}
impl Shape3D for Full3D {
fn shape(&self) -> (usize, usize, usize) {
Self::shape(self.width, self.height, self.depth)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_full_rectangle_basic_w4h3() {
let depth = 2;
let width = 4;
let height = 3;
let r = Full3D::new(width, height, depth);
assert_eq!(width * height * depth, r.len());
}
#[test]
fn test_index_w0() {
let r = Full3D::new(0, 10, 10);
assert_eq!(r.len(), 0);
}
#[test]
fn test_index_h0() {
let r = Full3D::new(10, 0, 10);
assert_eq!(r.len(), 0);
}
#[test]
fn test_index_d0() {
let r = Full3D::new(10, 10, 0);
assert_eq!(r.len(), 0);
}
#[test]
fn test_index_w0h0d0() {
let r = Full3D::new(0, 0, 0);
assert_eq!(r.len(), 0);
}
#[test]
fn test_index_w4h3d2() {
let r = Full3D::new(4, 3, 2);
assert!(r.index_to_xyz(0).is_ok());
assert!(r.index_to_xyz(11).is_ok());
assert!(r.index_to_xyz(23).is_ok());
assert!(r.index_to_xyz(24).is_err());
}
#[test]
fn test_xyz_w0h0d0() {
let r = Full3D::new(0, 0, 0);
assert!(r.xyz_to_index(0, 0, 0).is_err());
}
#[test]
fn test_xyz_w0() {
let r = Full3D::new(0, 10, 10);
assert!(r.xyz_to_index(0, 0, 0).is_err());
}
#[test]
fn test_xyz_h0() {
let r = Full3D::new(10, 0, 10);
assert!(r.xyz_to_index(0, 0, 0).is_err());
}
#[test]
fn test_xyz_d0() {
let r = Full3D::new(10, 10, 0);
assert!(r.xyz_to_index(0, 0, 0).is_err());
}
#[test]
fn test_xyz_w4h3d2() {
let r = Full3D::new(4, 3, 2);
assert_eq!(r.xyz_to_index(0, 0, 0).unwrap(), 0);
assert_eq!(r.xyz_to_index(1, 0, 0).unwrap(), 1);
assert_eq!(r.xyz_to_index(3, 2, 1).unwrap(), 23);
assert!(r.xyz_to_index(4, 0, 0).is_err());
assert!(r.xyz_to_index(0, 3, 0).is_err());
assert!(r.xyz_to_index(0, 0, 2).is_err());
}
#[test]
#[cfg(feature = "serde")]
fn test_serde() {
let mesh = Full3D::new(5, 5, 5);
let json = serde_json::to_string(&mesh).unwrap();
let deserialized: Full3D = serde_json::from_str(&json).unwrap();
assert_eq!(mesh.len(), deserialized.len());
assert_eq!(mesh.shape(), deserialized.shape());
}
}