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