cell_grid/legacy/
coord.rs

1/// A coordinate of a grid
2#[allow(missing_docs, clippy::exhaustive_structs)]
3#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Hash)]
4#[deprecated(since = "0.1.4", note = "Use `DynamicGrid` instead")]
5pub struct Coord {
6    pub x: i32,
7    pub y: i32,
8}
9
10impl Coord {
11    #[must_use]
12    #[allow(missing_docs)]
13    pub const fn new(x: i32, y: i32) -> Self {
14        Self { x, y }
15    }
16
17    #[must_use]
18    pub(crate) fn from_index(grid_width: usize, index: usize) -> Self {
19        Self {
20            x: (index % grid_width).try_into().unwrap(),
21            y: (index / grid_width).try_into().unwrap(),
22        }
23    }
24
25    #[must_use]
26    pub(crate) fn into_index(self, grid_width: usize) -> Option<usize> {
27        let x: usize = self.x.try_into().ok()?;
28        let y: usize = self.y.try_into().ok()?;
29        if x >= grid_width {
30            return None;
31        }
32        Some(y * grid_width + x)
33    }
34}
35
36impl From<[i32; 2]> for Coord {
37    fn from([x, y]: [i32; 2]) -> Self {
38        Self { x, y }
39    }
40}
41
42impl From<[f32; 2]> for Coord {
43    #[allow(clippy::cast_possible_truncation)]
44    fn from([x, y]: [f32; 2]) -> Self {
45        Self {
46            x: x as i32,
47            y: y as i32,
48        }
49    }
50}
51
52#[cfg(feature = "aline")]
53impl From<aline::IVec2> for Coord {
54    fn from(aline::IVec2 { x, y }: aline::IVec2) -> Self {
55        Self { x, y }
56    }
57}
58
59#[cfg(feature = "aline")]
60impl From<aline::Vec2> for Coord {
61    fn from(v: aline::Vec2) -> Self {
62        v.as_i32().into()
63    }
64}
65
66#[cfg(test)]
67mod tests {
68
69    use super::*;
70    use alloc::collections::BTreeSet;
71    use rstest::rstest;
72
73    #[rstest]
74    #[allow(clippy::cast_sign_loss)]
75    fn test_into_index_from_index(#[values([0, 0], [123, 456])] coord: impl Into<Coord>) {
76        const WIDTH: usize = 150;
77        let coord = coord.into();
78        let index = coord.into_index(WIDTH).unwrap();
79        assert_eq!(coord, Coord::from_index(WIDTH, index));
80    }
81
82    #[test]
83    fn ciirds_have_unique_index_in_grid_len() {
84        let coords = [[0, 0], [0, 1], [1, 0], [1, 1]];
85        let indices = coords
86            .into_iter()
87            .map(Coord::from)
88            .filter_map(|c| c.into_index(2))
89            .collect::<BTreeSet<usize>>();
90        assert_eq!(indices.len(), coords.len());
91        for index in indices {
92            assert!(index < 4, "index {index} is out of grid length");
93        }
94    }
95
96    #[rstest]
97    fn into_index_should_return_none_if_out_of_bounds(
98        #[values([-1, 0], [0, -1], [100, 0], [101, 0])] coord: impl Into<Coord>,
99    ) {
100        let coord = coord.into();
101        let index: Option<usize> = coord.into_index(100);
102        assert!(index.is_none(), "{index:?}");
103    }
104}