Skip to main content
GameDev.net gamedev.net
🔒 Locked

2D Vector Class

Started by Rob Loach Feb 23, 2005 at 12:18 PM 24 replies 20.9k views
Original Post
Rob Loach
Rob Loach
I've been working on an asteroids clone and am having second thoughts about the vector class I'm using. Does this look right to you guys:

// DECLARATION
class Vector2D {
private:
	float m_x, m_y, m_length, m_direction;
public:
	Vector2D(float x = 1, float y = 1){ SetPoints(x,y); }
	void x(float x);
	void y(float y);
	void Direction(float a);	
	void Length(float m);
	void SetPoints(float x, float y);

	void ReflectAngle(int NormalAngle);
	float x(){ return m_x; }
	float y(){ return m_y; }
	float Direction(){ return m_direction; }
	float Length(){ return m_length; }

	// Vector arithmetic operators
	Vector2D& operator+= (Vector2D& v);
	Vector2D& operator-= (Vector2D& v);
	Vector2D& operator*= (Vector2D& v);	
	Vector2D& operator/= (Vector2D& v);
	
	// scalar operators for speed and resolving ambiguity problems
	// with implicit constructor calls
	Vector2D& operator+= (float f);
	Vector2D& operator-= (float f);
	Vector2D& operator*= (float f);
	Vector2D& operator/= (float f);
};

//DEFINITION
	void Vector2D::x(float x)
	{
		m_x = x;
		m_direction = atan2(m_y,m_x);
		m_length = sqrt((m_x * m_x) + (m_y + m_y));
	}
	void Vector2D::y(float y)
	{
		m_y = y;
		m_direction = atan2(m_y,m_x);
		m_length = sqrt((m_x * m_x) + (m_y + m_y));
	}
	void Vector2D::Direction(float a){
		m_direction = a;
		m_x = cos(m_direction) * m_length;
		m_y = sin(m_direction) * m_length;
	}
	void Vector2D::Length(float m){
		m_length = m;
		m_x = cos(m_direction) * m_length;
		m_y = sin(m_direction) * m_length;
	}
	void Vector2D::SetPoints(float x, float y){
		m_x = x;
		m_y = y;
		m_length = sqrt((m_x * m_x) + (m_y + m_y));
		m_direction = atan2(m_y,m_x);
	}
	
	 
	void Vector2D::ReflectAngle(int NormalAngle)
	{
	   int NormDiffAngle; //difference between the inverted angle & the BoundaryNormal angle
	   int angle = (int)RAD2DEG(m_direction);	// new angle
	   int OppAngle = (angle + 180) % 360; // Inverted angle
	   if( NormalAngle >= OppAngle ){
	      NormDiffAngle = NormalAngle - OppAngle;
	      angle = (NormalAngle + NormDiffAngle) % 360;
	   } else {
	      NormDiffAngle = OppAngle - NormalAngle;
	      angle = NormalAngle - NormDiffAngle;
	      if( angle < 0 ) angle += 360;
	   }
	
	   this->Direction(DEG2RAD(angle));
	}


	// scalar operators for speed and resolving ambiguity problems
	// with implicit constructor calls
	
	/// Componentwise addition 
	Vector2D& Vector2D::operator+= (float f) {
		SetPoints(m_x+f,m_y+f);
		return *this;
	}
	
	/// Componentwise subtraction
	Vector2D& Vector2D::operator-= (float f) {
		SetPoints(m_x-f,m_y-f);
		return *this;
	}
	
	/// Componentwise multiplication
	Vector2D& Vector2D::operator*= (float f) {
		SetPoints(m_x*f,m_y*f);
		return *this;
	}
	
	/// Componentwise division
	Vector2D& Vector2D::operator/= (float f) {
		SetPoints(m_x/f,m_y/f);
		return *this;
	}


	// Vector arithmetic operators
	
	Vector2D& Vector2D::operator+= (Vector2D& v) {
		SetPoints(m_x + v.x(),m_y + v.y());
		return *this;
	}
	
	Vector2D& Vector2D::operator-= (Vector2D& v) {
		SetPoints(m_x-v.x(),m_y-v.y());
		return *this;
	}
	
	Vector2D& Vector2D::operator*= (Vector2D& v) {
		SetPoints(m_x*v.x(),m_y*v.y());
		return *this;
	}
	
	Vector2D& Vector2D::operator/= (Vector2D& v) {
		SetPoints(m_x/v.x(),m_y/v.y());
		return *this;
	}

