pub mod strategies;
mod trackers;
use std::{
collections::BinaryHeap,
cmp::Ordering,
sync::RwLock
};
use grid::Grid;
use self::trackers::VisitedCoordsTracker;
pub use self::strategies::WaveStrategy;
const EXPECT_WAVE_GRID_BOUNDS: &str = "expected wave grid coords to be in bounds";
pub type WaveGrid = Grid<RwLock<WaveCellData>>;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct WaveCellData {
pub region_index: u32,
depth: u32
}
impl Default for WaveCellData {
fn default() -> Self {
Self { region_index: 0, depth: u32::MAX }
}
}
#[derive(Clone, Default, PartialEq, Eq)]
struct WaveCellLink {
coords: (u32, u32),
depth: u32
}
impl Ord for WaveCellLink {
fn cmp(&self, other: &Self) -> Ordering {
self.depth.cmp(&other.depth)
.reverse() }
}
impl PartialOrd for WaveCellLink {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.depth.partial_cmp(&other.depth)
.map(Ordering::reverse) }
}
pub fn set_cell_region_index(wave_grid: &WaveGrid, coords: &(u32, u32), region_index: u32) {
wave_grid.get(coords.1 as usize, coords.0 as usize)
.expect(EXPECT_WAVE_GRID_BOUNDS)
.write()
.expect("wave grid cell must be writable")
.region_index = region_index;
}
pub fn expand_wave<S>(wave_grid: &WaveGrid, initial_coords: (u32, u32), strategy: &S) -> Option<S::Output>
where S: WaveStrategy
{
let mut wave_front = BinaryHeap::new();
let mut visited_coords = S::make_visited_coords_tracker(wave_grid.rows(), wave_grid.cols());
let initial_cell_link = WaveCellLink{
coords: initial_coords,
depth: 0,
};
wave_front.push(initial_cell_link);
while let Some(cell_link) = wave_front.pop() {
visited_coords.mark_visited_coords(cell_link.coords);
let region_index_at_coords = strategy.get_region_index_at_coords(wave_grid, &cell_link.coords);
let mut current_cell_data = wave_grid.get(cell_link.coords.1 as usize, cell_link.coords.0 as usize)
.expect(EXPECT_WAVE_GRID_BOUNDS)
.write()
.expect("wave grid cell must be writable");
let cell_response = strategy.process_cell(&mut current_cell_data, cell_link.depth, region_index_at_coords);
match cell_response {
strategies::WaveCellResponse::SkipNeighbours => {
continue;
},
strategies::WaveCellResponse::ReturnOutput(output) => {
return Some(output);
},
strategies::WaveCellResponse::ProceedWithDepth(new_depth) => {
if cell_link.coords.0 > 0 && !visited_coords.has_visited_coords(&(cell_link.coords.0 - 1, cell_link.coords.1)) {
wave_front.push(WaveCellLink { coords: (cell_link.coords.0 - 1, cell_link.coords.1), depth: new_depth });
}
if cell_link.coords.1 > 0 && !visited_coords.has_visited_coords(&(cell_link.coords.0, cell_link.coords.1 - 1)) {
wave_front.push(WaveCellLink { coords: (cell_link.coords.0, cell_link.coords.1 - 1), depth: new_depth });
}
if cell_link.coords.0 < (wave_grid.cols() as u32) - 1 && !visited_coords.has_visited_coords(&(cell_link.coords.0 + 1, cell_link.coords.1)) {
wave_front.push(WaveCellLink { coords: (cell_link.coords.0 + 1, cell_link.coords.1), depth: new_depth });
}
if cell_link.coords.1 < (wave_grid.rows() as u32) - 1 && !visited_coords.has_visited_coords(&(cell_link.coords.0, cell_link.coords.1 + 1)) {
wave_front.push(WaveCellLink { coords: (cell_link.coords.0, cell_link.coords.1 + 1), depth: new_depth });
}
}
};
}
None
}
pub fn new_wave_grid(rows: usize, cols: usize) -> WaveGrid {
let mut grid = Grid::new(rows, cols);
grid.fill_with(|| RwLock::new(WaveCellData::default()));
grid
}