[Edit: Checkout the improved Vector2 class here: improved Vector2 blog post.]

Hey, it's been a while since I've posted anything, and I've been wanting to share this Vector2 class that I have written. It started being developed less than a year ago, and has undergone some minor changes since then to try to improve efficiency and add functionality. I haven't touched it in a while since then, as I'm now working on a 3D game engine, but here is what I've got.

/*  __      __   ___     _____    ____
 *  \ \    / /  / _ \   |  __ \  |    |
 *   \ \/\/ /  / / \ \  | | / /  |  __|
 *    \_/\_/  /_/   \_\ |_| \_\  |_|
 *      Take it to the next Level
 *
 *  Copyright (c) 2009 Brian Ernst.
 */

#ifndef w_Vector2
#define w_Vector2

#include <math.h>

namespace _Warp
{
    typedef float Scalar;
    typedef int Bool;

    class Vector2
    {
    public:
        Scalar X;
        Scalar Y;

        Vector2(const Scalar x = 0,const Scalar y = 0);
        ~Vector2();

        Vector2 operator+(const Vector2&amp; pVector) const;
        Vector2 operator-(const Vector2&amp; pVector) const;
        Vector2 operator*(const Scalar&amp; num) const;
        Vector2 operator/(const Scalar&amp; num) const;
        Vector2 operator*(const Vector2&amp; vector) const;
        Vector2 operator/(const Vector2&amp; vector) const;
        void operator+=(const Vector2&amp; pVector);
        void operator-=(const Vector2&amp; pVector);
        void operator*=(const Scalar&amp; num);
        void operator/=(const Scalar&amp; num);
        void operator=(const Vector2&amp; pVector);
        Bool operator==(const Vector2&amp; vector) const;
        Bool operator!=(const Vector2&amp; vector) const;

        void Clamp(const Scalar&amp; value);
        void Normalize(const Scalar&amp; value = 1.0f);
        void Invert();

        Scalar Length() const;

        Vector2 Copy() const;

        static Vector2 Cartesian(const Scalar&amp; x,const Scalar&amp; y);
        static Vector2 Polar(const Scalar&amp; radius,const Scalar&amp; angle);
        static Scalar Dot(const Vector2&amp; pVec1,const Vector2&amp; pVec2);
        static Vector2 Rotate(const Vector2&amp; pVec,const Scalar&amp; angle);
        static const Vector2 Zero;
    };

    inline Vector2 Vector2::Copy() const
    {
        return Vector2(X,Y);
    }

    inline Bool Vector2::operator==(const Vector2&amp; vector) const
    {
        return X == vector.X &amp;&amp; Y == vector.Y;
    }

    inline Bool Vector2::operator!=(const Vector2&amp; vector) const
    {
        return X != vector.X || Y != vector.Y;
    }

    inline Vector2 Vector2::operator+(const Vector2&amp; pVector) const
    {
        return Vector2(X + pVector.X,Y + pVector.Y);
    }

    inline Vector2 Vector2::operator-(const Vector2&amp; pVector) const
    {
        return Vector2(X - pVector.X,Y - pVector.Y);
    }

    inline Vector2 Vector2::operator*(const Scalar&amp; num) const
    {
        return Vector2(X * num,Y * num);
    }

    inline Vector2 Vector2::operator/(const Scalar&amp; num) const
    {
        return Vector2(X / num,Y / num);
    }

    inline Vector2 Vector2::operator*(const Vector2&amp; vector) const
    {
        return Vector2(X * vector.X,Y * vector.Y);
    }

    inline Vector2 Vector2::operator/(const Vector2&amp; vector) const
    {
        return Vector2(X / vector.X,Y / vector.Y);
    }
}

#endif

And yes, I realize I need to comment this class, and I will. I might include the source files, but you can just as easily click on the "Source" button on the top right corner of the code box. And here's the cpp definitions file for the Vector2 functions:

#include "Vector2.h"

namespace _Warp
{
    const Vector2 Vector2::Zero = Vector2();

    Vector2::Vector2(const Scalar x,const Scalar y)
    {
        X = x;
        Y = y;
    }

    Vector2::~Vector2()
    {
    }

    void Vector2::operator+=(const Vector2&amp; pVector)
    {
        X += pVector.X;
        Y += pVector.Y;
    }

    void Vector2::operator-=(const Vector2&amp; pVector)
    {
        X -= pVector.X;
        Y -= pVector.Y;
    }

