1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
use std::fmt::{Display, Formatter};

#[cfg(test)]
mod tests;

/// FillCanvas is a wrapper around a matrix with special methods to fill the cells.
///
/// FillCanvas provides methods to fill individual cells with a value,
/// or multiple cells from a row/column or just all cells within an area at once.
///
/// The filled cells have a T value != the Default::default().
///
/// PS: Initially developed to have EntityIDs (from ECS) filled into a matrix
/// based on the space they occupy on a canvas grid.
pub struct FillCanvas<T> {
    rows: usize,
    cols: usize,
    matrix: Vec<T>, // [ rows * cols ]
}

impl<T> FillCanvas<T> where T: Default + Clone + PartialEq {
    pub fn new(rows: usize, cols: usize) -> Self {
        let mut matrix = Vec::new();
        matrix.resize(rows * cols, T::default());
        Self { rows, cols, matrix }
    }

    pub fn resize(&mut self, rows: usize, cols: usize) {
        self.rows = rows;
        self.cols = cols;
        self.matrix.resize(rows * cols, T::default());
    }

    pub fn clear(&mut self) {
        self.matrix.fill(T::default())
    }

    pub fn get(&self, r: usize, c: usize) -> Option<&T> {
        let index = self.index(r, c)?;
        Some(&self.matrix[index])
    }

    pub fn get_filled(&self, r: usize, c: usize) -> Option<&T> {
        self.get(r, c).and_then(|v| (v != &T::default()).then(|| v))
    }

    pub fn fill_cell(&mut self, v: T, r: usize, c: usize) -> Option<()> {
        let index = self.index(r, c)?;
        self.matrix[index] = v;
        Some(())
    }

    pub fn fill_row(&mut self, v: T, r: usize) -> Option<()> {
        let begin = self.index(r, 0)?;
        let end = self.index(r, self.cols - 1)?;
        for index in begin..=end {
            self.matrix[index] = v.clone();
        }
        Some(())
    }

    pub fn fill_col(&mut self, v: T, c: usize) -> Option<()> {
        self.index(0, c)?; // validate constrains
        for r in 0..self.rows {
            let index = self.index_unchecked(r, c);
            self.matrix[index] = v.clone();
        }
        Some(())
    }

    pub fn fill_area(&mut self, v: T, r0: usize, c0: usize, r1: usize, c1: usize) -> Option<()> {
        if r1 < r0 || c1 < c0 { return None; } // validate
        self.index(r0, c0)?; // validate
        self.index(r1, c1)?; // validate
        for r in r0..=r1 {
            let begin = self.index_unchecked(r, c0);
            let end = self.index_unchecked(r, c1);
            for index in begin..=end {
                self.matrix[index] = v.clone();
            }
        }
        Some(())
    }

    fn index(&self, r: usize, c: usize) -> Option<usize> {
        let index = self.index_unchecked(r, c);
        if c < self.cols && index < self.matrix.len() { Some(index) } else { None }
    }

    fn index_unchecked(&self, r: usize, c: usize) -> usize {
        self.cols * r + c
    }
}

impl<T> Display for FillCanvas<T> where T: Clone + Default + Display + PartialEq {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.write_str("FillCanvas {\n")?;
        for index in 0..self.matrix.len() {
            if index % self.cols == 0 && index != 0 {
                f.write_str("\n")?;
            }
            write!(f, "{} ", self.matrix[index])?;
        }
        f.write_str("\n}")
    }
}