use super::vec2::Vec2;
use std::ops;
#[derive(Copy, Clone, PartialEq)]
pub struct Vec3 {
pub x: f32,
pub y: f32,
pub z: f32,
}
impl Vec3 {
pub fn new(x: f32, y: f32, z: f32) -> Self {
Vec3 { x, y, z }
}
pub fn from_vec3(xy: Vec2, z: f32) -> Self {
Vec3 {
x: xy.x,
y: xy.y,
z,
}
}
pub fn zero() -> Self {
Self {
x: 0.0,
y: 0.0,
z: 0.0,
}
}
pub fn one() -> Self {
Self {
x: 1.0,
y: 1.0,
z: 1.0,
}
}
pub fn forward() -> Self {
Self {
x: 0.0,
y: 0.0,
z: 1.0,
}
}
pub fn right() -> Self {
Self {
x: 1.0,
y: 0.0,
z: 0.0,
}
}
pub fn up() -> Self {
Self {
x: 0.0,
y: 1.0,
z: 0.0,
}
}
#[inline]
pub fn dot(a: &Self, b: &Self) -> f32 {
a.x * b.x + a.y * b.y + a.z * b.z
}
#[inline]
pub fn cross(a: &Self, b: &Self) -> Self {
Self {
x: a.y * b.z - a.z * b.y,
y: a.z * b.x - a.x * b.z,
z: a.x * b.y - a.y * b.x,
}
}
#[inline]
pub fn reflect(ray: Self, normal: Self) -> Self {
let n = normal.norm();
ray - n * Self::dot(&ray, &n) * 2.0
}
#[inline]
pub fn project(a: Self, b: Self) -> Self {
let b = b.norm();
b * Self::dot(&a, &b)
}
#[inline]
pub fn project_plane(a: Self, normal: Self) -> Self {
let normal = normal.norm();
a - normal * Self::dot(&a, &normal)
}
#[inline]
pub fn lerp(a: Self, b: Self, t: f32) -> Self {
let t = if t < 0.0 {
0.0
} else if t > 1.0 {
1.0
} else {
t
};
a * (1.0 - t) + b * t
}
#[inline]
pub fn lerp_unclamped(a: Self, b: Self, t: f32) -> Self {
a * (1.0 - t) + b * t
}
#[inline]
pub fn mag(&self) -> f32 {
(self.x * self.x + self.y * self.y + self.z * self.z).sqrt()
}
#[inline]
pub fn sqrmag(&self) -> f32 {
self.x * self.x + self.y * self.y + self.z * self.z
}
#[inline]
pub fn norm(&self) -> Vec3 {
*self / self.mag()
}
pub fn smallest(&self) -> f32 {
if self.x < self.y {
if self.x < self.z {
return self.x;
}
return self.z;
} else if self.y < self.x {
if self.y < self.z {
return self.y;
}
return self.z;
} else if self.z < self.x {
if self.z < self.y {
return self.z;
}
return self.y;
}
self.x
}
pub fn largest(&self) -> f32 {
if self.x > self.y {
if self.x > self.z {
return self.x;
}
return self.z;
} else if self.y > self.x {
if self.y > self.z {
return self.y;
}
return self.z;
} else if self.z > self.x {
if self.z > self.y {
return self.z;
}
return self.y;
}
self.x
}
#[inline]
pub fn xy(&self) -> Vec2 {
Vec2 {
x: self.x,
y: self.y,
}
}
}
impl std::fmt::Display for Vec3 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({}, {}, {})", self.x, self.y, self.z)
}
}
impl std::fmt::Debug for Vec3 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({}, {}, {})", self.x, self.y, self.z)
}
}
impl ops::Add for Vec3 {
type Output = Self;
#[inline]
fn add(self, other: Self) -> Self {
Self {
x: self.x + other.x,
y: self.y + other.y,
z: self.z + other.z,
}
}
}
impl ops::AddAssign for Vec3 {
#[inline]
fn add_assign(&mut self, other: Self) {
self.x += other.x;
self.y += other.y;
self.z += other.z;
}
}
impl ops::Sub for Vec3 {
type Output = Self;
#[inline]
fn sub(self, other: Self) -> Self {
Self {
x: self.x - other.x,
y: self.y - other.y,
z: self.z - other.z,
}
}
}
impl ops::SubAssign for Vec3 {
#[inline]
fn sub_assign(&mut self, other: Self) {
self.x -= other.x;
self.y -= other.y;
self.z -= other.z;
}
}
impl ops::Mul for Vec3 {
type Output = Vec3;
#[inline]
fn mul(self, other: Self) -> Self {
Vec3 {
x: self.x * other.x,
y: self.y * other.y,
z: self.z * other.z,
}
}
}
impl ops::MulAssign for Vec3 {
#[inline]
fn mul_assign(&mut self, other: Self) {
self.x *= other.x;
self.y *= other.y;
self.z *= other.z;
}
}
impl ops::Neg for Vec3 {
type Output = Vec3;
#[inline]
fn neg(self) -> Vec3 {
Vec3 {
x: -self.x,
y: -self.y,
z: -self.z,
}
}
}
impl ops::Mul<f32> for Vec3 {
type Output = Vec3;
#[inline]
fn mul(self, rhs: f32) -> Vec3 {
Vec3 {
x: self.x * rhs,
y: self.y * rhs,
z: self.z * rhs,
}
}
}
impl ops::MulAssign<f32> for Vec3 {
#[inline]
fn mul_assign(&mut self, rhs: f32) {
self.x *= rhs;
self.y *= rhs;
self.z *= rhs;
}
}
impl ops::Div<f32> for Vec3 {
type Output = Vec3;
#[inline]
fn div(self, rhs: f32) -> Self {
Vec3 {
x: self.x / rhs,
y: self.y / rhs,
z: self.z / rhs,
}
}
}
impl ops::DivAssign<f32> for Vec3 {
#[inline]
fn div_assign(&mut self, rhs: f32) {
self.x /= rhs;
self.y /= rhs;
self.z /= rhs;
}
}
impl From<(f32, f32, f32)> for Vec3 {
fn from(t: (f32, f32, f32)) -> Self {
Vec3 {
x: t.0,
y: t.1,
z: t.2,
}
}
}
impl From<(i32, i32, i32)> for Vec3 {
fn from(t: (i32, i32, i32)) -> Self {
Vec3 {
x: t.0 as f32,
y: t.1 as f32,
z: t.2 as f32,
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn ops() {
assert_eq!(
Vec3::new(1.0, 2.0, 3.0) + Vec3::new(4.0, -3.0, 5.0),
Vec3::new(5.0, -1.0, 8.0)
);
assert_eq!(
Vec3::new(1.0, 2.0, 3.0) - Vec3::new(4.0, -3.0, -0.0),
Vec3::new(-3.0, 5.0, 3.0)
);
assert_eq!(
Vec3::new(1.0, 2.0, 3.0) * Vec3::new(4.0, -3.0, 0.5),
Vec3::new(4.0, -6.0, 1.5)
);
}
#[test]
fn dot() {
assert_eq!(
Vec3::dot(&Vec3::new(4.0, 0.0, 0.0).norm(), &Vec3::new(0.0, 0.0, 2.0)),
0.0
);
assert_eq!(
Vec3::dot(&Vec3::new(4.0, 0.0, 4.0).norm(), &Vec3::new(1.0, 0.0, 0.0)),
1.0 / 2_f32.sqrt()
);
}
}