use std::{ convert::{From, Into, TryFrom}, ops::Sub, }; #[derive(Debug)] pub struct Pixmap { pub width: u32, pub height: u32, pub data: Vec, } #[derive(Copy, Clone, Debug)] pub struct MapPoint { pub x: u32, pub y: u32, } impl From<(u32, u32)> for MapPoint { fn from((x, y): (u32, u32)) -> MapPoint { MapPoint { x, y } } } impl TryFrom<(i32, i32)> for MapPoint { type Error = (); fn try_from((x, y): (i32, i32)) -> Result { if x < 0 || y < 0 { return Err(()); } else { Ok(MapPoint { x: x as u32, y: y as u32, }) } } } impl TryFrom<(i64, i64)> for MapPoint { type Error = (); fn try_from((x, y): (i64, i64)) -> Result { if x < 0 || y < 0 { return Err(()); } else { Ok(MapPoint { x: x as u32, y: y as u32, }) } } } impl Pixmap where T: Copy + Clone + Default, { pub fn new(width: u32, height: u32) -> Self { let data = vec![Default::default(); (width * height) as usize]; Pixmap { width, height, data, } } pub fn new_with(width: u32, height: u32, start: T) -> Self { let data = vec![start; (width * height) as usize]; Pixmap { width, height, data, } } pub fn is_inside>(&self, pt: P) -> bool { let MapPoint { x, y } = pt.into(); x < self.width && y < self.height } pub fn idx>(&self, pt: P) -> usize { let MapPoint { x, y } = pt.into(); (y * self.width + x) as usize } pub fn get>(&self, pt: P) -> T { let idx = self.idx(pt); self.data[idx] } pub fn set>(&mut self, pt: P, new_val: T) -> T { let pt = pt.into(); let old_val = self.get(pt); let idx = self.idx(pt); self.data[idx] = new_val; old_val } pub fn get_circle>(&self, center: P, radius: u32) -> Vec { let mut circle: Vec<(i64, i64)> = vec![]; let MapPoint { x, y } = center.into(); let x = x as i64; let y = y as i64; let (mut dx, mut dy, mut err) = (radius as i64, 0i64, 1 - radius as i64); while dx >= dy { circle.push((x + dx, y + dy)); circle.push((x - dx, y + dy)); circle.push((x + dx, y - dy)); circle.push((x - dx, y - dy)); circle.push((x + dy, y + dx)); circle.push((x - dy, y + dx)); circle.push((x + dy, y - dx)); circle.push((x - dy, y - dx)); dy = dy + 1; if err < 0 { err = err + 2 * dy + 1; } else { dx -= 1; err += 2 * (dy - dx) + 1; } } for xi in 0..radius as i64 { for yi in 0..radius as i64 { if xi.pow(2) + yi.pow(2) < (radius as i64).pow(2) { circle.push((x + xi, y + yi)); circle.push((x - xi, y + yi)); circle.push((x + xi, y - yi)); circle.push((x - xi, y - yi)); } } } circle .into_iter() .flat_map(|pt| MapPoint::try_from(pt)) .filter(|&pt| self.is_inside(pt)) .collect() } pub fn get_line>(&self, start: P, end: P) -> Vec { let MapPoint { x: x1, y: y1 } = start.into(); let MapPoint { x: x2, y: y2 } = end.into(); let mut coordinates = vec![]; let dx = abs_difference(x1, x2) as i64; let dy = abs_difference(y1, y2) as i64; let sx = { if x1 < x2 { 1 } else { -1 } }; let sy = { if y1 < y2 { 1 } else { -1 } }; let mut err: i64 = dx - dy; let mut current_x = x1 as i64; let mut current_y = y1 as i64; loop { coordinates.push((current_x, current_y)); if current_x == x2 as i64 && current_y == y2 as i64 { break; } let e2 = 2 * err; if e2 > -dy { err -= dy; current_x += sx; } if e2 < dx { err += dx; current_y += sy; } } coordinates .into_iter() .flat_map(|pt| MapPoint::try_from(pt)) .filter(|&pt| self.is_inside(pt)) .collect() } } fn abs_difference + Ord>(x: T, y: T) -> T { if x < y { y - x } else { x - y } }