#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, PartialOrd)] pub struct Point { pub x: i32, pub y: i32, } impl Point { pub fn distance(&self, p: &Point) -> f64 { if self.x == p.x { return (p.y - self.y).abs().into(); } if self.y == p.y { return (p.x - self.x).abs().into(); } let x_distance = p.x - self.x; let y_distance = p.y - self.y; return f64::from(x_distance.pow(2) + y_distance.pow(2)).sqrt(); } pub(crate) fn new(positions: (i32, i32)) -> Self { Self { x: positions.0, y: positions.1, } } pub(crate) fn movement(&self, towards: Point, distance: u16) -> Point { let dst = self.distance(&towards); if dst == distance as f64 { return towards; } let ratio = distance as f64 / dst; // info!("X delta : {}", towards.x - self.x); // info!("Y delta : {}", towards.y - self.y); Point { x: ((towards.x - self.x) as f64 * ratio + self.x as f64).round() as i32, y: ((towards.y - self.y) as f64 * ratio + self.y as f64).round() as i32, } } } impl Ord for Point { fn cmp(&self, other: &Self) -> std::cmp::Ordering { if self.x > other.x { return std::cmp::Ordering::Greater; } if self.x < other.x { return std::cmp::Ordering::Less; } if self.y > other.y { return std::cmp::Ordering::Greater; } if self.y < other.y { return std::cmp::Ordering::Less; } std::cmp::Ordering::Equal } } #[cfg(test)] mod tests { use super::Point; #[test] fn distance_to_itself_is_zero() { let p = Point { x: 1, y: 1, }; assert_eq!(p.distance(&p), 0.0); } #[test] fn distance_same_x_is_y() { let p1 = Point { x: 1, y: 1, }; let p2 = Point { x: 1, y: 5, }; assert_eq!(p1.distance(&p2), 4.0); } #[test] fn distance_same_y_is_x() { let p1 = Point::new((1,1)); let p2 = Point::new((5,1)); assert_eq!(p1.distance(&p2), 4.0); } #[test] fn distance_3_4_5() { let p1 = Point::new((0,0)); let p2 = Point::new((3,4)); assert_eq!(p1.distance(&p2), 5.0); } #[test] fn distance_is_always_positive() { let p1 = Point::new((10,10)); let p2 = Point::new((5,10)); assert_eq!(p1.distance(&p2), 5.0); let p1 = Point::new((10,10)); let p2 = Point::new((10,5)); assert_eq!(p1.distance(&p2), 5.0); } #[test] fn distance_quadrant3() { let p1 = Point { x: 3, y: 4, }; let p2 = Point { x: 0, y: 0, }; assert_eq!(p1.distance(&p2), 5.0); } #[test] fn distance_quadrant2() { let p1 = Point { x: 3, y: 4, }; let p2 = Point { x: 0, y: 100, }; assert_eq!(p1.distance(&p2) as f32, 96.04687); } #[test] fn distance_quadrant2_fast() { let p1 = Point { x: 3, y: 4, }; let p2 = Point { x: 3, y: 50, }; assert_eq!(p1.distance(&p2), 46.0); } #[test] fn distance_quadrant4() { let p1 = Point { x: 3, y: 4, }; let p2 = Point { x: 50, y: -50, }; assert_eq!(p1.distance(&p2) as f32, 71.5891); } #[test] fn movement_does_nothing_when_right_length() { let root = Point::new((0,0)); let node = Point::new((0,1)); assert_eq!(root.movement(node.clone(), 1), node); } #[test] fn movement_does_not_overlap() { let root = Point::new((0,1)); let node = Point::new((0,0)); assert_eq!(root.movement(node.clone(), 2), Point::new((0, -1))); } #[test] fn movement_adjusts_to_asked_length() { let root = Point::new((0,0)); let node = Point::new((0,1)); assert_eq!(root.movement(node, 10), Point::new((0,10))); } #[test] fn movement_works_away_from_origin() { let root = Point::new((10,10)); let node = Point::new((10,11)); assert_eq!(root.movement(node, 10), Point::new((10,20))); } #[test] fn movement_works_in_two_dimension() { let root = Point::new((10,10)); let node = Point::new((40,50)); assert_eq!(root.movement(node, 50), Point::new((40,50))); let root = Point::new((10,10)); let node = Point::new((40,50)); assert_eq!(root.movement(node, 5), Point::new((13,14))); } #[test] fn movement_works_in_all_directions() { let root = Point::new((40,50)); let node = Point::new((10,10)); assert_eq!(root.movement(node, 5), Point::new((37,46))); let root = Point::new((50,10)); let node = Point::new((10,10)); assert_eq!(root.movement(node, 5), Point::new((45,10))); let root = Point::new((10,50)); let node = Point::new((10,10)); assert_eq!(root.movement(node, 5), Point::new((10,45))); } }