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 {
26        x: 0.0,
27        y: 0.0,
28        z: 0.0,
29    };
30
31    /// Construct from coordinates.
32    #[inline]
33    pub const fn new(x: f64, y: f64, z: f64) -> Self {
34        Self { x, y, z }
35    }
36
37    /// Displacement vector from `other` to `self`: `self - other`.
38    #[inline]
39    pub fn offset_from(self, other: Self) -> Vec3 {
40        Vec3::new(self.x - other.x, self.y - other.y, self.z - other.z)
41    }
42
43    /// Euclidean distance to `other`.
44    #[inline]
45    pub fn distance_to(self, other: Self) -> f64 {
46        self.offset_from(other).length()
47    }
48
49    /// Linearly interpolate between two points: `(1-t)*self + t*other`.
50    #[inline]
51    pub fn lerp(self, other: Self, t: f64) -> Self {
52        Self::new(
53            self.x + t * (other.x - self.x),
54            self.y + t * (other.y - self.y),
55            self.z + t * (other.z - self.z),
56        )
57    }
58
59    /// Convert to a vector from the origin.
60    #[inline]
61    pub fn to_vec(self) -> Vec3 {
62        Vec3::new(self.x, self.y, self.z)
63    }
64
65    /// Construct from a vector (treating it as an offset from origin).
66    #[inline]
67    pub fn from_vec(v: Vec3) -> Self {
68        Self::new(v.x, v.y, v.z)
69    }
70}
71
72// ── Arithmetic ───────────────────────────────────────────────────────────────
73
74/// `Point3 + Vec3 → Point3`
75impl Add<Vec3> for Point3 {
76    type Output = Self;
77    #[inline]
78    fn add(self, v: Vec3) -> Self {
79        Self::new(self.x + v.x, self.y + v.y, self.z + v.z)
80    }
81}
82impl AddAssign<Vec3> for Point3 {
83    #[inline]
84    fn add_assign(&mut self, v: Vec3) {
85        *self = *self + v;
86    }
87}
88
89/// `Point3 - Point3 → Vec3`
90impl Sub for Point3 {
91    type Output = Vec3;
92    #[inline]
93    fn sub(self, other: Self) -> Vec3 {
94        self.offset_from(other)
95    }
96}
97
98/// `Point3 - Vec3 → Point3`
99impl Sub<Vec3> for Point3 {
100    type Output = Self;
101    #[inline]
102    fn sub(self, v: Vec3) -> Self {
103        Self::new(self.x - v.x, self.y - v.y, self.z - v.z)
104    }
105}
106
107impl fmt::Debug for Point3 {
108    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109        write!(f, "Point3({:.6}, {:.6}, {:.6})", self.x, self.y, self.z)
110    }
111}
112impl fmt::Display for Point3 {
113    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114        write!(f, "({:.4}, {:.4}, {:.4})", self.x, self.y, self.z)
115    }
116}
117
118// ── Conversions ──────────────────────────────────────────────────────────────
119
120impl From<[f64; 3]> for Point3 {
121    fn from(a: [f64; 3]) -> Self {
122        Self::new(a[0], a[1], a[2])
123    }
124}
125impl From<Point3> for [f64; 3] {
126    fn from(p: Point3) -> [f64; 3] {
127        [p.x, p.y, p.z]
128    }
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134
135    #[test]
136    fn subtraction_gives_vec() {
137        let a = Point3::new(3.0, 0.0, 0.0);
138        let b = Point3::ORIGIN;
139        let v = a - b;
140        assert_eq!(v, Vec3::new(3.0, 0.0, 0.0));
141    }
142
143    #[test]
144    fn add_vec_moves_point() {
145        let p = Point3::ORIGIN + Vec3::Z;
146        assert_eq!(p, Point3::new(0.0, 0.0, 1.0));
147    }
148}