C++ Vector2D & Rectangle classes


I’n my current C++ class we’ve been playing with the Allegro C++ library to create some 2D games.  While Allegro is a fairly powerful library, it’s shortcomings made me miss the Microsoft XNA Framework quite a bit.  I suppose I cannot fault Allegro in this regard, but I have grown so accustomed to using Microsoft’s implementation of 2D vectors and rectangles that learning new variants seems convoluted.

Now, before anyone judges me for not wanting to learn a new API, consider this.  Allegro has a plethora of 2D methods, but the only vector methods I am able to find are 3D.  Fine you say, just zero the Z element and be done with it.

Well sure.  But would I rather use a non OOP implementation like this:

float x = <someXValue>;
float y = <someYValue>;
float z = 0.0f;
void normalize_vector_f(float *x, float *y, float *z);

Or would I rather take an OOP approach:

Vector2D vec( <someXValue>, <someYValue> );
vec.Normalize();

Call me crazy, but I prefer the later.  As such, I’m going to go ahead and post my Vector2D class here, as well as the Rectangle class I am using.  Normally I would cite sources I used for help in writing these classes.   However, in the case of the Vector2D class most of the methods used are fairly standard and there is nothing really out of the ordinary.

As for the Rectangle class, there are some cool static methods I added that Microsoft released in their RectangleExtensions class (released under the MSPL).  I ported these methods over from C# to C++,and they seem to work correctly, however I haven’t tested them thoroughly and they may have a bug or two.

Anyway, enough ranting.  Here’s the code.

Vector2D.h

#ifndef _VECTOR2D_H
#define _VECTOR2D_H
#pragma once

#include <math.h>

class Vector2D
{

public:
	Vector2D(float x = 0, float y = 0);
	~Vector2D() {};

	void Rotate( const float angle );
	float Magnitude() const;
	float Normalize();
	float DotProduct( const Vector2D& v2 ) const;
	float CrossProduct( const Vector2D& v2 ) const;

	static Vector2D Zero();
	static float Distance( const Vector2D& v1, const Vector2D& v2);

	Vector2D& operator= ( const Vector2D& v2 );

	Vector2D& operator+= ( const Vector2D& v2 );
	Vector2D& operator-= ( const Vector2D& v2 );
	Vector2D& operator*= ( const float scalar);
	Vector2D& operator/= ( const float scalar);

	const Vector2D operator+( const Vector2D &v2 ) const;
	const Vector2D operator-( const Vector2D &v2 ) const;
	const Vector2D operator*( const float scalar ) const;
	const Vector2D operator/( const float scalar ) const;

	bool operator== ( const Vector2D& v2 ) const;
	bool operator!= ( const Vector2D& v2 ) const;

public:
	float x, y;
};
#endif

Vector2D.cpp

#include "Vector2D.h"

//-----------------------------------------------------------------------------
// Purpose:	Constructor
//-----------------------------------------------------------------------------
Vector2D::Vector2D( float x, float y )
{
	this->x = x;
	this->y = y;
}

//-----------------------------------------------------------------------------
// Purpose:	Rotate a vector
//-----------------------------------------------------------------------------
void Vector2D::Rotate( const float angle )
{
	float xt = (x * cosf(angle)) - (y * sinf(angle));
	float yt = (y * cosf(angle)) + (x * sinf(angle));
	x = xt;
	y = yt;
}

//-----------------------------------------------------------------------------
// Purpose:	Get vector magnitude
//-----------------------------------------------------------------------------
float Vector2D::Magnitude() const
{
	return sqrtf(x * x + y * y);
}

//-----------------------------------------------------------------------------
// Purpose:	Convert vector to a unit vector and return previous magnitude
//-----------------------------------------------------------------------------
float Vector2D::Normalize()
{
	float mag = Magnitude();

	if(mag != 0.0)
	{
		x /= mag;
		y /= mag;
	}

	return mag;
}

//-----------------------------------------------------------------------------
// Purpose:	Dot Product
//-----------------------------------------------------------------------------
float Vector2D::DotProduct( const Vector2D &v2 ) const
{
	return (x * v2.x) + (y * v2.y);
}

