use std::{collections::HashSet, iter::FromIterator};
use crate::coord::Coord;
pub fn neighborhood<'a, C: Into<Coord>>(coord: C) -> impl Iterator<Item = Coord> + 'a {
let coord = coord.into();
[
(0, 1),
(1, 1),
(1, 0),
(1, -1),
(0, -1),
(-1, -1),
(-1, 0),
(-1, 1),
]
.iter()
.map(move |&offset| coord + offset.into())
}
pub fn ortho_neighborhood<'a, C: Into<Coord>>(coord: C) -> impl Iterator<Item = Coord> + 'a {
let coord = coord.into();
[(0, 1), (1, 0), (0, -1), (-1, 0)]
.iter()
.map(move |&offset| coord + offset.into())
}
pub fn diag_neighborhood<'a, C: Into<Coord>>(coord: C) -> impl Iterator<Item = Coord> + 'a {
let coord = coord.into();
[(1, 1), (1, -1), (-1, -1), (-1, 1)]
.iter()
.map(move |&offset| coord + offset.into())
}
pub fn line<C1, C2>(from: C1, to: C2) -> impl Iterator<Item = Coord>
where
C1: Into<Coord>,
C2: Into<Coord>,
{
let from = from.into();
let to = to.into();
let delta = to - from;
let x_step = Coord::new(delta.x.signum(), 0);
let y_step = Coord::new(0, delta.y.signum());
let x_is_major = delta.x.abs() > delta.y.abs();
let (major_step, minor_step) = if x_is_major {
(x_step, y_step)
} else {
(y_step, x_step)
};
let (major_fault, minor_fault) = if x_is_major {
(delta.x.abs(), delta.y.abs())
} else {
(delta.y.abs(), delta.x.abs())
};
LineIter {
end_coord: to,
next_coord: from,
major_step,
minor_step,
fault: major_fault as f32 / 2.0,
major_fault,
minor_fault,
is_finished: false,
}
}
struct LineIter {
end_coord: Coord,
next_coord: Coord,
major_step: Coord,
minor_step: Coord,
fault: f32,
major_fault: i32,
minor_fault: i32,
is_finished: bool,
}
impl Iterator for LineIter {
type Item = Coord;
fn next(&mut self) -> Option<Coord> {
if self.is_finished {
return None;
}
if self.next_coord == self.end_coord {
self.is_finished = true;
return Some(self.end_coord);
}
let return_coord = self.next_coord;
self.next_coord += self.major_step;
self.fault -= self.minor_fault as f32;
if self.fault < 0.0 {
self.fault += self.major_fault as f32;
self.next_coord += self.minor_step;
}
Some(return_coord)
}
}
#[derive(Debug)]
pub struct ClusterLayers {
pub interior: Vec<Coord>,
pub internal_border: Vec<Coord>,
pub external_border: Vec<Coord>,
}
pub fn cluster_layers(cluster: Vec<Coord>) -> ClusterLayers {
let cluster_set: HashSet<Coord> = HashSet::from_iter(cluster.iter().map(|&x| x));
let mut internal_boundary = HashSet::new();
let mut external_boundary = HashSet::new();
let mut interior = HashSet::new();
for coord in cluster {
let non_cluster_coords = neighborhood(coord)
.filter(|neighbor| !cluster_set.contains(neighbor))
.collect::<Vec<_>>();
if non_cluster_coords.len() == 0 {
interior.insert(coord);
} else {
internal_boundary.insert(coord);
for neighbor in non_cluster_coords {
external_boundary.insert(neighbor);
}
}
}
ClusterLayers {
internal_border: Vec::from_iter(internal_boundary),
external_border: Vec::from_iter(external_boundary),
interior: Vec::from_iter(interior),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn single_coord_cluster_layers() {
let layers = cluster_layers(vec![Coord::new(0, 0)]);
assert!(layers.interior.len() == 0);
assert!(layers.internal_border.len() == 1);
assert!(layers.external_border.len() == 8);
}
#[test]
fn square_cluster_layers() {
let cluster = [
(0, 0),
(1, 0),
(2, 0),
(0, 1),
(1, 1),
(2, 1),
(0, 2),
(1, 2),
(2, 2),
]
.iter()
.map(|&x| x.into())
.collect::<Vec<_>>();
let layers = cluster_layers(cluster);
assert!(layers.interior.len() == 1);
assert!(layers.internal_border.len() == 8);
assert!(layers.external_border.len() == 16);
}
}