use super::{Raster, RasterElement};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Neighborhood {
Queen3x3,
Rook3x3,
Queen5x5,
Square(usize),
Circle(usize),
}
impl Neighborhood {
pub fn radius(&self) -> usize {
match self {
Neighborhood::Queen3x3 | Neighborhood::Rook3x3 => 1,
Neighborhood::Queen5x5 => 2,
Neighborhood::Square(r) | Neighborhood::Circle(r) => *r,
}
}
pub fn size(&self) -> usize {
self.radius() * 2 + 1
}
pub fn contains(&self, dr: isize, dc: isize) -> bool {
match self {
Neighborhood::Queen3x3 => dr.abs() <= 1 && dc.abs() <= 1,
Neighborhood::Rook3x3 => (dr.abs() <= 1 && dc == 0) || (dr == 0 && dc.abs() <= 1),
Neighborhood::Queen5x5 => dr.abs() <= 2 && dc.abs() <= 2,
Neighborhood::Square(r) => {
let r = *r as isize;
dr.abs() <= r && dc.abs() <= r
}
Neighborhood::Circle(r) => {
let r = *r as f64;
let dist = ((dr * dr + dc * dc) as f64).sqrt();
dist <= r
}
}
}
pub fn offsets(&self) -> Vec<(isize, isize)> {
let r = self.radius() as isize;
let mut offsets = Vec::new();
for dr in -r..=r {
for dc in -r..=r {
if self.contains(dr, dc) {
offsets.push((dr, dc));
}
}
}
offsets
}
pub fn offsets_no_center(&self) -> Vec<(isize, isize)> {
self.offsets()
.into_iter()
.filter(|&(dr, dc)| dr != 0 || dc != 0)
.collect()
}
}
#[allow(dead_code)]
pub mod d8 {
pub const OFFSETS: [(isize, isize); 9] = [
(0, 0), (0, 1), (-1, 1), (-1, 0), (-1, -1), (0, -1), (1, -1), (1, 0), (1, 1), ];
pub const DISTANCES: [f64; 9] = [
0.0,
1.0,
std::f64::consts::SQRT_2,
1.0,
std::f64::consts::SQRT_2,
1.0,
std::f64::consts::SQRT_2,
1.0,
std::f64::consts::SQRT_2,
];
pub fn opposite(dir: u8) -> u8 {
if dir == 0 { 0 } else { ((dir - 1 + 4) % 8) + 1 }
}
}
pub struct NeighborhoodIterator<'a, T: RasterElement> {
raster: &'a Raster<T>,
center_row: usize,
center_col: usize,
offsets: Vec<(isize, isize)>,
index: usize,
}
impl<'a, T: RasterElement> NeighborhoodIterator<'a, T> {
pub fn new(raster: &'a Raster<T>, row: usize, col: usize, neighborhood: Neighborhood) -> Self {
Self {
raster,
center_row: row,
center_col: col,
offsets: neighborhood.offsets_no_center(),
index: 0,
}
}
}
impl<'a, T: RasterElement> Iterator for NeighborhoodIterator<'a, T> {
type Item = Option<(usize, usize, T)>;
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.offsets.len() {
return None;
}
let (dr, dc) = self.offsets[self.index];
self.index += 1;
let new_row = self.center_row as isize + dr;
let new_col = self.center_col as isize + dc;
if new_row < 0
|| new_col < 0
|| new_row >= self.raster.rows() as isize
|| new_col >= self.raster.cols() as isize
{
Some(None)
} else {
let r = new_row as usize;
let c = new_col as usize;
let value = unsafe { self.raster.get_unchecked(r, c) };
Some(Some((r, c, value)))
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.offsets.len() - self.index;
(remaining, Some(remaining))
}
}
impl<'a, T: RasterElement> ExactSizeIterator for NeighborhoodIterator<'a, T> {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_neighborhood_offsets() {
let queen = Neighborhood::Queen3x3;
let offsets = queen.offsets();
assert_eq!(offsets.len(), 9);
let rook = Neighborhood::Rook3x3;
let offsets = rook.offsets();
assert_eq!(offsets.len(), 5);
let queen5 = Neighborhood::Queen5x5;
let offsets = queen5.offsets();
assert_eq!(offsets.len(), 25); }
#[test]
fn test_d8_opposite() {
assert_eq!(d8::opposite(1), 5); assert_eq!(d8::opposite(3), 7); assert_eq!(d8::opposite(2), 6); }
}