use std::{ fmt::{Display, Formatter}, ops, }; #[derive(Debug, Clone, Copy, Default, PartialEq)] pub struct Point { pub x: f64, pub y: f64, } impl Point { pub fn new(x: f64, y: f64) -> Point { Point { x, y } } pub fn zero() -> Point { Point { x: 0.0, y: 0.0 } } pub fn is_valid(&self) -> bool { !self.x.is_nan() && !self.y.is_nan() } pub fn module(&self) -> f64 { f64::sqrt(self.x * self.x + self.y * self.y) } pub fn phase(&self) -> f64 { f64::atan2(self.y, self.x) } pub fn distance(a: &Point, b: &Point) -> f64 { (a - b).module() } pub fn distance_to(&self, other: &Point) -> f64 { (self - other).module() } pub fn as_versor(&self) -> Option { if self.x == 0.0 && self.y == 0.0 { None } else { Some(self / self.module()) } } pub fn rotate_by(&mut self, α: f64) { let m = self.module(); let (sin, cos) = f64::sin_cos(self.phase() + α); self.x = m * cos; self.y = m * sin; } } impl Display for Point { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { write!(f, "({:.2},{:.2})", &self.x, &self.y) } } impl ops::Add for Point { type Output = Point; fn add(self, rhs: Point) -> Point { Point { x: self.x + rhs.x, y: self.y + rhs.y, } } } impl ops::Add<&Point> for &Point { type Output = Point; fn add(self, rhs: &Point) -> Point { Point { x: self.x + rhs.x, y: self.y + rhs.y, } } } impl ops::AddAssign<&Point> for Point { fn add_assign(&mut self, rhs: &Point) { *self = Self { x: self.x + rhs.x, y: self.y + rhs.y, }; } } impl ops::AddAssign for Point { fn add_assign(&mut self, rhs: Point) { *self = Self { x: self.x + rhs.x, y: self.y + rhs.y, }; } } impl ops::SubAssign<&Point> for Point { fn sub_assign(&mut self, rhs: &Point) { *self = Self { x: self.x - rhs.x, y: self.y - rhs.y, }; } } impl ops::SubAssign for Point { fn sub_assign(&mut self, rhs: Point) { *self = Self { x: self.x - rhs.x, y: self.y - rhs.y, }; } } impl ops::Sub for Point { type Output = Point; fn sub(self, rhs: Point) -> Point { Point { x: self.x - rhs.x, y: self.y - rhs.y, } } } impl ops::Sub<&Point> for &Point { type Output = Point; fn sub(self, rhs: &Point) -> Point { Point { x: self.x - rhs.x, y: self.y - rhs.y, } } } impl ops::Mul for Point { type Output = Point; fn mul(self, rhs: f64) -> Point { Point { x: self.x * rhs, y: self.y * rhs, } } } impl ops::MulAssign for Point { fn mul_assign(&mut self, rhs: f64) { *self = Point { x: self.x * rhs, y: self.y * rhs, } } } impl ops::Mul for &Point { type Output = Point; fn mul(self, rhs: f64) -> Point { Point { x: self.x * rhs, y: self.y * rhs, } } } impl ops::Div for Point { type Output = Point; fn div(self, rhs: f64) -> Point { Point { x: self.x / rhs, y: self.y / rhs, } } } impl ops::DivAssign for Point { fn div_assign(&mut self, rhs: f64) { *self = Point { x: self.x / rhs, y: self.y / rhs, } } } impl ops::Div for &Point { type Output = Point; fn div(self, rhs: f64) -> Point { Point { x: self.x / rhs, y: self.y / rhs, } } } #[test] fn test() { use std::f64::consts::{FRAC_1_SQRT_2, FRAC_PI_2, FRAC_PI_4, SQRT_2}; // New let p = Point::new(0.0, 0.0); print!("Testing Point::new()"); assert_eq!(p, Point { x: 0.0, y: 0.0 }); assert_ne!(p, Point { x: -1.0, y: 1.0 }); println!(" ... ok"); // is_valid let n = Point::new(std::f64::NAN, std::f64::NAN); let nn = Point::new(std::f64::NAN, 0.0); print!("Testing Point::is_valid()"); assert_eq!(p.is_valid(), true); assert_eq!(n.is_valid(), false); assert_eq!(nn.is_valid(), false); println!(" ... ok"); // module let p = Point::new(1.0, 1.0); let r = Point::new(2.0, 0.0); print!("Testing Point::module()"); assert!(f64::abs(p.module() - SQRT_2) < 1e-10); assert!(f64::abs(r.module() - 2.0) < 1e-10); println!(" ... ok"); // phase let p = Point::new(1.0, 1.0); let r = Point::new(2.0, 0.0); let q = Point::new(2.0, -2.0); print!("Testing Point::phase()"); assert!(f64::abs(p.phase() - FRAC_PI_4) < 1e-6); assert!(f64::abs(r.phase() - 0.0) < 1e-6); assert!(f64::abs(q.phase() + FRAC_PI_4) < 1e-6); println!(" ... ok"); //distance let z = Point::zero(); let p = Point::new(1.0, 0.0); let q = Point::new(1.0, 1.0); print!("Testing Point::distance() and distance_to()"); assert_eq!(z.distance_to(&p), 1.0); assert_eq!(Point::distance(&z, &p), 1.0); assert!(f64::abs(Point::distance(&z, &q) - SQRT_2) < 1e-10); println!(" ... ok"); //versor print!("Testing Point::as_versor()"); assert_eq!(z.as_versor(), None); assert_eq!(p, p.as_versor().unwrap()); let q_ver = q.as_versor().unwrap(); assert!(f64::abs(q_ver.x - FRAC_1_SQRT_2) < 1e-10); assert!(f64::abs(q_ver.y - FRAC_1_SQRT_2) < 1e-10); println!(" ... ok"); //rotate_by let mut p = Point::new(1.0, 0.0); print!("Testing Point::rotate_by()"); p.rotate_by(FRAC_PI_2); assert!(f64::abs(p.x - 0.0) < 1e-10); assert!(f64::abs(p.y - 1.0) < 1e-10); p.rotate_by(-FRAC_PI_4); assert!(f64::abs(p.x - FRAC_1_SQRT_2) < 1e-10); assert!(f64::abs(p.y - FRAC_1_SQRT_2) < 1e-10); println!(" ... ok"); }