use std::collections::HashSet;
use grid::Grid;
use crate::texture::{self, RegionsMapImageBuffer};
use super::{
EXPECT_WAVE_GRID_BOUNDS,
WaveGrid,
VisitedCoordsTracker, WaveCellData,
};
pub enum WaveCellResponse<O> {
ProceedWithDepth(u32),
SkipNeighbours,
ReturnOutput(O)
}
pub trait WaveStrategy {
type Output;
type VisitedCoords: VisitedCoordsTracker;
fn make_visited_coords_tracker(wave_grid_rows: usize, wave_grid_cols: usize) -> Self::VisitedCoords;
fn get_region_index_at_coords(&self, wave_grid: &WaveGrid, coords: &(u32, u32)) -> u32 {
wave_grid.get(coords.1 as usize, coords.0 as usize)
.expect(EXPECT_WAVE_GRID_BOUNDS)
.read()
.expect("wave grid cell must be readable")
.region_index
}
fn process_cell(&self, _cell_data: &mut WaveCellData, expansion_depth: u32, _region_index_at_coords: u32) -> WaveCellResponse<Self::Output> {
WaveCellResponse::ProceedWithDepth(expansion_depth + 1)
}
}
pub struct ExpandInitialRegionStrategy<'a> {
initial_region_index: u32,
regions_map_image_buffer: &'a RegionsMapImageBuffer
}
impl<'a> WaveStrategy for ExpandInitialRegionStrategy<'a> {
type Output = ();
type VisitedCoords = Grid<bool>;
fn make_visited_coords_tracker(wave_grid_rows: usize, wave_grid_cols: usize) -> Self::VisitedCoords {
Self::VisitedCoords::new(wave_grid_rows, wave_grid_cols)
}
fn get_region_index_at_coords(&self, wave_grid: &WaveGrid, coords: &(u32, u32)) -> u32 {
assert_eq!((wave_grid.cols() as u32, wave_grid.rows() as u32), self.regions_map_image_buffer.dimensions());
let pixel_at_coords = self.regions_map_image_buffer.get_pixel(coords.0, coords.1);
texture::decode_index_from_rgb(&pixel_at_coords.0[..3])
}
fn process_cell(&self, cell_data: &mut WaveCellData, expansion_depth: u32, region_index_at_coords: u32) -> WaveCellResponse<Self::Output> {
if region_index_at_coords != 0 && region_index_at_coords != self.initial_region_index {
return WaveCellResponse::SkipNeighbours;
}
let new_depth = expansion_depth + if region_index_at_coords == 0 { 1 } else { 0 };
if cell_data.region_index == 0 || cell_data.depth > new_depth {
cell_data.region_index = self.initial_region_index;
cell_data.depth = new_depth;
WaveCellResponse::ProceedWithDepth(new_depth)
} else {
WaveCellResponse::SkipNeighbours
}
}
}
impl<'a> ExpandInitialRegionStrategy<'a> {
pub fn new(initial_region_index: u32, regions_map_image_buffer: &'a RegionsMapImageBuffer) -> Self {
Self{initial_region_index, regions_map_image_buffer}
}
}
#[derive(Default, Copy, Clone)]
pub struct FindNearestNonZeroRegionStrategy;
impl WaveStrategy for FindNearestNonZeroRegionStrategy {
type Output = u32;
type VisitedCoords = HashSet<(u32, u32)>;
fn make_visited_coords_tracker(_wave_grid_rows: usize, _wave_grid_cols: usize) -> Self::VisitedCoords {
Self::VisitedCoords::new()
}
fn process_cell(&self, cell_data: &mut WaveCellData, expansion_depth: u32, region_index_at_coords: u32) -> WaveCellResponse<Self::Output> {
assert_eq!(cell_data.region_index, region_index_at_coords);
if cell_data.region_index == 0 {
WaveCellResponse::ProceedWithDepth(expansion_depth + 1)
} else {
WaveCellResponse::ReturnOutput(cell_data.region_index)
}
}
}
impl FindNearestNonZeroRegionStrategy {
pub fn new() -> Self {
Self::default()
}
}