1use crate::{
2 geometry::{Dimensions, Point, Size},
3 primitives::{
4 ellipse::{self, EllipseContains},
5 rectangle::Rectangle,
6 ContainsPoint,
7 },
8};
9
10/// A quadrant around an origin
11#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
12#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
13pub enum Quadrant {
14 TopLeft,
15 TopRight,
16 BottomRight,
17 BottomLeft,
18}
19
20#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
21#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
22pub(in crate::primitives) struct EllipseQuadrant {
23 bounding_box: Rectangle,
24 center_2x: Point,
25 ellipse: EllipseContains,
26}
27
28impl EllipseQuadrant {
29 pub fn new(top_left: Point, radius: Size, quadrant: Quadrant) -> Self {
30 let ellipse_top_left: Point = match quadrant {
31 Quadrant::TopLeft => top_left,
32 Quadrant::TopRight => top_left - radius.x_axis(),
33 Quadrant::BottomRight => top_left - radius,
34 Quadrant::BottomLeft => top_left - radius.y_axis(),
35 };
36
37 Self {
38 bounding_box: Rectangle::new(top_left, size:radius),
39 center_2x: ellipse::center_2x(ellipse_top_left, size:radius * 2),
40 ellipse: EllipseContains::new(size:radius * 2),
41 }
42 }
43}
44
45impl Dimensions for EllipseQuadrant {
46 fn bounding_box(&self) -> Rectangle {
47 self.bounding_box
48 }
49}
50
51impl ContainsPoint for EllipseQuadrant {
52 fn contains(&self, point: Point) -> bool {
53 self.ellipse.contains(point:point * 2 - self.center_2x)
54 }
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60 use crate::{
61 draw_target::DrawTarget,
62 geometry::{Point, Size},
63 iterator::PixelIteratorExt,
64 mock_display::MockDisplay,
65 pixelcolor::BinaryColor,
66 primitives::PointsIter,
67 Pixel,
68 };
69
70 fn draw_quadrant<D: DrawTarget<Color = BinaryColor>>(
71 quadrant: &EllipseQuadrant,
72 target: &mut D,
73 ) -> Result<(), D::Error> {
74 quadrant
75 .bounding_box
76 .points()
77 .filter(|p| quadrant.contains(*p))
78 .map(|p| Pixel(p, BinaryColor::On))
79 .draw(target)
80 }
81
82 #[test]
83 fn quadrants_even_size() {
84 let cases = [
85 (
86 Quadrant::TopLeft,
87 &[
88 " ####",
89 " #######",
90 " #########",
91 "##########",
92 "##########",
93 ],
94 ),
95 (
96 Quadrant::TopRight,
97 &[
98 "#### ",
99 "####### ",
100 "######### ",
101 "##########",
102 "##########",
103 ],
104 ),
105 (
106 Quadrant::BottomRight,
107 &[
108 "##########",
109 "##########",
110 "######### ",
111 "####### ",
112 "#### ",
113 ],
114 ),
115 (
116 Quadrant::BottomLeft,
117 &[
118 "##########",
119 "##########",
120 " #########",
121 " #######",
122 " ####",
123 ],
124 ),
125 ];
126
127 for (quadrant, expected) in cases.iter() {
128 let ellipse_quadrant =
129 EllipseQuadrant::new(Point::new(0, 0), Size::new(10, 5), *quadrant);
130
131 let mut display = MockDisplay::new();
132 draw_quadrant(&ellipse_quadrant, &mut display).unwrap();
133 display.assert_pattern(*expected);
134 }
135 }
136
137 #[test]
138 fn quadrants_equal_even_ellipse() {
139 let mut display = MockDisplay::new();
140
141 let radius = Size::new(10, 5);
142 let top_left = Point::new(0, 0);
143
144 draw_quadrant(
145 &EllipseQuadrant::new(top_left, radius, Quadrant::TopLeft),
146 &mut display,
147 )
148 .unwrap();
149 draw_quadrant(
150 &EllipseQuadrant::new(top_left + radius.x_axis(), radius, Quadrant::TopRight),
151 &mut display,
152 )
153 .unwrap();
154 draw_quadrant(
155 &EllipseQuadrant::new(top_left + radius, radius, Quadrant::BottomRight),
156 &mut display,
157 )
158 .unwrap();
159 draw_quadrant(
160 &EllipseQuadrant::new(top_left + radius.y_axis(), radius, Quadrant::BottomLeft),
161 &mut display,
162 )
163 .unwrap();
164
165 display.assert_pattern(&[
166 " ######## ",
167 " ############## ",
168 " ################## ",
169 "####################",
170 "####################",
171 "####################",
172 "####################",
173 " ################## ",
174 " ############## ",
175 " ######## ",
176 ]);
177 }
178
179 #[test]
180 fn quadrants_equal_odd_ellipse() {
181 let mut display = MockDisplay::new();
182
183 let radius = Size::new(7, 9);
184 let top_left = Point::new(0, 0);
185
186 draw_quadrant(
187 &EllipseQuadrant::new(top_left, radius, Quadrant::TopLeft),
188 &mut display,
189 )
190 .unwrap();
191 draw_quadrant(
192 &EllipseQuadrant::new(top_left + radius.x_axis(), radius, Quadrant::TopRight),
193 &mut display,
194 )
195 .unwrap();
196 draw_quadrant(
197 &EllipseQuadrant::new(top_left + radius, radius, Quadrant::BottomRight),
198 &mut display,
199 )
200 .unwrap();
201 draw_quadrant(
202 &EllipseQuadrant::new(top_left + radius.y_axis(), radius, Quadrant::BottomLeft),
203 &mut display,
204 )
205 .unwrap();
206
207 display.assert_pattern(&[
208 " #### ",
209 " ######## ",
210 " ########## ",
211 " ############ ",
212 " ############ ",
213 " ############ ",
214 "##############",
215 "##############",
216 "##############",
217 "##############",
218 "##############",
219 "##############",
220 " ############ ",
221 " ############ ",
222 " ############ ",
223 " ########## ",
224 " ######## ",
225 " #### ",
226 ]);
227 }
228}
229