Skip to main content

cadcore_math/
point3.rs

1//! 3-D point (`Point3`).
2
3use std::fmt;
4use std::ops::{Add, AddAssign, Sub};
5
6use crate::Vec3;
7
8/// A location in 3-D space.
9///
10/// Separate from [`Vec3`] so the type system enforces correct usage
11/// (e.g. `Point3 - Point3 → Vec3`, `Point3 + Vec3 → Point3`).
12#[derive(Clone, Copy, PartialEq)]
13#[repr(C)]
14pub struct Point3 {
15    /// X coordinate (mm).
16    pub x: f64,
17    /// Y coordinate (mm).
18    pub y: f64,
19    /// Z coordinate (mm).
20    pub z: f64,
21}
22
23impl Point3 {
24    /// Origin (0, 0, 0).
25    pub const ORIGIN: Self = Self { x: 0.0, y: 0.0, z: 0.0 };
26
27    /// Construct from coordinates.
28    #[inline] pub const fn new(x: f64, y: f64, z: f64) -> Self { Self { x, y, z } }
29
30    /// Displacement vector from `other` to `self`: `self - other`.
31    #[inline]
32    pub fn offset_from(self, other: Self) -> Vec3 {
33        Vec3::new(self.x - other.x, self.y - other.y, self.z - other.z)
34    }
35
36    /// Euclidean distance to `other`.
37    #[inline]
38    pub fn distance_to(self, other: Self) -> f64 { self.offset_from(other).length() }
39
40    /// Linearly interpolate between two points: `(1-t)*self + t*other`.
41    #[inline]
42    pub fn lerp(self, other: Self, t: f64) -> Self {
43        Self::new(
44            self.x + t * (other.x - self.x),
45            self.y + t * (other.y - self.y),
46            self.z + t * (other.z - self.z),
47        )
48    }
49
50    /// Convert to a vector from the origin.
51    #[inline]
52    pub fn to_vec(self) -> Vec3 { Vec3::new(self.x, self.y, self.z) }
53
54    /// Construct from a vector (treating it as an offset from origin).
55    #[inline]
56    pub fn from_vec(v: Vec3) -> Self { Self::new(v.x, v.y, v.z) }
57}
58
59// ── Arithmetic ───────────────────────────────────────────────────────────────
60
61/// `Point3 + Vec3 → Point3`
62impl Add<Vec3> for Point3 {
63    type Output = Self;
64    #[inline]
65    fn add(self, v: Vec3) -> Self {
66        Self::new(self.x + v.x, self.y + v.y, self.z + v.z)
67    }
68}
69impl AddAssign<Vec3> for Point3 {
70    #[inline] fn add_assign(&mut self, v: Vec3) { *self = *self + v; }
71}
72
73/// `Point3 - Point3 → Vec3`
74impl Sub for Point3 {
75    type Output = Vec3;
76    #[inline]
77    fn sub(self, other: Self) -> Vec3 { self.offset_from(other) }
78}
79
80/// `Point3 - Vec3 → Point3`
81impl Sub<Vec3> for Point3 {
82    type Output = Self;
83    #[inline]
84    fn sub(self, v: Vec3) -> Self { Self::new(self.x - v.x, self.y - v.y, self.z - v.z) }
85}
86
87impl fmt::Debug for Point3 {
88    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89        write!(f, "Point3({:.6}, {:.6}, {:.6})", self.x, self.y, self.z)
90    }
91}
92impl fmt::Display for Point3 {
93    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94        write!(f, "({:.4}, {:.4}, {:.4})", self.x, self.y, self.z)
95    }
96}
97
98// ── Conversions ──────────────────────────────────────────────────────────────
99
100impl From<[f64; 3]> for Point3 {
101    fn from(a: [f64; 3]) -> Self { Self::new(a[0], a[1], a[2]) }
102}
103impl From<Point3> for [f64; 3] {
104    fn from(p: Point3) -> [f64; 3] { [p.x, p.y, p.z] }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110
111    #[test]
112    fn subtraction_gives_vec() {
113        let a = Point3::new(3.0, 0.0, 0.0);
114        let b = Point3::ORIGIN;
115        let v = a - b;
116        assert_eq!(v, Vec3::new(3.0, 0.0, 0.0));
117    }
118
119    #[test]
120    fn add_vec_moves_point() {
121        let p = Point3::ORIGIN + Vec3::Z;
122        assert_eq!(p, Point3::new(0.0, 0.0, 1.0));
123    }
124}