nationtech-website/src/space_colonization/point.rs
2023-08-04 22:38:49 -04:00

238 lines
5.3 KiB
Rust

#[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), node);
}
#[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)));
}
}