use crate::{
draw_target::DrawTarget,
drawable::Drawable,
drawable::Pixel,
geometry::Dimensions,
geometry::Point,
geometry::Size,
pixelcolor::PixelColor,
primitives::{Primitive, ThickLineIterator},
style::PrimitiveStyle,
style::Styled,
transform::Transform,
};
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
pub struct Line {
pub start: Point,
pub end: Point,
}
impl Primitive for Line {}
impl Dimensions for Line {
fn top_left(&self) -> Point {
Point::new(self.start.x.min(self.end.x), self.start.y.min(self.end.y))
}
fn bottom_right(&self) -> Point {
self.top_left() + self.size()
}
fn size(&self) -> Size {
Size::from_bounding_box(self.start, self.end)
}
}
impl Line {
pub const fn new(start: Point, end: Point) -> Self {
Self { start, end }
}
}
impl Transform for Line {
fn translate(&self, by: Point) -> Self {
Self {
start: self.start + by,
end: self.end + by,
..*self
}
}
fn translate_mut(&mut self, by: Point) -> &mut Self {
self.start += by;
self.end += by;
self
}
}
impl<'a, C> IntoIterator for &'a Styled<Line, PrimitiveStyle<C>>
where
C: PixelColor,
{
type Item = Pixel<C>;
type IntoIter = StyledLineIterator<C>;
fn into_iter(self) -> Self::IntoIter {
StyledLineIterator {
style: self.style,
line_iter: ThickLineIterator::new(&self.primitive, self.style.stroke_width_i32()),
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct StyledLineIterator<C>
where
C: PixelColor,
{
style: PrimitiveStyle<C>,
line_iter: ThickLineIterator,
}
impl<C: PixelColor> Iterator for StyledLineIterator<C> {
type Item = Pixel<C>;
fn next(&mut self) -> Option<Self::Item> {
if self.style.stroke_width == 0 {
return None;
}
let stroke_color = self.style.stroke_color?;
self.line_iter
.next()
.map(|point| Pixel(point, stroke_color))
}
}
impl<'a, C: 'a> Drawable<C> for &Styled<Line, PrimitiveStyle<C>>
where
C: PixelColor,
{
fn draw<D: DrawTarget<C>>(self, display: &mut D) -> Result<(), D::Error> {
display.draw_line(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{drawable::Pixel, mock_display::MockDisplay, pixelcolor::BinaryColor};
fn test_expected_line(start: Point, end: Point, expected: &[(i32, i32)]) {
let line =
Line::new(start, end).into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1));
let mut expected_iter = expected.iter();
for Pixel(coord, _) in line.into_iter() {
match expected_iter.next() {
Some(point) => assert_eq!(coord, Point::from(*point)),
None => unreachable!(),
}
}
assert!(expected_iter.next().is_none())
}
#[test]
fn bounding_box() {
let start = Point::new(10, 10);
let end = Point::new(20, 20);
let line: Line = Line::new(start, end);
let backwards_line: Line = Line::new(end, start);
assert_eq!(line.top_left(), start);
assert_eq!(line.bottom_right(), end);
assert_eq!(line.size(), Size::new(10, 10));
assert_eq!(backwards_line.top_left(), start);
assert_eq!(backwards_line.bottom_right(), end);
assert_eq!(backwards_line.size(), Size::new(10, 10));
}
#[test]
fn draws_no_dot() {
let start = Point::new(10, 10);
let end = Point::new(10, 10);
let expected = [];
test_expected_line(start, end, &expected);
}
#[test]
fn no_stroke_width_no_line() {
let start = Point::new(2, 3);
let end = Point::new(3, 2);
let line =
Line::new(start, end).into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 0));
assert!(line.into_iter().eq(core::iter::empty()));
}
#[test]
fn draws_short_correctly() {
let start = Point::new(2, 3);
let end = Point::new(3, 2);
let expected = [(2, 3), (3, 2)];
test_expected_line(start, end, &expected);
}
#[test]
fn draws_octant_1_correctly() {
let start = Point::new(10, 10);
let end = Point::new(15, 13);
let expected = [(10, 10), (11, 11), (12, 11), (13, 12), (14, 12), (15, 13)];
test_expected_line(start, end, &expected);
}
#[test]
fn draws_octant_2_correctly() {
let start = Point::new(10, 10);
let end = Point::new(13, 15);
let expected = [(10, 10), (11, 11), (11, 12), (12, 13), (12, 14), (13, 15)];
test_expected_line(start, end, &expected);
}
#[test]
fn draws_octant_3_correctly() {
let start = Point::new(10, 10);
let end = Point::new(7, 15);
let expected = [(10, 10), (9, 11), (9, 12), (8, 13), (8, 14), (7, 15)];
test_expected_line(start, end, &expected);
}
#[test]
fn draws_octant_4_correctly() {
let start = Point::new(10, 10);
let end = Point::new(5, 13);
let expected = [(10, 10), (9, 11), (8, 11), (7, 12), (6, 12), (5, 13)];
test_expected_line(start, end, &expected);
}
#[test]
fn draws_octant_5_correctly() {
let start = Point::new(10, 10);
let end = Point::new(5, 7);
let expected = [(10, 10), (9, 9), (8, 9), (7, 8), (6, 8), (5, 7)];
test_expected_line(start, end, &expected);
}
#[test]
fn draws_octant_6_correctly() {
let start = Point::new(10, 10);
let end = Point::new(7, 5);
let expected = [(10, 10), (9, 9), (9, 8), (8, 7), (8, 6), (7, 5)];
test_expected_line(start, end, &expected);
}
#[test]
fn draws_octant_7_correctly() {
let start = Point::new(10, 10);
let end = Point::new(13, 5);
let expected = [(10, 10), (11, 9), (11, 8), (12, 7), (12, 6), (13, 5)];
test_expected_line(start, end, &expected);
}
#[test]
fn draws_octant_8_correctly() {
let start = Point::new(10, 10);
let end = Point::new(15, 7);
let expected = [(10, 10), (11, 9), (12, 9), (13, 8), (14, 8), (15, 7)];
test_expected_line(start, end, &expected);
}
#[test]
fn thick_line_octant_1() {
let mut display: MockDisplay<BinaryColor> = MockDisplay::new();
Line::new(Point::new(2, 2), Point::new(20, 8))
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 5))
.draw(&mut display)
.unwrap();
assert_eq!(
display,
MockDisplay::from_pattern(&[
" # ",
" ##### ",
" ######## ",
" ########### ",
" ############ ",
" ############ ",
" ############ ",
" ######## ",
" ##### ",
" ## ",
])
);
}
#[test]
fn thick_line_2px() {
let mut display: MockDisplay<BinaryColor> = MockDisplay::new();
Line::new(Point::new(2, 2), Point::new(10, 2))
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 2))
.draw(&mut display)
.unwrap();
Line::new(Point::new(2, 5), Point::new(2, 10))
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::Off, 2))
.draw(&mut display)
.unwrap();
assert_eq!(
display,
MockDisplay::from_pattern(&[
" ",
" ######### ",
" ######### ",
" ",
" ",
" .. ",
" .. ",
" .. ",
" .. ",
" .. ",
" .. ",
])
);
}
#[test]
fn diagonal() {
let mut display: MockDisplay<BinaryColor> = MockDisplay::new();
Line::new(Point::new(3, 2), Point::new(10, 9))
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 7))
.draw(&mut display)
.unwrap();
assert_eq!(
display,
MockDisplay::from_pattern(&[
" # ",
" ### ",
" ##### ",
" ####### ",
" ######### ",
" ######### ",
" ######### ",
" ######### ",
" ####### ",
" ##### ",
" ### ",
" # ",
])
);
}
#[test]
fn thick_line_3px() {
let mut display: MockDisplay<BinaryColor> = MockDisplay::new();
Line::new(Point::new(2, 2), Point::new(10, 2))
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 3))
.draw(&mut display)
.unwrap();
Line::new(Point::new(2, 5), Point::new(2, 10))
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::Off, 3))
.draw(&mut display)
.unwrap();
assert_eq!(
display,
MockDisplay::from_pattern(&[
" ",
" ######### ",
" ######### ",
" ######### ",
" ",
" ... ",
" ... ",
" ... ",
" ... ",
" ... ",
" ... ",
])
);
}
#[test]
fn event_width_offset() {
let mut display: MockDisplay<BinaryColor> = MockDisplay::new();
Line::new(Point::new(2, 3), Point::new(10, 3))
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 4))
.draw(&mut display)
.unwrap();
Line::new(Point::new(2, 9), Point::new(10, 8))
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 4))
.draw(&mut display)
.unwrap();
assert_eq!(
display,
MockDisplay::from_pattern(&[
" ",
" ######### ",
" ######### ",
" ######### ",
" ######### ",
" ",
" #### ",
" ######### ",
" ######### ",
" ######### ",
" ##### ",
])
);
}
}