use derive_ops::*;
use std::{fmt::Display, ops::Neg};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Axis3 {
X,
Y,
Z,
}
#[derive(Clone, Default, Debug, PartialEq, PartialOrd)]
pub struct Angles {
pub pitch: f64,
pub yaw: f64,
pub roll: f64,
}
impl Display for Angles {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} {} {}", self.pitch, self.yaw, self.roll)
}
}
#[derive(AddRef, SubRef, MulRef, DivRef)]
#[derive(AddAssignRef, SubAssignRef, MulAssignRef, DivAssignRef)]
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Vector3<T> {
pub x: T,
pub y: T,
pub z: T,
}
impl Vector3<f32> {
pub const fn origin() -> Self {
Self::new(0.0, 0.0, 0.0)
}
pub fn new_with_round(mut x: f32, mut y: f32, mut z: f32, allow_frac: bool) -> Self {
if !allow_frac {
x = x.round();
y = y.round();
z = z.round();
}
Self::new(x, y, z)
}
pub fn abs(self) -> Self {
Self { x: self.x.abs(), y: self.y.abs(), z: self.z.abs() }
}
pub fn round(self) -> Self {
let mut x = self.x.round();
let mut y = self.y.round();
let mut z = self.z.round();
if x == -0.0 {
x = 0.0;
}
if y == -0.0 {
y = 0.0;
}
if z == -0.0 {
z = 0.0;
}
Self { x, y, z }
}
pub fn magnitude(&self) -> f32 {
(self.x.powi(2) + self.y.powi(2) + self.z.powi(2)).sqrt()
}
pub fn dot(&self, other: &Self) -> f32 {
self.x * other.x + self.y * other.y + self.z * other.z
}
pub fn dist(&self, other: &Self) -> f32 {
let x = self.x - other.x;
let y = self.y - other.y;
let z = self.z - other.z;
f32::sqrt(x * x + y * y + z * z)
}
pub fn cross(&self, other: &Self) -> Self {
Self {
x: self.y * other.z - self.z * other.y,
y: self.z * other.x - self.x * other.z,
z: self.x * other.y - self.y * other.x,
}
}
pub fn normalize(&self) -> Self {
self.clone() / self.magnitude()
}
pub fn normalize_mut(&mut self) -> &mut Self {
let magnitude = self.clone().magnitude();
*self /= magnitude;
self
}
#[inline]
pub fn greatest_axis(&self) -> Axis3 {
if self.x > self.y {
if self.x > self.z {
Axis3::X
} else {
Axis3::Z
}
} else if self.y > self.z {
Axis3::Y
} else {
Axis3::Z
}
}
#[inline]
pub fn is_axis_aligned(&self) -> bool {
let mut num_zeros = 0u8;
num_zeros += u8::from(self.x == 0.0);
num_zeros += u8::from(self.y == 0.0);
num_zeros += u8::from(self.z == 0.0);
num_zeros >= 2
}
}
impl<T> Vector3<T> {
pub const fn new(x: T, y: T, z: T) -> Self {
Self { x, y, z }
}
}
impl<T: Copy> Vector3<T> {
pub const fn const_clone(&self) -> Self {
Self { ..*self }
}
pub const fn into_vector2(self) -> Vector2<T> {
Vector2 { x: self.x, y: self.y }
}
pub const fn into_vector2_and_z(self) -> (Vector2<T>, T) {
(Vector2 { x: self.x, y: self.y }, self.z)
}
}
impl<T> From<[T; 3]> for Vector3<T> {
fn from(value: [T; 3]) -> Self {
let [x, y, z] = value;
Self { x, y, z }
}
}
impl<T: Display> Display for Vector3<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} {} {}", self.x, self.y, self.z)
}
}
impl<T: Neg<Output = T>> Neg for Vector3<T> {
type Output = Vector3<T>;
fn neg(self) -> Self::Output {
Self { x: -self.x, y: -self.y, z: -self.z }
}
}
#[derive(AddRef, SubRef, MulRef, DivRef)]
#[derive(AddAssignRef, SubAssignRef, MulAssignRef, DivAssignRef)]
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Vector2<T> {
pub x: T,
pub y: T,
}
impl<T> Vector2<T> {
pub const fn new(x: T, y: T) -> Self {
Self { x, y }
}
}
impl Vector2<f32> {
pub const fn origin() -> Self {
Self::new(0.0, 0.0)
}
pub fn new_with_round(mut x: f32, mut y: f32, allow_frac: bool) -> Self {
if !allow_frac {
x = x.round();
y = y.round();
}
Self::new(x, y)
}
pub fn dist(&self, other: &Self) -> f32 {
let delta = self.clone() - other;
delta.x.hypot(delta.y)
}
}
impl<T: Copy> Vector2<T> {
pub const fn const_clone(&self) -> Self {
Self { ..*self }
}
pub const fn with_z(&self, z: T) -> Vector3<T> {
Vector3 { x: self.x, y: self.y, z }
}
}
impl<T: Neg<Output = T>> Neg for Vector2<T> {
type Output = Vector2<T>;
fn neg(self) -> Self::Output {
Self { x: -self.x, y: -self.y }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn greatest_axis() {
let nan = std::f32::NAN;
assert_eq!(Axis3::Z, Vector3::new(0.0, 0.0, 0.0).greatest_axis());
assert_eq!(Axis3::X, Vector3::new(1.0, 0.0, 0.0).greatest_axis());
assert_eq!(Axis3::Y, Vector3::new(0.0, 1.0, 0.0).greatest_axis());
assert_eq!(Axis3::Y, Vector3::new(1.0, 1.0, 0.0).greatest_axis());
assert_eq!(Axis3::Z, Vector3::new(0.0, 0.0, 1.0).greatest_axis());
assert_eq!(Axis3::Z, Vector3::new(1.0, 0.0, 1.0).greatest_axis());
assert_eq!(Axis3::Z, Vector3::new(0.0, 1.0, 1.0).greatest_axis());
assert_eq!(Axis3::Z, Vector3::new(1.0, 1.0, 1.0).greatest_axis());
assert_eq!(Axis3::Z, Vector3::new(nan, 0.0, 0.0).greatest_axis());
assert_eq!(Axis3::Y, Vector3::new(nan, 1.0, 0.0).greatest_axis());
assert_eq!(Axis3::Z, Vector3::new(2.0, nan, 0.0).greatest_axis());
assert_eq!(Axis3::Z, Vector3::new(nan, nan, 0.0).greatest_axis());
assert_eq!(Axis3::Z, Vector3::new(2.0, 1.0, nan).greatest_axis());
assert_eq!(Axis3::Z, Vector3::new(nan, 1.0, nan).greatest_axis());
assert_eq!(Axis3::Z, Vector3::new(2.0, nan, nan).greatest_axis());
assert_eq!(Axis3::Z, Vector3::new(nan, nan, nan).greatest_axis());
}
#[test]
fn axis_aligned() {
assert_eq!(true, Vector3::new(-0.0, 0.0, 0.0).is_axis_aligned());
assert_eq!(true, Vector3::new(1.0, -0.0, -0.0).is_axis_aligned());
assert_eq!(true, Vector3::new(0.0, 1.0, 0.0).is_axis_aligned());
assert_eq!(false, Vector3::new(1.0, 1.0, -0.0).is_axis_aligned());
assert_eq!(true, Vector3::new(0.0, -0.0, 1.0).is_axis_aligned());
assert_eq!(false, Vector3::new(1.0, -0.0, 1.0).is_axis_aligned());
assert_eq!(false, Vector3::new(-0.0, 1.0, 1.0).is_axis_aligned());
assert_eq!(false, Vector3::new(1.0, 1.0, 1.0).is_axis_aligned());
}
}