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
107
108
109
110
111
112
113
114
115
116
117
118
use crate::orientation::Orientation;
use coord_2d::*;
use grid_2d::*;
use std::hash::{Hash, Hasher};

#[derive(Clone)]
pub struct TiledGridSlice<'a, T: 'a> {
    grid: &'a Grid<T>,
    offset: Coord,
    size: Size,
    orientation: Orientation,
}

pub struct TiledGridSliceIter<'a, T: 'a> {
    grid: &'a TiledGridSlice<'a, T>,
    coord_iter: CoordIter,
}

impl<'a, T> Iterator for TiledGridSliceIter<'a, T> {
    type Item = &'a T;
    fn next(&mut self) -> Option<Self::Item> {
        self.coord_iter
            .next()
            .map(|coord| self.grid.get_valid(coord))
    }
}

impl<'a, T> TiledGridSlice<'a, T> {
    pub fn new(
        grid: &'a Grid<T>,
        offset: Coord,
        size: Size,
        orientation: Orientation,
    ) -> Self {
        TiledGridSlice {
            grid,
            offset,
            size,
            orientation,
        }
    }
    pub fn size(&self) -> Size {
        self.size
    }
    fn get_valid(&self, coord: Coord) -> &'a T {
        let transformed_coord = self.orientation.transform_coord(self.size, coord);
        self.grid.get_tiled(self.offset + transformed_coord)
    }
    pub fn get_checked(&self, coord: Coord) -> &'a T {
        if coord.is_valid(self.size) {
            self.get_valid(coord)
        } else {
            panic!("coord is out of bounds");
        }
    }
    pub fn offset(&self) -> Coord {
        self.offset
    }
    pub fn iter(&self) -> TiledGridSliceIter<T> {
        TiledGridSliceIter {
            grid: self,
            coord_iter: CoordIter::new(self.size),
        }
    }
}

impl<'a, T: Hash> Hash for TiledGridSlice<'a, T> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        for value in self.iter() {
            value.hash(state);
        }
    }
}

impl<'a, T: PartialEq> PartialEq for TiledGridSlice<'a, T> {
    fn eq(&self, other: &Self) -> bool {
        self.size == other.size && self.iter().zip(other.iter()).all(|(s, o)| s.eq(o))
    }
}
impl<'a, T: Eq> Eq for TiledGridSlice<'a, T> {}

#[cfg(test)]
mod test {
    use super::*;
    use crate::orientation::Orientation;
    use coord_2d::{Coord, Size};
    use std::collections::HashSet;

    #[test]
    fn tiling() {
        let grid = Grid::new_fn(Size::new(4, 4), |coord| coord);
        let slice = TiledGridSlice::new(
            &grid,
            Coord::new(-1, -1),
            Size::new(2, 2),
            Orientation::Original,
        );
        let value = *slice.get_valid(Coord::new(0, 1));
        assert_eq!(value, Coord::new(3, 0));
    }
    #[test]
    fn tiled_grid_slice_hash() {
        let mut grid = Grid::new_fn(Size::new(4, 4), |_| 0);
        *grid.get_mut(Coord::new(1, 3)).unwrap() = 1;
        let size = Size::new(2, 2);
        let a = TiledGridSlice::new(&grid, Coord::new(0, 0), size, Orientation::Original);
        let b = TiledGridSlice::new(&grid, Coord::new(2, 2), size, Orientation::Original);
        let c = TiledGridSlice::new(&grid, Coord::new(0, 2), size, Orientation::Original);
        let d =
            TiledGridSlice::new(&grid, Coord::new(1, 2), size, Orientation::Clockwise270);
        let mut set = HashSet::new();
        set.insert(a);
        set.insert(b);
        set.insert(c);
        set.insert(d);
        assert_eq!(set.len(), 2);
    }
}