mod convert;
mod float_lerp;
mod ops;
use float_lerp::Lerp;
use rand::{thread_rng, Rng};
#[derive(Debug, Default, Clone, Copy, PartialEq)]
pub struct Vector3 {
x: f64,
y: f64,
z: f64,
}
const X_AXIS: Vector3 = Vector3 { x: 1.0, y: 0.0, z: 0.0 };
const Y_AXIS: Vector3 = Vector3 { x: 0.0, y: 1.0, z: 0.0 };
const Z_AXIS: Vector3 = Vector3 { x: 0.0, y: 0.0, z: 1.0 };
const VECTOR3_ZERO: Vector3 = Vector3 { x: 0.0, y: 0.0, z: 0.0 };
const VECTOR3_ONE: Vector3 = Vector3 { x: 1.0, y: 1.0, z: 1.0 };
impl Vector3 {
pub fn new(x: f64, y: f64, z: f64) -> Self {
if x.is_nan() || y.is_nan() || z.is_nan() {
panic!("Vector3 cannot have NaN coordinates!");
}
Vector3 { x, y, z }
}
pub fn from_i32(x: i32, y: i32, z: i32) -> Self {
Vector3 {x: x as f64, y: y as f64, z: z as f64}
}
pub fn from_u32(x: u32, y: u32, z: u32) -> Self {
Vector3 {x: x as f64, y: y as f64, z: z as f64}
}
pub fn from_i64(x: i64, y: i64, z: i64) -> Self {
Vector3 {x: x as f64, y: y as f64, z: z as f64}
}
pub fn from_u64(x: u64, y: u64, z: u64) -> Self {
Vector3 {x: x as f64, y: y as f64, z: z as f64}
}
pub fn random() -> Self {
Vector3 {
x: thread_rng().gen(),
y: thread_rng().gen(),
z: thread_rng().gen(),
}
}
pub fn x_axis() -> &'static Self {
&X_AXIS
}
pub fn y_axis() -> &'static Self {
&Y_AXIS
}
pub fn z_axis() -> &'static Self {
&Z_AXIS
}
pub fn zero() -> &'static Self {
&VECTOR3_ZERO
}
pub fn one() -> &'static Self {
&VECTOR3_ONE
}
pub fn normalize(&mut self) {
*self /= self.magnitude();
}
pub fn magnitude(&self) -> f64 {
(self.x * self.x + self.y * self.y + self.z * self.z).sqrt()
}
pub fn lerp(&self, target: &Self, alpha: f64) -> Self {
Vector3 {
x: self.x.lerp(target.x, alpha),
y: self.y.lerp(target.y, alpha),
z: self.z.lerp(target.z, alpha),
}
}
pub fn dot(&self, target: &Self) -> f64 {
self.x * target.x + self.y * target.y + self.z * target.z
}
pub fn cross(&self, target: &Self) -> Vector3 {
Vector3 {
x: self.y * target.z - self.z * target.y,
y: self.z * target.x - self.x * target.z,
z: self.x * target.y - self.y * target.x,
}
}
pub fn max(&self, target: &Self) -> Self {
Vector3 {
x: self.x.max(target.x),
y: self.y.max(target.y),
z: self.z.max(target.z),
}
}
pub fn min(&self, target: &Self) -> Self {
Vector3 {
x: self.x.min(target.x),
y: self.y.min(target.y),
z: self.z.min(target.z),
}
}
pub fn angle(&self, target: &Self) -> f64 {
let dot_product = self.dot(target);
let magnitude_product = self.magnitude() * target.magnitude();
(dot_product / magnitude_product).acos()
}
pub fn angle_deg(&self, target: &Self) -> f64 {
self.angle(target) * (180.0 / std::f64::consts::PI)
}
pub fn fuzzy_equal(&self, target: &Self, epsilon: f64) -> bool {
(self.x - target.x).abs() <= epsilon
&& (self.y - target.y).abs() <= epsilon
&& (self.z - target.z).abs() <= epsilon
}
pub fn get_x(&self) -> f64 {
self.x
}
pub fn get_y(&self) -> f64 {
self.y
}
pub fn get_z(&self) -> f64 {
self.z
}
}
impl std::fmt::Display for Vector3 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Vector3({}, {}, {})", self.x, self.y, self.z)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn angle() {
let angle = 1.5707963267948966;
let calc_angle = Vector3::x_axis().angle(Vector3::y_axis());
assert_eq!(calc_angle, angle);
}
#[test]
fn create() {
let my_vec = Vector3::new(1.3, 0.0, -5.35501);
assert_eq!(my_vec.get_x(), 1.3);
assert_eq!(my_vec.get_y(), 0.0);
assert_eq!(my_vec.get_z(), -5.35501);
}
#[test]
fn sum() {
let vec1 = Vector3::new(1.0, 2.0, 3.0);
let vec2 = Vector3::new(5.0, 0.0, -1.0);
assert_eq!(vec1 + vec2, Vector3::new(6.0, 2.0, 2.0));
}
#[test]
fn normalization() {
let mut test_vec = Vector3::new(1.0, 2.3, 100.123);
test_vec.normalize();
assert_eq!(
test_vec,
Vector3::new(0.00998458316076644, 0.02296454126976281, 0.9996864198054183)
);
assert!((1.0 - test_vec.magnitude()).abs() < 0.00000001);
}
#[test]
fn lerp() {
let start = Vector3::new(0.0, 0.0, 0.0);
let end = Vector3::new(1.0, 2.0, 3.0);
let lerp_result = start.lerp(&end, 0.75);
assert_eq!(lerp_result, Vector3::new(0.75, 1.5, 2.25));
}
#[test]
fn dot_product() {
let vec1 = Vector3::new(1.0, 2.0, 3.0);
let vec2 = Vector3::new(5.0, 0.0, -1.0);
let dot_result = vec1.dot(&vec2);
assert_eq!(dot_result, 2.0);
}
#[test]
fn cross_product() {
let vec1 = Vector3::new(1.0, 0.0, 0.0);
let vec2 = Vector3::new(0.0, 1.0, 0.0);
let cross_result = vec1.cross(&vec2);
assert_eq!(cross_result, Vector3::new(0.0, 0.0, 1.0));
}
#[test]
fn max_components() {
let vec1 = Vector3::new(1.0, 5.0, 3.0);
let vec2 = Vector3::new(3.0, 2.0, 4.0);
let max_result = vec1.max(&vec2);
assert_eq!(max_result, Vector3::new(3.0, 5.0, 4.0));
}
#[test]
fn min_components() {
let vec1 = Vector3::new(1.0, 5.0, 3.0);
let vec2 = Vector3::new(3.0, 2.0, 4.0);
let min_result = vec1.min(&vec2);
assert_eq!(min_result, Vector3::new(1.0, 2.0, 3.0));
}
#[test]
fn fuzzy_equality() {
let vec1 = Vector3::new(1.0, 2.0, 3.0);
let vec2 = Vector3::new(1.01, 1.99, 3.01);
let epsilon = 0.02;
let fuzzy_equal_result = vec1.fuzzy_equal(&vec2, epsilon);
assert!(fuzzy_equal_result);
}
}