use std::fmt;
use std::marker::PhantomData;
use std::ops::Index;
use crate::image_io::buffer::{CoverSource, ImageBuffer};
pub mod hill;
pub struct CostMap<'img> {
pub(crate) data: Vec<f32>,
pub(crate) width: u32,
pub(crate) height: u32,
_phantom: PhantomData<&'img ImageBuffer>,
}
impl<'img> CostMap<'img> {
pub(crate) fn new(image: &'img ImageBuffer, data: Vec<f32>) -> Self {
let (width, height) = image.dimensions();
Self {
data,
width,
height,
_phantom: PhantomData,
}
}
pub fn dimensions(&self) -> (u32, u32) {
(self.width, self.height)
}
pub fn pixel_count(&self) -> usize {
self.width as usize * self.height as usize
}
pub fn costs(&self) -> &[f32] {
&self.data
}
pub fn get(&self, x: u32, y: u32) -> Option<f32> {
if x >= self.width || y >= self.height {
return None;
}
self.data.get(self.linear_index(x, y)).copied()
}
fn linear_index(&self, x: u32, y: u32) -> usize {
y as usize * self.width as usize + x as usize
}
}
impl Index<(u32, u32)> for CostMap<'_> {
type Output = f32;
fn index(&self, (x, y): (u32, u32)) -> &f32 {
&self.data[self.linear_index(x, y)]
}
}
impl fmt::Debug for CostMap<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CostMap")
.field("width", &self.width)
.field("height", &self.height)
.field("costs", &self.data.len())
.finish()
}
}
pub trait CostProvider {
type Error;
fn compute<'img>(&self, image: &'img ImageBuffer) -> Result<CostMap<'img>, Self::Error>;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::image_io::buffer::ColorSpace;
fn map(image: &ImageBuffer) -> CostMap<'_> {
let costs = (0..image.pixel_count()).map(|index| index as f32).collect();
CostMap::new(image, costs)
}
fn image() -> ImageBuffer {
ImageBuffer::new(vec![0u8; 4 * 3], 4, 3, ColorSpace::Luma8)
}
#[test]
fn the_map_reports_the_geometry_of_its_source() {
let image = image();
let map = map(&image);
assert_eq!(map.dimensions(), image.dimensions());
assert_eq!(map.pixel_count(), image.pixel_count());
assert_eq!(map.costs().len(), image.pixel_count());
}
#[test]
fn coordinates_address_the_map_row_by_row() {
let image = image();
let map = map(&image);
assert_eq!(map.get(0, 0), Some(0.0));
assert_eq!(map.get(3, 0), Some(3.0));
assert_eq!(map.get(1, 2), Some(9.0));
assert_eq!(map[(1, 2)], 9.0);
assert_eq!(map.get(4, 0), None);
assert_eq!(map.get(0, 3), None);
}
#[test]
fn the_debug_form_is_a_summary() {
let image = image();
let rendered = format!("{:?}", map(&image));
assert!(rendered.contains("width: 4"), "got: {rendered}");
assert!(rendered.contains("height: 3"), "got: {rendered}");
assert!(rendered.contains("costs: 12"), "got: {rendered}");
assert!(!rendered.contains("0.0"), "got: {rendered}");
}
}