Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Triangles #86

Merged
merged 18 commits into from
May 5, 2019
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@

.vscode/
/target/
**/*.rs.bk
Cargo.lock
Expand Down
184 changes: 78 additions & 106 deletions embedded-graphics/src/primitives/line.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,121 +90,84 @@ where

impl<'a, C: PixelColor> IntoIterator for &'a Line<C> {
type Item = Pixel<C>;
type IntoIter = LineIterator<'a, C>;
type IntoIter = LineIterator<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);

// Find out if our line is steep or shallow
let is_steep = (y1 - y0).abs() > (x1 - x0).abs();

// Determine if endpoints should be switched
// based on the "quick" direction
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)
}
};

// Setup our pre-calculated values
let (dquick, mut dslow) = if is_steep {
(y1 - y0, x1 - x0)
} else {
(x1 - x0, y1 - y0)
};
let mut d = self.end - self.start;
if d[0] < 0 {
d = Coord::new(-d[0], d[1]);
}
if d[1] > 0 {
d = Coord::new(d[0], -d[1]);
}

// Determine how we should increment the slow direction
let increment = if dslow < 0 {
dslow = -dslow;
-1
} else {
1
let s = match (self.start[0] >= self.end[0],
self.start[1] >= self.end[1]) {
(false, false) => Coord::new(1, 1),
(false, true) => Coord::new(1, -1),
(true, false) => Coord::new(-1, 1),
(true, true) => Coord::new(-1, -1),
};

// Compute the default error
let error = 2 * dslow - dquick;

// Set our inital quick & slow
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,
style: self.style,

start: self.start,
end: self.end,
jamwaffles marked this conversation as resolved.
Show resolved Hide resolved
d,
s,
err: d[0] + d[1],
e2: 0,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these single letters are from Bresenham's algorithm, no? Can you give d, s and e2 more descriptive names please?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be fixed now

stop: false,
}
}
}

/// Pixel iterator for each pixel in the line
#[derive(Debug)]
pub struct LineIterator<'a, C: 'a>
#[derive(Debug, Clone, Copy)]
pub struct LineIterator<C>
where
C: PixelColor,
{
line: &'a Line<C>,

dquick: i32,
dslow: i32,
increment: i32,
error: i32,
is_steep: bool,

quick: i32,
slow: i32,
end: i32,
style: Style<C>,

start: Coord,
end: Coord,
d: Coord,
s: Coord,
err: i32,
e2: i32,
stop: bool,
}

// [Bresenham's line algorithm](https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm)
impl<'a, C: PixelColor> Iterator for LineIterator<'a, C> {
impl<C: PixelColor> Iterator for LineIterator<C> {
type Item = Pixel<C>;

fn next(&mut self) -> Option<Self::Item> {
if self.quick > self.end {
return None;
}
self.style.stroke_color?;
jamwaffles marked this conversation as resolved.
Show resolved Hide resolved

// Get the next point
// let &Line { ref color, .. } = self.line;
let coord = if self.is_steep {
Coord::new(self.slow, self.quick)
} else {
Coord::new(self.quick, self.slow)
};
while !self.stop {
let p_coord = self.start;

// Update error and increment slow direction
if self.error > 0 {
self.slow = self.slow + self.increment;
self.error -= 2 * self.dquick;
if self.start == self.end {
self.stop = true;
}
self.e2 = 2 * self.err;
if self.e2 > self.d[1] {
self.err += self.d[1];
self.start += Coord::new(self.s[0], 0);
}
if self.e2 < self.d[0] {
self.err += self.d[0];
self.start += Coord::new(0, self.s[1]);
}
if p_coord[0] >= 0 && p_coord[1] >= 0 {
return Some(Pixel(p_coord.to_unsigned(),
self.style.stroke_color.unwrap()));
}
}
self.error += 2 * self.dslow;

// Increment fast direction
self.quick += 1;

// Return if there is a stroke on the line
self.line
.style
.stroke_color
.map(|color| Pixel(coord.to_unsigned(), color))
None
}
}

Expand Down Expand Up @@ -235,7 +198,7 @@ where
Self {
start: self.start + by,
end: self.end + by,
..self.clone()
..*self
}
}

Expand Down Expand Up @@ -296,6 +259,22 @@ mod tests {
assert_eq!(backwards_line.size(), UnsignedCoord::new(10, 10));
}

#[test]
fn draws_dot_correctly() {
let start = Coord::new(10, 10);
let end = Coord::new(10, 10);
let expected = [(10, 10)];
test_expected_line(start, end, &expected);
}

#[test]
fn draws_short_correctly() {
let start = Coord::new(2, 3);
let end = Coord::new(3, 2);
let expected = [(2, 3), (3, 2)];
test_expected_line(start, end, &expected);
}

#[test]
fn draws_octant_1_correctly() {
let start = Coord::new(10, 10);
Expand Down Expand Up @@ -324,54 +303,47 @@ mod tests {
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)];
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 = Coord::new(10, 10);
let end = Coord::new(5, 7);
let expected = [(5, 7), (6, 8), (7, 8), (8, 9), (9, 9), (10, 10)];
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 = Coord::new(10, 10);
let end = Coord::new(7, 5);
let expected = [(7, 5), (8, 6), (8, 7), (9, 8), (9, 9), (10, 10)];
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 = Coord::new(10, 10);
let end = Coord::new(13, 5);
let expected = [(13, 5), (12, 6), (12, 7), (11, 8), (11, 9), (10, 10)];
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 = 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(),
];
let expected = [(10, 10), (11, 9), (12, 9), (13, 8), (14, 8), (15, 7),];
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()];
let expected = [(0, 0), (1, 1), (2, 2)];
test_expected_line(start, end, &expected);
}
}
2 changes: 2 additions & 0 deletions embedded-graphics/src/primitives/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ use crate::drawable::Dimensions;
pub mod circle;
pub mod line;
pub mod rect;
pub mod triangle;

/// Primitive trait
pub trait Primitive: Dimensions {}

pub use self::circle::Circle;
pub use self::line::Line;
pub use self::rect::Rect;
pub use self::triangle::Triangle;
Loading