board_game_range/
circular.rs

1#[derive(Debug, Clone)]
2pub struct CircularRangeIter {
3    current: [i32; 2],
4    radius: i32,
5    radius_squared: i32,
6}
7
8fn len2([x, y]: [i32; 2]) -> i32 {
9    x.pow(2) + y.pow(2)
10}
11
12impl Iterator for CircularRangeIter {
13    type Item = [i32; 2];
14
15    fn next(&mut self) -> Option<Self::Item> {
16        loop {
17            if self.current[0] > self.radius {
18                return None;
19            } else if self.current[1] > self.radius {
20                self.current[0] += 1;
21                self.current[1] = -self.radius;
22            } else {
23                let position = self.current;
24                self.current[1] += 1;
25                if len2(position) <= self.radius_squared {
26                    return Some(position);
27                }
28            }
29        }
30    }
31}
32
33/// Iterate through tiles where their centers are inside a circle based on real distance.
34///
35/// `offset`: Increase the radius of a circle by a floating point value, commonly `0.5`
36/// to make the selection look better on smaller ranges.
37pub fn circular_range(radius: u32, offset: f32) -> CircularRangeIter {
38    CircularRangeIter {
39        current: [-(radius as i32), -(radius as i32)],
40        radius: radius as i32,
41        radius_squared: (radius as f32 + offset).max(0.).powi(2).floor() as i32,
42    }
43}
44
45#[cfg(test)]
46mod test {
47    use super::circular_range;
48
49    type Arr = Vec<[i32; 2]>;
50
51    #[test]
52    fn test() {
53        for f in [0.0, -1.0, -0.5, 0.5, 1.0] {
54            assert_eq!(circular_range(0, f).collect::<Arr>(), vec![[0, 0]]);
55        }
56
57        assert_eq!(
58            circular_range(1, 0.).collect::<Arr>(),
59            vec![[-1, 0], [0, -1], [0, 0], [0, 1], [1, 0]]
60        );
61
62        assert_eq!(
63            circular_range(1, 0.5).collect::<Arr>(),
64            vec![
65                [-1, -1],
66                [-1, 0],
67                [-1, 1],
68                [0, -1],
69                [0, 0],
70                [0, 1],
71                [1, -1],
72                [1, 0],
73                [1, 1]
74            ]
75        );
76
77        assert_eq!(
78            circular_range(2, 0.).collect::<Arr>(),
79            vec![
80                [-2, 0],
81                [-1, -1],
82                [-1, 0],
83                [-1, 1],
84                [0, -2],
85                [0, -1],
86                [0, 0],
87                [0, 1],
88                [0, 2],
89                [1, -1],
90                [1, 0],
91                [1, 1],
92                [2, 0]
93            ]
94        );
95
96        assert_eq!(
97            circular_range(2, 0.5).collect::<Arr>(),
98            vec![
99                [-2, -1],
100                [-2, 0],
101                [-2, 1],
102                [-1, -2],
103                [-1, -1],
104                [-1, 0],
105                [-1, 1],
106                [-1, 2],
107                [0, -2],
108                [0, -1],
109                [0, 0],
110                [0, 1],
111                [0, 2],
112                [1, -2],
113                [1, -1],
114                [1, 0],
115                [1, 1],
116                [1, 2],
117                [2, -1],
118                [2, 0],
119                [2, 1]
120            ]
121        );
122
123        // assert_eq!(
124        //     cross_range(2, 2).collect::<Arr>(),
125        //     vec![[-2, 0], [0, -2], [2, 0], [0, 2]]
126        // );
127
128        // assert_eq!(
129        //     cross_range(1, 2).collect::<Arr>(),
130        //     vec![
131        //         [-1, 0], [0, -1], [1, 0], [0, 1],
132        //         [-2, 0], [0, -2], [2, 0], [0, 2],
133        //     ]
134        // );
135
136        // assert_eq!(
137        //     cross_range(0, 2).collect::<Arr>(),
138        //     vec![
139        //         [0, 0],
140        //         [-1, 0], [0, -1], [1, 0], [0, 1],
141        //         [-2, 0], [0, -2], [2, 0], [0, 2],
142        //     ]
143        // );
144    }
145}