Implementing a Vector Class

We want to create an easy-to-use vector class for 2D vectors. Let's call it Vector2. It should have two members, for holding the x and y components of the vector. Additionally it should have a couple of nice methods that allow us to the following:

■ Add and subtract vectors

■ Multiply the vector components with a scalar

■ Measure the length of a vector

■ Normalize a vector

■ Calculate the angle between a vector and the x-axis

■ Rotate the vector

Java lacks operator overloading, so we have to come up with a mechanism that makes working with the Vector2 class less cumbersome. Ideally we should have something like the following:

Vector2 v = new Vector2(); v.add(10,5).mul(10).rotate(54);

We can easily achieve this by letting each of the Vector2 methods return a reference to the vector itself. Of course, we also want to overload methods like Vector2.add() so that we can either pass in two floats or an instance of another Vector2. Listing 8-1 shows our Vector2 class in its full glory.

Listing 8-1. Implementing Some Nice 2D Vector Functionality package com.badlogic.androidgames.framework.math;

import android.util.FloatMath;

public class Vector2 {

public static float TO_RADIANS public static float TO_DEGREES public float x, y;

public Vector2(float x, float y) { this.x = x; this.y = y;

= (1 / 180.0f) * (float) Math.PI; = (1 / (float) Math.PI) * 180;

public Vector2(Vector2 other) { this.x = other.x; this.y = other.y;

We put that class in the package com.badlogic.androidgames.framework.math, where we'll house any other math-related classes as well.

We start off by defining two static constants, TO_RADIANS and TO_DEGREES. To convert an angle given in radians, we just need to multiply it by TO_DEGREES; to convert an angle given in degrees to radians, we multiply it by TO_RADIANS. You can double-check this by looking at the two previously defined equations that govern degree-to-radian conversion. With this little trick we can shave off a division to speed things up a little.

Next we define the two members x and y that store the components of the vector and a couple of constructors—nothing too complex:

public Vector2 cpy() {

return new Vector2(x, y);

We also have a cpy() method that will create a duplicate instance of the current vector and return it. This might come in handy if we want to manipulate a copy of a vector, preserving the value of the original vector.

public Vector2 set(float x, float y) { this.x = x; this.y = y; return this;

public Vector2 set(Vector2 other) { this.x = other.x; this.y = other.y; return this;

The set() methods allow us to set the x and y components of a vector, based on either two float arguments or another vector. The methods return a reference to this vector, so we can chain operations as discussed previously.

public Vector2 add(float x, float y) { this.x += x; this.y += y; return this;

public Vector2 add(Vector2 other) { this.x += other.x; this.y += other.y; return this;

public Vector2 sub(float x, float y) { this.x -= x;

public Vector2 sub(Vector2 other) { this.x -= other.x; this.y -= other.y; return this;

The add() and sub() methods come in two flavors: in once case they work with two float arguments, and in the other case they take another Vector2 instance. All four methods return a reference to this vector so we can chain operations.

public Vector2 mul(float scalar) { this.x *= scalar; this.y *= scalar; return this;

The mul() method just multiplies the x and y components of the vector with the given scalar value, and again returns a reference to the vector itself for chaining.

public float len() {

The len() method calculates the length of the vector exactly like we defined it previously. Note that we use the FastMath class instead of the usual Math class that Java SE provides. This is a special Android API class that works with floats instead of doubles, and is a little bit faster than the Math equivalent.

public Vector2 nor() { float len = len(); if (len != 0) { this.x /= len; this.y /= len;

return this;

The nor() method normalizes the vector to unit length. We use the len() method internally to calculate the length first. If it is zero, we can bail out early and avoid a division by zero. Otherwise we divide each component of the vector by its length to arrive at a unit-length vector. For chaining we return the reference to this vector again.

public float angle() {

float angle = (float) Math.atan2(y, x) * TO_DEGREES; if (angle < 0)

angle += 360; return angle;

The angle() method calculates the angle between the vector and the x-axis using the atan2() method, as discussed previously. We have to use the Math.atan2() method, as the FastMath class doesn't have that method. The returned angle is given in radians, so we convert it to degrees by multiplying it by TO_DEGREES. If the angle is less than zero, we add 360 degrees to it so we can return a value in the range 0 to 360 degrees.

public Vector2 rotate(float angle) { float rad = angle * TO_RADIANS; float cos = FloatMath.cos(rad); float sin = FloatMath.sin(rad);

float newX = this.x * cos - this.y * sin; float newY = this.x * sin + this.y * cos;

return this;

The rotate() method simply rotates the vector around the origin by the give angle. Since the FastMath.cos() and FastMath.sin() methods expect the angle to be given in radians, we first convert from degrees to radians. Next we use the previously defined equations to calculate the new x and y components of the vector, and finally return the vector itself again for chaining.

public float dist(Vector2 other) { float distX = this.x - other.x; float distY = this.y - other.y;

return FloatMath.sqrt(distX * distX + distY * distY);

public float dist(float x, float y) { float distX = this.x - x; float distY = this.y - y;

return FloatMath.sqrt(distX * distX + distY * distY);

Finally we have two methods that calculate the distance between this and another vector.

And that's our shiny Vector2 class, which we'll use to represent positions, velocities, distances, and directions in the code that follows. To get a feeling for our new class, let's use it in a simple example.

Was this article helpful?

0 0

Post a comment