From c644b784a636554102ecd7e99b205da75abf3645 Mon Sep 17 00:00:00 2001 From: tolelom <98kimsungmin@naver.com> Date: Tue, 24 Mar 2026 19:45:28 +0900 Subject: [PATCH] feat(math): add Vec2 and Vec4 types Co-Authored-By: Claude Sonnet 4.6 --- crates/voltex_math/src/lib.rs | 5 ++ crates/voltex_math/src/vec2.rs | 106 +++++++++++++++++++++++++++++++ crates/voltex_math/src/vec4.rs | 112 +++++++++++++++++++++++++++++++++ 3 files changed, 223 insertions(+) create mode 100644 crates/voltex_math/src/vec2.rs create mode 100644 crates/voltex_math/src/vec4.rs diff --git a/crates/voltex_math/src/lib.rs b/crates/voltex_math/src/lib.rs index 1414e8b..d10a09d 100644 --- a/crates/voltex_math/src/lib.rs +++ b/crates/voltex_math/src/lib.rs @@ -1,2 +1,7 @@ +pub mod vec2; pub mod vec3; +pub mod vec4; + +pub use vec2::Vec2; pub use vec3::Vec3; +pub use vec4::Vec4; diff --git a/crates/voltex_math/src/vec2.rs b/crates/voltex_math/src/vec2.rs new file mode 100644 index 0000000..3e6a658 --- /dev/null +++ b/crates/voltex_math/src/vec2.rs @@ -0,0 +1,106 @@ +use std::ops::{Add, Sub, Mul, Neg}; + +/// 2D vector (f32) +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Vec2 { + pub x: f32, + pub y: f32, +} + +impl Vec2 { + pub const ZERO: Self = Self { x: 0.0, y: 0.0 }; + pub const ONE: Self = Self { x: 1.0, y: 1.0 }; + + pub const fn new(x: f32, y: f32) -> Self { + Self { x, y } + } + + pub fn dot(self, rhs: Self) -> f32 { + self.x * rhs.x + self.y * rhs.y + } + + pub fn length_squared(self) -> f32 { + self.dot(self) + } + + pub fn length(self) -> f32 { + self.length_squared().sqrt() + } + + pub fn normalize(self) -> Self { + let len = self.length(); + Self { + x: self.x / len, + y: self.y / len, + } + } +} + +impl Add for Vec2 { + type Output = Self; + fn add(self, rhs: Self) -> Self { + Self { x: self.x + rhs.x, y: self.y + rhs.y } + } +} + +impl Sub for Vec2 { + type Output = Self; + fn sub(self, rhs: Self) -> Self { + Self { x: self.x - rhs.x, y: self.y - rhs.y } + } +} + +impl Mul for Vec2 { + type Output = Self; + fn mul(self, rhs: f32) -> Self { + Self { x: self.x * rhs, y: self.y * rhs } + } +} + +impl Neg for Vec2 { + type Output = Self; + fn neg(self) -> Self { + Self { x: -self.x, y: -self.y } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_new() { + let v = Vec2::new(1.0, 2.0); + assert_eq!(v.x, 1.0); + assert_eq!(v.y, 2.0); + } + + #[test] + fn test_add() { + let a = Vec2::new(1.0, 2.0); + let b = Vec2::new(3.0, 4.0); + let c = a + b; + assert_eq!(c, Vec2::new(4.0, 6.0)); + } + + #[test] + fn test_dot() { + let a = Vec2::new(1.0, 2.0); + let b = Vec2::new(3.0, 4.0); + assert_eq!(a.dot(b), 11.0); + } + + #[test] + fn test_length() { + let v = Vec2::new(3.0, 4.0); + assert!((v.length() - 5.0).abs() < f32::EPSILON); + } + + #[test] + fn test_normalize() { + let v = Vec2::new(4.0, 0.0); + let n = v.normalize(); + assert!((n.length() - 1.0).abs() < 1e-6); + assert_eq!(n, Vec2::new(1.0, 0.0)); + } +} diff --git a/crates/voltex_math/src/vec4.rs b/crates/voltex_math/src/vec4.rs new file mode 100644 index 0000000..afc2d8e --- /dev/null +++ b/crates/voltex_math/src/vec4.rs @@ -0,0 +1,112 @@ +use std::ops::{Add, Sub, Mul, Neg}; + +use crate::Vec3; + +/// 4D vector (f32) +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Vec4 { + pub x: f32, + pub y: f32, + pub z: f32, + pub w: f32, +} + +impl Vec4 { + pub const ZERO: Self = Self { x: 0.0, y: 0.0, z: 0.0, w: 0.0 }; + pub const ONE: Self = Self { x: 1.0, y: 1.0, z: 1.0, w: 1.0 }; + + pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self { + Self { x, y, z, w } + } + + pub fn from_vec3(v: Vec3, w: f32) -> Self { + Self { x: v.x, y: v.y, z: v.z, w } + } + + pub fn xyz(self) -> Vec3 { + Vec3::new(self.x, self.y, self.z) + } + + pub fn dot(self, rhs: Self) -> f32 { + self.x * rhs.x + self.y * rhs.y + self.z * rhs.z + self.w * rhs.w + } + + pub fn length_squared(self) -> f32 { + self.dot(self) + } + + pub fn length(self) -> f32 { + self.length_squared().sqrt() + } +} + +impl Add for Vec4 { + type Output = Self; + 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 Vec4 { + type Output = Self; + 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 for Vec4 { + type Output = Self; + fn mul(self, rhs: f32) -> Self { + Self { x: self.x * rhs, y: self.y * rhs, z: self.z * rhs, w: self.w * rhs } + } +} + +impl Neg for Vec4 { + type Output = Self; + fn neg(self) -> Self { + Self { x: -self.x, y: -self.y, z: -self.z, w: -self.w } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_new() { + let v = Vec4::new(1.0, 2.0, 3.0, 4.0); + assert_eq!(v.x, 1.0); + assert_eq!(v.y, 2.0); + assert_eq!(v.z, 3.0); + assert_eq!(v.w, 4.0); + } + + #[test] + fn test_from_vec3() { + let v3 = Vec3::new(1.0, 2.0, 3.0); + let v4 = Vec4::from_vec3(v3, 1.0); + assert_eq!(v4, Vec4::new(1.0, 2.0, 3.0, 1.0)); + } + + #[test] + fn test_xyz() { + let v4 = Vec4::new(1.0, 2.0, 3.0, 4.0); + let v3 = v4.xyz(); + assert_eq!(v3, Vec3::new(1.0, 2.0, 3.0)); + } + + #[test] + fn test_dot() { + let a = Vec4::new(1.0, 2.0, 3.0, 4.0); + let b = Vec4::new(5.0, 6.0, 7.0, 8.0); + assert_eq!(a.dot(b), 70.0); + } + + #[test] + fn test_add() { + let a = Vec4::new(1.0, 2.0, 3.0, 4.0); + let b = Vec4::new(5.0, 6.0, 7.0, 8.0); + let c = a + b; + assert_eq!(c, Vec4::new(6.0, 8.0, 10.0, 12.0)); + } +}