use crate::{
geometry::{Dimensions, Point, Size},
primitives::{PointsIter, Primitive, Rectangle},
transform::Transform,
};
mod points;
pub(in crate::primitives) mod scanline_intersections;
mod scanline_iterator;
mod styled;
pub use points::Points;
pub use styled::StyledPixelsIterator;
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
pub struct Polyline<'a> {
pub translate: Point,
pub vertices: &'a [Point],
}
impl<'a> Polyline<'a> {
pub const fn new(vertices: &'a [Point]) -> Self {
Self {
vertices,
translate: Point::zero(),
}
}
}
impl<'a> Primitive for Polyline<'a> {}
impl<'a> PointsIter for Polyline<'a> {
type Iter = Points<'a>;
fn points(&self) -> Self::Iter {
Points::new(self)
}
}
impl<'a> Dimensions for Polyline<'a> {
fn bounding_box(&self) -> Rectangle {
match self.vertices {
[] => Rectangle::zero(),
[v] => Rectangle::new(*v, Size::zero()),
vertices => {
let top_left = vertices
.iter()
.map(|v| *v + self.translate)
.fold(Point::new(core::i32::MAX, core::i32::MAX), |accum, v| {
Point::new(accum.x.min(v.x), accum.y.min(v.y))
});
let bottom_right = vertices
.iter()
.map(|v| *v + self.translate)
.fold(Point::new(core::i32::MIN, core::i32::MIN), |accum, v| {
Point::new(accum.x.max(v.x), accum.y.max(v.y))
});
Rectangle::with_corners(top_left, bottom_right)
}
}
}
}
impl<'a> Transform for Polyline<'a> {
fn translate(&self, by: Point) -> Self {
Self {
translate: self.translate + by,
..*self
}
}
fn translate_mut(&mut self, by: Point) -> &mut Self {
self.translate += by;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::geometry::{Point, Size};
pub(in crate::primitives::polyline) const HEARTBEAT: [Point; 10] = [
Point::new(10, 64),
Point::new(50, 64),
Point::new(60, 44),
Point::new(70, 64),
Point::new(80, 64),
Point::new(90, 74),
Point::new(100, 10),
Point::new(110, 84),
Point::new(120, 64),
Point::new(300, 64),
];
pub(in crate::primitives::polyline) const SMALL: [Point; 4] = [
Point::new(2, 5),
Point::new(5, 2),
Point::new(10, 5),
Point::new(15, 2),
];
#[test]
fn special_case_dimensions() {
assert_eq!(Polyline::new(&[]).bounding_box(), Rectangle::zero(),);
assert_eq!(
Polyline::new(&[Point::new(15, 17)]).bounding_box(),
Rectangle::new(Point::new(15, 17), Size::zero())
);
}
#[test]
fn positive_dimensions() {
let polyline = Polyline::new(&HEARTBEAT);
let bb = polyline.bounding_box();
assert_eq!(
bb,
Rectangle::with_corners(Point::new(10, 10), Point::new(300, 84))
);
}
#[test]
fn negative_dimensions() {
let mut negative: [Point; 10] = [Point::zero(); 10];
for (i, v) in HEARTBEAT.iter().enumerate() {
negative[i] = *v - Point::new(100, 100);
}
let polyline = Polyline::new(&negative);
let bb = polyline.bounding_box();
assert_eq!(
bb,
Rectangle::with_corners(Point::new(-90, -90), Point::new(200, -16))
);
}
#[test]
fn transformed_dimensions() {
let polyline = Polyline::new(&HEARTBEAT).translate(Point::new(-100, -100));
let bb = polyline.bounding_box();
assert_eq!(
bb,
Rectangle::with_corners(Point::new(-90, -90), Point::new(200, -16))
);
}
#[test]
fn translate_does_not_modify_size() {
let points = [
Point::new(5, 10),
Point::new(7, 7),
Point::new(5, 8),
Point::new(10, 10),
];
let polyline = Polyline::new(&points);
let moved = polyline.translate(Point::new(10, 12));
assert_eq!(moved.bounding_box().size, polyline.bounding_box().size);
}
#[test]
fn translate_translated() {
let points = [
Point::new(5, 10),
Point::new(7, 7),
Point::new(5, 8),
Point::new(10, 10),
];
let polyline = Polyline::new(&points);
let moved = polyline.translate(Point::new(10, 12));
let moved2 = moved.translate(Point::new(10, 12));
assert_eq!(
moved.bounding_box(),
polyline.bounding_box().translate(Point::new(10, 12))
);
assert_eq!(
moved2.bounding_box(),
polyline.bounding_box().translate(Point::new(20, 24))
);
}
}