Skip to content

195. All about 2D vectors (vec2)

January 21, 2015

I don’t know how I got this far without doing a post on vectors. But here it is.

If you’ve seen reference to vec2 in code, but don’t know much about them, then it’s time you did, as they are extremely useful in graphics programming.

In Codea, vectors come in 2D, 3D and 4D.

v = vec2(x,y)     --2D
v = vec3(x,y,z)   --3D
v = vec3(x,y,z,w) --4D

This post will be about 2D vectors.

What is a 2D vector?

A 2D vector (vec2 in Codea)  is an x,y pair of numbers, which is a convenient way of storing a point on the screen, as shown below.

pos = vec2(200,350) --create a vec2
pos.x = pos.x + 1 --adjust x value
pos = pos + velocity --add another vec2
pos = pos * 1.2 --multiply by 1.2
sprite("picture",pos.x,pos.y) --draw a sprite at that point

But convenience is not the reason we have vec2. Vectors are very important in programming graphics.

Direction and length

To understand this, we first need to realise that a 2D vector is not just a point on the screen. It has

  • direction and
  • length

and the vector = direction * length

I’ll use an example to explain. Take a point somewhere on the screen, say x=3, y=4.  If you draw a line from (0,0) to (3,4), you can see the direction of (3,4) below.

All the other points on that blue line have the same direction, too. But what is it?

Calculating direction

From the formula above, direction = vector / length (the length of the line between 0,0 and the point).

Let’s see how that works for our point of (3,4). Using Pythagoras, the length of the line is sqrt(3×3 + 4×4) = 5.

So the direction is (3, 4) divided by 5, which is (0.6, 0.8), a vec2,

so our point (3, 4) = direction * length = (0.6, 0.8) * 5

Using the same direction, and varying the length, we can produce the position of any point on our blue line, to infinity in both directions. And if we vary the direction as well, we can describe every point on our screen as some direction vector * some length. Of course, this would be a clumsy way of writing a point value, but when we start rotating, direction becomes very important.

So a direction vector defines a line, that goes through (0,0) and that vector.  Any point on that line can be defined by multiplying the direction by a certain length.

Now –

  • direction vectors always have a length of 1 – because we have divided them by their length
  • vectors with a length of 1 are called unit vectors, unitised, or normalised,
  • so all direction vectors are unitised, and normalised.

Codea has a specific normalize command which divides a vector by its length to produce a normalised vector, and any time you see this function being used, it’s likely the vector is going to be used as a direction.

Using direction to move around the screen

Suppose we have a spaceship. It is moving in the direction (0.6, 0.8) at a speed of 30 pixels per second. Each time we draw, we need to calculate how much time has passed since we drew last, and move our spaceship.

direction = vec2(0.6, 0.8) --direction of movement
speed = 30 --pixels per second
--calculate distance moved this time
--DeltaTime is fraction of a second since we drew last
distance = speed * DeltaTime 
velocity = direction * distance
--suppose pos = current position, a vec2
pos = pos + velocity --new spaceship position

I hope it’s clear how this works, and that you can see how useful it is to split direction from length.

Adjusting a joystick value

As another example, suppose you have a very simple joystick which is a circle that you touch inside. You take the touched point, and subtract the centre position of the joystick, so you get the direction of the touch in pixels. You can then turn this into a direction vector by normalising it, ie making its length 1.

joyCentre = vec2(100,100) --centre of joystick, defined in setup

--in the touch function
touchPoint=vec2(touch.x,touch.y)
touchVector = touchPoint - joyCentre
touchDirection = touchVector:normalize()

What if the direction is an angle, not a vector?

Maybe you manage your spaceship direction not with a vector like (0.6, 0.8), but with an angle, like 40 degrees.

In that case, we need to turn the angle into a direction vector. Below is a diagram of the rotation of 40 degrees, and we need to solve for the vector (x,y), where the length is 1 (because it is a direction vector).

We can calculate the x and y values using trigonometry.

x = sin(40) / 1   = sin(40)

y = cos(40) / 1   = cos(40)

Why  divide by 1? Because the hypotenuse equals the length of the vector, which is 1.

One more thing. I showed a rotation of 40 degrees as turning left, not right, because that’s how OpenGL does it. The problem is that it messes up the calculations, because trigonometry rotates the other way, and you can see from the diagram that x has to be negative.

So we need to make a correction: x = sin(40) . We don’t need to change cos, because cos always gives a positive answer.

And then our vector (x,y) = vec2(-sin(40), cos(40))

Here is some code to show how it’s done. The changes are shown in blue.

angle = 40
direction = vec2(-math.sin(angle), math.cos(angle))
speed = 30 --pixels per second
--calculate distance moved this time
--DeltaTime is fraction of a second since we drew last
distance = speed * DeltaTime 
velocity = direction * distance
--suppose pos = current position, a vec2
pos = pos + velocity --new spaceship position

Note we don’t need to normalise the direction vector (calculated in blue) because the x and y values are calculated to have a length of 1, as required.

Useful functions

Codea has a number of useful functions for vec2, including

  • dist, which calculates the distance between two vectors
  • angleBetween, which calculates the angle (in radians) between two vectors
  • len, which gives the length of a vector
  • normalize, which divides a vector by its length
  • dot and cross, which I’ll cover in another post

From → Graphics, Programming

Leave a comment