    void Vector2::operator*=(const Scalar&amp; num)
    {
        X *= num;
        Y *= num;
    }

    void Vector2::operator/=(const Scalar&amp; num)
    {
        X /= num;
        Y /= num;
    }

    void Vector2::operator=(const Vector2&amp; pVector)
    {
        X = pVector.X;
        Y = pVector.Y;
    }

    Scalar Vector2::Length() const
    {
        return sqrt(pow(X,2.0f) + pow(Y,2.0f));
    }

    void Vector2::Clamp(const Scalar&amp; value)
    {
        if(Length() &lt;= value)
            return;
        Normalize();
        *this *= value;
    }

    void Vector2::Normalize(const Scalar&amp; value)
    {
        Scalar vecLength = Length();

        if(vecLength == 0)
            return;

        X = X/vecLength * value;
        Y = Y/vecLength * value;
    }

    void Vector2::Invert()
    {
        X *= -1;
        Y *= -1;
    }

    Vector2 Vector2::Polar(const Scalar&amp; x,const Scalar&amp; y)
    {
        Vector2 pVector = Vector2();
        pVector.X = atan2(y,x);
        pVector.Y = sqrt(x * x + y * y);
        return pVector;
    }

    Vector2 Vector2::Cartesian(const Scalar&amp; radius,const Scalar&amp; angle)
    {
        Vector2 pVector = Vector2();
        pVector.X = radius * cos(angle);
        pVector.Y = radius * sin(angle);
        return pVector;
    }

    Scalar Vector2::Dot(const Vector2&amp; pVec1,const Vector2&amp; pVec2)
    {
        return pVec1.X * pVec2.X + pVec1.Y * pVec2.Y;
    }

    Vector2 Vector2::Rotate(const Vector2&amp; pVec,const Scalar&amp; angle)
    {
        Scalar cosResult = cos(angle);
        Scalar sinResult = sin(angle);

        //Essentially, apply a 2x2 rotation matrix to the vector
        Scalar newX = pVec.X * cosResult - pVec.Y * sinResult;
        Scalar newY = pVec.X * sinResult + pVec.Y * cosResult;

        return Vector2(newX,newY);
    }
}

It's pretty simple, but quite useful and powerful I think. I put it up here for other people to use for quick reference; let me know what you think or just drop me a line telling me you're using it for something, that'd make my day. (:

So here's a zip file you can download: Vector2

Comments

