Skip to main content

grid_map/loc/square/
neighbor.rs

1use crate::{Dim, Dir, Loc, Scale};
2
3impl Loc {
4    //! Neighbors
5
6    /// Gets the neighbor in the `dir`, or `None` if outside the `dims`.
7    #[inline]
8    pub fn neighbor(self, dir: Dir, dims: Dim) -> Option<Loc> {
9        match dir {
10            Dir::North => {
11                if self.y > 0 {
12                    Some(Loc::new(self.x, self.y - 1))
13                } else {
14                    None
15                }
16            }
17            Dir::East => {
18                let x: Scale = self.x + 1;
19                if x < dims.w {
20                    Some(Loc::new(x, self.y))
21                } else {
22                    None
23                }
24            }
25            Dir::South => {
26                let y: Scale = self.y + 1;
27                if y < dims.h {
28                    Some(Loc::new(self.x, y))
29                } else {
30                    None
31                }
32            }
33            Dir::West => {
34                if self.x > 0 {
35                    Some(Loc::new(self.x - 1, self.y))
36                } else {
37                    None
38                }
39            }
40        }
41    }
42
43    /// Gets the neighbor in the `dir`.
44    #[inline]
45    pub fn neighbor_unchecked(self, dir: Dir) -> Loc {
46        match dir {
47            Dir::North => Loc::new(self.x, self.y - 1),
48            Dir::East => Loc::new(self.x + 1, self.y),
49            Dir::South => Loc::new(self.x, self.y + 1),
50            Dir::West => Loc::new(self.x - 1, self.y),
51        }
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use crate::{Dim, Dir, Loc};
58
59    #[test]
60    fn neighbor() {
61        let dims: Dim = Dim::new(3, 3);
62        let test_cases: &[(Loc, Dir, Option<Loc>)] = &[
63            // center
64            (Loc::new(1, 1), Dir::North, Some(Loc::new(1, 0))),
65            (Loc::new(1, 1), Dir::East, Some(Loc::new(2, 1))),
66            (Loc::new(1, 1), Dir::South, Some(Loc::new(1, 2))),
67            (Loc::new(1, 1), Dir::West, Some(Loc::new(0, 1))),
68            // top-left corner
69            (Loc::new(0, 0), Dir::North, None),
70            (Loc::new(0, 0), Dir::West, None),
71            (Loc::new(0, 0), Dir::East, Some(Loc::new(1, 0))),
72            (Loc::new(0, 0), Dir::South, Some(Loc::new(0, 1))),
73            // bottom-right corner
74            (Loc::new(2, 2), Dir::East, None),
75            (Loc::new(2, 2), Dir::South, None),
76            (Loc::new(2, 2), Dir::North, Some(Loc::new(2, 1))),
77            (Loc::new(2, 2), Dir::West, Some(Loc::new(1, 2))),
78        ];
79        for &(loc, dir, expected) in test_cases {
80            assert_eq!(loc.neighbor(dir, dims), expected, "loc={loc} dir={dir}");
81        }
82
83        // unchecked matches checked
84        let loc: Loc = Loc::new(1, 1);
85        for dir in Dir::ALL {
86            assert_eq!(
87                loc.neighbor(dir, dims).unwrap(),
88                loc.neighbor_unchecked(dir)
89            );
90        }
91    }
92
93    #[test]
94    fn neighbor_unchecked() {
95        let loc: Loc = Loc::new(2, 2);
96        for dir in Dir::ALL {
97            let n: Loc = loc.neighbor_unchecked(dir);
98            assert_eq!(n.neighbor_unchecked(dir.opposite()), loc);
99        }
100    }
101}