use std::ops::{Add, Div, Mul, Neg, Sub};
use super::point::PgaPoint;
use super::plane::PgaPlane;
#[repr(C, align(32))]
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "vulkan", derive(bytemuck::Pod, bytemuck::Zeroable))]
pub struct PgaLine {
pub vx: f32, pub vy: f32, pub vz: f32, pub mx: f32, pub my: f32, pub mz: f32, pub _pad0: f32,
pub _pad1: f32,
}
impl Default for PgaLine {
#[inline(always)]
fn default() -> Self {
Self::z_axis()
}
}
impl PgaLine {
#[inline(always)]
pub const fn new(vx: f32, vy: f32, vz: f32, mx: f32, my: f32, mz: f32) -> Self {
Self {
vx,
vy,
vz,
mx,
my,
mz,
_pad0: 0.0,
_pad1: 0.0,
}
}
#[inline(always)]
pub const fn from_direction_and_moment(dir: [f32; 3], mom: [f32; 3]) -> Self {
Self::new(dir[0], dir[1], dir[2], mom[0], mom[1], mom[2])
}
#[inline(always)]
pub const fn x_axis() -> Self {
Self::new(1.0, 0.0, 0.0, 0.0, 0.0, 0.0)
}
#[inline(always)]
pub const fn y_axis() -> Self {
Self::new(0.0, 1.0, 0.0, 0.0, 0.0, 0.0)
}
#[inline(always)]
pub const fn z_axis() -> Self {
Self::new(0.0, 0.0, 1.0, 0.0, 0.0, 0.0)
}
#[inline]
pub fn from_point_and_direction(point: &[f32; 3], dir: &[f32; 3]) -> Self {
let mx = point[1] * dir[2] - point[2] * dir[1];
let my = point[2] * dir[0] - point[0] * dir[2];
let mz = point[0] * dir[1] - point[1] * dir[0];
Self::new(dir[0], dir[1], dir[2], mx, my, mz)
}
#[inline(always)]
pub const fn direction(&self) -> [f32; 3] {
[self.vx, self.vy, self.vz]
}
#[inline(always)]
pub const fn moment(&self) -> [f32; 3] {
[self.mx, self.my, self.mz]
}
#[inline(always)]
pub fn norm(&self) -> f32 {
(self.vx * self.vx + self.vy * self.vy + self.vz * self.vz).sqrt()
}
#[inline]
pub fn normalized(&self) -> Self {
let n = self.norm();
if n > 1e-12 {
let inv = 1.0 / n;
Self::new(
self.vx * inv,
self.vy * inv,
self.vz * inv,
self.mx * inv,
self.my * inv,
self.mz * inv,
)
} else {
*self
}
}
#[inline]
pub fn closest_point_to_origin(&self) -> PgaPoint {
let v2 = self.vx * self.vx + self.vy * self.vy + self.vz * self.vz;
if v2 > 1e-12 {
let inv_v2 = 1.0 / v2;
let px = (self.vy * self.mz - self.vz * self.my) * inv_v2;
let py = (self.vz * self.mx - self.vx * self.mz) * inv_v2;
let pz = (self.vx * self.my - self.vy * self.mx) * inv_v2;
PgaPoint::new(px, py, pz)
} else {
PgaPoint::origin()
}
}
#[inline]
pub fn project_point(&self, p: &PgaPoint) -> PgaPoint {
let norm_line = self.normalized();
let pt = p.to_euclidean();
let p0 = norm_line.closest_point_to_origin().to_euclidean();
let dx = pt[0] - p0[0];
let dy = pt[1] - p0[1];
let dz = pt[2] - p0[2];
let dot = dx * norm_line.vx + dy * norm_line.vy + dz * norm_line.vz;
PgaPoint::new(
p0[0] + dot * norm_line.vx,
p0[1] + dot * norm_line.vy,
p0[2] + dot * norm_line.vz,
)
}
#[inline]
pub fn distance_to_point(&self, p: &PgaPoint) -> f32 {
let proj = self.project_point(p);
p.distance(&proj)
}
#[inline]
pub fn meet_plane(&self, plane: &PgaPlane) -> PgaPoint {
let norm_p = plane.normalized();
let [nx, ny, nz] = norm_p.normal();
let d = norm_p.d;
let denom = self.vx * nx + self.vy * ny + self.vz * nz;
if denom.abs() < 1e-12 {
PgaPoint::ideal(self.vx, self.vy, self.vz)
} else {
let p0 = self.closest_point_to_origin().to_euclidean();
let t = -(nx * p0[0] + ny * p0[1] + nz * p0[2] + d) / denom;
PgaPoint::new(
p0[0] + t * self.vx,
p0[1] + t * self.vy,
p0[2] + t * self.vz,
)
}
}
}
impl Neg for PgaLine {
type Output = Self;
#[inline(always)]
fn neg(self) -> Self {
Self::new(
-self.vx,
-self.vy,
-self.vz,
-self.mx,
-self.my,
-self.mz,
)
}
}
impl Add for PgaLine {
type Output = Self;
#[inline(always)]
fn add(self, rhs: Self) -> Self {
Self::new(
self.vx + rhs.vx,
self.vy + rhs.vy,
self.vz + rhs.vz,
self.mx + rhs.mx,
self.my + rhs.my,
self.mz + rhs.mz,
)
}
}
impl Sub for PgaLine {
type Output = Self;
#[inline(always)]
fn sub(self, rhs: Self) -> Self {
Self::new(
self.vx - rhs.vx,
self.vy - rhs.vy,
self.vz - rhs.vz,
self.mx - rhs.mx,
self.my - rhs.my,
self.mz - rhs.mz,
)
}
}
impl Mul<f32> for PgaLine {
type Output = Self;
#[inline(always)]
fn mul(self, rhs: f32) -> Self {
Self::new(
self.vx * rhs,
self.vy * rhs,
self.vz * rhs,
self.mx * rhs,
self.my * rhs,
self.mz * rhs,
)
}
}
impl Div<f32> for PgaLine {
type Output = Self;
#[inline(always)]
fn div(self, rhs: f32) -> Self {
let inv = 1.0 / rhs;
Self::new(
self.vx * inv,
self.vy * inv,
self.vz * inv,
self.mx * inv,
self.my * inv,
self.mz * inv,
)
}
}