crystal_ball 0.3.0

A path tracing library written in Rust.
Documentation
use std::convert::TryFrom;
use std::ops::{
    Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
};

use nanorand::tls::TlsWyRand;

use crate::math::Vec3;
use crate::util::random_float;

/// A 4-dimensional vector.
#[derive(Copy, Clone, Default, Debug, PartialEq)]
pub struct Vec4 {
    pub x: f64,
    pub y: f64,
    pub z: f64,
    pub w: f64,
}

impl Vec4 {
    pub const X: Vec4 = Vec4 {
        x: 1.0,
        y: 0.0,
        z: 0.0,
        w: 0.0,
    };

    pub const Y: Vec4 = Vec4 {
        x: 0.0,
        y: 1.0,
        z: 0.0,
        w: 0.0,
    };

    pub const Z: Vec4 = Vec4 {
        x: 0.0,
        y: 0.0,
        z: 1.0,
        w: 0.0,
    };

    pub const W: Vec4 = Vec4 {
        x: 0.0,
        y: 0.0,
        z: 0.0,
        w: 1.0,
    };

    pub const ZERO: Vec4 = Vec4 {
        x: 0.0,
        y: 0.0,
        z: 0.0,
        w: 0.0,
    };

    /// Create a new [`Vec4`].
    pub fn new(x: f64, y: f64, z: f64, w: f64) -> Self {
        Vec4 { x, y, z, w }
    }

    /// Create a new [`Vec4`] with all elements set to `value`.
    pub fn splat(value: f64) -> Self {
        Vec4 {
            x: value,
            y: value,
            z: value,
            w: value,
        }
    }

    /// Generate a Vec4 where each component is a uniform random number between `min` and `max`.
    pub fn random(rng: &mut TlsWyRand, min: f64, max: f64) -> Self {
        Vec4 {
            x: random_float(rng, min, max),
            y: random_float(rng, min, max),
            z: random_float(rng, min, max),
            w: random_float(rng, min, max),
        }
    }

    /// Calculate the vector's magnitude (length).
    #[doc(alias = "length")]
    pub fn magnitude(&self) -> f64 {
        Self::dot(*self, *self).sqrt()
    }

    /// Calculate the square of the vector's magnitude (length).
    #[doc(alias = "length_squared")]
    pub fn magnitude_squared(&self) -> f64 {
        Self::dot(*self, *self)
    }

    /// Return the absolute value (synonym [`magnitude`](Self::magnitude)).
    pub fn abs(&self) -> f64 {
        self.magnitude()
    }

    /// Return the unit vector parallel to self.
    ///
    /// # Panics
    ///
    /// Panics if `self` cannot be normalized.
    pub fn normalize(&self) -> Self {
        assert_ne!(self.magnitude(), 0.0, "Can't normalize zero vector");

        *self / self.magnitude()
    }

    /// Calculate the dot product between two [`Vec3`]s.
    pub fn dot(vec_a: Vec4, vec_b: Vec4) -> f64 {
        vec_a.x * vec_b.x + vec_a.y * vec_b.y + vec_a.z * vec_b.z + vec_a.w * vec_b.w
    }

    /// Create a new [`Vec3`] discarding the `w` value.
    pub fn xyz(&self) -> Vec3 {
        Vec3::new(self.x, self.y, self.z)
    }
}

impl Add<Vec4> for Vec4 {
    type Output = Vec4;

    fn add(self, rhs: Vec4) -> Self::Output {
        Vec4 {
            x: self.x + rhs.x,
            y: self.y + rhs.y,
            z: self.z + rhs.z,
            w: self.w + rhs.w,
        }
    }
}

impl AddAssign<Vec4> for Vec4 {
    fn add_assign(&mut self, rhs: Vec4) {
        *self = *self + rhs;
    }
}

impl Sub<Vec4> for Vec4 {
    type Output = Vec4;

    fn sub(self, rhs: Vec4) -> Self::Output {
        Vec4 {
            x: self.x - rhs.x,
            y: self.y - rhs.y,
            z: self.z - rhs.z,
            w: self.w - rhs.w,
        }
    }
}

impl SubAssign<Vec4> for Vec4 {
    fn sub_assign(&mut self, rhs: Vec4) {
        *self = *self - rhs;
    }
}

impl Mul<f64> for Vec4 {
    type Output = Vec4;

    fn mul(self, rhs: f64) -> Self::Output {
        Vec4 {
            x: self.x * rhs,
            y: self.y * rhs,
            z: self.z * rhs,
            w: self.w * rhs,
        }
    }
}

impl MulAssign<f64> for Vec4 {
    fn mul_assign(&mut self, rhs: f64) {
        *self = *self * rhs;
    }
}

impl Mul<Vec4> for f64 {
    type Output = Vec4;

    fn mul(self, rhs: Vec4) -> Self::Output {
        rhs * self
    }
}

impl Div<f64> for Vec4 {
    type Output = Vec4;

    fn div(self, rhs: f64) -> Self::Output {
        let rhs_inverse = 1.0 / rhs;

        Vec4 {
            x: self.x * rhs_inverse,
            y: self.y * rhs_inverse,
            z: self.z * rhs_inverse,
            w: self.w * rhs_inverse,
        }
    }
}

impl DivAssign<f64> for Vec4 {
    fn div_assign(&mut self, rhs: f64) {
        *self = *self / rhs;
    }
}

impl Neg for Vec4 {
    type Output = Vec4;

    fn neg(self) -> Self::Output {
        Vec4 {
            x: -self.x,
            y: -self.y,
            z: -self.z,
            w: -self.w,
        }
    }
}

impl From<[f64; 4]> for Vec4 {
    fn from(s: [f64; 4]) -> Self {
        Vec4 {
            x: s[0],
            y: s[1],
            z: s[2],
            w: s[3],
        }
    }
}

impl From<(f64, f64, f64, f64)> for Vec4 {
    fn from(t: (f64, f64, f64, f64)) -> Self {
        Vec4 {
            x: t.0,
            y: t.1,
            z: t.2,
            w: t.3,
        }
    }
}

impl TryFrom<Vec<f64>> for Vec4 {
    type Error = &'static str;

    fn try_from(v: Vec<f64>) -> Result<Self, Self::Error> {
        if v.len() != 4 {
            Err("Vec4 can only be build from a vector of length 4.")
        } else {
            Ok(Vec4 {
                x: v[0],
                y: v[1],
                z: v[2],
                w: v[3],
            })
        }
    }
}

impl TryFrom<&[f64]> for Vec4 {
    type Error = &'static str;

    fn try_from(s: &[f64]) -> Result<Self, Self::Error> {
        if s.len() != 4 {
            Err("Vec4 can only be build from a slice of length 4.")
        } else {
            Ok(Vec4 {
                x: s[0],
                y: s[1],
                z: s[2],
                w: s[3],
            })
        }
    }
}

impl Index<usize> for Vec4 {
    type Output = f64;

    fn index(&self, index: usize) -> &Self::Output {
        match index {
            0 => &self.x,
            1 => &self.y,
            2 => &self.z,
            3 => &self.w,
            _ => panic!(
                "index out of bounds: the len is 4 but the index is {}",
                index
            ),
        }
    }
}

impl IndexMut<usize> for Vec4 {
    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
        match index {
            0 => &mut self.x,
            1 => &mut self.y,
            2 => &mut self.z,
            3 => &mut self.w,
            _ => panic!(
                "index out of bounds: the len is 4 but the index is {}",
                index
            ),
        }
    }
}