use super::{Drawable, hspan, vspan};
use crate::CoordinateI32;
use crate::image::ImageViewMut;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Crosshair<P> {
pub center: CoordinateI32,
pub arm_length: u32,
pub color: P,
}
impl<P: Copy> Drawable<P> for Crosshair<P> {
fn draw_into(&self, image: &mut impl ImageViewMut<Pixel = P>) {
let (cx, cy) = (i64::from(self.center.x), i64::from(self.center.y));
let arm = i64::from(self.arm_length);
hspan(image, cx - arm, cx + arm, cy, self.color);
vspan(image, cx, cy - arm, cy + arm, self.color);
}
}
pub fn draw_crosshair<P: Copy>(
image: &mut impl ImageViewMut<Pixel = P>,
center: impl Into<CoordinateI32>,
arm_length: u32,
color: P,
) {
Crosshair {
center: center.into(),
arm_length,
color,
}
.draw_into(image);
}
#[cfg(test)]
mod tests {
use super::super::tests::inked;
use super::*;
use crate::image::Image;
use crate::pixel::Mono8;
fn ink() -> Mono8 {
Mono8::new(255)
}
#[test]
fn touches_exactly_the_two_arms() {
let mut image: Image<Mono8> = Image::zero(9, 9);
draw_crosshair(&mut image, (4, 4), 3, ink());
let drawn = inked(&image);
assert_eq!(drawn.len(), 13); for (x, y) in drawn {
assert!(
(y == 4 && (1..=7).contains(&x)) || (x == 4 && (1..=7).contains(&y)),
"({x}, {y}) off both arms"
);
}
}
#[test]
fn arm_length_zero_is_the_center_pixel() {
let mut image: Image<Mono8> = Image::zero(5, 5);
draw_crosshair(&mut image, (2, 2), 0, ink());
assert_eq!(inked(&image), vec![(2, 2)]);
}
#[test]
fn clips_at_the_image_corner() {
let mut image: Image<Mono8> = Image::zero(5, 5);
draw_crosshair(&mut image, (0, 0), 2, ink());
assert_eq!(inked(&image), vec![(0, 0), (1, 0), (2, 0), (0, 1), (0, 2)]);
}
#[test]
fn off_image_center_shows_one_arm() {
let mut image: Image<Mono8> = Image::zero(5, 5);
draw_crosshair(&mut image, (2, -2), 3, ink());
assert_eq!(inked(&image), vec![(2, 0), (2, 1)]);
}
}