Ian Chamberlain
1 year ago
3 changed files with 298 additions and 4 deletions
@ -0,0 +1,177 @@
@@ -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 @@
@@ -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