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}