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},
},
};
#[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(|x| {
self.ellipse_contains
.contains(Point::new(*x * 2 - self.center_2x.x, scaled_y))
})
.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);
}
}
}