1 | use crate::{ |
2 | geometry::Point, |
3 | primitives::{ |
4 | common::Scanline, |
5 | rounded_rectangle::{RoundedRectangle, RoundedRectangleContains}, |
6 | ContainsPoint, |
7 | }, |
8 | }; |
9 | |
10 | /// Iterator over all points inside the rounded rectangle. |
11 | #[derive (Clone, Eq, PartialEq, Hash, Debug)] |
12 | #[cfg_attr (feature = "defmt" , derive(::defmt::Format))] |
13 | pub struct Points { |
14 | scanlines: Scanlines, |
15 | current_scanline: Scanline, |
16 | } |
17 | |
18 | impl Points { |
19 | pub(in crate::primitives) fn new(rounded_rectangle: &RoundedRectangle) -> Self { |
20 | Self { |
21 | scanlines: Scanlines::new(rounded_rectangle), |
22 | current_scanline: Scanline::new_empty(0), |
23 | } |
24 | } |
25 | } |
26 | |
27 | impl Iterator for Points { |
28 | type Item = Point; |
29 | |
30 | fn next(&mut self) -> Option<Self::Item> { |
31 | self.current_scanline.next().or_else(|| { |
32 | self.current_scanline = self.scanlines.next()?; |
33 | self.current_scanline.next() |
34 | }) |
35 | } |
36 | } |
37 | |
38 | #[derive (Clone, Eq, PartialEq, Hash, Debug)] |
39 | #[cfg_attr (feature = "defmt" , derive(::defmt::Format))] |
40 | pub struct Scanlines { |
41 | rounded_rectangle: RoundedRectangleContains, |
42 | } |
43 | |
44 | impl Scanlines { |
45 | pub fn new(rounded_rectangle: &RoundedRectangle) -> Self { |
46 | Self { |
47 | rounded_rectangle: RoundedRectangleContains::new(rounded_rectangle), |
48 | } |
49 | } |
50 | } |
51 | |
52 | impl Iterator for Scanlines { |
53 | type Item = Scanline; |
54 | |
55 | fn next(&mut self) -> Option<Self::Item> { |
56 | let columns = self.rounded_rectangle.columns.clone(); |
57 | let y = self.rounded_rectangle.rows.next()?; |
58 | |
59 | let x_start = if y < self.rounded_rectangle.straight_rows_left.start { |
60 | columns |
61 | .clone() |
62 | .find(|x| self.rounded_rectangle.top_left.contains(Point::new(*x, y))) |
63 | } else if y >= self.rounded_rectangle.straight_rows_left.end { |
64 | columns.clone().find(|x| { |
65 | self.rounded_rectangle |
66 | .bottom_left |
67 | .contains(Point::new(*x, y)) |
68 | }) |
69 | } else { |
70 | None |
71 | } |
72 | .unwrap_or(columns.start); |
73 | |
74 | let x_end = if y < self.rounded_rectangle.straight_rows_right.start { |
75 | columns |
76 | .clone() |
77 | .rfind(|x| self.rounded_rectangle.top_right.contains(Point::new(*x, y))) |
78 | } else if y >= self.rounded_rectangle.straight_rows_right.end { |
79 | columns.clone().rfind(|x| { |
80 | self.rounded_rectangle |
81 | .bottom_right |
82 | .contains(Point::new(*x, y)) |
83 | }) |
84 | } else { |
85 | None |
86 | } |
87 | .map(|x| x + 1) |
88 | .unwrap_or(columns.end); |
89 | |
90 | Some(Scanline::new(y, x_start..x_end)) |
91 | } |
92 | } |
93 | |
94 | #[cfg (test)] |
95 | mod tests { |
96 | use super::*; |
97 | use crate::{ |
98 | geometry::Size, |
99 | mock_display::MockDisplay, |
100 | pixelcolor::BinaryColor, |
101 | primitives::{PointsIter, Primitive, PrimitiveStyle, Rectangle}, |
102 | Drawable, |
103 | }; |
104 | |
105 | #[test ] |
106 | fn points_equals_filled() { |
107 | let rounded_rect = RoundedRectangle::with_equal_corners( |
108 | Rectangle::new(Point::zero(), Size::new(10, 20)), |
109 | Size::new(4, 8), |
110 | ); |
111 | |
112 | let mut expected = MockDisplay::new(); |
113 | rounded_rect |
114 | .into_styled(PrimitiveStyle::with_fill(BinaryColor::On)) |
115 | .draw(&mut expected) |
116 | .unwrap(); |
117 | |
118 | MockDisplay::from_points(rounded_rect.points(), BinaryColor::On).assert_eq(&expected); |
119 | } |
120 | } |
121 | |