Monday, October 20, 2014

Let's write a c++ math libraryI (part 2)


Last time we made a class to encapsulate a 2D vector with floating point entries.  In this post we'll finish up with the 3 and 4 dimensional vector classes.  None of these will be particularly useful however, until we make a matrix class that can transform these vectors.
So let's get started.

vector3f.h


#ifndef _VECTOR3F_H_
#define _VECTOR3F_H_

class vector3f
{
private:
    float elements[3];
public:

    static const int SIZE;

    vector3f(float x = 0.0f, float y = 0.0f, float z = 0.0f);

    vector3f(const vector3f& other);

    vector3f normal() const;

    void normalize();

    float length() const;

    static float dot(const vector3f& vector1, const vector3f& vector2);

    static vector3f cross(const vector3f& vector1, const vector3f& vector2);

    vector3f& operator=(const vector3f& other);

    vector3f operator-() const;

    vector3f operator*(float scalar) const;

    vector3f operator/(float scalar) const;

    friend vector3f operator+(const vector3f& vector1, const vector3f& vector2);

    friend vector3f operator-(const vector3f& vector1, const vector3f& vector2);

    friend vector3f operator*(float scalar, const vector3f& vector);

    friend bool operator==(const vector3f& vector1, const vector3f& vector2);

    friend bool operator!=(const vector3f& vector1, const vector3f& vector2);

    float& operator[](int index);
};

#endif


You'll notice we have a function that the vector2f class does not:  cross
This is the cross product of two 3D vectors which produces a vector perpendicular to both input vectors in a direction determined by the right hand rule.  It is only defined for 3 dimensional vectors.

vector3f.cpp


#include "vector3f.h"
#include <cmath>

const int vector3f::SIZE = 3;

vector3f::vector3f(float x, float y, float z)
{
    elements[0] = x;
    elements[1] = y;
    elements[2] = z;
}

vector3f::vector3f(const vector3f& other)
{
    elements[0] = other.elements[0];
    elements[1] = other.elements[1];
    elements[2] = other.elements[2];
}

vector3f vector3f::normal() const
{
    float l = length();
    return vector3f(elements[0] / l, elements[1] / l, elements[2] / l);
}

void vector3f::normalize()
{
    float l = length();
    elements[0] /= l;
    elements[1] /= l;
    elements[2] /= l;
}

float vector3f::length() const
{
    return std::sqrt(elements[0] * elements[0] + elements[1] * elements[1] + elements[2] * elements[2]);
}

float vector3f::dot(const vector3f& vector1, const vector3f& vector2)
{
    return vector1.elements[0] * vector2.elements[0] + vector1.elements[1] * vector2.elements[1] +
        vector1.elements[2] * vector2.elements[2];
}

vector3f vector3f::cross(const vector3f& vector1, const vector3f& vector2)
{
    return vector3f(vector1.elements[1] * vector2.elements[2] - vector1.elements[2] * vector2.elements[0],
        vector1.elements[2] * vector2.elements[0] - vector1.elements[0] * vector2.elements[2],
        vector1.elements[0] * vector2.elements[1] - vector1.elements[1] * vector2.elements[0]);
}

vector3f& vector3f::operator=(const vector3f& other)
{
    if (this != &other)
    {
        elements[0] = other.elements[0];
        elements[1] = other.elements[1];
        elements[2] = other.elements[2];
    }
        return *this;
}

vector3f vector3f::operator-() const
{
    return vector3f(-elements[0], -elements[1], -elements[2]);
}

vector3f vector3f::operator*(float scalar) const
{
    return vector3f(scalar * elements[0], scalar * elements[1], scalar * elements[2]);
}

vector3f vector3f::operator/(float scalar) const
{
    return vector3f(elements[0] / scalar, elements[1] / scalar, elements[2] / scalar);
}

vector3f operator+(const vector3f& vector1, const vector3f& vector2)
{
    return vector3f(vector1.elements[0] + vector2.elements[0], vector1.elements[1] + vector2.elements[1],
        vector1.elements[2] * vector2.elements[2]);
}

vector3f operator-(const vector3f& vector1, const vector3f& vector2)
{
    return vector3f(vector1.elements[0] - vector2.elements[0], vector1.elements[1] - vector2.elements[1],
        vector1.elements[2] - vector2.elements[2]);
}

vector3f operator*(float scalar, const vector3f& vector)
{
    return vector3f(scalar * vector.elements[0], scalar * vector.elements[1],
        scalar * vector.elements[2]);
}

bool operator==(const vector3f& vector1, const vector3f& vector2)
{
    return ((vector1.elements[0] == vector2.elements[0]) && (vector1.elements[1] == vector2.elements[1]) &&
        vector1.elements[2] == vector2.elements[2]);
}

