bc-lifehash 0.1.0

LifeHash visual hashing algorithm
Documentation
use crate::{
    bit_enumerator::{BitAggregator, BitEnumerator},
    change_grid::ChangeGrid,
    grid::Grid,
};

pub struct CellGrid {
    pub grid: Grid<bool>,
}

impl CellGrid {
    pub fn new(width: usize, height: usize) -> Self {
        Self { grid: Grid::new(width, height) }
    }

    #[inline]
    fn is_alive_in_next_generation(
        current_alive: bool,
        neighbors_count: usize,
    ) -> bool {
        if current_alive {
            neighbors_count == 2 || neighbors_count == 3
        } else {
            neighbors_count == 3
        }
    }

    fn count_neighbors(&self, px: usize, py: usize) -> usize {
        let mut total = 0;
        self.grid.for_neighborhood(px, py, |ox, oy, nx, ny| {
            if ox == 0 && oy == 0 {
                return;
            }
            if self.grid.get_value(nx, ny) {
                total += 1;
            }
        });
        total
    }

    pub fn data(&self) -> Vec<u8> {
        let mut a = BitAggregator::new();
        self.grid.for_all(|x, y| {
            a.append(self.grid.get_value(x, y));
        });
        a.data()
    }

    pub fn set_data(&mut self, data: &[u8]) {
        debug_assert_eq!(self.grid.width * self.grid.height, data.len() * 8);
        let mut e = BitEnumerator::new(data.to_vec());
        let mut i = 0;
        e.for_all(|b| {
            self.grid.storage[i] = b;
            i += 1;
        });
        debug_assert_eq!(i, self.grid.storage.len());
    }

    pub fn next_generation(
        &self,
        current_change_grid: &ChangeGrid,
        next_cell_grid: &mut CellGrid,
        next_change_grid: &mut ChangeGrid,
    ) {
        next_cell_grid.grid.set_all(false);
        next_change_grid.grid.set_all(false);
        let width = self.grid.width;
        let height = self.grid.height;
        for y in 0..height {
            for x in 0..width {
                let current_alive = self.grid.get_value(x, y);
                if current_change_grid.grid.get_value(x, y) {
                    let neighbors_count = self.count_neighbors(x, y);
                    let next_alive = Self::is_alive_in_next_generation(
                        current_alive,
                        neighbors_count,
                    );
                    if next_alive {
                        next_cell_grid.grid.set_value(true, x, y);
                    }
                    if current_alive != next_alive {
                        next_change_grid.set_changed(x, y);
                    }
                } else {
                    next_cell_grid.grid.set_value(current_alive, x, y);
                }
            }
        }
    }
}