use super::super::drawable::{Drawable, Pixel};
use super::super::transform::Transform;
use crate::geometry::{Dimensions, Point, Size};
use crate::pixelcolor::PixelColor;
use crate::primitives::Primitive;
use crate::style::Style;
use crate::style::WithStyle;
#[derive(Debug, Clone, Copy)]
pub struct Rectangle<C: PixelColor> {
pub top_left: Point,
pub bottom_right: Point,
pub style: Style<C>,
}
impl<C> Primitive for Rectangle<C> where C: PixelColor {}
impl<C> Dimensions for Rectangle<C>
where
C: PixelColor,
{
fn top_left(&self) -> Point {
self.top_left
}
fn bottom_right(&self) -> Point {
self.bottom_right
}
fn size(&self) -> Size {
Size::from_bounding_box(self.top_left, self.bottom_right)
}
}
impl<C> Rectangle<C>
where
C: PixelColor,
{
pub fn new(top_left: Point, bottom_right: Point) -> Self {
Rectangle {
top_left,
bottom_right,
style: Style::default(),
}
}
}
impl<C> WithStyle<C> for Rectangle<C>
where
C: PixelColor,
{
fn style(mut self, style: Style<C>) -> Self {
self.style = style;
self
}
fn stroke(mut self, color: Option<C>) -> Self {
self.style.stroke_color = color;
self
}
fn stroke_width(mut self, width: u8) -> Self {
self.style.stroke_width = width;
self
}
fn fill(mut self, color: Option<C>) -> Self {
self.style.fill_color = color;
self
}
}
impl<C> IntoIterator for Rectangle<C>
where
C: PixelColor,
{
type Item = Pixel<C>;
type IntoIter = RectangleIterator<C>;
fn into_iter(self) -> Self::IntoIter {
(&self).into_iter()
}
}
impl<'a, C> IntoIterator for &'a Rectangle<C>
where
C: PixelColor,
{
type Item = Pixel<C>;
type IntoIter = RectangleIterator<C>;
fn into_iter(self) -> Self::IntoIter {
RectangleIterator {
top_left: self.top_left,
bottom_right: self.bottom_right,
style: self.style,
p: self.top_left,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct RectangleIterator<C: PixelColor>
where
C: PixelColor,
{
top_left: Point,
bottom_right: Point,
style: Style<C>,
p: Point,
}
impl<C> Iterator for RectangleIterator<C>
where
C: PixelColor,
{
type Item = Pixel<C>;
fn next(&mut self) -> Option<Self::Item> {
if self.style.stroke_color.is_none() && self.style.fill_color.is_none() {
return None;
}
loop {
let mut out = None;
if self.p.y > self.bottom_right.y {
break None;
}
let border_width = self.style.stroke_width as i32;
let tl = self.top_left;
let br = self.bottom_right;
if (
(self.p.y >= tl.y && self.p.y < tl.y + border_width)
|| (self.p.y <= br.y && self.p.y > br.y - border_width)
|| (self.p.x >= tl.x && self.p.x < tl.x + border_width)
|| (self.p.x <= br.x && self.p.x > br.x - border_width)
) && self.style.stroke_color.is_some()
{
out = Some(Pixel(
self.p,
self.style.stroke_color.expect("Expected stroke"),
));
}
else if let Some(fill) = self.style.fill_color {
out = Some(Pixel(self.p, fill));
}
self.p.x += 1;
if self.p.x > self.bottom_right.x {
self.p.x = self.top_left.x;
self.p.y += 1;
}
if out.is_some() {
break out;
}
}
}
}
impl<C> Drawable for Rectangle<C> where C: PixelColor {}
impl<C> Transform for Rectangle<C>
where
C: PixelColor,
{
fn translate(&self, by: Point) -> Self {
Self {
top_left: self.top_left + by,
bottom_right: self.bottom_right + by,
..self.clone()
}
}
fn translate_mut(&mut self, by: Point) -> &mut Self {
self.top_left += by;
self.bottom_right += by;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pixelcolor::BinaryColor;
use crate::pixelcolor::{Rgb565, RgbColor};
#[test]
fn dimensions() {
let rect: Rectangle<BinaryColor> = Rectangle::new(Point::new(5, 10), Point::new(15, 30));
let moved = rect.translate(Point::new(-10, -20));
assert_eq!(rect.top_left(), Point::new(5, 10));
assert_eq!(rect.bottom_right(), Point::new(15, 30));
assert_eq!(rect.size(), Size::new(10, 20));
assert_eq!(moved.top_left(), Point::new(-5, -10));
assert_eq!(moved.bottom_right(), Point::new(5, 10));
assert_eq!(moved.size(), Size::new(10, 20));
}
#[test]
fn it_can_be_translated() {
let rect: Rectangle<BinaryColor> = Rectangle::new(Point::new(5, 10), Point::new(15, 20));
let moved = rect.translate(Point::new(10, 15));
assert_eq!(moved.top_left, Point::new(15, 25));
assert_eq!(moved.bottom_right, Point::new(25, 35));
}
#[test]
fn it_draws_unfilled_rect() {
let mut rect: RectangleIterator<Rgb565> =
Rectangle::new(Point::new(2, 2), Point::new(4, 4))
.style(Style::stroke(Rgb565::RED))
.into_iter();
assert_eq!(rect.next(), Some(Pixel(Point::new(2, 2), Rgb565::RED)));
assert_eq!(rect.next(), Some(Pixel(Point::new(3, 2), Rgb565::RED)));
assert_eq!(rect.next(), Some(Pixel(Point::new(4, 2), Rgb565::RED)));
assert_eq!(rect.next(), Some(Pixel(Point::new(2, 3), Rgb565::RED)));
assert_eq!(rect.next(), Some(Pixel(Point::new(4, 3), Rgb565::RED)));
assert_eq!(rect.next(), Some(Pixel(Point::new(2, 4), Rgb565::RED)));
assert_eq!(rect.next(), Some(Pixel(Point::new(3, 4), Rgb565::RED)));
assert_eq!(rect.next(), Some(Pixel(Point::new(4, 4), Rgb565::RED)));
}
#[test]
fn it_can_be_negative() {
let negative: RectangleIterator<Rgb565> =
Rectangle::new(Point::new(-2, -2), Point::new(2, 2))
.fill(Some(Rgb565::GREEN))
.into_iter();
let positive: RectangleIterator<Rgb565> =
Rectangle::new(Point::new(2, 2), Point::new(6, 6))
.fill(Some(Rgb565::GREEN))
.into_iter();
assert!(negative.eq(positive.map(|Pixel(p, c)| Pixel(p - Point::new(4, 4), c))));
}
}