Advanced JS: Natural Simulations

# Vectors

This course is all about looking at the world around us and coming up with clever ways to simulate that world with code. We will start by looking at basic physics—how an apple falls from a tree, how a pendulum swings in the air, how the earth revolves around the sun, etc. Everything that we'll discuss here requires the use of the most basic building block for programming motion—the vector. And so this is where we begin our story.

Now, the word vector can mean a lot of different things. Vector is the name of a New Wave rock band formed in Sacramento, CA in the early 1980s. It’s the name of a breakfast cereal manufactured by Kellogg’s Canada. In the field of epidemiology, a vector is used to describe an organism that transmits infection from one host to another. In the C++ programming language, a vector (std::vector) is an implementation of a dynamically resizable array data structure. While all these definitions are interesting, they’re not what we’re looking for. What we want is called a Euclidean vector (named for the Greek mathematician Euclid and also known as a geometric vector). When you see the term “vector” in this course, you can assume it refers to a Euclidean vector, defined as an entity that has both magnitude and direction.

A vector is typically drawn as a arrow; the direction is indicated by where the arrow is pointing, and the magnitude by the length of the arrow itself.

Figure 1.1: A vector (drawn as an arrow) has magnitude (length of arrow) and direction (which way it is pointing).

In the above illustration, the vector is drawn as an arrow from point A to point B and serves as an instruction for how to travel from A to B.

### Why use vectors?

Before we dive into more of the details about vectors, let’s look at a basic program that demonstrates why we should care about vectors in the first place. If you went through the introductory JS course here on Khan Academy, you probably, at one point or another, learned how to write a simple bouncing ball program.

In the above example, we have a very simple world—a blank canvas with a circular shape (a “ball”) traveling around. This ball has some properties, which are represented in the code as variables.

 Location x and y Velocity xspeed and yspeed

In a more advanced program, we could imagine having many more variables:

 Acceleration xacceleration and yacceleration Target location xtarget and ytarget Wind xwind and ywind Friction xfriction and yfriction

It’s becoming clearer that for every concept in this world (wind, location, acceleration, etc.), we’ll need two variables. And this is only a two-dimensional world. In a 3D world, we’ll need x, y, z, xspeed, yspeed, zspeed, and so on.

Wouldn’t it be nice if we could simplify our code and use fewer variables?

var x = 5;
var y = 10;
var xspeed;
var yspeed;

We could simply have two variables, where each variable is a vector-like object with two dimensions of information:

var location;
var speed;

Taking this first step in using vectors won’t allow us to do anything new. Just using vector-like objects for your variables won’t magically make your program simulate physics. However, they will simplify your code and provide a set of functions for common mathematical operations that happen over and over and over again while programming motion.

As an introduction to vectors, we’re going to live in two dimensions. All of these examples can be fairly easily extended to three dimensions (and the object we will use—PVector—allows for three dimensions.) However, it’s easier to start with just two.

### Programming with PVector

One way to think of a vector is the difference between two points. Consider how you might go about providing instructions to walk from one point to another.

Here are some vectors and possible translations: Figure 1.2

 (-15, 3) Walk fifteen steps west; turn and walk three steps north. (3, 4) Walk three steps east; turn and walk five steps north. (2, -1) Walk two steps east; turn and walk one step south.

You’ve probably done this before when programming motion. For every frame of animation (i.e. a single cycle through ProcessingJS’s draw() loop), you instruct each object on the screen to move a certain number of pixels horizontally and a certain number of pixels vertically.Figure 1.3

For every frame:

new location = velocity applied to current location

If velocity is a vector (the difference between two points), what is location? Is it a vector too? Technically, one might argue that location is not a vector, since it’s not describing how to move from one point to another—it’s simply describing a singular point in space.

Nevertheless, another way to describe a location is the path taken from the origin to reach that location. Hence, a location can be the vector representing the difference between location and origin.Figure 1.4

Let’s examine the underlying data for both location and velocity. In the bouncing ball example, we had the following:

 Location x and y Velocity xspeed and yspeed

