1use core::ops::Range;
2
3use crate::{
4 geometry::{Dimensions, Point},
5 primitives::{
6 common::Scanline,
7 ellipse::{Ellipse, EllipseContains},
8 },
9};
10
11/// Iterator over all points inside the ellipse
12#[derive(Clone, Eq, PartialEq, Hash, Debug)]
13#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
14pub struct Points {
15 scanlines: Scanlines,
16 current_scanline: Scanline,
17}
18
19impl Points {
20 pub(in crate::primitives) fn new(ellipse: &Ellipse) -> Self {
21 Self {
22 scanlines: Scanlines::new(ellipse),
23 current_scanline: Scanline::new_empty(0),
24 }
25 }
26}
27
28impl Iterator for Points {
29 type Item = Point;
30
31 fn next(&mut self) -> Option<Self::Item> {
32 self.current_scanline.next().or_else(|| {
33 self.current_scanline = self.scanlines.next()?;
34 self.current_scanline.next()
35 })
36 }
37}
38
39#[derive(Clone, Eq, PartialEq, Hash, Debug)]
40#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
41pub struct Scanlines {
42 rows: Range<i32>,
43 columns: Range<i32>,
44 pub(super) center_2x: Point,
45 ellipse_contains: EllipseContains,
46}
47
48impl Scanlines {
49 pub fn new(ellipse: &Ellipse) -> Self {
50 let bounding_box: Rectangle = ellipse.bounding_box();
51
52 Self {
53 rows: bounding_box.rows(),
54 columns: bounding_box.columns(),
55 center_2x: ellipse.center_2x(),
56 ellipse_contains: EllipseContains::new(ellipse.size),
57 }
58 }
59}
60
61impl Iterator for Scanlines {
62 type Item = Scanline;
63
64 fn next(&mut self) -> Option<Self::Item> {
65 let y: i32 = self.rows.next()?;
66
67 let scaled_y: i32 = y * 2 - self.center_2x.y;
68
69 self.columns
70 .clone()
71 // Find the first pixel that is inside the ellipse.
72 .find(|x: &i32| {
73 self.ellipse_contains
74 .contains(Point::new(*x * 2 - self.center_2x.x, scaled_y))
75 })
76 // Shorten the right side of the scanline by the same amount as the left side.
77 .map(|x: i32| Scanline::new(y, x:x..self.columns.end - (x - self.columns.start)))
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84 use crate::{
85 geometry::Size,
86 primitives::{Circle, PointsIter},
87 };
88
89 #[test]
90 fn matches_circles_points() {
91 for diameter in 0..50 {
92 let circle_points = Circle::new(Point::new(0, 0), diameter).points();
93
94 let ellipse_points =
95 Ellipse::new(Point::new(0, 0), Size::new(diameter, diameter)).points();
96
97 assert!(circle_points.eq(ellipse_points), "diameter = {}", diameter);
98 }
99 }
100}
101

Provided by KDAB

Privacy Policy