1use crate::new_hash_set;
2use crate::prelude::*;
3use crate::shape_box::ShapeBox;
4#[cfg(feature = "serde")]
5use serde::{Deserialize, Serialize};
6
7#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
8#[derive(Debug, Clone, Eq, PartialEq, Hash)]
9pub struct Ellipse {
10 center: Coord,
11 top: Coord,
12 right: Coord,
13 rotation: isize,
14}
15
16impl IntersectsContains for Ellipse {}
17
18impl Ellipse {
19 #[must_use]
20 pub fn new<P: Into<Coord>>(center: P, width: usize, height: usize) -> Self {
21 let center = center.into();
22 Self {
23 center,
24 top: center - (0, height / 2),
25 right: center + (width / 2, 0),
26 rotation: 0,
27 }
28 }
29
30 pub fn new_rotated<P1: Into<Coord>, P2: Into<Coord>, P3: Into<Coord>>(
31 center: P1,
32 top: P2,
33 right: P3,
34 ) -> Self {
35 let center = center.into();
36 let top = top.into();
37 let right = right.into();
38 let angle = center.angle_to(top);
39 let right = Coord::from_angle(center, center.distance(right), -angle + 90);
40 let top = Coord::from_angle(center, center.distance(top), -angle);
41 Self {
42 center,
43 top,
44 right,
45 rotation: angle,
46 }
47 }
48}
49
50impl Ellipse {
51 #[inline]
52 #[must_use]
53 pub fn width(&self) -> usize {
54 (self.right() - self.left()).unsigned_abs()
55 }
56
57 #[inline]
58 #[must_use]
59 pub fn height(&self) -> usize {
60 (self.bottom() - self.top()).unsigned_abs()
61 }
62
63 pub fn angle(&self) -> isize {
64 self.rotation
65 }
66
67 #[inline(always)]
68 fn no_rotate_point(x: isize, y: isize, _: Coord, _: isize) -> Coord {
69 coord!(x, y)
70 }
71
72 #[inline(always)]
73 fn rotate_point(x: isize, y: isize, center: Coord, degrees: isize) -> Coord {
74 let orig = coord!(x, y);
75 let offset = center.angle_to(orig) + degrees;
76 Coord::from_angle(center, center.distance(orig), offset)
77 }
78}
79
80impl Shape for Ellipse {
81 fn from_points(points: &[Coord]) -> Self
84 where
85 Self: Sized,
86 {
87 debug_assert!(points.len() >= 3);
88 let center = points[0];
89 let top = points[1];
90 let right = points[2];
91 let rotation = center.angle_to(top);
92 let top = Coord::from_angle(center, top.distance(center), 0);
93 let right = Coord::from_angle(center, right.distance(center), 90);
94 Ellipse {
95 center,
96 top,
97 right,
98 rotation,
99 }
100 }
101
102 fn rebuild(&self, points: &[Coord]) -> Self
103 where
104 Self: Sized,
105 {
106 Ellipse::from_points(points)
107 }
108
109 fn translate_by(&self, delta: Coord) -> Self {
110 let points: Vec<Coord> = self.points().into_iter().map(|c| c + delta).collect();
111 Ellipse::from_points(&points)
112 }
113
114 fn move_to(&self, point: Coord) -> Self {
115 self.move_center_to(point)
116 }
117
118 fn contains(&self, point: Coord) -> bool {
119 ((point.x - self.center.x) ^ 2) / ((self.width() as isize) ^ 2)
120 + ((point.y - self.center.y) ^ 2) / ((self.height() as isize) ^ 2)
121 <= 1
122 }
123
124 fn points(&self) -> Vec<Coord> {
130 vec![
131 self.center,
132 Coord::from_angle(self.center, self.center.distance(self.top), self.rotation),
133 Coord::from_angle(
134 self.center,
135 self.center.distance(self.right),
136 self.rotation + 90,
137 ),
138 ]
139 }
140
141 #[inline]
142 fn center(&self) -> Coord {
143 self.center
144 }
145
146 #[inline]
147 fn left(&self) -> isize {
148 self.right.x - (self.center.distance(self.right) * 2) as isize
149 }
150
151 #[inline]
152 fn right(&self) -> isize {
153 self.right.x
154 }
155
156 #[inline]
157 fn top(&self) -> isize {
158 self.top.y
159 }
160
161 #[inline]
162 fn bottom(&self) -> isize {
163 self.top.y + (self.center.distance(self.top) * 2) as isize
164 }
165
166 fn outline_pixels(&self) -> Vec<Coord> {
167 let center = self.center;
168 let degrees = self.rotation;
169 let rotate = if degrees == 0 {
170 Self::no_rotate_point
171 } else {
172 Self::rotate_point
173 };
174
175 let center_x = self.center.x;
176 let center_y = self.center.y;
177 let rx = (self.width() / 2) as f32;
178 let ry = (self.height() / 2) as f32;
179 let mut output = new_hash_set();
180
181 let mut x = 0;
182 let mut y = ry as isize;
183 let mut p1 = ry * ry - (rx * rx) * ry + (rx * rx) * (0.25);
184 let mut dx = 2.0 * (ry * ry) * (x as f32);
185 let mut dy = 2.0 * (rx * rx) * (y as f32);
186 while dx < dy {
187 output.insert(rotate(center_x + x, center_y + y, center, degrees));
188 output.insert(rotate(center_x - x, center_y + y, center, degrees));
189 output.insert(rotate(center_x + x, center_y - y, center, degrees));
190 output.insert(rotate(center_x - x, center_y - y, center, degrees));
191 if p1 < 0.0 {
192 x += 1;
193 dx = 2.0 * (ry * ry) * (x as f32);
194 p1 += dx + (ry * ry);
195 } else {
196 x += 1;
197 y -= 1;
198 dx = 2.0 * (ry * ry) * (x as f32);
199 dy = 2.0 * (rx * rx) * (y as f32);
200 p1 += dx - dy + (ry * ry);
201 }
202 }
203 let mut p2 = (ry * ry) * ((x as f32) + 0.5) * ((x as f32) + 0.5)
204 + (rx * rx) * ((y as f32) - 1.0) * ((y as f32) - 1.0)
205 - (rx * rx) * (ry * ry);
206
207 while y >= 0 {
208 output.insert(rotate(center_x + x, center_y + y, center, degrees));
209 output.insert(rotate(center_x - x, center_y + y, center, degrees));
210 output.insert(rotate(center_x + x, center_y - y, center, degrees));
211 output.insert(rotate(center_x - x, center_y - y, center, degrees));
212 if p2 > 0.0 {
213 y -= 1;
214 dy = 2.0 * (rx * rx) * (y as f32);
215 p2 -= dy + (rx * rx);
216 } else {
217 x += 1;
218 y -= 1;
219 dy -= 2.0 * (rx * rx);
220 dx += 2.0 * (ry * ry);
221 p2 += dx - dy + (rx * rx);
222 }
223 }
224
225 output.into_iter().collect()
226 }
227
228 fn filled_pixels(&self) -> Vec<Coord> {
229 let mut output = new_hash_set();
230 let height = self.height() as isize / 2;
231 let width = self.width() as isize / 2;
232 let height_sq = height * height;
233 let width_sq = width * width;
234 let limit = height_sq * width_sq;
235 for y in -height..height {
236 let y_amount = y * y * width_sq;
237 for x in -width..width {
238 if x * x * height_sq + y_amount <= limit {
239 output.insert(coord!(self.center.x + x, self.center.y + y));
240 }
241 }
242 }
243 output.into_iter().collect()
244 }
245
246 fn to_shape_box(&self) -> ShapeBox {
247 ShapeBox::Ellipse(self.clone())
248 }
249}
250
251impl Ellipse {
252 #[must_use]
253 pub fn as_rect(&self) -> Rect {
254 Rect::new((self.left(), self.top()), (self.right(), self.bottom()))
255 }
256
257 #[must_use]
258 pub fn as_horizontal_line(&self) -> Line {
259 Line::new((self.left(), self.center.y), (self.right(), self.center.y))
260 }
261
262 #[must_use]
263 pub fn as_vertical_line(&self) -> Line {
264 Line::new((self.center.x, self.top()), (self.center.x, self.bottom()))
265 }
266
267 #[must_use]
269 pub fn as_radius_line(&self) -> Line {
270 Line::new((self.center.x, self.center.y), (self.center.x, self.top()))
271 }
272
273 #[must_use]
275 pub fn as_circle(&self) -> Option<Circle> {
276 if self.width() == self.height() {
277 Some(Circle::new(self.center, self.width() / 2))
278 } else {
279 None
280 }
281 }
282
283 #[must_use]
284 pub fn as_largest_circle(&self) -> Circle {
285 let radius = self.width().max(self.height());
286 Circle::new(self.center, radius)
287 }
288
289 #[must_use]
290 pub fn as_smallest_circle(&self) -> Circle {
291 let radius = self.width().min(self.height());
292 Circle::new(self.center, radius)
293 }
294
295 #[must_use]
296 pub fn as_polygon(&self) -> Polygon {
297 let x = self.center.x as f64;
298 let y = self.center.y as f64;
299 let w = self.width() as f64 / 2.0;
300 let h = self.height() as f64 / 2.0;
301
302 let segments = (((w + h) / 2.0) * 20.0).sqrt().floor().max(8.0) as usize;
303 let points = discretise_ellipse(x, y, w, h, segments);
304
305 Polygon::from_points(&points).rotate(self.angle())
306 }
307}
308
309fn discretise_ellipse(x: f64, y: f64, a: f64, b: f64, segments: usize) -> Vec<Coord> {
310 let angle_shift = 6.29 / (segments as f64);
311 let mut phi = 0.0;
312 let mut vertices = vec![];
313 for _ in 0..segments {
314 phi += angle_shift;
315 vertices.push(coord!(x + a * phi.cos(), y + b * phi.sin()));
316 }
317
318 vertices
319}
320
321#[cfg(test)]
322mod test {
323 use crate::circle::Circle;
324 use crate::ellipse::Ellipse;
325 use crate::Shape;
326
327 #[test]
328 fn check_circle_ellipse() {
329 let ellipse = Ellipse::new((40, 40), 20, 20);
330 let gen_circle = ellipse.as_circle().unwrap();
331 let expected = Circle::new((40, 40), 10);
332 assert_eq!(gen_circle, expected);
333 }
334
335 #[test]
336 fn translate() {
337 let ellipse = Ellipse::new((40, 40), 20, 20);
338 let moved = ellipse.move_center_to(coord!(10, 10));
339 assert_eq!(ellipse.width(), moved.width());
340 assert_eq!(ellipse.height(), moved.height());
341 assert_eq!(ellipse.angle(), moved.angle());
342 }
343
344 #[test]
345 fn to_from_points() {
346 let ellipse = Ellipse::new((100, 100), 30, 60);
347 assert_eq!(ellipse.center, coord!(100, 100));
348 assert_eq!(ellipse.top, coord!(100, 70));
349 assert_eq!(ellipse.right, coord!(115, 100));
350 let points = ellipse.points();
351 assert_eq!(points, coord_vec![(100, 100), (100, 70), (115, 100)]);
352 let gen_ellipse = Ellipse::from_points(&points);
353 assert_eq!(ellipse.width(), gen_ellipse.width());
354 assert_eq!(ellipse.height(), gen_ellipse.height());
355 assert_eq!(ellipse, gen_ellipse);
356 }
357
358 #[test]
359 fn rotation() {
360 let ellipse = Ellipse::new((100, 100), 200, 50);
361 assert_eq!(
362 ellipse.points(),
363 vec![coord!(100, 100), coord!(100, 75), coord!(200, 100)]
364 );
365 assert_eq!(ellipse.center, coord!(100, 100));
366 assert_eq!(ellipse.top, coord!(100, 75));
367 assert_eq!(ellipse.right, coord!(200, 100));
368 assert_eq!(ellipse.rotation, 0);
369 let rotated = ellipse.rotate(90);
370 assert_eq!(
371 rotated.points(),
372 vec![coord!(100, 100), coord!(125, 100), coord!(100, 200)]
373 );
374 assert_eq!(rotated.center, coord!(100, 100));
375 assert_eq!(rotated.top, coord!(100, 75));
376 assert_eq!(rotated.right, coord!(200, 100));
377 assert_eq!(rotated.rotation, 90);
378 }
379
380 #[test]
381 fn move_center() {
382 let ellipse = Ellipse::new((100, 100), 20, 20);
383 let moved = ellipse.move_center_to(coord!(50, 50));
384
385 assert_eq!(moved.center, coord!(50, 50));
386
387 let ellipse = Ellipse::new((100, 100), 20, 20);
388 let moved = ellipse.move_center_to(coord!(120, 50));
389
390 assert_eq!(moved.center, coord!(120, 50));
391 }
392
393 #[test]
394 fn move_center_rotated() {
395 let ellipse = Ellipse::new((100, 100), 20, 20).rotate(45);
396 let moved = ellipse.move_center_to(coord!(50, 50));
397
398 assert_eq!(ellipse.angle(), 45);
399 assert_eq!(moved.angle(), 45);
400 assert_eq!(moved.center, coord!(50, 50));
401 }
402}