Tuples and Vectors
The first chapter of the book deals with writing a data structure called Tuple. Tuples are used as a way to represent points and vectors.
To create the data type of tuple, I defined the struct as follows:
struct Tuple {
x: f32,
y: f32,
z: f32,
w: f32
}
While x, y and z are cartesian coordinates, w is used to identify between a vector and a point. For a vector, w is set to 0 and for a point, it is set to 1.
Afterwards, numerous operations are written:
• addition of vectors • subtraction of vectors, and subtraction of points which lead to a vector • negation of points and vectors • the dot product of vectors • the cross product of vectors • the magnitude of a vector • normalising a vector
Overloading is not a concept in Rust, therefore to implement these operations I rely on traits. Traits are defined abstractly, but not implemented. They are implemented differently for each struct.
Addition
I implement the addition operation as follows
impl Add for Tuple {
type Output = Tuple;
fn add(self, other: Tuple) -> Tuple {
Tuple {
x: self.x + other.x,
y: self.y + other.y,
z: self.z + other.z,
w: self.w + other.w,
}
}
}
Subtraction
impl Sub for Tuple {
type Output = Tuple;
fn sub(self, other: Tuple) -> Tuple {
Tuple {
x: self.x - other.x,
y: self.y - other.y,
z: self.z - other.z,
w: self.w - other.w,
}
}
}
I implement the subtraction operation in a similar way to how the addition operation was implemented.
Negation
impl Neg for Tuple {
type Output = Tuple;
fn neg(self) -> Self::Output {
Tuple {
x: -self.x,
y: -self.y,
z: -self.z,
w: -self.w
}
}
}
Scalar Multiplication with Vector
impl Mul<f32> for Tuple {
type Output = Tuple;
fn mul (self, rhs: f32) -> Tuple {
Tuple { x: rhs*self.x, y: rhs*self.y, z: rhs*self.z, w: rhs*self.w }
}
}
This seems a bit different as here the multiplication trait is informed that a float value will be multiplied with this Tuple object and thus the slight change in syntax.
Dot Product
impl Mul<Tuple> for Tuple {
type Output = f32;
fn mul (self, other: Tuple) -> f32 {
return self.x *other.x + self.y*other.y + self.z*other.z + self.w*other.w;
}
}
The dot product is implemented similarly to scalar multiplication.
Cross Product
Previously, I was overloading the +, -, * signs. However, for the cross-product, I don't have an operation. I write a method which takes two vectors as input and outputs the cross-product vector.
fn cross_product(a: Tuple, b: Tuple) -> Tuple {
Tuple { 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, w: 0.0 }
}
Magnitude and Normalisation
As these are related to each vector, I added these functions to the implementation of the Tuple struct as follows:
impl Tuple {
fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
Self {
x: x,
y: y,
z: z,
w: w,
}
}
fn magnitude(&self) -> f32 {
self.x*self.x + self.y*self.y +self.z*self.z + self.w*self.w
}
fn normalize(&self) -> Tuple {
let mag = self.magnitude();
Tuple { x: self.x/mag, y: self.y/mag, z: self.z/mag, w: self.w/mag }
}
}
This concluded all the methods the chapter required us to write, however, an interesting point to note is that I don't have checks to see that operations such as cross-product, dot-product, magnitude and normalisation can only operate on vectors and not points. I currently avoid this and would return when I learn about error handling in Rust.