A simple vector library in C++
August 31, 2011
I should start off by saying that if you are planning to do any serious work which requires vector mathematics, you should probably just use one of the many open source libraries or packages:
- Boost Basic Linear Algebra
- Eigen library
- GLM - API similar to GLSL
Vectors are really a solved problem. They are relatively simple, and there are many libraries which can handle any operation that you can think of efficiently. However, there is certainly value in rolling your own, especially if you are interested in understanding what is going on behind the scenes.
Purpose
Vectors can be used for all sorts of things. They are particularly useful for maintaining state in physics and particle simulations. It is logical to break up the world into an x, y, and z component, and then manipulate these components separately. These vectors can then be used to represent location, velocity, acceleration, or other multidimensional values that might be interesting.
Important operations
What do we need to be able to do with vectors? Here's a short list of what I would like out of a basic vector library:
- Vector addition and subtraction
- Scalar multiplication and division
- Equivalence operator
- Dot product
- Cross product
- Length
Depending on your needs, there are any number of operations that you may want to add, but these cover the basics.
Basic class
The first thing that we need to do is define the basic structure of the vector class which will house the vector components and the operations listed above. I've chosen to name this class vec3 (as in GLSL), and generally follow other naming conventions from GLSL wherever possible. I've chosen to do this in order to make switching between shader code and c++ code less jarring, even though it pains me to use a lowercase class name.
Anyhow, here's the class definition:
class vec3 {
public:
vec3();
vec3(float, float, float);
bool operator==(vec3 rhs);
vec3 operator+(vec3 rhs);
vec3 operator-(vec3 rhs);
vec3 operator*(float scalar);
vec3 operator/(float scalar);
vec3 cross(vec3 rhs);
float dot(vec3 rhs);
float length();
float x;
float y;
float z;
};
Constructors
Starting from the top, we have the default constructor which will just set x, y, and z to zero. The second constructor will set the values to the provided floats. These are both trivial, so I won't insult you with the actual implementation code.
Equivalence Operator
From there, we move on to the implementation of the equivalence operation. This is quite simple. It is simple the result of each component equality combined using the and operation. It's a simple one liner:
bool vec3::operator==(vec3 rhs) {
return(x == rhs.x && y == rhs.y && z == rhs.z);
}
Vector Arithmetic
Basic vector arithmetic is straight forward. The desired operation is simply carried out per component. So, this:
vec3 a = b + c;
Is the same as this:
vec3 a = vec3(b.x +c.x, b.y + c.y, b.z + c.z);
For simplicity, I'll just include the code for vector addition. The other operations are identical aside from using the appropriate operation:
vec3 vec3::operator+(vec3 rhs) {
return vec3( x + rhs.x,
y + rhs.y,
z + rhs.z);
}
Scalar arithmetic
It can sometimes be useful to apply the same scalar operation to each component of a vector. This is what the overloaded operators will do when passed a single float.
Here's an example with division. The other operations again follow the same form.
vec3 vec3::operator/(float scalar) {
return vec3(x / scalar,
y / scalar,
z / scalar);
}
Dot Product
Now that we have the basics covered, it's time to dig in to the slightly more complex operations. The vector dot product can be defined as the product of the vector magnitudes multiplied by the cosine of the angle between the vectors. More simply, it is the sum of the product of the each vector component. This can be easily implemented as follows:
float vec3::dot(vec3 rhs) {
return (x * rhs.x +
y * rhs.y +
z * rhs.z);
}
Note that the dot product always returns a single scalar value, so it is often also referred to as a scalar product.
Cross product
The magnitude of the cross product will be the area of the parallelogram which has the two vectors as sides. The direction of the cross product will be perpendicular to both vectors. The actual calculation of the cross product performed by the following routine:
vec3 vec3::cross(vec3 rhs) {
return vec3( y * rhs.z - z * rhs.y,
z * rhs.x - x * rhs.z,
x * rhs.y - y * rhs.x);
}
Length
To calculate the length of the vector, we can use the distance formula. For our three component vector, that is sqrt(x^2 + y^2 + z^2). Here's the code:
float vec3::length() {
return float(sqrt( x*x + y*y + z*z ));
}
Optimization
This is a post about how to create a basic vector library which can be easily read and understood. Therefore, even the most basic optimizations are left out. It would be quite beneficial to implement many of the operations using SIMD instruction which can handle processing all of the floats at once. This code probably isn't helpful in production, but sometimes it is great to just see how things work.
Check out my other pages tagged "blog".