use super::super::drawable::*;
use super::super::transform::*;
use coord::{Coord, ToUnsigned};
use pixelcolor::PixelColor;
use primitives::Primitive;
use style::Style;
use style::WithStyle;
use unsignedcoord::{ToSigned, UnsignedCoord};
#[derive(Debug, Copy, Clone)]
pub struct Line<C: PixelColor> {
pub start: Coord,
pub end: Coord,
pub style: Style<C>,
}
impl<C> Primitive for Line<C> where C: PixelColor {}
impl<C> Dimensions for Line<C>
where
C: PixelColor,
{
fn top_left(&self) -> Coord {
Coord::new(
self.start[1].min(self.end[0]),
self.start[1].min(self.end[1]),
)
}
fn bottom_right(&self) -> Coord {
self.top_left() + self.size().to_signed()
}
fn size(&self) -> UnsignedCoord {
(self.end - self.start).abs().to_unsigned()
}
}
impl<C> Line<C>
where
C: PixelColor,
{
pub fn new(start: Coord, end: Coord) -> Self {
Line {
start,
end,
style: Style::default(),
}
}
}
impl<C> WithStyle<C> for Line<C>
where
C: PixelColor,
{
fn with_style(mut self, style: Style<C>) -> Self {
self.style = style;
self
}
fn with_stroke(mut self, color: Option<C>) -> Self {
self.style.stroke_color = color;
self
}
fn with_stroke_width(mut self, width: u8) -> Self {
self.style.stroke_width = width;
self
}
fn with_fill(mut self, color: Option<C>) -> Self {
self.style.fill_color = color;
self
}
}
impl<'a, C: PixelColor> IntoIterator for &'a Line<C> {
type Item = Pixel<C>;
type IntoIter = LineIterator<'a, C>;
fn into_iter(self) -> Self::IntoIter {
let x0 = self.start[0].max(0);
let y0 = self.start[1].max(0);
let x1 = self.end[0].max(0);
let y1 = self.end[1].max(0);
let is_steep = (y1 - y0).abs() > (x1 - x0).abs();
let (x0, y0, x1, y1) = if is_steep {
if y0 > y1 {
(x1, y1, x0, y0)
} else {
(x0, y0, x1, y1)
}
} else {
if x0 > x1 {
(x1, y1, x0, y0)
} else {
(x0, y0, x1, y1)
}
};
let (dquick, mut dslow) = if is_steep {
(y1 - y0, x1 - x0)
} else {
(x1 - x0, y1 - y0)
};
let increment = if dslow < 0 {
dslow = -dslow;
-1
} else {
1
};
let error = 2 * dslow - dquick;
let (quick, slow, end) = if is_steep { (y0, x0, y1) } else { (x0, y0, x1) };
LineIterator {
line: self,
is_steep,
dquick,
dslow,
increment,
error,
quick,
slow,
end,
}
}
}
#[derive(Debug)]
pub struct LineIterator<'a, C: 'a>
where
C: PixelColor,
{
line: &'a Line<C>,
dquick: i32,
dslow: i32,
increment: i32,
error: i32,
is_steep: bool,
quick: i32,
slow: i32,
end: i32,
}
impl<'a, C: PixelColor> Iterator for LineIterator<'a, C> {
type Item = Pixel<C>;
fn next(&mut self) -> Option<Self::Item> {
if self.quick > self.end {
return None;
}
let coord = if self.is_steep {
Coord::new(self.slow, self.quick)
} else {
Coord::new(self.quick, self.slow)
};
if self.error > 0 {
self.slow = self.slow + self.increment;
self.error -= 2 * self.dquick;
}
self.error += 2 * self.dslow;
self.quick += 1;
self.line
.style
.stroke_color
.map(|color| Pixel(coord.to_unsigned(), color))
}
}
impl<C> Drawable for Line<C> where C: PixelColor {}
impl<C> Transform for Line<C>
where
C: PixelColor,
{
fn translate(&self, by: Coord) -> Self {
Self {
start: self.start + by,
end: self.end + by,
..self.clone()
}
}
fn translate_mut(&mut self, by: Coord) -> &mut Self {
self.start += by;
self.end += by;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use dev::TestPixelColor;
use drawable::Pixel;
use pixelcolor::PixelColorU8;
use style::Style;
use unsignedcoord::UnsignedCoord;
fn test_expected_line(start: Coord, end: Coord, expected: &[(u32, u32)]) {
let line = Line::new(start, end).with_style(Style::with_stroke(PixelColorU8(1)));
for (idx, Pixel(coord, _)) in line.into_iter().enumerate() {
assert_eq!(coord, UnsignedCoord::new(expected[idx].0, expected[idx].1));
}
}
#[test]
fn bounding_box() {
let start = Coord::new(10, 10);
let end = Coord::new(20, 20);
let line: Line<TestPixelColor> = Line::new(start, end);
let backwards_line: Line<TestPixelColor> = Line::new(end, start);
assert_eq!(line.top_left(), start);
assert_eq!(line.bottom_right(), end);
assert_eq!(line.size(), UnsignedCoord::new(10, 10));
assert_eq!(backwards_line.top_left(), start);
assert_eq!(backwards_line.bottom_right(), end);
assert_eq!(backwards_line.size(), UnsignedCoord::new(10, 10));
}
#[test]
fn draws_octant_1_correctly() {
let start = Coord::new(10, 10);
let end = Coord::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 = Coord::new(10, 10);
let end = Coord::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 = Coord::new(10, 10);
let end = Coord::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 = Coord::new(10, 10);
let end = Coord::new(5, 13);
let expected = [(5, 13), (6, 12), (7, 12), (8, 11), (9, 11), (10, 10)];
test_expected_line(start, end, &expected);
}
#[test]
fn draws_octant_5_correctly() {
let start = Coord::new(10, 10);
let end = Coord::new(5, 7);
let expected = [(5, 7), (6, 8), (7, 8), (8, 9), (9, 9), (10, 10)];
test_expected_line(start, end, &expected);
}
#[test]
fn draws_octant_6_correctly() {
let start = Coord::new(10, 10);
let end = Coord::new(7, 5);
let expected = [(7, 5), (8, 6), (8, 7), (9, 8), (9, 9), (10, 10)];
test_expected_line(start, end, &expected);
}
#[test]
fn draws_octant_7_correctly() {
let start = Coord::new(10, 10);
let end = Coord::new(13, 5);
let expected = [(13, 5), (12, 6), (12, 7), (11, 8), (11, 9), (10, 10)];
test_expected_line(start, end, &expected);
}
#[test]
fn draws_octant_8_correctly() {
let start = Coord::new(10, 10);
let end = Coord::new(15, 7);
let expected = [
(10, 10).into(),
(11, 9).into(),
(12, 9).into(),
(13, 8).into(),
(14, 8).into(),
(15, 7).into(),
];
test_expected_line(start, end, &expected);
}
#[test]
fn it_truncates_lines_out_of_bounds() {
let start = Coord::new(-2, -2);
let end = Coord::new(2, 2);
let expected = [(0, 0).into(), (1, 1).into(), (2, 2).into()];
test_expected_line(start, end, &expected);
}
}