bool operator!=(const vector3f& vector1, const vector3f& vector2)
{
    return ((vector1.elements[0] != vector2.elements[0]) || (vector1.elements[1] != vector2.elements[1]) ||
        vector1.elements[2] != vector2.elements[2]);
}

float& vector3f::operator[](int index)
{
    return elements[index];
}


vector4f.h

#ifndef _VECTOR4F_H_
#define _VECTOR4F_H_

class KOGAPI vector4f
{
private:
    float elements[4];
 
public:

    static const int SIZE;

    vector4f(float x = 0.0f, float y = 0.0f, float z = 0.0f, float w = 0.0f);

    vector4f(const vector4f& other);

    vector4f normal() const;

    void normalize();

    float length() const;

    static float dot(const vector4f& vector1, const vector4f& vector2);

    vector4f& operator=(const vector4f& other);

    vector4f operator-() const;

    vector4f operator*(float scalar) const;

    vector4f operator/(float scalar) const;

    friend vector4f operator+(const vector4f& vector1, const vector4f& vector2);

    friend vector4f operator-(const vector4f& vector1, const vector4f& vector2);

    friend vector4f operator*(float scalar, const vector4f& vector);

    friend bool operator==(const vector4f& vector1, const vector4f& vector2);

    friend bool operator!=(const vector4f& vector1, const vector4f& vector2);

    float& operator[](int index);
};

#endif


vector4f.cpp

#include "vector4f.h"
#include <cmath>

vector4f::vector4f(float x, float y, float z, float w)
{
    elements[0] = x;
    elements[1] = y;
    elements[2] = z;
    elements[3] = w;
}

vector4f::vector4f(const vector4f& other)
{
    elements[0] = other.elements[0];
    elements[1] = other.elements[1];
    elements[2] = other.elements[2];
    elements[3] = other.elements[3];
}

vector4f vector4f::normal() const
{
    float l = length();
    return vector4f(elements[0] / l, elements[1] / l, elements[2] / l, elements[3] / l);
}

void vector4f::normalize()
{
    float l = length();
    elements[0] /= l;
    elements[1] /= l;
    elements[2] /= l;
    elements[3] /= l;
}

float vector4f::length() const
{
    return std::sqrt(elements[0] * elements[0] + elements[1] * elements[1] + elements[2] * elements[2] +
        elements[3] * elements[3]);
}

float vector4f::dot(const vector4f& vector1, const vector4f& vector2)
{
    return vector1.elements[0] * vector2.elements[0] + vector1.elements[1] * vector2.elements[1] +
        vector1.elements[2] * vector2.elements[2] + vector1.elements[3] * vector2.elements[3];
}

vector4f& vector4f::operator=(const vector4f& other)
{
    if (this != &other)
    {
        elements[0] = other.elements[0];
        elements[1] = other.elements[1];
        elements[2] = other.elements[2];
        elements[3] = other.elements[3];
    }
    return *this;
}

vector4f vector4f::operator-() const
{
    return vector4f(-elements[0], -elements[1], -elements[2], -elements[3]);
}

vector4f vector4f::operator*(float scalar) const
{
    return vector4f(scalar * elements[0], scalar * elements[1], scalar * elements[2],
        scalar * elements[3]);
}

vector4f vector4f::operator/(float scalar) const
{
    return vector4f(elements[0] / scalar, elements[1] / scalar, elements[2] / scalar,
        elements[3] / scalar);
}

vector4f operator+(const vector4f& vector1, const vector4f& vector2)
{
    return vector4f(vector1.elements[0] + vector2.elements[0], vector1.elements[1] + vector2.elements[1],
        vector1.elements[2] * vector2.elements[2], vector1.elements[3] + vector2.elements[3]);
}

vector4f operator-(const vector4f& vector1, const vector4f& vector2)
{
    return vector4f(vector1.elements[0] - vector2.elements[0], vector1.elements[1] - vector2.elements[1],
        vector1.elements[2] - vector2.elements[2], vector1.elements[3] - vector2.elements[3]);
}

vector4f operator*(float scalar, const vector4f& vector)
{
    return vector4f(scalar * vector.elements[0], scalar * vector.elements[1],
        scalar * vector.elements[2], scalar * vector.elements[3]);
}

bool operator==(const vector4f& vector1, const vector4f& vector2)
{
    return ((vector1.elements[0] == vector2.elements[0]) && (vector1.elements[1] == vector2.elements[1]) &&
        vector1.elements[2] == vector2.elements[2] && vector1.elements[3] == vector2.elements[3]);
}

