irox-geometry 0.1.0

2D and 3D Geometry Primitives
Documentation
// SPDX-License-Identifier: MIT
// Copyright 2025 IROX Contributors
//

use crate::geometry::{Centroid, Geometry};
use crate::rectangle::Rectangle;
use crate::{Vector, Vector2D};
use core::ops::Add;
use core::ops::Sub;
use core::ops::{AddAssign, SubAssign};
use irox_tools::FloatIsh;

pub trait Point2D<T: FloatIsh>: Default + Copy + Clone + PartialEq + PartialOrd {
    fn x(&self) -> T;
    fn y(&self) -> T;
    fn z(&self) -> Option<T>;
    fn m(&self) -> Option<T>;

    fn new_point(x: T, y: T) -> Point<T> {
        Point {
            x,
            y,
            z: None,
            m: None,
        }
    }

    fn as_pointz(&self, or_else_z: T) -> PointZ<T>;
    fn as_pointm(&self, or_else_m: T) -> PointM<T>;
    fn as_pointzm(&self, or_else_z: T, or_else_m: T) -> PointZM<T>;
}

#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
pub struct Point<T: FloatIsh> {
    pub x: T,
    pub y: T,
    pub z: Option<T>,
    pub m: Option<T>,
}
impl<T: FloatIsh> Point<T> {
    pub fn to_vector(&self) -> Vector<T> {
        Vector {
            vx: self.x,
            vy: self.y,
        }
    }
}
impl<T: FloatIsh> Point2D<T> for Point<T> {
    fn x(&self) -> T {
        self.x
    }

    fn y(&self) -> T {
        self.y
    }

    fn z(&self) -> Option<T> {
        self.z
    }

    fn m(&self) -> Option<T> {
        self.m
    }

    fn as_pointz(&self, default_z: T) -> PointZ<T> {
        PointZ {
            x: self.x,
            y: self.y,
            z: self.z.unwrap_or(default_z),
            m: self.m,
        }
    }

    fn as_pointm(&self, or_else_m: T) -> PointM<T> {
        PointM {
            x: self.x,
            y: self.y,
            z: self.z,
            m: self.m.unwrap_or(or_else_m),
        }
    }

    fn as_pointzm(&self, or_else_z: T, or_else_m: T) -> PointZM<T> {
        PointZM {
            x: self.x,
            y: self.y,
            z: self.z.unwrap_or(or_else_z),
            m: self.m.unwrap_or(or_else_m),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
pub struct PointZ<T: FloatIsh> {
    pub x: T,
    pub y: T,
    pub z: T,
    pub m: Option<T>,
}

#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
pub struct PointM<T: FloatIsh> {
    pub x: T,
    pub y: T,
    pub z: Option<T>,
    pub m: T,
}

#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
pub struct PointZM<T: FloatIsh> {
    pub x: T,
    pub y: T,
    pub z: T,
    pub m: T,
}

impl<T: FloatIsh> Add<Vector<T>> for Point<T> {
    type Output = Self;

    fn add(self, rhs: Vector<T>) -> Self::Output {
        Self {
            x: self.x + rhs.vx,
            y: self.y + rhs.vy,
            z: self.z,
            m: self.m,
        }
    }
}

impl<T: FloatIsh> Sub for Point<T> {
    type Output = Vector<T>;

    fn sub(self, rhs: Self) -> Self::Output {
        self.to_vector() - rhs.to_vector()
    }
}
impl<T: FloatIsh> SubAssign<Vector<T>> for Point<T> {
    fn sub_assign(&mut self, rhs: Vector<T>) {
        self.x -= rhs.vx;
        self.y -= rhs.vy;
    }
}
impl<T: FloatIsh> AddAssign<Vector<T>> for Point<T> {
    fn add_assign(&mut self, rhs: Vector<T>) {
        self.x += rhs.vx;
        self.y += rhs.vy;
    }
}

impl<T: FloatIsh> Centroid<T> for Point<T> {
    fn centroid(&self) -> Point<T> {
        *self
    }
}

impl<T: FloatIsh> Geometry<T> for Point<T> {
    fn contains(&self, point: &Point<T>) -> bool {
        self == point
    }

    fn distance_to(&self, point: &Point<T>) -> T {
        let d = *self - *point;
        d.magnitude()
    }

    fn intersects(&self, point: &Point<T>) -> bool {
        self.contains(point)
    }

    fn bounding_rectangle(&self) -> Rectangle<T> {
        Rectangle {
            min: *self,
            size: Vector::default(),
        }
    }
}