Files
KairoXYZ/kairo-common/src/point.rs

244 lines
5.9 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<Point> {
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<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::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<Point> 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<Point> for Point {
fn sub_assign(&mut self, rhs: Point) {
*self = Self {
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::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<f64> for Point {
type Output = Point;
fn mul(self, rhs: f64) -> Point {
Point {
x: self.x * rhs,
y: self.y * rhs,
}
}
}
impl ops::MulAssign<f64> for Point {
fn mul_assign(&mut self, rhs: f64) {
*self = Point {
x: self.x * rhs,
y: self.y * rhs,
}
}
}
impl ops::Mul<f64> for &Point {
type Output = Point;
fn mul(self, rhs: f64) -> Point {
Point {
x: self.x * rhs,
y: self.y * rhs,
}
}
}
impl ops::Div<f64> for Point {
type Output = Point;
fn div(self, rhs: f64) -> Point {
Point {
x: self.x / rhs,
y: self.y / rhs,
}
}
}
impl ops::DivAssign<f64> for Point {
fn div_assign(&mut self, rhs: f64) {
*self = Point {
x: self.x / rhs,
y: self.y / rhs,
}
}
}
impl ops::Div<f64> 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");
}