use std::ops::{Add, Div, Mul, Neg, Sub};
use super::line::PgaLine;
use super::plane::PgaPlane;
#[repr(C, align(16))]
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "vulkan", derive(bytemuck::Pod, bytemuck::Zeroable))]
pub struct PgaPoint {
pub x: f32, pub y: f32, pub z: f32, pub w: f32, }
impl Default for PgaPoint {
#[inline(always)]
fn default() -> Self {
Self::origin()
}
}
impl PgaPoint {
#[inline(always)]
pub const fn new(x: f32, y: f32, z: f32) -> Self {
Self { x, y, z, w: 1.0 }
}
#[inline(always)]
pub const fn from_homogeneous(x: f32, y: f32, z: f32, w: f32) -> Self {
Self { x, y, z, w }
}
#[inline(always)]
pub const fn origin() -> Self {
Self { x: 0.0, y: 0.0, z: 0.0, w: 1.0 }
}
#[inline(always)]
pub const fn ideal(dx: f32, dy: f32, dz: f32) -> Self {
Self { x: dx, y: dy, z: dz, w: 0.0 }
}
#[inline(always)]
pub fn is_ideal(&self) -> bool {
self.w.abs() < 1e-12
}
#[inline]
pub fn to_euclidean(&self) -> [f32; 3] {
if self.w.abs() > 1e-12 {
let inv_w = 1.0 / self.w;
[self.x * inv_w, self.y * inv_w, self.z * inv_w]
} else {
[self.x, self.y, self.z]
}
}
#[inline]
pub fn normalized(&self) -> Self {
if self.w.abs() > 1e-12 {
let inv_w = 1.0 / self.w;
Self {
x: self.x * inv_w,
y: self.y * inv_w,
z: self.z * inv_w,
w: 1.0,
}
} else {
*self
}
}
#[inline]
pub fn distance(&self, other: &Self) -> f32 {
self.distance_sq(other).sqrt()
}
#[inline]
pub fn distance_sq(&self, other: &Self) -> f32 {
let p1 = self.to_euclidean();
let p2 = other.to_euclidean();
let dx = p1[0] - p2[0];
let dy = p1[1] - p2[1];
let dz = p1[2] - p2[2];
dx * dx + dy * dy + dz * dz
}
#[inline]
pub fn midpoint(&self, other: &Self) -> Self {
let p1 = self.to_euclidean();
let p2 = other.to_euclidean();
Self::new(
0.5 * (p1[0] + p2[0]),
0.5 * (p1[1] + p2[1]),
0.5 * (p1[2] + p2[2]),
)
}
#[inline]
pub fn join(&self, other: &Self) -> PgaLine {
let p1 = self.to_euclidean();
let p2 = other.to_euclidean();
let vx = p2[0] - p1[0];
let vy = p2[1] - p1[1];
let vz = p2[2] - p1[2];
let mx = p1[1] * p2[2] - p1[2] * p2[1];
let my = p1[2] * p2[0] - p1[0] * p2[2];
let mz = p1[0] * p2[1] - p1[1] * p2[0];
PgaLine::from_direction_and_moment([vx, vy, vz], [mx, my, mz])
}
#[inline]
pub fn join_line(&self, line: &PgaLine) -> PgaPlane {
let p = self.to_euclidean();
let dir = line.direction();
let mom = line.moment();
let nx = p[1] * dir[2] - p[2] * dir[1] - mom[0];
let ny = p[2] * dir[0] - p[0] * dir[2] - mom[1];
let nz = p[0] * dir[1] - p[1] * dir[0] - mom[2];
let d = -(nx * p[0] + ny * p[1] + nz * p[2]);
PgaPlane::new(nx, ny, nz, d)
}
}
impl Neg for PgaPoint {
type Output = Self;
#[inline(always)]
fn neg(self) -> Self {
Self {
x: -self.x,
y: -self.y,
z: -self.z,
w: self.w,
}
}
}
impl Add for PgaPoint {
type Output = Self;
#[inline(always)]
fn add(self, rhs: Self) -> Self {
Self {
x: self.x + rhs.x,
y: self.y + rhs.y,
z: self.z + rhs.z,
w: self.w + rhs.w,
}
}
}
impl Sub for PgaPoint {
type Output = Self;
#[inline(always)]
fn sub(self, rhs: Self) -> Self {
Self {
x: self.x - rhs.x,
y: self.y - rhs.y,
z: self.z - rhs.z,
w: self.w - rhs.w,
}
}
}
impl Mul<f32> for PgaPoint {
type Output = Self;
#[inline(always)]
fn mul(self, rhs: f32) -> Self {
Self {
x: self.x * rhs,
y: self.y * rhs,
z: self.z * rhs,
w: self.w * rhs,
}
}
}
impl Div<f32> for PgaPoint {
type Output = Self;
#[inline(always)]
fn div(self, rhs: f32) -> Self {
let inv = 1.0 / rhs;
Self {
x: self.x * inv,
y: self.y * inv,
z: self.z * inv,
w: self.w * inv,
}
}
}