user icon Erli Moen
I am using it in my 2d game framework. Thank you!
user icon leetnightshade
hm, nm, I bet you meant this Leander: namespace MyNamespace { class MyClass { //member functions }; void NonMember1 (MyClass& myObject, /*other arguments*/); void NonMember2 (MyClass& myObject, /*other arguments*/); } I realize, and yes I know you pointed it out, this Vector2 class has only public members so I don’t technically need to make friend functions. I just need to read around more I guess! I found this article to check out on the topic of non-member functions: http://www.codeproject.com/KB/architecture/nonmemberfnoo.aspx
user icon leetnightshade
Also inside of clamp. Good call though on the x == -y.
user icon leetnightshade
Hey Leander, thanks for all your input, I just have a few questions. Here's one of them: You've mentioned non-member, do you mean using them like this: namespace _Warp { class Vector2 { public: ... friend Scalar len2(const Vector2& vect); .... }; inline Scalar len2(const Vector2& vect) { return vect.X * vect.X + vect.Y * vect.Y; } }; Or do you suggest I move them out into a separate class somewhere like Vector2_Util or something? As far as splitting up header files, couldn't I do that anyway just using compile flags in various places in the header file (though I think it looks kind of ugly)? Right, I realize I rushed in posting this without remodeling it with some \"common sense\"; making a basic data type const in the parameter is pointless, check. Passing references to basic data types isn't worth the computation time, check. Using var * var instead of pow should have been a given for me, as the extra function call adds to the runtime. etc. Wow, you're definitely on top of your stuff to easily pick all this out man! You're not lead programmer for nothing. (: What would you have me do with a unary + anyway? I know the - just makes the vector negative, did you have in mind it makes negative components positive? -- When I post the modified Vector2 class I'll probably post it in a new post, and I'll definitely acknowledge you for your suggestions/fixes.
user icon Aury
\"* Your if(vecLength == 0) tests should probably skip doing the length thing and just check X+Y == 0, it's likely to be faster since it avoids the sqrt.\" I assume you mean in Normalize(), as this is the only place I can find Length() compared against 0 inside the class. However, if you were to do this, it would not normalize any vector where X == -Y. (Incidentally, X*Y == 0 also does not work for any vector where only one component is zero.) I recommend testing both components against 0, unless there is some optimization I am unaware of.
user icon Leander
Oh -- right: * throw unary - in there instead of or in addition to \"Invert\" (and since you have unary -, do unary + too) Nice tables here: http://web.cecs.pdx.edu/~karlaf/CS202_Slides/Operator_Overloading_Guidelines.htm
user icon Leander
(Put this through an html encoder first, hopefully it'll show up correctly.) A few notes. Where ever I say "+" here I mean any of "+-*/". * Your include got eaten by the html stripping. * Any time you have a constructor that _can_ take one argument (two defaults is a possibility), use the "explicit" keyword to make sure you don't get an implicit conversion. In this case the compiler could "magically" transform a Scalar into a Vector2 -- which may not be a problem now, but could be later with other things that take Vector2s or conversion operators. * See Scott Meyer's Effective C++ and More Effective C++ for some operator overloading tips. * Prefer non-member binary operators: Vec2 operator+( const Vec2 & a, const Vec2 & b ) gives the compiler the opportunity to overload the left hand side as well as the right hand side. Keep it in the same namespace though for good ADL. * Consider implementing non-member operator+ in terms of member operator+=; that way the non-member can be a non-friend too! (Although friendship doesn't matter much since your members are public.) * operator+= should return Vector2 &. This allows it to chain properly, as with the basic types, as in: "while ( ( someInt += 3 ) < limit )" * (some people actually suggest const reference return here, but this violates assumptions about the behavior of basic data types, even if it does prevent some common errors.) * No reason to overload operator= if the compiler-generated default ("shallow copy", i.e. copy each member) is good enough. If you _are_ going to write it, you should also write a copy constructor for symmetry. (Note that writing an operator=, a copy constructor, or a destructor is often a sign that you need the other two -- these are the "big three".) * For the same reason, you don't need a Copy; the default copy constructor and assignment operator should do a good job here. Just write Vector2 myNewVec( myOldVec ) or myNewVec = myOldVec. * Consider pulling Clamp, Normalize, and Invert out to non-member status. Maybe Length too. Think about the way abs, ceil, floor, etc all work. Some people think this is less pretty, but it is better encapsulation, and has a few other advantages: - Versions that process a whole array of vectors at once don't pollute the class decl, and share the same general syntax as the other non-member operators. (See the XNA math libs for a good example here.) - You can split the header up for better compile times. Don't need vector "utility" functions? Don't need array versions? Don't need SSE/VFP versions? Don't include the extra headers, and get a small boost to compile times. - See Meyer's article in Dr. Dobbs for more inspiration: http://www.drdobbs.com/184401197 * For the same reasons as operator+, and also for the same reasons as Clamp/Normalize/Invert/Length, consider pulling the other binary operations out: Cartesian, Polar, Dot, Rotate. Make them friends if needed (although it shouldn't be with public data). * Consider providing a swap( Vector2 &a, Vector2 &b ) function; non-member should be fine. * Consider providing a LengthSquared. Avoiding the sqrt is sometimes a big performance win, and if you can store e.g. the value you're comparing against as itself squared, you don't need the sqrt on the other side. * Strictly opinion: I applaud your decision to make the members public! =) For a simple composite numeric data type, this is a good idea, imho. * You may want to provide an implicit conversion to a struct that has no constructor, or get your members by inheriting from a struct that has no constructor. This way you can initialize arrays of these with standard struct initialization syntax, e.g. Vector2Struct someStaticArray[] = { {1, 2}, {3, 4}, ...}; * You may want to provide a specific uninitialized constructor, if you don't have implicit conversions from constructorless structs. Sometimes it's much more efficient to not initialize members to 0 when you know you're setting them later. You can do this in a tricksy way in the class by taking a dummy object, something like: class Vector2 { public: class Uninitialized {}; // invoke via Vector2 myVec( Vector2::Uninitialized() ) explicit Vector2( Uninitialized dummyArg ); // normal and default constructor explicit Vector2( Scalar x = 0; Scalar y = 0 ); //... } ...you can't accidentally make things uninitialized this way, the default is still zero-initialized, and everyone's happier. * You may want to implement == and != in terms of one or the other. * Don't pass Scalar around as "const Scalar &" -- chances are your Scalar will fit in a register (if it's float or int it will), and adding an indirection is silly. * If you really want to potentially pass around larger-than-register Scalars or the like, template not only on SCALAR_TYPE but also SCALAR_ARG_TYPE. That way you can have e.g. SCALAR_TYPE be "GinormousNum" and SCALAR_ARG_TYPE be "const GinormousNum &" * (Personally I'd avoid the templating in this case anyway. It tends to play hell with compiler's ability to generate good code, and limits your SIMD options. Give it another half-decade.) * Don't use Vector::Zero. I know it's tempting, but it's almost always static data; it bloats the exe, and may cause a load. You might as well compare against a trivially locally-constructed zero vector, which might be in faster stack memory if it needs to be loaded (if your architecture has faster stack, not many do) or might be entirely in registers and not need to be loaded, or the like. It's far more amenable to optimization by the compiler that way. So "if ( myVec == Vector::Zero )" becomes "if ( myVec == Vector(0,0) )", or "if ( myVec == Vector() )" because the default construction is zero-filled, or if you're feeling passionate about saying the word zero, "if ( myVec == Vector::zero() )"... the last being a static inline function that returns a 0-filled vector. * Geting rid of Zero also gets rid of your need for a .cpp. Truly, keep everything in the header -- create a ".inl" file and include it from the header if you must have separation, but nearly everything here deserves a chance to be inlined, which it will only get if it's in the header. (Exception: things that pull in other headers -- maybe sqrt? -- might want to live in the cpp for compile time reasons; but they might also collapse to intrinsics in which case they really should still live in the .h and be inlined where it makes sense to the compiler.) * Side note: Amusingly you don't need the "const" in the class declaration body for basic non-pointer non-reference data types, only in the definitions. It doesn't alter what people can pass to the function, only what you can do with the var within the function body in that case. (This is as opposed to "const" to the left of a * or &, which does alter what can be passed in -- i.e. the function signature.) This is not a big deal, some people prefer the consistency. * Use a constructor initializer list rather than assignments in the constructor body, it's better form, and can be more efficient if your Scalar type becomes nontrivial later. * Don't bother with a destructor. There's nothing to destroy. Having it there, and worse yet having the body in the cpp, forces a function call when you don't need one. * Don't use pow( foo, 2.0f ) -- it's almost always faster to use foo*foo, and it may help you avoid bringing in the pow-containing header. * Provide a zero-argument normalize and skip the * value. If you want a version that takes a value, I suggest calling it "SetLength" or somesuch. * Also, if you create this SetLength, use it in Clamp rather than doing a normalize then a scalar multiply. =) * You can use if ( LengthSquared() <= value*value ) for a potential speedup in Clamp. * Your if(vecLength == 0) tests should probably skip doing the length thing and just check X+Y == 0, it's likely to be faster since it avoids the sqrt. * Polar and Cartesian should avoid default-constructing the vector then setting X and Y, it's redundant. Pass two args to the constructor. If you're concerned about debugging and code beauty, go ahead and put the results in two Scalars first, that's fine (they'll be in registers, likely): Scalar x = radius * cos( angle ) Scalar y = radius * sin( angle ) return Vector2( x, y ); * If this is c++, use <cmath> rather than <math.h> and use std:: prefix for sin, cos, sqrt, etc. Including <math.h> would pollute the root namespace for all people that included your Vector2.h. * int will be 64 bit and so will Bool if you compile with the correct options in a 64-bit compiler.
user icon leetnightshade
Along the lines of a possible comment I would expect: this Vector2 class is useful for a single type of use, such as 2d gameplay, which is why a Scalar is defined to be a float. Though I'm wondering, should I use a template so I can use this for both 2d gameplay and UI stuff or whatever else? The only difference if I'm using floats and ints would be the performance difference of the calculations. I'm also unaware if there are performance differences using a template vs none; this is something I have to test or research.
user icon leetnightshade
Just updated the source files and code boxes above, let me know if you have thoughts comments. The previous stuff I had posted I forgot to define Bool; and yes I'm aware of the existence of bool, but for the sake byte alignment and possible performance I'm choosing to define my own Bool; I've never tested this, though I will one of these days; and for this reason, I wonder if I should define a 64bit Bool for a 64bit system. Also I'm sorry your comments didn't show up earlier, my spam filter picked your comments out as Spam, only just realized this. I'll try to stay more on top of it in the future, so sorry for the delay of getting your comments posted.

Next Post Previous Post