If you're seeing this message, it means we're having trouble loading external resources on our website.

If you're behind a web filter, please make sure that the domains *.kastatic.org and *.kasandbox.org are unblocked.

Main content

Pointing towards movement

Let’s go all the way back to one of our first examples, the one where a Mover object accelerates towards the mouse.
You might notice that almost all of the shapes we’ve been drawing so far are circles. This is convenient for a number of reasons, one of which is that we don’t have to consider the question of rotation. Rotate a circle and, well, it looks exactly the same. However, there comes a time in all motion programmers’ lives when they want to draw something on the screen that points in the direction of movement. Perhaps you are drawing an ant, or a car, or a spaceship. And when we say "point in the direction of movement," what we are really saying is “rotate according to the velocity vector.” Velocity is a vector, with an x and a y component, but to rotate in ProcessingJS we need an angle. Let’s draw our trigonometry diagram one more time, with an object’s velocity vector:
OK. We know that the definition of tangent is:
tangent(angle)=velocityyvelocityx
The problem with the above is that we know velocity, but we don’t know the angle. We have to solve for the angle. This is where a special function known as inverse tangent comes in, sometimes referred to as arctangent or tan-1. (There is also an inverse sine and an inverse cosine.)
If the tangent of some value a equals some value b, then the inverse tangent of b equals a. For example:
iftangent(a)=b
thena=arctangent(b)
See how that is the inverse? The above now allows us to solve for the angle:
iftangent(angle)=velocityy/velocityx
thenangle=arctangent(velocityy/velocityx)
Now that we have the formula, let’s see where it should go in our mover’s display() method. Notice that in ProcessingJS, the function for arctangent is called atan(). JavaScript also provides Math.atan() natively (as well as all the basic trig functions), but we'll stick with the ProcessingJS provided functions.
Mover.prototype.display = function () {
  var angle = atan(this.velocity.y / this.velocity.x);

  stroke(0, 0, 0);
  fill(127, 127, 127);
  pushMatrix();
  rectMode(CENTER);
  translate(this.position.x, this.position.y);
  rotate(angle);
  rect(0, 0, 30, 10);
  popMatrix();
};
Now the above code is pretty darn close, and almost works. We still have a big problem, though. Let’s consider the two velocity vectors depicted below.
Though superficially similar, the two vectors point in quite different directions—opposite directions, in fact! However, if we were to apply our formula to solve for the angle to each vector…
V1 ⇒ angle = atan(3/-4) = atan(-0.75) = -0.644 radians = -36.898 degrees
V2 ⇒ angle = atan(-3/4) = atan(-0.75) = -0.644 radians = -36.898 degrees
…we get the same angle for each vector. This can’t be right for both; the vectors point in opposite directions! The thing is, this is a pretty common problem in computer graphics. Rather than simply using atan() along with a bunch of conditional statements to account for positive/negative scenarios, ProcessingJS (along with JavaScript and pretty much all programming environments) has a nice function called atan2() that does it for you.
Mover.prototype.display = function () {
  var angle = atan2(this.velocity.y, this.velocity.x);

  stroke(0, 0, 0);
  fill(127, 127, 127);
  pushMatrix();
  rectMode(CENTER);
  translate(this.position.x, this.position.y);
  rotate(angle);
  rect(0, 0, 30, 10);
  popMatrix();
};
To simplify this even further, the PVector object itself provides a function called heading(), which takes care of calling atan2() for you so you can get the 2D direction angle, in radians, for any PVector.
Here's what the program looks like, all together. Move your mouse over it and see how it rotates!

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.

