mfform_lib/
pos.rs

1use std::cmp::{max, min};
2
3/// Position on screen
4#[derive(Debug, Clone, Copy, Eq, PartialEq)]
5pub struct Pos {
6    pub x: u16,
7    pub y: u16,
8}
9
10impl Pos {
11    pub(crate) fn constrain<I: Into<Self> + Copy>(self, other: I) -> Self {
12        Self {
13            x: max(min(self.x, other.into().x - 1), 0),
14            y: max(min(self.y, other.into().y - 1), 0),
15        }
16    }
17
18    pub(crate) fn within(self, other: Self, length: u16) -> Option<usize> {
19        if self.x >= other.x && self.x <= other.x + length && self.y == other.y {
20            Some((self.x - other.x) as usize)
21        } else {
22            None
23        }
24    }
25
26    pub(crate) fn move_x(self, by: i16, max: u16) -> Self {
27        Pos {
28            x: (self.x as i16 + by) as u16,
29            y: self.y,
30        }
31        .constrain((max, u16::MAX))
32    }
33}
34
35impl From<(u16, u16)> for Pos {
36    fn from((x, y): (u16, u16)) -> Self {
37        Self { x, y }
38    }
39}
40
41impl Ord for Pos {
42    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
43        match self.y.cmp(&other.y) {
44            core::cmp::Ordering::Equal => self.x.cmp(&other.x),
45            ord => ord,
46        }
47    }
48}
49
50impl PartialOrd for Pos {
51    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
52        Some(self.cmp(other))
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use std::cmp::Ordering;
59
60    use super::*;
61
62    #[test]
63    fn within_1() {
64        let pos: Pos = (2, 2).into();
65        let field: Pos = (0, 2).into();
66
67        let res = pos.within(field, 4);
68
69        assert_eq!(res, Some(2));
70    }
71
72    #[test]
73    fn within_2() {
74        let pos: Pos = (6, 2).into();
75        let field: Pos = (2, 2).into();
76
77        let res = pos.within(field, 4);
78
79        assert_eq!(res, Some(4));
80    }
81
82    #[test]
83    fn outside_x() {
84        let pos: Pos = (2, 2).into();
85        let field: Pos = (7, 2).into();
86
87        let res = pos.within(field, 4);
88
89        assert_eq!(res, None);
90    }
91
92    #[test]
93    fn outside_y() {
94        let pos: Pos = (2, 2).into();
95        let field: Pos = (2, 0).into();
96
97        let res = pos.within(field, 4);
98
99        assert_eq!(res, None);
100    }
101
102    #[test]
103    fn ordering() {
104        let test_pos: Pos = (2, 2).into();
105
106        assert_eq!(test_pos.cmp(&(0, 0).into()), Ordering::Greater);
107        assert_eq!(test_pos.cmp(&(2, 0).into()), Ordering::Greater);
108        assert_eq!(test_pos.cmp(&(0, 2).into()), Ordering::Greater);
109        assert_eq!(test_pos.cmp(&(2, 2).into()), Ordering::Equal);
110        assert_eq!(test_pos.cmp(&(2, 4).into()), Ordering::Less);
111        assert_eq!(test_pos.cmp(&(0, 4).into()), Ordering::Less);
112        assert_eq!(test_pos.cmp(&(4, 4).into()), Ordering::Less);
113    }
114}