bool operator!=(const vector4f& vector1, const vector4f& vector2)
{
    return ((vector1.elements[0] != vector2.elements[0]) || (vector1.elements[1] != vector2.elements[1]) ||
        (vector1.elements[2] != vector2.elements[2]) || (vector1.elements[3] != vector2.elements[3]));
}

float& vector4f::operator[](int index)
{
    return elements[index];
}


That's it for now.  Next post we'll set up the matrix and quaternion class.
Questions are always welcome and if you have an idea for a tutorial or series you'd like to see feel free to comment it!.

Friday, October 17, 2014

Let's write a c++ math library!

Wow, it's been a long time since I've updated this blog. So I've really been getting into C++ and OpenGL lately.
Why C++?
Well,
  • It's been around forever
  • It's easier to write cross platform applications
  • Unlike C it's object oriented out of the box
  • Pointers are fun
  • I like it.  Whatever, I know someone who puts garlic on their peanut butter sandwiches, go bother him.
If you don't know C++ there's endless resources out there.  So I'd like to start by writing a math library for rendering graphics.  This means linear algebra.  Go beef up on your linear algebra if you don't know much about it.  Here's an MIT course on the subject.  Have fun.
Let's get started!  First we'll need a vector class.  A few actually so let's start with a 2D vector with floating point elements.


vector2f.h

#ifndef _VECTOR2F_H_
#define _VECTOR2F_H_

class vector2f
{
private:
    float elements[2];

public:
    vector2f(float x = 0.0f, float y = 0.0f);

    vector2f(const vector2f& other);
    
    float length() const;

    static float dot(const vector2f& vector1, const vector2f& vector2);

    vector2f operator-();
    
    vector2f operator*(float scalar);

    friend operator*(float scalar, const vector2f& vector);

    friend operator/(const vector2f& vector, float scalar);

    friend operator+(const vector2f& vector1, const vector2f& vector2);

    friend operator-(const vector2f& vector1, const vector2f& vector2);

    vector2f& operator=(const vector2f& other);

    float& operator[](int index);

    friend bool operator==(const vector2f& vector1, const vector2f& vector2);

    friend bool operator!=(const vector2f& vector1, const vector2f& vector2);
};

#endif

That's good for now but we'll probably add more to this class later.  Let's take a look at the source code.

vector2f.cpp

#include "vector2f.h"
#include <cmath>

vector2f::vector2f(float x, float y)
{
 elements[0] = x;
 elements[1] = y;
}

vector2f::vector2f(const vector2f& other)
{
    elements[0] = other.elements[0];
    elements[1] = other.elements[1];
}

float vector2f::length() const
{
    return std::sqrt(elements[0] * elements[0] + elements[1] * elements[1]);
}

float vector2f::dot(const vector2f& vector1, const vector2f& vector2)
{
    return vector1.elements[0] * vector2.elements[0] + vector1.elements[1] * vector2.elements[1];
}

vector2f vector2f::operator-()
{
    return vector2f(-elements[0], -elements[1]);
}

vector2f vector2f::operator*(float scalar)
{
    return vector2f(scalar * elements[0], scalar * elements[1]);
}

vector2f operator*(float scalar, const vector2f& vector)
{
    return vector2f(scalar * vector.elements[0], scalar * vector.elements[1]);
}

vector2f operator/(const vector2f& vector, float scalar)
{
    return vector2f(vector.elements[0] / scalar, vector.elements[1] / scalar);
}

vector2f operator+(const vector2f& vector1, const vector2f& vector2)
{
    return vector2f(vector1.elements[0] + vector2.elements[0], vector1.elements[1] + vector2.elements[1]);
}

vector2f operator-(const vector2f& vector1, const vector2f& vector2)
{
    return vector2f(vector1.elements[0] - vector2.elements[0], vector1.elements[1] - vector2.elements[1]);
}

vector2f& vector2f::operator=(const vector2f& other)
{
    if (this != &other)
    {
     elements[0] = other.elements[0];
     elements[1] = other.elements[1];
    }
    return *this;
}

float& vector2f::operator[](int index)
{
     //unsafe!  TODO:  add bounds checking
     return elements[index];
}

bool operator==(const vector2f& vector1, const vector2f& vector2)
{
    return (vector1.elements[0] == vector2.elements[0] &&
        vector1.elements[1] == vector2.elements[1]);
}

bool operator!=(const vector2f& vector1, const vector2f& vector2)
{
    return (vector1.elements[0] != vector2.elements[0] ||
        vector1.elements[1] != vector2.elements[1]);
}

Hey, alright!  That should be pretty good for now.  This is pretty basic stuff but I thought someone might benefit from seeing how it's done.  Later we'll add matrix classes and even a quaternion class for rotation.
That's it for now, though!

next:  part 2