Want to join the conversation?

  • leaf red style avatar for user Blaze
    So heading is basically atan2?
    (6 votes)
    Default Khan Academy avatar avatar for user
  • aqualine seed style avatar for user Troy Dickie
    What are the variables xoff, yoff & r doing here? I see that's they are set to values of 1000, 0 & 16 respectively but then neither those variables, or the numbers are used anywhere in the program.
    (5 votes)
    Default Khan Academy avatar avatar for user
    • old spice man green style avatar for user Jim E
      You are right. They are not used in this project.
      That happens often when using object oriented coding. It is so easy to re-use the code, so you just copy from your ready made objects, but you don't need all parts of the objects in all projects.
      (3 votes)
  • starky tree style avatar for user Kaos
    In the turning car challenge pressing the keys to turn the car results in a stutter before it starts turning. Any idea on how I can make it smoother? Also how can I implement multiple keys at the same time?
    (3 votes)
    Default Khan Academy avatar avatar for user
  • duskpin sapling style avatar for user Tris Parr
    Here's my last part of code:

    Car.prototype.turnLeft = function() {
    //println("turning left!");
    var force = this.velocity.get();
    force.rotate(-PI/2);
    this.applyForce(force);
    };

    Car.prototype.turnRight = function() {
    //println("turning right!");
    var force = this.velocity.get();
    force.rotate(PI/2);
    this.applyForce(force);
    };

    var car = new Car();

    var keyPressed = function() {
    if (keyIsPressed && keyCode === LEFT) {
    car.turnLeft();
    } else if (keyIsPressed && keyCode === RIGHT) {
    car.turnRight();
    }
    };

    draw = function() {
    background(102, 209, 104);
    keyPressed();
    car.update();
    car.checkEdges();
    car.display();
    };
    (4 votes)
    Default Khan Academy avatar avatar for user
  • winston default style avatar for user Luoqi Wang
    Took me quite a while to figure out the next challenge solution.
    I wonder if there is any other ways to achieve the task?

    Car.prototype.turnLeft = function() {
    var f = this.velocity.get();
    f.rotate(-PI/2);
    this.applyForce(f);
    };
    (3 votes)
    Default Khan Academy avatar avatar for user
  • leaf green style avatar for user Hồ Ngọc Quang
    It seems to have an anomaly inside. First, we measure the angle. Then we rotate all the way to that angle. That process should make the rectangle to move not very smooth. For example, we move the mouse to the upper boundary, it will move up forever and we slowly introduce the mouse from outside of the screen to the sides bounadry. The angle should calculated to 90 and rotate immediately to the mouse. And that is not what i've seen
    (2 votes)
    Default Khan Academy avatar avatar for user
  • starky sapling style avatar for user a.h
    For some reason, in the next challenge when I press the left or right arrow key, the car teleports to the top left. I don't understand what I'm doing wrong but programme seems to accept the code. Here is my code:

    angleMode = "radians";

    var Car = function() {
    this.position = new PVector(width/2, height/2);
    this.velocity = new PVector(3, 0);
    this.acceleration = new PVector(0, 0);
    this.topspeed = 4;
    this.xoff = 1000;
    this.yoff = 0;
    this.r = 16;
    };

    Car.prototype.update = function () {
    this.velocity.add(this.acceleration);
    this.velocity.limit(this.topspeed);
    this.position.add(this.velocity);
    this.acceleration.mult(0);
    };

    Car.prototype.applyForce = function(force) {
    this.acceleration.add(force);
    };

    Car.prototype.turnLeft = function() {
    println("turning left!");
    var rotate = this.velocity.get;
    this.velocity.rotate(-0.1);
    this.applyForce(rotate);
    };

    Car.prototype.turnRight = function() {
    println("turning right!");
    this.velocity.rotate(0.1);
    this.applyForce(rotate);
    };

    Car.prototype.display = function () {
    // Step 3:
    var angle = this.velocity.heading();

    stroke(0, 0, 0);
    strokeWeight(2);
    fill(127, 127, 127);
    pushMatrix();
    rectMode(CENTER);
    translate(this.position.x, this.position.y);
    // Step 3:
    rotate(angle);
    // draw the car
    fill(255, 0, 0);
    rect(0, 0, 70, 30);
    rect(0, 0, 29, 30);
    fill(79, 79, 79);
    ellipse(-15, -18, 20, 8);
    ellipse(-15, 18, 20, 8);
    ellipse(15, 18, 20, 8);
    ellipse(15, -18, 20, 8);
    rect(21, 0, 11, 26);
    popMatrix();
    };

    Car.prototype.checkEdges = function () {
    if (this.position.x > width) {
    this.position.x = 0;
    } else if (this.position.x < 0) {
    this.position.x = width;
    }

    if (this.position.y > height) {
    this.position.y = 0;
    } else if (this.position.y < 0) {
    this.position.y = height;
    }
    };

    var car = new Car();

    draw = function() {
    background(102, 209, 104);
    car.update();
    car.checkEdges();
    car.display();
    };

    keyPressed = function() {
    if (keyCode === LEFT) {
    car.turnLeft();
    } else if (keyCode === RIGHT) {
    car.turnRight();
    }
    };
    (2 votes)
    Default Khan Academy avatar avatar for user
  • duskpin ultimate style avatar for user zoesterosh
    What is the difference between ProcessingJS and JavaScript?
    (2 votes)
    Default Khan Academy avatar avatar for user
  • leaf green style avatar for user cameronsil
    I've done "Challenge: Turning car", it says I've completed all the steps, but when I press the left and right arrow keys, my car won't turn. Why is that?

    Here's my code:
    angleMode = "radians";

    var Car = function() {
    this.position = new PVector(width/2, height/2);
    this.velocity = new PVector(3, 0);
    this.acceleration = new PVector(0, 0);
    this.topspeed = 4;
    this.xoff = 1000;
    this.yoff = 0;
    this.r = 16;
    };

    Car.prototype.update = function () {
    this.velocity.add(this.acceleration);
    this.velocity.limit(this.topspeed);
    this.position.add(this.velocity);
    this.acceleration.mult(0);
    };

    Car.prototype.applyForce = function(force) {
    this.acceleration.add(force);
    };

    Car.prototype.turnLeft = function() {
    println("turning left!");
    var force = PVector.get(this.velocity);
    force.rotate((-PI/2));
    this.applyForce(force);
    };

    Car.prototype.turnRight = function() {
    println("turning right!");
    var force = PVector.get(this.velocity);
    force.rotate(PI/2);
    this.applyForce(force);
    };

    Car.prototype.display = function () {
    // Step 3:
    var angle = this.velocity.heading();

    stroke(0, 0, 0);
    strokeWeight(2);
    fill(127, 127, 127);
    pushMatrix();
    rectMode(CENTER);
    translate(this.position.x, this.position.y);
    // Step 3:
    rotate(angle);
    // draw the car
    fill(255, 0, 0);
    rect(0, 0, 70, 30);
    rect(0, 0, 29, 30);
    fill(79, 79, 79);
    ellipse(-15, -18, 20, 8);
    ellipse(-15, 18, 20, 8);
    ellipse(15, 18, 20, 8);
    ellipse(15, -18, 20, 8);
    rect(21, 0, 11, 26);
    popMatrix();
    };

    Car.prototype.checkEdges = function () {
    if (this.position.x > width) {
    this.position.x = 0;
    } else if (this.position.x < 0) {
    this.position.x = width;
    }

    if (this.position.y > height) {
    this.position.y = 0;
    } else if (this.position.y < 0) {
    this.position.y = height;
    }
    };

    var car = new Car();

    draw = function() {
    background(102, 209, 104);
    car.update();
    car.checkEdges();
    car.display();

    var keyPressed = function() {
    if (keyIsPressed && keyCode === LEFT) {
    car.turnLeft();
    } else if (keyIsPressed && keyCode === RIGHT) {
    car.turnRight();
    }
    };
    };
    (2 votes)
    Default Khan Academy avatar avatar for user
  • male robot johnny style avatar for user techmasterau
    I'm so confused with the next challenge, here's my code...

    angleMode = "radians";

    var Car = function() {
    this.position = new PVector(width/2, height/2);
    this.velocity = new PVector(3, 0);
    this.acceleration = new PVector(0, 0);
    this.topspeed = 4;
    this.xoff = 1000;
    this.yoff = 0;
    this.r = 16;
    };

    Car.prototype.update = function () {
    this.velocity.add(this.acceleration);
    this.velocity.limit(this.topspeed);
    this.position.add(this.velocity);
    this.acceleration.mult(0);
    };

    Car.prototype.applyForce = function(force) {
    this.acceleration.add(force);
    };

    Car.prototype.turnLeft = function() {
    println("turning left!");
    };

    Car.prototype.turnRight = function() {
    println("turning right!");
    };

    Car.prototype.display = function () {
    // Step 3:
    var angle = this.velocity.heading();

    stroke(0, 0, 0);
    strokeWeight(2);
    fill(127, 127, 127);
    pushMatrix();
    rectMode(CENTER);
    translate(this.position.x, this.position.y);
    // Step 3:
    rotate(angle);
    // draw the car
    fill(255, 0, 0);
    rect(0, 0, 70, 30);
    rect(0, 0, 29, 30);
    fill(79, 79, 79);
    ellipse(-15, -18, 20, 8);
    ellipse(-15, 18, 20, 8);
    ellipse(15, 18, 20, 8);
    ellipse(15, -18, 20, 8);
    rect(21, 0, 11, 26);
    popMatrix();
    };

    Car.prototype.checkEdges = function () {
    if (this.position.x > width) {
    this.position.x = 0;
    } else if (this.position.x < 0) {
    this.position.x = width;
    }

    if (this.position.y > height) {
    this.position.y = 0;
    } else if (this.position.y < 0) {
    this.position.y = height;
    }
    };

    var car = new Car();

    var keypresed = function() {
    if (keyIsPressed === LEFT) {
    car.turnLeft();}
    else if (keyIsPressed === RIGHT) {
    car.turnRight();}
    };

    draw = function() {
    background(102, 209, 104);
    car.update();
    car.checkEdges();
    car.display();
    };

    But then oh noes pops up and says "Which function are you using to process input? You should be using the processingJS function that is called whenever you press a key."

    but everything i used is processingJS...isn't it?
    (2 votes)
    Default Khan Academy avatar avatar for user