logo
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
use core::ops::Range;

use crate::{
    geometry::{Dimensions, Point},
    primitives::{
        common::Scanline,
        ellipse::{Ellipse, EllipseContains},
    },
};

/// Iterator over all points inside the ellipse
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct Points {
    scanlines: Scanlines,
    current_scanline: Scanline,
}

impl Points {
    pub(in crate::primitives) fn new(ellipse: &Ellipse) -> Self {
        Self {
            scanlines: Scanlines::new(ellipse),
            current_scanline: Scanline::new_empty(0),
        }
    }
}

impl Iterator for Points {
    type Item = Point;

    fn next(&mut self) -> Option<Self::Item> {
        self.current_scanline.next().or_else(|| {
            self.current_scanline = self.scanlines.next()?;
            self.current_scanline.next()
        })
    }
}

#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct Scanlines {
    rows: Range<i32>,
    columns: Range<i32>,
    pub(super) center_2x: Point,
    ellipse_contains: EllipseContains,
}

impl Scanlines {
    pub fn new(ellipse: &Ellipse) -> Self {
        let bounding_box = ellipse.bounding_box();

        Self {
            rows: bounding_box.rows(),
            columns: bounding_box.columns(),
            center_2x: ellipse.center_2x(),
            ellipse_contains: EllipseContains::new(ellipse.size),
        }
    }
}

impl Iterator for Scanlines {
    type Item = Scanline;

    fn next(&mut self) -> Option<Self::Item> {
        let y = self.rows.next()?;

        let scaled_y = y * 2 - self.center_2x.y;

        self.columns
            .clone()
            // Find the first pixel that is inside the ellipse.
            .find(|x| {
                self.ellipse_contains
                    .contains(Point::new(*x * 2 - self.center_2x.x, scaled_y))
            })
            // Shorten the right side of the scanline by the same amount as the left side.
            .map(|x| Scanline::new(y, x..self.columns.end - (x - self.columns.start)))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{
        geometry::Size,
        primitives::{Circle, PointsIter},
    };

    #[test]
    fn matches_circles_points() {
        for diameter in 0..50 {
            let circle_points = Circle::new(Point::new(0, 0), diameter).points();

            let ellipse_points =
                Ellipse::new(Point::new(0, 0), Size::new(diameter, diameter)).points();

            assert!(circle_points.eq(ellipse_points), "diameter = {}", diameter);
        }
    }
}