//-----------------------------------------------------------------------------
// Purpose:	Cross Product
//-----------------------------------------------------------------------------
float Vector2D::CrossProduct( const Vector2D &v2 ) const
{
	return (x * v2.y) - (y * v2.x);
}

//-----------------------------------------------------------------------------
// Purpose:	Return an empty vector
//-----------------------------------------------------------------------------
Vector2D Vector2D::Zero()
{
	return Vector2D(0, 0);
}

//-----------------------------------------------------------------------------
// Purpose:	Get distance between two vectors
//-----------------------------------------------------------------------------
float Vector2D::Distance( const Vector2D& v1, const Vector2D& v2)
{
	return sqrtf( pow((v2.x - v1.x), 2 ) + pow((v2.y - v1.y), 2) );
}

Vector2D& Vector2D::operator= ( const Vector2D& v2 )
{
	if (this == &v2)
		return *this;

	x = v2.x;
	y = v2.y;

	return *this;
}

Vector2D& Vector2D::operator+= ( const Vector2D& v2 )
{
	x += v2.x;
	y += v2.y;

	return *this;
}

Vector2D& Vector2D::operator-= ( const Vector2D& v2 )
{
	x -= v2.x;
	y -= v2.y;

	return *this;
}

Vector2D& Vector2D::operator*= ( const float scalar )
{
	x *= scalar;
	y *= scalar;

	return *this;
}

Vector2D& Vector2D::operator/= ( const float scalar )
{
	x /= scalar;
	y /= scalar;

	return *this;
}

const Vector2D Vector2D::operator+( const Vector2D &v2 ) const
{
	return Vector2D(*this) += v2;
}

const Vector2D Vector2D::operator-( const Vector2D &v2 ) const
{
	return Vector2D(*this) -= v2;
}

const Vector2D Vector2D::operator*( const float scalar ) const
{
	return Vector2D(*this) *= scalar;
}

const Vector2D Vector2D::operator/( const float scalar ) const
{
	return Vector2D(*this) /= scalar;
}

bool Vector2D::operator== ( const Vector2D& v2 ) const
{
	return ((x == v2.x) && (y == v2.y));
}

bool Vector2D::operator!= ( const Vector2D& v2 ) const
{
	return !((x == v2.x) && (y == v2.y));
}

Rectangle.h

#ifndef _RECTANGLE_H
#define _RECTANGLE_H
#pragma once

#include "Vector2D.h"

class Rectangle
{
public:
	Rectangle( int x = 0, int y = 0, int w = 0, int h = 0 );
	~Rectangle( void ) {};

	inline int Left( void ) const { return x; }
	inline int Right( void ) const { return x + w; }
	inline int Top( void ) const { return y; }
	inline int Bottom( void ) const { return y + h; }

	bool Contains( Vector2D& vVec ) const;
	bool Contains( int x, int y ) const;

	static Rectangle Empty();

	// Static methods below are derived from the RectangleExtensions class
	// written in C#, released under the MSPL
	static Vector2D GetIntersectionDepth( const Rectangle& rectA, const Rectangle& rectB );
	static Vector2D GetBottomCenter( const Rectangle& rect );
	static Vector2D GetCenter( const Rectangle& rect );
	static float GetDistance( const Rectangle& rectA, const Rectangle& rectB);
	static Vector2D GetDirection( const Rectangle& rectA, const Rectangle& rectB);

	Rectangle& operator= ( const Rectangle& r2 );

	bool operator== ( const Rectangle& r2 ) const;
	bool operator!= ( const Rectangle& r2 ) const;

public:
	int x, y, w, h;
};

#endif

Rectangle.cpp

#include "Rectangle.h"
#include <stdlib.h>

//-----------------------------------------------------------------------------
// Purpose:	Constructor
//-----------------------------------------------------------------------------
Rectangle::Rectangle( int x, int y, int w, int h )
{
	this->x = x;
	this->y = y;
	this->w = w;
	this->h = h;
}

//-----------------------------------------------------------------------------
// Purpose:	Check if rectangle contains a 2D vector
//-----------------------------------------------------------------------------
bool Rectangle::Contains( Vector2D& vVec ) const
{
	if ( ( vVec.x >= x )&&
		 ( vVec.x <= x + w ) &&
		 ( vVec.y >= y ) &&
		 ( vVec.x <= y + h ) )
	{
		return true;
	}
	else
		return false;
}

