sark_grids/geometry/
grid_circle.rs1use glam::{IVec2, UVec2, Vec2};
3
4use crate::{GridPoint, GridShape};
5
6use super::{grid_rect::GridRectIter, GridRect};
7
8#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
10pub struct GridCircle {
11 pub center: IVec2,
12 pub radius: usize,
13}
14
15impl GridCircle {
16 pub fn new(center: impl GridPoint, radius: usize) -> Self {
17 GridCircle {
18 center: center.to_ivec2(),
19 radius,
20 }
21 }
22
23 pub fn origin(radius: usize) -> Self {
25 Self::new([0, 0], radius)
26 }
27
28 pub fn outline(&self) -> GridCircleOutline {
30 GridCircleOutline::new(self.center, self.radius)
31 }
32
33 #[inline]
34 pub fn overlaps(&self, other: GridCircle) -> bool {
35 let a = (self.radius + other.radius) as i32;
36 let d = self.center - other.center;
37 a * a > (d.x * d.x + d.y * d.y)
38 }
39
40 #[inline]
41 pub fn contains(&self, p: impl GridPoint) -> bool {
42 let p = p.to_ivec2() - self.center;
43 let dist_sq = p.x * p.x + p.y * p.y;
44 dist_sq <= (self.radius * self.radius) as i32
45 }
46}
47
48#[derive(Debug, Clone)]
49pub struct GridCircleIter {
50 rect_iter: GridRectIter,
51 center: Vec2,
52 radius: f32,
53}
54
55impl GridCircleIter {
56 pub fn new(center: impl GridPoint, radius: usize) -> Self {
57 let c = center.to_vec2() + 0.5;
58 let r = radius as f32;
59 let rect = GridRect::center_origin(UVec2::splat(radius as u32 * 2 + 1));
60 GridCircleIter {
61 rect_iter: rect.into_iter(),
62 center: c,
63 radius: r,
64 }
65 }
66}
67
68impl Iterator for GridCircleIter {
69 type Item = IVec2;
70
71 fn next(&mut self) -> Option<Self::Item> {
72 for p in self.rect_iter.by_ref() {
73 if inside_circle(p.as_vec2(), self.radius + 0.5) {
74 return Some(self.center.as_ivec2() + p);
75 }
76 }
77
78 None
79 }
80}
81
82#[inline]
83fn inside_circle(p: Vec2, radius: f32) -> bool {
84 let dist_sq = p.x * p.x + p.y * p.y;
85 dist_sq <= radius * radius
86}
87
88impl IntoIterator for GridCircle {
89 type Item = IVec2;
90 type IntoIter = GridCircleIter;
91
92 fn into_iter(self) -> Self::IntoIter {
93 GridCircleIter::new(self.center, self.radius)
94 }
95}
96
97#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
99pub struct GridCircleOutline {
100 center: IVec2,
101 radius: usize,
102}
103
104impl GridCircleOutline {
105 pub fn new(center: impl GridPoint, radius: usize) -> Self {
106 GridCircleOutline {
107 center: center.to_ivec2(),
108 radius,
109 }
110 }
111
112 pub fn origin(radius: usize) -> Self {
114 Self::new([0, 0], radius)
115 }
116
117 pub fn filled(&self) -> GridCircle {
119 GridCircle::new(self.center, self.radius)
120 }
121}
122
123#[derive(Debug, Clone)]
124pub struct GridCircleOutlineIter {
125 radius: f32,
126 center: IVec2,
127 r: usize,
128 end: usize,
129 points: [IVec2; 8],
130 curr: usize,
131}
132
133impl GridCircleOutlineIter {
134 pub fn new(center: impl GridPoint, radius: usize) -> Self {
135 let radius = radius as f32 + 0.5;
136 let end = (radius * 0.5_f32.sqrt()).floor() as usize;
137
138 GridCircleOutlineIter {
139 radius,
140 center: center.to_ivec2(),
141 r: 0,
142 end,
143 points: Default::default(),
144 curr: 8,
145 }
146 }
147}
148
149impl Iterator for GridCircleOutlineIter {
150 type Item = IVec2;
151
152 fn next(&mut self) -> Option<Self::Item> {
153 if self.curr >= 8 {
154 if self.r > self.end {
155 return None;
156 }
157 self.curr = 0;
158 let r = self.r as f32;
159 let d = (self.radius * self.radius - r * r).sqrt().floor();
160
161 let c = self.center.as_vec2();
162 self.points[0] = Vec2::new(c.x - d, c.y + r).as_ivec2();
163 self.points[1] = Vec2::new(c.x + d, c.y + r).as_ivec2();
164 self.points[2] = Vec2::new(c.x - d, c.y - r).as_ivec2();
165 self.points[3] = Vec2::new(c.x + d, c.y - r).as_ivec2();
166 self.points[4] = Vec2::new(c.x + r, c.y - d).as_ivec2();
167 self.points[5] = Vec2::new(c.x + r, c.y + d).as_ivec2();
168 self.points[6] = Vec2::new(c.x - r, c.y - d).as_ivec2();
169 self.points[7] = Vec2::new(c.x - r, c.y + d).as_ivec2();
170
171 self.r += 1;
172 }
173 let curr = self.points[self.curr];
174
175 self.curr += 1;
176
177 Some(curr)
178 }
179}
180
181impl IntoIterator for GridCircleOutline {
182 type Item = IVec2;
183 type IntoIter = GridCircleOutlineIter;
184
185 fn into_iter(self) -> Self::IntoIter {
186 GridCircleOutlineIter::new(self.center, self.radius)
187 }
188}
189
190impl GridShape for GridCircle {
191 fn iter(&self) -> crate::GridShapeIterator {
192 crate::GridShapeIterator::Circle(GridCircleIter::new(self.center, self.radius))
193 }
194
195 fn pos(&self) -> IVec2 {
196 self.center
197 }
198
199 fn set_pos(&mut self, pos: IVec2) {
200 self.center = pos;
201 }
202
203 fn bounds(&self) -> GridRect {
204 let min = self.center - self.radius as i32;
205 let max = min + self.radius as i32 * 2;
206 GridRect::from_points(min, max)
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use crate::util::Canvas;
213
214 use super::*;
215
216 #[test]
217 #[ignore]
218 fn draw_circles() {
219 for size in 1..15 {
220 let empty_circle = GridCircleOutline::new([-(size as i32) / 2 - 2, 0], size);
221 let mut canvas = Canvas::new([size * 4 + 3, size * 2 + 3]);
222
223 for p in empty_circle {
224 canvas.put(p, '*');
225 }
226
227 let filled_circle = GridCircle::new([size / 2 + 2, 0], size);
228
229 canvas.put_shape(filled_circle, '*');
230
231 canvas.print();
232 println!();
233 }
234 }
235
236 #[test]
237 #[ignore]
238 fn circle_rects() {
239 for radius in 1..2 {
240 let circle = GridCircle::new([-(radius as i32) * 2, 0], radius);
241 let mut rect = circle.bounds();
242 let radius = radius as i32;
243
244 rect.pos.x = radius + 2;
245
246 let mut canvas = Canvas::new([radius * 2 + 10, radius * 2 + 2]);
247
248 canvas.put_shape(circle, '*');
249 canvas.put_shape(rect, 'r');
250 canvas.put([0, 0], 'O');
251
252 canvas.print();
253 println!();
254 }
255 }
256}