embedded_graphics/primitives/circle/
mod.rs1use crate::{
4 geometry::{Dimensions, Point, PointExt, Size},
5 primitives::{
6 common::DistanceIterator, ContainsPoint, OffsetOutline, PointsIter, Primitive, Rectangle,
7 },
8 transform::Transform,
9};
10
11mod points;
12mod styled;
13
14pub use points::Points;
15pub use styled::StyledPixelsIterator;
16
17#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
58#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
59pub struct Circle {
60 pub top_left: Point,
62
63 pub diameter: u32,
65}
66
67impl Circle {
68 pub const fn new(top_left: Point, diameter: u32) -> Self {
70 Circle { top_left, diameter }
71 }
72
73 pub const fn with_center(center: Point, diameter: u32) -> Self {
75 let top_left = Rectangle::with_center(center, Size::new_equal(diameter)).top_left;
76
77 Circle { top_left, diameter }
78 }
79
80 pub fn center(&self) -> Point {
82 self.bounding_box().center()
83 }
84
85 pub(in crate::primitives) fn center_2x(&self) -> Point {
90 let radius = self.diameter.saturating_sub(1);
92
93 self.top_left * 2 + Size::new(radius, radius)
94 }
95
96 pub(in crate::primitives) const fn threshold(&self) -> u32 {
98 diameter_to_threshold(self.diameter)
99 }
100
101 pub(in crate::primitives) fn distances(&self) -> DistanceIterator {
103 DistanceIterator::new(self.center_2x(), &self.bounding_box())
104 }
105}
106
107impl OffsetOutline for Circle {
108 fn offset(&self, offset: i32) -> Self {
109 let diameter = if offset >= 0 {
110 self.diameter.saturating_add(2 * offset as u32)
111 } else {
112 self.diameter.saturating_sub(2 * (-offset) as u32)
113 };
114
115 Self::with_center(self.center(), diameter)
116 }
117}
118
119impl Primitive for Circle {}
120
121impl PointsIter for Circle {
122 type Iter = Points;
123
124 fn points(&self) -> Self::Iter {
125 Points::new(self)
126 }
127}
128
129impl ContainsPoint for Circle {
130 fn contains(&self, point: Point) -> bool {
131 let delta = self.center_2x() - point * 2;
132 let distance = delta.length_squared() as u32;
133
134 distance < self.threshold()
135 }
136}
137
138impl Dimensions for Circle {
139 fn bounding_box(&self) -> Rectangle {
140 Rectangle::new(self.top_left, Size::new_equal(self.diameter))
141 }
142}
143
144impl Transform for Circle {
145 fn translate(&self, by: Point) -> Self {
157 Self {
158 top_left: self.top_left + by,
159 ..*self
160 }
161 }
162
163 fn translate_mut(&mut self, by: Point) -> &mut Self {
174 self.top_left += by;
175
176 self
177 }
178}
179
180pub(in crate::primitives) const fn diameter_to_threshold(diameter: u32) -> u32 {
181 if diameter <= 4 {
182 diameter.pow(2) - diameter / 2
183 } else {
184 diameter.pow(2)
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191 use crate::{
192 geometry::{Dimensions, Point, Size},
193 primitives::ContainsPoint,
194 };
195
196 #[test]
197 fn negative_dimensions() {
198 let circle = Circle::new(Point::new(-15, -15), 20);
199
200 assert_eq!(
201 circle.bounding_box(),
202 Rectangle::new(Point::new(-15, -15), Size::new(20, 20))
203 );
204 }
205
206 #[test]
207 fn dimensions() {
208 let circle = Circle::new(Point::new(5, 15), 10);
209
210 assert_eq!(
211 circle.bounding_box(),
212 Rectangle::new(Point::new(5, 15), Size::new(10, 10))
213 );
214 }
215
216 #[test]
217 fn center_is_correct() {
218 let circle = Circle::new(Point::new(10, 10), 5);
220 assert_eq!(circle.center(), Point::new(12, 12));
221
222 let circle = Circle::new(Point::new(10, 10), 6);
224 assert_eq!(circle.center(), Point::new(12, 12));
225
226 let circle = Circle::with_center(Point::new(10, 10), 5);
228 assert_eq!(circle.center(), Point::new(10, 10));
229
230 let circle = Circle::with_center(Point::new(10, 10), 6);
232 assert_eq!(circle.center(), Point::new(10, 10));
233 }
234
235 #[test]
236 fn contains() {
237 let circle = Circle::new(Point::zero(), 5);
238
239 let contained_points = Rectangle::new(Point::new(-10, -10), Size::new(20, 20))
240 .points()
241 .filter(|p| circle.contains(*p));
242
243 assert!(contained_points.eq(circle.points()));
244 }
245
246 #[test]
247 fn offset() {
248 let center = Point::new(1, 2);
249 let circle = Circle::with_center(center, 3);
250
251 assert_eq!(circle.offset(0), circle);
252
253 assert_eq!(circle.offset(1), Circle::with_center(center, 5));
254 assert_eq!(circle.offset(2), Circle::with_center(center, 7));
255
256 assert_eq!(circle.offset(-1), Circle::with_center(center, 1));
257 assert_eq!(circle.offset(-2), Circle::with_center(center, 0));
258 assert_eq!(circle.offset(-3), Circle::with_center(center, 0));
259 }
260}