use std::cmp::Ordering;
use float_cmp::{ApproxEq, F64Margin};
use super::point::Point;
#[derive(Debug, Clone, Copy)]
pub struct Point2 {
x: f64,
y: f64,
}
impl Point2 {
pub fn new(x: f64, y: f64) -> Self {
Self { x, y }
}
pub fn x(&self) -> f64 {
self.x
}
pub fn y(&self) -> f64 {
self.y
}
}
impl Point for Point2 {
fn is_valid(&self) -> bool {
(self.x.is_normal() || self.x.is_finite()) && (self.y.is_normal() || self.y.is_finite())
}
fn add(&mut self, other: Point2) {
self.x += other.x;
self.y += other.y;
}
fn sub(&mut self, other: Point2) {
self.x -= other.x;
self.y -= other.y;
}
fn distance(&self, other: Point2) -> f64 {
((other.x - self.x).powi(2) + (other.y - self.y).powi(2)).sqrt()
}
fn distance_from_origin(&self) -> f64 {
self.distance(Point2::new(0.0, 0.0))
}
fn scale(&mut self, factor: f64) {
self.x *= factor;
self.y *= factor;
}
fn lerp(&self, other: Point2, t: f64) -> Point2 {
Point2::new(
self.x + (other.x - self.x) * t,
self.y + (other.y - self.y) * t,
)
}
fn is_equal_marginal(&self, other: Point2, margin: F64Margin) -> bool {
self.x.approx_eq(other.x, margin) && self.y.approx_eq(other.y, margin)
}
}
impl PartialEq<Self> for Point2 {
fn eq(&self, other: &Self) -> bool {
self.x == other.x && self.y == other.y
}
}
impl PartialOrd<Self> for Point2 {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if !self.is_valid() || !other.is_valid() {
return None;
}
Some(if self == other {
Ordering::Equal
} else if self.x > other.x && self.y > other.y {
Ordering::Greater
} else {
Ordering::Less
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn point2d_eq() {
let point1 = Point2::new(1.0, 2.0);
let point2 = Point2::new(1.0, 2.0);
assert_eq!(point1, point2);
}
#[test]
fn point2d_compare() {
let p1 = Point2::new(1.0, 2.0);
let p2 = Point2::new(3.0, 4.0);
assert_eq!(p1.partial_cmp(&p2), Some(Ordering::Less));
assert_eq!(p2.partial_cmp(&p1), Some(Ordering::Greater));
}
#[test]
fn point2d_add() {
let mut p1 = Point2::new(1.0, 2.0);
let p2 = Point2::new(3.0, 4.0);
p1.add(p2);
assert_eq!(p1, Point2::new(4.0, 6.0));
}
#[test]
fn point2d_sub() {
let mut p1 = Point2::new(1.0, 2.0);
let p2 = Point2::new(3.0, 4.0);
p1.sub(p2);
assert_eq!(p1, Point2::new(-2.0, -2.0));
}
#[test]
fn point2d_distance() {
let p1 = Point2::new(1.0, 2.0);
let p2 = Point2::new(4.0, 6.0);
assert_eq!(p1.distance(p2), 5.0);
}
#[test]
fn point2d_distance_from_origin() {
let p = Point2::new(3.0, 4.0);
assert_eq!(p.distance_from_origin(), 5.0);
}
#[test]
fn point2d_scale() {
let mut p = Point2::new(1.0, 2.0);
p.scale(2.0);
assert_eq!(p, Point2::new(2.0, 4.0));
}
#[test]
fn point2d_lerp() {
let p1 = Point2::new(0.0, 2.0);
let p2 = Point2::new(2.0, 1.0);
assert_eq!(p1.lerp(p2, 0.38), Point2::new(0.76, 1.62));
}
#[test]
fn point2d_is_equal_marginal() {
let p1 = Point2::new(1.0, 2.0);
let p2 = Point2::new(1.0000000000000001, 2.0);
assert!(p1.is_equal_marginal(p2, F64Margin::default()));
}
}