1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//! We operate in a 3-D coordinate space. This is a helper struct for vector calculations.
use std::ops::{Add, Sub};

/// 3-D vector, this struct includes functions for conveniently perform
#[derive(Clone, Copy, Debug)]
pub struct Vec3 {
    x: f32,
    y: f32,
    z: f32,
}

impl Vec3 {
    /// Create an origin vector (0, 0, 0)
    pub fn orig() -> Self {
        Self {
            x: 0.,
            y: 0.,
            z: 0.,
        }
    }

    /// Create a new vector by specifying its coordinates
    pub fn new(v: (f32, f32, f32)) -> Self {
        Self {
            x: v.0,
            y: v.1,
            z: v.2,
        }
    }

    /// Get the [L2 norm](https://mathworld.wolfram.com/L2-Norm.html) of the vector.
    /// L_2 norm is the length of the vector, in 3-D space is basically the distance of a vector from the origin.
    /// Let say you have 2 vectors v1 and v2, running (v1-v2).l2() will give you the distance between those points.
    /// That is, the distance between v_1 and v_2 is the length of a vector from v_1 to v_2
    pub fn l2(&self) -> f32 {
        (self.x.powf(2.) + self.y.powf(2.) + self.z.powf(2.)).sqrt()
    }
    /// This gives us the [Dot Product](https://mathworld.wolfram.com/DotProduct.html) of 2 vectors.
    /// This is a very useful quantity for projection of vectors.
    /// Key property of the dot-product is this: ![](https://mathworld.wolfram.com/images/equations/DotProduct/NumberedEquation1.gif)
    /// What this means is that the dot-product of two vectors is a product of their lengths and the cosine of the angle between them. [Vector Projection](https://en.wikipedia.org/wiki/Vector_projection)
    ///
    /// ![](https://upload.wikimedia.org/wikipedia/commons/thumb/9/98/Projection_and_rejection.png/300px-Projection_and_rejection.png)
    pub fn dot(&self, other: &Self) -> f32 {
        self.x * other.x + self.y * other.y + self.z * other.z
    }

    /// [Cross Product](https://mathworld.wolfram.com/CrossProduct.html)
    ///
    /// This gives us a vector that is orthogonal to self and other with norm of ![](https://mathworld.wolfram.com/images/equations/CrossProduct/Inline42.gif)
    pub fn cross(&self, other: &Self) -> Self {
        let x = self.y * other.z - self.z * other.y;
        let y = self.z * other.x - self.x * other.z;
        let z = self.x * other.y - self.y * other.x;
        Self { x, y, z }
    }

    /// Helper function that finds the projection of a vector to another vector.
    /// I'm going to expand on this at some point. If you don't understand what's going on in this function I strongly recommend this series of videos
    /// [3Brown1Blue's Essence of Linear Algebra](https://www.youtube.com/playlist?list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab)
    pub fn project_on(&self, other: &Self) -> Self {
        let v = other.normalized();
        v.mult(self.dot(&v))
    }

    /// Get 1-norm vector
    pub fn normalized(&self) -> Self {
        let d = self.l2();
        if d == 0. {
            return *self;
        }
        Self {
            x: self.x / d,
            y: self.y / d,
            z: self.z / d,
        }
    }

    /// Multiply a vector by a scalar
    pub fn mult(&self, v: f32) -> Self {
        Self {
            x: self.x * v,
            y: self.y * v,
            z: self.z * v,
        }
    }

    /// Find a reflection of a vector, from a surface generated by a normal.
    pub fn reflect(&self, normal: Vec3) -> Self {
        let proj_to_normal = self.dot(&normal);
        *self - normal.mult(proj_to_normal).mult(2.)
    }

    pub fn refract(&self, normal: Vec3, refract_index: f32) -> Self {
        let cosi = -self.dot(&normal);
        let eta_i = 1f32;
        let eta_t = refract_index;
        let mut n = normal;

        let mut eta = eta_i / eta_t;

        if cosi < 0. {
            eta = 1. / eta;
            n = n.mult(-1.);
        }

        let k = 1. - eta.powf(2.) * (1. - cosi.powf(2.));

        if k < 0. {
            Self::orig()
        } else {
            self.mult(eta) + n.mult(eta * cosi - k.sqrt())
        }
    }
}

impl Sub for Vec3 {
    type Output = Vec3;

    fn sub(self, other: Self) -> Vec3 {
        Vec3 {
            x: self.x - other.x,
            y: self.y - other.y,
            z: self.z - other.z,
        }
    }
}

impl Add for Vec3 {
    type Output = Vec3;

    fn add(self, other: Self) -> Vec3 {
        Vec3 {
            x: self.x + other.x,
            y: self.y + other.y,
            z: self.z + other.z,
        }
    }
}