//-----------------------------------------------------------------------------
// Purpose:	Check if rectangle contains a set of coords
//-----------------------------------------------------------------------------
bool Rectangle::Contains( int x, int y ) const
{
	if ( ( x >= this->x )&&
		( x <= this->x + this->w ) &&
		( y >= this->y ) &&
		( x <= this->y + this->h ) )
	{
		return true;
	}
	else
		return false;
}

//-----------------------------------------------------------------------------
// Purpose:	Return an empty rectangle
//-----------------------------------------------------------------------------
Rectangle Rectangle::Empty()
{
	return Rectangle();
}

//-----------------------------------------------------------------------------
// Purpose:	Get intersection depth between two rectangles
//-----------------------------------------------------------------------------
Vector2D Rectangle::GetIntersectionDepth( const Rectangle& rectA, const Rectangle& rectB )
{
	// Calculate half sizes.
	float halfWidthA = rectA.w / 2.0f;
	float halfHeightA = rectA.h / 2.0f;
	float halfWidthB = rectB.w / 2.0f;
	float halfHeightB = rectB.h / 2.0f;

	// Calculate centers.
	Vector2D centerA(rectA.x + halfWidthA, rectA.y + halfHeightA);
	Vector2D centerB(rectB.x + halfWidthB, rectB.y + halfHeightB);

	// Calculate current and minimum-non-intersecting distances between centers.
	float distanceX = centerA.x - centerB.x;
	float distanceY = centerA.y - centerB.y;
	float minDistanceX = halfWidthA + halfWidthB;
	float minDistanceY = halfHeightA + halfHeightB;

	// If we are not intersecting at all, return (0, 0).
	if ( abs(distanceX) >= minDistanceX || abs(distanceY) >= minDistanceY )
		return Vector2D::Zero();

	// Calculate and return intersection depths.
	float depthX = distanceX > 0 ? minDistanceX - distanceX : -minDistanceX - distanceX;
	float depthY = distanceY > 0 ? minDistanceY - distanceY : -minDistanceY - distanceY;
	return Vector2D(depthX, depthY);
}

//-----------------------------------------------------------------------------
// Purpose:	Gets the position of the center of the bottom edge of the rectangle.
//-----------------------------------------------------------------------------
Vector2D Rectangle::GetBottomCenter(const Rectangle& rect)
{
	return Vector2D( (float)(rect.x + rect.w / 2.0f), (float)(rect.y + rect.h) );
}

//-----------------------------------------------------------------------------
// Purpose:	Gets the position of the center point of a rectangle
//-----------------------------------------------------------------------------
Vector2D Rectangle::GetCenter(const Rectangle& rect)
{
	return Vector2D( (float)(rect.x + rect.w / 2.0f), (float)(rect.y + rect.h / 2.0f) );
}

//-----------------------------------------------------------------------------
// Purpose:	Gets the floating point distance between the center point
//			of one rectangle and the center point of another.
//-----------------------------------------------------------------------------
float Rectangle::GetDistance( const Rectangle& rectA, const Rectangle& rectB )
{
	return Vector2D::Distance(GetCenter(rectA), GetCenter(rectB));
}

//-----------------------------------------------------------------------------
// Purpose:	Gets the unit vector from one rectangle to another
//-----------------------------------------------------------------------------
Vector2D Rectangle::GetDirection( const Rectangle& rectA, const Rectangle& rectB )
{
	Vector2D direction = GetCenter(rectA) - GetCenter(rectB);
	direction.Normalize();
	return direction;
}

Rectangle& Rectangle::operator= ( const Rectangle& r2 )
{
	if (this == &r2)
		return *this;

	x = r2.x;
	y = r2.y;
	w = r2.w;
	h = r2.h;

	return *this;
}

bool Rectangle::operator== ( const Rectangle& r2 ) const
{
	return ((w == r2.w) && (h == r2.h));
}

bool Rectangle::operator!= ( const Rectangle& r2 ) const
{
	return !((w == r2.w) && (h == r2.h));
}

,