Rob Loach [Website] [Projects] [
Webbster
Webbster
I don't mean to hijack this thread but seeing as your writing a vector class I was just wondering if it's worth (given the development of a 3d game) to write two seperate vector classes a 2d one and a 3d one?

Rob Loach
Rob Loach
Oh, sorry. I should've been more clear. This is going to be used explicitly for 2D games.
Rob Loach [Website] [Projects] [
Zipster
Zipster
Besides implementing vector-vector multiplication and division (which I don't believe is defined), everything seems fine to me. You should also add regular, binary addition, subtraction, multiplication and division for full flexibility.

Is there a reason you started having second thoughts about your vector?
Zakwayda
Zakwayda
Note: I'm not a professional programmer, so these are just thoughts, nothing authoritative.

I'm not sure about storing infomation other than the coordinates in the class. Most vector classes I've seen keep it pretty fundamental, and don't keep any extra information around. There are good reasons for this - memory, interfacing with APIs, etc. The length and direction can both be found with some math (albiet at more expense than having them as member variables).

Some people will tell you to make the member data public, but not me :-) You can also overload [] to return the components though. But just return a float - if you return a float&, you might as well make the data public.

If you're going to include default arguments in the constructor, I'd make it the zero vector.

Anyway, for being so fundamental, you can fiddle around with vector and matrix classes pretty much indefinitely. There's a lot of functionality you could add here, and probably will, when you need it - normalization, squared length, dot product, perp-vector, perp-dot, etc. Most likely the class will evolve naturally to fit your needs.

(Also, my login doesn't seem to be working today. This is jyk.)
The Rug
The Rug
This:

m_length = sqrt((m_x * m_x) + (m_y + m_y));

Looks dodgy. Shouldn't that last + be a * ?

That might explain the problem you're having in your other thread as well.
the rug - funpowered.com
Zipster
Zipster
Yeah, that looks like a typo that got Copy and Pasted all over [smile]
Rob Loach
Rob Loach
Ahhh, I can't believe I didn't see that. I guess I'm blind... Anyway, thanks a lot guys. I'll eventually add in the dot/cross product and some other functionality. I don't know whether or not to store the direction and length in there or just calculate everything out when you request the values. Go for speed or lower memory usage is the question I guess. Thanks again.
Rob Loach [Website] [Projects] [
Nemesis2k2
Nemesis2k2
Well, my personal preferance is to make the x and y members public, and do away with the Get/Set methods, seeing as the class is basically a container class. Really just a style issue though. Apart from that, I've got a few tips:

1. You need to add in a plain old assignment operator, for statements like vector1 = vector2.
2. Provide the standalone versions for all those operators you have, so rather than just += and -=, you have + and -, which return a temporary object. You'll need these.
3. I'd recommend providing operators for equality and inequality. This can come in handy.
4. Provide the unary - operator to negate a vector.
5. I'd add constant static members for the zero vector, and unitX and unitY vectors. These can come in handy.
Zakwayda
Zakwayda
Quote:
1. You need to add in a plain old assignment operator, for statements like vector1 = vector2.
Doesn't c++ provide a default memberwise-copy = operator?
Quote:
3. I'd recommend providing operators for equality and inequality. This can come in handy.
Remember that under most circumstances vectors will rarely be exactly equal. Instead of =, some libraries I've seen have different compare functions; one for exact comparison, and another that perhaps takes an epsilon as an argument. This can be useful, as the ideal epsilon depends on the circumstances.
Raymond_Porter420
Raymond_Porter420
vectors like to change. They sometimes like to change alot. Think of your pos vel and accel vectors, in any given frame they most likely will change. Because of this i dont think you should be calculating the angle and length on assignment. sqrt and atan2f are both fairly expensive operations. I would just make the functions length and angle do the computations. This way you can store the value if you know it wont change and you know you will reuse it. This way you can speed it up a bit.
also a urnary - is nice for vectors

edit.. I use length sqared almost as much as i use length. It might be worth putting in there, there are many cases where sqare roots are uneeded and it might be the thing to remind you that you can avoid it.
JohnHurt
JohnHurt
Quote:
Original post by jyk
Quote:
3. I'd recommend providing operators for equality and inequality. This can come in handy.
Remember that under most circumstances vectors will rarely be exactly equal. Instead of =, some libraries I've seen have different compare functions; one for exact comparison, and another that perhaps takes an epsilon as an argument. This can be useful, as the ideal epsilon depends on the circumstances.


This is exactly what I did, I have one "perfect equality" operator, and one than takes a float tolerance value, which comes in useful for numerous things.

I would also keep the attributes public too, makes code with horrible calcs in it more readable.

I wrote a 2d asteroids game a while ago (Nastyroids 3D - yes the title makes you think it's 3d, but it not), and I just wrote a 3d vector class instead of a 2d one, cos i knew somewhere down the road i would need 3d. Plus, does a cross-product exist in 2d?

BTW: if you need any asteroids related questions answering, give us a shout!
Zakwayda
Zakwayda
Quote:
Plus, does a cross-product exist in 2d?
Note: I am not a mathemetician, and this is not a rigorous mathematical answer!

The cross product of a and b is defined as a vector perpendicular to both a and b, whose length is proportional to their lengths and to the sine of the angle between them. So that pretty much rules out 2d.

I have read (I think) that the cross product is also defined in 7d. But I have no idea what that means :)

There is a somewhat correlative operation in 2d, the perp-dot. The perp-dot of a and b is the dot product of b with a vector perpendicular to a. For a vector x, y, the perpendicular vector can be either -y, x, or y, -x. This operation is sometimes called the psuedo-cross, or even 'kross'.

Examination of the components of the 3d cross product result reveal an interesting correlation. For aXb, the ith component of the result is the perp-dot of a and b projected into the plane of the other two components. I imagine someone with some training in math could explain the correlations between perp-dot and cross easily, but that's as far as I've gotten with it.
Nemesis2k2
Nemesis2k2
The cross product in 2D involves one vector. The cross product in 3D involves 2, the cross product in 4D involves 3, and so on. You can consider the following to be the cross product in 2D:

Vector2[-y, x]

This will produce a vector that is perpendicular to the vector it operates on.
Squirm
Squirm
I keep my vector 'clean' ... only x and y, no length or direction fields, and no virtual functions, including a non-virtual destructor, so that an array of vectors equates to an array of floats, and can be passed as such into a graphics library. Just a convenience thing :)

I don't know if someone else has come up with this next comment - didn't read everyone replies - but you might want a SetLengthAndDirection() function, because the way you have it, setting one and then setting the other will result in 2 unnecessary calls to sin & cos.
JohnHurt
JohnHurt
Another thing, I'm currently working on a purely 2d engine in OpenGL, but I'm still using a 3d vector, using the z component for the depth buffer. Even though the projection of the images is 2d, the underlying scene is still technically in 3 dimensions.
Zakwayda
Zakwayda
Quote:
The cross product in 2D involves one vector. The cross product in 3D involves 2, the cross product in 4D involves 3, and so on. You can consider the following to be the cross product in 2D:

Vector2[-y, x]

This will produce a vector that is perpendicular to the vector it operates on.
This may be correct, but I'm not sure it's the whole story. The math behind the cross product is actually fairly deep, and I don't pretend to understand it all. But according to at least one definition (perhaps the most formal one), the cross product is only defined in dimensions 3 and 7. Also, according to mathworld and some other sources, the 2d equivalent of the cross product is the perp-dot rather than the perp.
spunkybuttockszc
spunkybuttockszc
I've just skimmed through this thread, so I may repeat something, but here's my thoughts:

Firstly, you need to inline those functions! They are small, and will be called many, MANY times during the game's execution. These could serve as a minor performance bottleneck. Inlining will help tremendously, as no actual function call (and thus no grabbing the memory address of the function) would ever be made.

I read that somebody said they had heard it was good practice to expose the x and y members publicly. I disagree, as I think they did. Though it may seem OK since it's just a simple vector class, there's is always the possibility that you need to change the way the data is accessed. This would be very troublesome if the members were exposed and accessed publicly.

And last but not least, somebody mentioned adding operators + and -. I disagree. I realize that having those operators would make the class much easier to use, but contrary to what the person who suggested this said, temporaries *aren't* something good to have being made behind your back when writing games. It wastes time because the constructor of the temp object is called when the temp is created. This can easily be wholly avoided with some extra typing.

BTW: I'm sure you're aware of this, but sqrt() is very slow! To combat this, maybe add a method which will return the length squared, because this would work fine in several situations (tho I can't think of any as of now).

Other than these things (which are more of tips than actual things wrong with your code), everything looks fine. I didn't check the math, as it seems others have already verified it.

Well, I hope this helps.
Nice Coder
Nice Coder
Add a dirty/clean variable per vector.

Ie.
C++ish psudocode
Int getlength(&Vector vec;) {if (vec.cleanlength) {return vec.length;}vec.length = sqrt(x * x + y * y);vec.cleanlengh = true;return vec.length;}


Now, when you change it, you reset it to being dirty

void setx(int x, &vector vec;) {if ((vec.x = x) == x) {return;}vec.cleanlength = false;}


So now, you only calculate it each time you need to, and when it changes.

From,
Nice coder
Click here to patch the mozilla IDN exploit, or click Here then type in Network.enableidn and set its value to false. Restart the browser for the patches to work.
Evil Bachus
Evil Bachus
Quote:
Original post by Squirm
I keep my vector 'clean' ... only x and y, no length or direction fields, and no virtual functions, including a non-virtual destructor, so that an array of vectors equates to an array of floats, and can be passed as such into a graphics library. Just a convenience thing :)


That's how I set up my geometry class. I have 2, 3, and 4 element vector classes, each of which are just arrays of floats for easy integration with OpenGL (or whatever). Length and direction really shouldn't be kept around in the class, as you won't always need them. It's faster to compute them just when you need them instead of every time they change.

You definitely want a function that returns the length squared (without the sqrt), as that's faster for comparisons between two vectors.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.