Notice how we are storing the same data for both—two floating point numbers, an x and a y. If we were to write a vector class ourselves, we’d start with something rather basic:

var Vector = function(x, y) {
this.x = x;
this.y = y;
};

At its core, a PVector is just a convenient way to store two values (or three, as we’ll see in 3D examples).

And so this …

var x = 100;
var y = 100;
var xspeed = 1;
var yspeed = 3.3;

becomes …

var location = new PVector(100,100);
var velocity = new PVector(1,3.3);

Now that we have two vector objects (location and velocity), we’re ready to implement the algorithm for motion—location = location + velocity. In Example 1.1, without vectors, we had:

x = x + xspeed;
y = y + yspeed;

In an ideal world, we would be able to rewrite the above as:

location = location + velocity;

However, in JavaScript, the addition operator + is reserved for primitive values (numbers, strings) only. In some programming languages, operators can be "overloaded", but not in JavaScript. Fortunately for us, the PVector object includes methods for common mathematical operations, like add().

Before we continue looking at the PVector object and its add() method, let’s examine vector addition using the notation found in math and physics textbooks.

Vectors are typically written either in boldface type or with an arrow on top. For the purposes of these lessons, to distinguish a vector from a scalar (scalar refers to a single value, such as an integer or a floating point number), we’ll use the arrow notation:

• Vector: \vec{u}

• Scalar: x​

Let’s say I have the following two vectors:Figure 1.5

Each vector has two components, an x and a y. To add two vectors together, we simply add both x’s and both y’s. Figure 1.6

In other words:

\vec{w} = \vec{u} + \vec{v}

can be written as:

\text{w}_x = \text{u}_x + \text{v}_x\\ \text{w}_y = \text{u}_y + \text{v}_y 

Then, replacing u and v with their values from Figure 1.6, we get:

\text{w}_x = 5 + 3\\ \text{w}_y = 2 + 4 

which means that:

\text{w}_x = 8\\ \text{w}_y = 6 

Finally, we write that as a vector:

\vec{w} = (8, 6)

Now that we understand how to add two vectors together, we can look at how addition is implemented in the PVector object itself. Let’s write a method called add() that takes another PVector object as its argument, and simply adds the x and y components together.

var Vector = function(x, y) {
this.x = x;
this.y = y;
};

Vector.prototype.add = function(v) {
this.y = this.y + v.y;
this.x = this.x + v.x;
};


Now that we see how add() is written inside of PVector, we can return to our bouncing ball example with its location + velocity algorithm and implement vector addition:

location.add(velocity);

And now we're ready to rewrite the bouncing ball example using the PVector object! Take a look through the code and note the differences from before.

We should note an important aspect of the above transition to programming with vectors. Even though we are using PVector objects to describe two values—the x and y of location and the x and y of velocity—we still often need to refer to the x and y components of each PVector individually. When we go to draw an object in ProcessingJS, there’s no means for us to say:

ellipse(location, 16, 16);

The ellipse() function does not allow for a PVector as an argument. An ellipse can only be drawn with two scalar values, an x-coordinate and a y-coordinate. And so we must dig into the PVector object and pull out the x and y components using object-oriented dot notation instead:

ellipse(location.x, location.y, 16, 16);

The same issue arises when testing if the circle has reached the edge of the window, and we need to access the individual components of both vectors: location and velocity.

if ((location.x > width) || (location.x < 0)) {
velocity.x = velocity.x * -1;
}

Now, you might feel somewhat disappointed. After all, this change to using vectors may initially appear to have made the code more complicated than the original version. While this is a perfectly reasonable and valid critique, it’s important to understand that we haven’t fully realized the power of programming with vectors just yet. Looking at a simple bouncing ball and only implementing vector addition is just the first step. As we move forward into a more complex world of multiple objects and multiple forces (which we’ll introduce soon), the benefits of PVector will become more apparent. Keep going!

This "Natural Simulations" course is a derivative of "The Nature of Code" by Daniel Shiffman, used under a Creative Commons Attribution-NonCommercial 3.0 Unported License.