Ian Chamberlain
1 year ago
3 changed files with 298 additions and 4 deletions
@ -0,0 +1,177 @@ |
|||||||
|
//! Floating-point vectors.
|
||||||
|
|
||||||
|
use std::fmt; |
||||||
|
|
||||||
|
/// A vector of `f32`s.
|
||||||
|
#[derive(Clone, Copy)] |
||||||
|
pub struct FVec<const N: usize>(pub(super) citro3d_sys::C3D_FVec); |
||||||
|
|
||||||
|
/// A 3-vector of `f32`s.
|
||||||
|
pub type FVec3 = FVec<3>; |
||||||
|
|
||||||
|
/// A 4-vector of `f32`s.
|
||||||
|
pub type FVec4 = FVec<4>; |
||||||
|
|
||||||
|
impl<const N: usize> fmt::Debug for FVec<N> { |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||||
|
let inner = unsafe { self.0.c }; |
||||||
|
f.debug_tuple(std::any::type_name::<Self>()) |
||||||
|
.field(&inner) |
||||||
|
.finish() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<Rhs, const N: usize> PartialEq<Rhs> for FVec<N> |
||||||
|
where |
||||||
|
Rhs: Copy, |
||||||
|
Self: From<Rhs>, |
||||||
|
{ |
||||||
|
fn eq(&self, other: &Rhs) -> bool { |
||||||
|
unsafe { self.0.c == Self::from(*other).0.c } |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<const N: usize> Eq for FVec<N> {} |
||||||
|
|
||||||
|
impl FVec4 { |
||||||
|
/// Create a new [`FVec4`] from its components.
|
||||||
|
pub fn new(x: f32, y: f32, z: f32, w: f32) -> Self { |
||||||
|
Self(unsafe { citro3d_sys::FVec4_New(x, y, z, w) }) |
||||||
|
} |
||||||
|
|
||||||
|
/// Create a new [`FVec4`], setting each component to `v`.
|
||||||
|
pub fn splat(v: f32) -> Self { |
||||||
|
Self::new(v, v, v, v) |
||||||
|
} |
||||||
|
|
||||||
|
/// The vector's `w` component (sometimes also called the `r` component of `ijk[r]`).
|
||||||
|
#[doc(alias = "r")] |
||||||
|
pub fn w(&self) -> f32 { |
||||||
|
unsafe { self.0.__bindgen_anon_1.w } |
||||||
|
} |
||||||
|
|
||||||
|
/// Divide the vector's XYZ components by its W component.
|
||||||
|
pub fn perspective_divide(&self) -> Self { |
||||||
|
Self(unsafe { citro3d_sys::FVec4_PerspDivide(self.0) }) |
||||||
|
} |
||||||
|
|
||||||
|
/// The dot product of two vectors.
|
||||||
|
pub fn dot(&self, rhs: &Self) -> f32 { |
||||||
|
unsafe { citro3d_sys::FVec3_Dot(self.0, rhs.0) } |
||||||
|
} |
||||||
|
|
||||||
|
/// The magnitude of the vector.
|
||||||
|
pub fn magnitude(&self) -> f32 { |
||||||
|
unsafe { citro3d_sys::FVec3_Magnitude(self.0) } |
||||||
|
} |
||||||
|
|
||||||
|
/// Normalize the vector to a magnitude of `1.0`.
|
||||||
|
pub fn normalize(&self) -> Self { |
||||||
|
Self(unsafe { citro3d_sys::FVec3_Normalize(self.0) }) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl FVec3 { |
||||||
|
/// Create a new [`FVec3`] from its components.
|
||||||
|
pub fn new(x: f32, y: f32, z: f32) -> Self { |
||||||
|
Self(unsafe { citro3d_sys::FVec3_New(x, y, z) }) |
||||||
|
} |
||||||
|
|
||||||
|
/// Create a new [`FVec3`], setting each component to the given `v`.
|
||||||
|
pub fn splat(v: f32) -> Self { |
||||||
|
Self::new(v, v, v) |
||||||
|
} |
||||||
|
|
||||||
|
/// The distance between two points in 3D space.
|
||||||
|
pub fn distance(&self, rhs: &Self) -> f32 { |
||||||
|
unsafe { citro3d_sys::FVec3_Distance(self.0, rhs.0) } |
||||||
|
} |
||||||
|
|
||||||
|
/// The cross product of two 3D vectors.
|
||||||
|
pub fn cross(&self, rhs: &Self) -> Self { |
||||||
|
Self(unsafe { citro3d_sys::FVec3_Cross(self.0, rhs.0) }) |
||||||
|
} |
||||||
|
|
||||||
|
/// The dot product of two vectors.
|
||||||
|
pub fn dot(&self, rhs: &Self) -> f32 { |
||||||
|
unsafe { citro3d_sys::FVec3_Dot(self.0, rhs.0) } |
||||||
|
} |
||||||
|
|
||||||
|
/// The magnitude of the vector.
|
||||||
|
pub fn magnitude(&self) -> f32 { |
||||||
|
unsafe { citro3d_sys::FVec3_Magnitude(self.0) } |
||||||
|
} |
||||||
|
|
||||||
|
/// Normalize the vector to a magnitude of `1.0`.
|
||||||
|
pub fn normalize(&self) -> Self { |
||||||
|
Self(unsafe { citro3d_sys::FVec3_Normalize(self.0) }) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<const N: usize> FVec<N> { |
||||||
|
/// The vector's `x` component (sometimes also called the `i` component of `ijk[r]`).
|
||||||
|
pub fn x(&self) -> f32 { |
||||||
|
unsafe { self.0.__bindgen_anon_1.x } |
||||||
|
} |
||||||
|
|
||||||
|
/// The vector's `y` component (sometimes also called the `j` component of `ijk[r]`).
|
||||||
|
pub fn y(&self) -> f32 { |
||||||
|
unsafe { self.0.__bindgen_anon_1.y } |
||||||
|
} |
||||||
|
|
||||||
|
/// The vector's `i` component (sometimes also called the `k` component of `ijk[r]`).
|
||||||
|
pub fn z(&self) -> f32 { |
||||||
|
unsafe { self.0.__bindgen_anon_1.z } |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(test)] |
||||||
|
mod tests { |
||||||
|
use super::*; |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn fvec4() { |
||||||
|
let l = FVec4::new(2.0, 2.0, 2.0, 2.0); |
||||||
|
|
||||||
|
assert_eq!(l, FVec4::splat(2.0)); |
||||||
|
|
||||||
|
for component in [l.x(), l.y(), l.z(), l.w()] { |
||||||
|
assert!((component - 2.0).abs() < f32::EPSILON); |
||||||
|
} |
||||||
|
|
||||||
|
assert_eq!(l.perspective_divide(), FVec4::splat(1.0)); |
||||||
|
|
||||||
|
let dot = l.dot(&FVec4::splat(3.0)); |
||||||
|
assert!((dot - 24.0).abs() < f32::EPSILON); |
||||||
|
|
||||||
|
assert!((l.magnitude() - 8.0).abs() < f32::EPSILON); |
||||||
|
|
||||||
|
let norm = l.normalize(); |
||||||
|
assert!((norm.magnitude() - 1.0).abs() < f32::EPSILON); |
||||||
|
for component in [l.y(), l.z(), l.w()] { |
||||||
|
assert!((component - l.x()).abs() < f32::EPSILON); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn fvec3() { |
||||||
|
let l = FVec3::new(2.0, 2.0, 2.0); |
||||||
|
|
||||||
|
assert_eq!(l, FVec3::splat(2.0)); |
||||||
|
|
||||||
|
for component in [l.x(), l.y(), l.z()] { |
||||||
|
assert!((component - 2.0).abs() < f32::EPSILON); |
||||||
|
} |
||||||
|
|
||||||
|
let dot = l.dot(&FVec3::splat(3.0)); |
||||||
|
assert!((dot - 18.0).abs() < f32::EPSILON); |
||||||
|
|
||||||
|
assert!((l.magnitude() - 8.0).abs() < f32::EPSILON); |
||||||
|
|
||||||
|
let norm = l.normalize(); |
||||||
|
assert!((norm.magnitude() - 1.0).abs() < f32::EPSILON); |
||||||
|
for component in [l.y(), l.z()] { |
||||||
|
assert!((l.x() - component).abs() < f32::EPSILON); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,114 @@ |
|||||||
|
use std::borrow::Borrow; |
||||||
|
use std::ops::{Add, Div, Mul, Neg, Sub}; |
||||||
|
|
||||||
|
use super::{FVec, FVec3, FVec4}; |
||||||
|
|
||||||
|
impl<Rhs: Borrow<Self>> Add<Rhs> for FVec4 { |
||||||
|
type Output = Self; |
||||||
|
|
||||||
|
fn add(self, rhs: Rhs) -> Self::Output { |
||||||
|
Self(unsafe { citro3d_sys::FVec4_Add(self.0, rhs.borrow().0) }) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<Rhs: Borrow<Self>> Sub<Rhs> for FVec4 { |
||||||
|
type Output = Self; |
||||||
|
|
||||||
|
fn sub(self, rhs: Rhs) -> Self::Output { |
||||||
|
Self(unsafe { citro3d_sys::FVec4_Add(self.0, rhs.borrow().0) }) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Neg for FVec4 { |
||||||
|
type Output = Self; |
||||||
|
|
||||||
|
fn neg(self) -> Self::Output { |
||||||
|
Self(unsafe { citro3d_sys::FVec4_Negate(self.0) }) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Mul<f32> for FVec4 { |
||||||
|
type Output = Self; |
||||||
|
|
||||||
|
fn mul(self, rhs: f32) -> Self::Output { |
||||||
|
Self(unsafe { citro3d_sys::FVec4_Scale(self.0, rhs) }) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<Rhs: Borrow<Self>> Add<Rhs> for FVec3 { |
||||||
|
type Output = Self; |
||||||
|
|
||||||
|
fn add(self, rhs: Rhs) -> Self::Output { |
||||||
|
Self(unsafe { citro3d_sys::FVec3_Add(self.0, rhs.borrow().0) }) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<Rhs: Borrow<Self>> Sub<Rhs> for FVec3 { |
||||||
|
type Output = Self; |
||||||
|
|
||||||
|
fn sub(self, rhs: Rhs) -> Self::Output { |
||||||
|
Self(unsafe { citro3d_sys::FVec3_Add(self.0, rhs.borrow().0) }) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Neg for FVec3 { |
||||||
|
type Output = Self; |
||||||
|
|
||||||
|
fn neg(self) -> Self::Output { |
||||||
|
Self(unsafe { citro3d_sys::FVec3_Negate(self.0) }) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Mul<f32> for FVec3 { |
||||||
|
type Output = Self; |
||||||
|
|
||||||
|
fn mul(self, rhs: f32) -> Self::Output { |
||||||
|
Self(unsafe { citro3d_sys::FVec3_Scale(self.0, rhs) }) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<const N: usize> Div<f32> for FVec<N> |
||||||
|
where |
||||||
|
FVec<N>: Mul<f32>, |
||||||
|
{ |
||||||
|
type Output = <Self as Mul<f32>>::Output; |
||||||
|
|
||||||
|
fn div(self, rhs: f32) -> Self::Output { |
||||||
|
self * (1.0 / rhs) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(test)] |
||||||
|
mod tests { |
||||||
|
#![allow(clippy::op_ref)] |
||||||
|
|
||||||
|
use super::*; |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn vec3_ops() { |
||||||
|
let l = FVec3::splat(1.0); |
||||||
|
let r = FVec3::splat(2.0); |
||||||
|
|
||||||
|
assert_eq!(l + r, FVec3::splat(3.0)); |
||||||
|
assert_eq!(l + &r, FVec3::splat(3.0)); |
||||||
|
assert_eq!(l - r, FVec3::splat(-1.0)); |
||||||
|
assert_eq!(l - &r, FVec3::splat(-1.0)); |
||||||
|
assert_eq!(-l, FVec3::splat(-1.0)); |
||||||
|
assert_eq!(l * 1.5, FVec3::splat(1.5)); |
||||||
|
assert_eq!(l / 2.0, FVec3::splat(0.5)); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn vec4_ops() { |
||||||
|
let l = FVec4::splat(1.0); |
||||||
|
let r = FVec4::splat(2.0); |
||||||
|
|
||||||
|
assert_eq!(l + r, FVec4::splat(3.0)); |
||||||
|
assert_eq!(l + &r, FVec4::splat(3.0)); |
||||||
|
assert_eq!(l - r, FVec4::splat(-1.0)); |
||||||
|
assert_eq!(l - &r, FVec4::splat(-1.0)); |
||||||
|
assert_eq!(-l, FVec4::splat(-1.0)); |
||||||
|
assert_eq!(l * 1.5, FVec4::splat(1.5)); |
||||||
|
assert_eq!(l / 2.0, FVec4::splat(0.5)); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue