embedded_graphics/primitives/polyline/
mod.rs1use crate::{
4 geometry::{Dimensions, Point, Size},
5 primitives::{PointsIter, Primitive, Rectangle},
6 transform::Transform,
7};
8
9mod points;
10pub(in crate::primitives) mod scanline_intersections;
11mod scanline_iterator;
12mod styled;
13
14pub use points::Points;
15pub use styled::StyledPixelsIterator;
16
17#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
57#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
58pub struct Polyline<'a> {
59 pub translate: Point,
61
62 pub vertices: &'a [Point],
64}
65
66impl<'a> Polyline<'a> {
67 pub const fn new(vertices: &'a [Point]) -> Self {
71 Self {
72 vertices,
73 translate: Point::zero(),
74 }
75 }
76}
77
78impl<'a> Primitive for Polyline<'a> {}
79
80impl<'a> PointsIter for Polyline<'a> {
81 type Iter = Points<'a>;
82
83 fn points(&self) -> Self::Iter {
84 Points::new(self)
85 }
86}
87
88impl<'a> Dimensions for Polyline<'a> {
89 fn bounding_box(&self) -> Rectangle {
90 match self.vertices {
91 [] => Rectangle::zero(),
92 [v] => Rectangle::new(*v, Size::zero()),
93 vertices => {
94 let top_left = vertices
95 .iter()
96 .map(|v| *v + self.translate)
97 .fold(Point::new(core::i32::MAX, core::i32::MAX), |accum, v| {
98 Point::new(accum.x.min(v.x), accum.y.min(v.y))
99 });
100
101 let bottom_right = vertices
102 .iter()
103 .map(|v| *v + self.translate)
104 .fold(Point::new(core::i32::MIN, core::i32::MIN), |accum, v| {
105 Point::new(accum.x.max(v.x), accum.y.max(v.y))
106 });
107
108 Rectangle::with_corners(top_left, bottom_right)
109 }
110 }
111 }
112}
113
114impl<'a> Transform for Polyline<'a> {
115 fn translate(&self, by: Point) -> Self {
135 Self {
136 translate: self.translate + by,
137 ..*self
138 }
139 }
140
141 fn translate_mut(&mut self, by: Point) -> &mut Self {
160 self.translate += by;
161
162 self
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169 use crate::geometry::{Point, Size};
170
171 pub(in crate::primitives::polyline) const HEARTBEAT: [Point; 10] = [
173 Point::new(10, 64),
174 Point::new(50, 64),
175 Point::new(60, 44),
176 Point::new(70, 64),
177 Point::new(80, 64),
178 Point::new(90, 74),
179 Point::new(100, 10),
180 Point::new(110, 84),
181 Point::new(120, 64),
182 Point::new(300, 64),
183 ];
184
185 pub(in crate::primitives::polyline) const SMALL: [Point; 4] = [
187 Point::new(2, 5),
188 Point::new(5, 2),
189 Point::new(10, 5),
190 Point::new(15, 2),
191 ];
192
193 #[test]
194 fn special_case_dimensions() {
195 assert_eq!(Polyline::new(&[]).bounding_box(), Rectangle::zero(),);
196
197 assert_eq!(
198 Polyline::new(&[Point::new(15, 17)]).bounding_box(),
199 Rectangle::new(Point::new(15, 17), Size::zero())
200 );
201 }
202
203 #[test]
204 fn positive_dimensions() {
205 let polyline = Polyline::new(&HEARTBEAT);
206
207 let bb = polyline.bounding_box();
208
209 assert_eq!(
210 bb,
211 Rectangle::with_corners(Point::new(10, 10), Point::new(300, 84))
212 );
213 }
214
215 #[test]
216 fn negative_dimensions() {
217 let mut negative: [Point; 10] = [Point::zero(); 10];
218
219 for (i, v) in HEARTBEAT.iter().enumerate() {
220 negative[i] = *v - Point::new(100, 100);
221 }
222
223 let polyline = Polyline::new(&negative);
224
225 let bb = polyline.bounding_box();
226
227 assert_eq!(
228 bb,
229 Rectangle::with_corners(Point::new(-90, -90), Point::new(200, -16))
230 );
231 }
232
233 #[test]
234 fn transformed_dimensions() {
235 let polyline = Polyline::new(&HEARTBEAT).translate(Point::new(-100, -100));
236
237 let bb = polyline.bounding_box();
238
239 assert_eq!(
240 bb,
241 Rectangle::with_corners(Point::new(-90, -90), Point::new(200, -16))
242 );
243 }
244
245 #[test]
246 fn translate_does_not_modify_size() {
247 let points = [
248 Point::new(5, 10),
249 Point::new(7, 7),
250 Point::new(5, 8),
251 Point::new(10, 10),
252 ];
253
254 let polyline = Polyline::new(&points);
255 let moved = polyline.translate(Point::new(10, 12));
256
257 assert_eq!(moved.bounding_box().size, polyline.bounding_box().size);
258 }
259
260 #[test]
261 fn translate_translated() {
262 let points = [
263 Point::new(5, 10),
264 Point::new(7, 7),
265 Point::new(5, 8),
266 Point::new(10, 10),
267 ];
268
269 let polyline = Polyline::new(&points);
270 let moved = polyline.translate(Point::new(10, 12));
271 let moved2 = moved.translate(Point::new(10, 12));
272
273 assert_eq!(
274 moved.bounding_box(),
275 polyline.bounding_box().translate(Point::new(10, 12))
276 );
277 assert_eq!(
278 moved2.bounding_box(),
279 polyline.bounding_box().translate(Point::new(20, 24))
280 );
281 }
282}