use core::ops::{Add, AddAssign, Index, IndexMut, Mul, Sub, SubAssign};
use crate::Scalar;
use crate::tensor::Vector3;
pub struct Point3<T> {
pub x: T,
pub y: T,
pub z: T,
}
impl<T: ::core::marker::Copy> ::core::marker::Copy for Point3<T> {}
impl<T: ::core::clone::Clone> ::core::clone::Clone for Point3<T> {
#[inline]
fn clone(&self) -> Self {
Self {
x: self.x.clone(),
y: self.y.clone(),
z: self.z.clone(),
}
}
}
impl<T: ::core::fmt::Debug> ::core::fmt::Debug for Point3<T> {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.debug_struct("Point3")
.field("x", &self.x)
.field("y", &self.y)
.field("z", &self.z)
.finish()
}
}
impl<T: ::core::cmp::PartialEq> ::core::cmp::PartialEq for Point3<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.x == other.x && self.y == other.y && self.z == other.z
}
}
impl<T> Point3<T> {
#[inline]
pub const fn new(x: T, y: T, z: T) -> Self {
Self { x, y, z }
}
#[inline]
pub fn from_array(array: [T; 3]) -> Self {
let [x, y, z] = array;
Self { x, y, z }
}
#[inline]
pub fn to_array(self) -> [T; 3] {
[self.x, self.y, self.z]
}
#[inline]
pub fn with_x(self, x: T) -> Self {
Self {
x,
y: self.y,
z: self.z,
}
}
#[inline]
pub fn with_y(self, y: T) -> Self {
Self {
x: self.x,
y,
z: self.z,
}
}
#[inline]
pub fn with_z(self, z: T) -> Self {
Self {
x: self.x,
y: self.y,
z,
}
}
#[inline]
pub fn from_vector(vector: Vector3<T>) -> Self {
Self {
x: vector.x,
y: vector.y,
z: vector.z,
}
}
#[inline]
pub fn to_vector(self) -> Vector3<T> {
Vector3::new(self.x, self.y, self.z)
}
#[inline]
pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> Point3<U> {
Point3 {
x: f(self.x),
y: f(self.y),
z: f(self.z),
}
}
#[inline]
pub fn zip_map<U, R, F: FnMut(T, U) -> R>(self, rhs: Point3<U>, mut f: F) -> Point3<R> {
Point3 {
x: f(self.x, rhs.x),
y: f(self.y, rhs.y),
z: f(self.z, rhs.z),
}
}
}
impl<T: Copy> Point3<T> {
#[inline]
pub const fn splat(value: T) -> Self {
Self {
x: value,
y: value,
z: value,
}
}
#[inline]
pub fn from_slice(slice: &[T]) -> Self {
Self {
x: slice[0],
y: slice[1],
z: slice[2],
}
}
}
impl<T> Index<usize> for Point3<T> {
type Output = T;
#[inline]
fn index(&self, index: usize) -> &T {
match index {
0 => &self.x,
1 => &self.y,
2 => &self.z,
_ => panic!("index out of bounds: Point3 has 3 coordinates but the index is {index}"),
}
}
}
impl<T> IndexMut<usize> for Point3<T> {
#[inline]
fn index_mut(&mut self, index: usize) -> &mut T {
match index {
0 => &mut self.x,
1 => &mut self.y,
2 => &mut self.z,
_ => panic!("index out of bounds: Point3 has 3 coordinates but the index is {index}"),
}
}
}
impl<T: Default> Default for Point3<T> {
#[inline]
fn default() -> Self {
Self {
x: T::default(),
y: T::default(),
z: T::default(),
}
}
}
impl<T: Add<Output = T>> Add<Vector3<T>> for Point3<T> {
type Output = Self;
#[inline]
fn add(self, rhs: Vector3<T>) -> Self {
Self::new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z)
}
}
impl<T: AddAssign> AddAssign<Vector3<T>> for Point3<T> {
#[inline]
fn add_assign(&mut self, rhs: Vector3<T>) {
self.x += rhs.x;
self.y += rhs.y;
self.z += rhs.z;
}
}
impl<T: Sub<Output = T>> Sub<Vector3<T>> for Point3<T> {
type Output = Self;
#[inline]
fn sub(self, rhs: Vector3<T>) -> Self {
Self::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z)
}
}
impl<T: SubAssign> SubAssign<Vector3<T>> for Point3<T> {
#[inline]
fn sub_assign(&mut self, rhs: Vector3<T>) {
self.x -= rhs.x;
self.y -= rhs.y;
self.z -= rhs.z;
}
}
impl<T: Sub<Output = T>> Sub for Point3<T> {
type Output = Vector3<T>;
#[inline]
fn sub(self, rhs: Self) -> Vector3<T> {
Vector3::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z)
}
}
impl<T: Add<Output = T> + Sub<Output = T> + Copy> Point3<T> {
#[inline]
pub fn lerp<S: Scalar>(self, rhs: Self, t: S) -> Self
where
T: Mul<S, Output = T>,
{
self + (rhs - self) * t
}
}
impl<V: Scalar> Point3<V> {
pub const ORIGIN: Self = Self::new(V::ZERO, V::ZERO, V::ZERO);
#[inline]
pub fn distance_squared(self, rhs: Self) -> V {
(self - rhs).norm_squared()
}
#[inline]
pub fn distance(self, rhs: Self) -> V {
(self - rhs).norm()
}
#[inline]
pub fn midpoint(self, rhs: Self) -> Self {
self + (rhs - self) * V::from_f64(0.5)
}
#[inline]
pub fn centroid(points: &[Self]) -> Self {
if points.is_empty() {
return Self::ORIGIN;
}
let mut sum = Vector3::<V>::ZERO;
for p in points {
sum += p.to_vector();
}
Self::from_vector(sum / V::from_f64(points.len() as f64))
}
#[inline]
pub fn min(self, rhs: Self) -> Self {
Self::new(self.x.min(rhs.x), self.y.min(rhs.y), self.z.min(rhs.z))
}
#[inline]
pub fn max(self, rhs: Self) -> Self {
Self::new(self.x.max(rhs.x), self.y.max(rhs.y), self.z.max(rhs.z))
}
#[inline]
pub fn clamp(self, min: Self, max: Self) -> Self {
Self::new(
self.x.clamp(min.x, max.x),
self.y.clamp(min.y, max.y),
self.z.clamp(min.z, max.z),
)
}
#[inline]
pub fn floor(self) -> Self {
Self::new(self.x.floor(), self.y.floor(), self.z.floor())
}
#[inline]
pub fn ceil(self) -> Self {
Self::new(self.x.ceil(), self.y.ceil(), self.z.ceil())
}
#[inline]
pub fn round(self) -> Self {
Self::new(self.x.round(), self.y.round(), self.z.round())
}
#[inline]
pub fn round_ties_even(self) -> Self {
Self::new(
self.x.round_ties_even(),
self.y.round_ties_even(),
self.z.round_ties_even(),
)
}
#[inline]
pub fn trunc(self) -> Self {
Self::new(self.x.trunc(), self.y.trunc(), self.z.trunc())
}
#[inline]
pub fn fract(self) -> Self {
Self::new(self.x.fract(), self.y.fract(), self.z.fract())
}
#[inline]
pub fn is_finite(self) -> bool {
self.x.is_finite() && self.y.is_finite() && self.z.is_finite()
}
#[inline]
pub fn is_infinite(self) -> bool {
self.x.is_infinite() || self.y.is_infinite() || self.z.is_infinite()
}
#[inline]
pub fn is_nan(self) -> bool {
self.x.is_nan() || self.y.is_nan() || self.z.is_nan()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new() {
let p = Point3::new(1.0, 2.0, 3.0);
assert_eq!((p.x, p.y, p.z), (1.0, 2.0, 3.0));
}
#[test]
fn from_array() {
assert_eq!(
Point3::from_array([1.0, 2.0, 3.0]),
Point3::new(1.0, 2.0, 3.0)
);
}
#[test]
fn to_array() {
assert_eq!(Point3::new(1.0, 2.0, 3.0).to_array(), [1.0, 2.0, 3.0]);
}
#[test]
fn splat() {
assert_eq!(Point3::splat(5.0), Point3::new(5.0, 5.0, 5.0));
}
#[test]
fn from_slice() {
assert_eq!(
Point3::from_slice(&[1.0, 2.0, 3.0, 4.0]),
Point3::new(1.0, 2.0, 3.0)
);
}
#[test]
#[should_panic]
fn from_slice_panics_when_too_short() {
Point3::<f64>::from_slice(&[1.0, 2.0]);
}
#[test]
fn with_x() {
assert_eq!(
Point3::new(1.0, 2.0, 3.0).with_x(9.0),
Point3::new(9.0, 2.0, 3.0)
);
}
#[test]
fn with_y() {
assert_eq!(
Point3::new(1.0, 2.0, 3.0).with_y(9.0),
Point3::new(1.0, 9.0, 3.0)
);
}
#[test]
fn with_z() {
assert_eq!(
Point3::new(1.0, 2.0, 3.0).with_z(9.0),
Point3::new(1.0, 2.0, 9.0)
);
}
#[test]
fn from_vector() {
assert_eq!(
Point3::from_vector(Vector3::new(1.0, 2.0, 3.0)),
Point3::new(1.0, 2.0, 3.0)
);
}
#[test]
fn to_vector() {
assert_eq!(
Point3::new(1.0, 2.0, 3.0).to_vector(),
Vector3::new(1.0, 2.0, 3.0)
);
}
#[test]
fn map() {
assert_eq!(
Point3::new(1.0, 2.0, 3.0).map(|c| c * 2.0),
Point3::new(2.0, 4.0, 6.0)
);
}
#[test]
fn zip_map() {
assert_eq!(
Point3::new(1.0, 2.0, 3.0).zip_map(Point3::new(4.0, 5.0, 6.0), |a, b| a + b),
Point3::new(5.0, 7.0, 9.0)
);
}
#[test]
fn default_is_origin() {
assert_eq!(Point3::<f64>::default(), Point3::new(0.0, 0.0, 0.0));
}
#[test]
fn copy_and_clone() {
let a = Point3::new(1.0, 2.0, 3.0);
let b = a;
let c = ::core::clone::Clone::clone(&a);
assert_eq!(a, b);
assert_eq!(a, c);
}
#[test]
fn eq() {
let a = Point3::new(1.0, 2.0, 3.0);
assert_eq!(a, Point3::new(1.0, 2.0, 3.0));
assert_ne!(a, Point3::new(1.0, 2.0, 4.0));
}
#[test]
fn debug() {
assert_eq!(
format!("{:?}", Point3::new(1.0, 2.0, 3.0)),
"Point3 { x: 1.0, y: 2.0, z: 3.0 }"
);
}
#[test]
fn index() {
let p = Point3::new(1.0, 2.0, 3.0);
assert_eq!((p[0], p[1], p[2]), (1.0, 2.0, 3.0));
}
#[test]
fn index_mut() {
let mut p = Point3::new(1.0, 2.0, 3.0);
p[1] = 9.0;
assert_eq!(p.y, 9.0);
}
#[test]
#[should_panic]
fn index_panics_when_out_of_bounds() {
let _ = Point3::new(1.0, 2.0, 3.0)[3];
}
#[test]
#[should_panic]
fn index_mut_panics_when_out_of_bounds() {
Point3::new(1.0, 2.0, 3.0)[3] = 0.0;
}
#[test]
fn origin_constant() {
assert_eq!(Point3::<f64>::ORIGIN, Point3::new(0.0, 0.0, 0.0));
}
#[test]
fn add_vector() {
assert_eq!(
Point3::new(1.0, 2.0, 3.0) + Vector3::new(1.0, 1.0, 1.0),
Point3::new(2.0, 3.0, 4.0)
);
}
#[test]
fn add_assign_vector() {
let mut p = Point3::new(1.0, 2.0, 3.0);
p += Vector3::new(1.0, 1.0, 1.0);
assert_eq!(p, Point3::new(2.0, 3.0, 4.0));
}
#[test]
fn sub_vector() {
assert_eq!(
Point3::new(2.0, 3.0, 4.0) - Vector3::new(1.0, 1.0, 1.0),
Point3::new(1.0, 2.0, 3.0)
);
}
#[test]
fn sub_assign_vector() {
let mut p = Point3::new(2.0, 3.0, 4.0);
p -= Vector3::new(1.0, 1.0, 1.0);
assert_eq!(p, Point3::new(1.0, 2.0, 3.0));
}
#[test]
fn sub_point_yields_vector() {
assert_eq!(
Point3::new(3.0, 3.0, 3.0) - Point3::new(1.0, 1.0, 1.0),
Vector3::new(2.0, 2.0, 2.0)
);
}
#[test]
fn lerp() {
assert_eq!(
Point3::new(0.0, 0.0, 0.0).lerp(Point3::new(2.0, 4.0, 6.0), 0.5),
Point3::new(1.0, 2.0, 3.0)
);
}
#[test]
fn distance_squared() {
assert_eq!(
Point3::new(0.0, 0.0, 0.0).distance_squared(Point3::new(3.0, 4.0, 0.0)),
25.0
);
}
#[test]
fn distance() {
assert_eq!(
Point3::new(0.0, 0.0, 0.0).distance(Point3::new(3.0, 4.0, 0.0)),
5.0
);
}
#[test]
fn midpoint() {
assert_eq!(
Point3::new(0.0, 0.0, 0.0).midpoint(Point3::new(2.0, 4.0, 6.0)),
Point3::new(1.0, 2.0, 3.0)
);
}
#[test]
fn centroid() {
let points = [
Point3::new(0.0, 0.0, 0.0),
Point3::new(2.0, 0.0, 0.0),
Point3::new(1.0, 3.0, 0.0),
];
assert_eq!(Point3::centroid(&points), Point3::new(1.0, 1.0, 0.0));
}
#[test]
fn centroid_empty_is_origin() {
assert_eq!(Point3::<f64>::centroid(&[]), Point3::ORIGIN);
}
#[test]
fn min() {
assert_eq!(
Point3::new(1.0, 5.0, 3.0).min(Point3::new(4.0, 2.0, 6.0)),
Point3::new(1.0, 2.0, 3.0)
);
}
#[test]
fn max() {
assert_eq!(
Point3::new(1.0, 5.0, 3.0).max(Point3::new(4.0, 2.0, 6.0)),
Point3::new(4.0, 5.0, 6.0)
);
}
#[test]
fn clamp() {
assert_eq!(
Point3::new(5.0, -1.0, 2.0).clamp(Point3::splat(0.0), Point3::splat(3.0)),
Point3::new(3.0, 0.0, 2.0)
);
}
#[test]
#[should_panic]
fn clamp_panics_when_min_gt_max() {
Point3::new(1.0, 1.0, 1.0).clamp(Point3::splat(3.0), Point3::splat(0.0));
}
#[test]
fn floor() {
assert_eq!(
Point3::new(1.7, -1.2, 2.0).floor(),
Point3::new(1.0, -2.0, 2.0)
);
}
#[test]
fn ceil() {
assert_eq!(
Point3::new(1.2, -1.7, 2.0).ceil(),
Point3::new(2.0, -1.0, 2.0)
);
}
#[test]
fn round() {
assert_eq!(
Point3::new(1.5, -1.5, 2.4).round(),
Point3::new(2.0, -2.0, 2.0)
);
}
#[test]
fn round_ties_even() {
assert_eq!(
Point3::new(1.5, 2.5, -1.5).round_ties_even(),
Point3::new(2.0, 2.0, -2.0)
);
}
#[test]
fn trunc() {
assert_eq!(
Point3::new(1.7, -1.7, 2.0).trunc(),
Point3::new(1.0, -1.0, 2.0)
);
}
#[test]
fn fract() {
assert_eq!(
Point3::new(2.5, -1.25, 0.0).fract(),
Point3::new(0.5, -0.25, 0.0)
);
}
#[test]
fn is_finite() {
assert!(Point3::new(1.0, 2.0, 3.0).is_finite());
assert!(!Point3::new(1.0, f64::INFINITY, 3.0).is_finite());
}
#[test]
fn is_infinite() {
assert!(Point3::new(1.0, f64::INFINITY, 3.0).is_infinite());
assert!(!Point3::new(1.0, 2.0, 3.0).is_infinite());
}
#[test]
fn is_nan() {
assert!(Point3::new(1.0, f64::NAN, 3.0).is_nan());
assert!(!Point3::new(1.0, 2.0, 3.0).is_nan());
}
#[test]
fn f32_distance() {
assert_eq!(
Point3::<f32>::new(0.0, 0.0, 0.0).distance(Point3::new(3.0, 4.0, 0.0)),
5.0
);
}
}