8. PVector

On the processing website, there is a comprehensive PVector tutorial about the PVector object. The PVector object is used to represent vector objects, and provides data elements and methods for vector math operations. We can consider a vector to be a collection of values that describe some concept that has some spatial dimension, or some properties that have directional components. A vector in mathematics represents the difference between 2 points in space. When a vector is used to represent a single point’s location, it is assumed that the 0,0 origin is one of the vector endpoints. The simple ball bouncing animation below gives an example of using the ball’s x,y location and adding horizontal speed and vertical speed to the balls current location to animate the motion of the ball. Location, velocity (speed), and acceleration can all be represented using the PVector object, in addition, a PVector could also represent something like friction which might have different values along different x, y, or z dimensions. The PVector object has methods to add() 2 vectors which simplifies working with motion concepts in multiple dimensions. In addition, the PVector object provides a good introduction to object oriented programming.

At its core, a PVector is just a convenient way to store two values Daniel Shiffman PVector tutorial

8.1. Bouncing Ball - No Vectors

In the PVector tutorial and in Learning Processing Section 5.7, the example programs create a bouncing ball. The bouncing ball programs provide a good example of how to program object motion. The code below is from the Processing.org PVector tutorial and it creates a simple bouncing ball.:

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

void setup() {
  size(200,200);
}

void draw() {
  fill(255,10);
  rect(0,0,width,height);  //transparent overlay background

  // Add the current speed to the location.
  x = x + xspeed;
  y = y + yspeed;

  // Check for bouncing
  if ((x > width) || (x < 0)) {
    xspeed = xspeed * -1;  //reverse speed
  }
  if ((y > height) || (y < 0)) {
    yspeed = yspeed * -1;   //reverse speed
  }

  // Display at x,y location
  fill(175);
  ellipse(x,y,16,16);
}

8.2. Bouncing Ball with PVector

The PVector object provides variables to hold the data of the ball’s location coordinates and also provides methods that allow for easy use of vector operations for movement. In the code above, the ball’s location is updated during each loop, so the new location = location + speed, and this is calculated separately for each of our canvas’s 2 dimensions: x and y. We can instead use a PVector to hold the location and another PVector to represent speed, and then we can use the PVector add() method to calculate the new location of the ball. Below is the same bouncing ball program using PVectors for location and velocity.:

PVector speed = new PVector(1 ,  3.3);  // create a new PVector object instance
PVector location = new PVector(100,100);

void setup() {
    size(200,200);
}

void draw() {
    fill(255,10);
    rect(0,0,width,height);  //transparent overlay background

    // Add the current speed to the location: vector addition
    location.add(speed);

    // Check for bouncing
    if((location.x > width) || (location.x < 0)) {
        speed.x = speed.x * -1;  //reverse speed
    }
    if ((location.y > height) || (location.y < 0)) {
        speed.y = speed.y * -1;   //reverse speed
    }

    // Display at x,y location
    fill(175);
    ellipse(location.x,location.y,16,16);
  }

Below is a processing sketch for the PVector Bouncing ball. Click on the image to stop / start the animation.

8.3. PVector Object

The PVector object allows us to explore the processing object syntax. When we want to create an instance of an object, we use the object’s constructor function. According to the processing PVector reference, the PVector class has 3 different constructor functions. Notice that each constructor has a unique function signature, this is an important concept called function overloading. We can have several versions of the same function, but the signature of each function must be unique. For objects, it’s helpful to have different constructor functions, for the PVector, this allows it to represent both 2D or 3D vectors depending on how we initialize our instance.

PVector();  //default constructor function
PVector(float x, float y);  // 2 dimensional constructor function
PVector(float x, float y, float z);  //3 dimensional constructor function

To create a new instance of a PVector object we must use the Processing object syntax depending on which constructor we choose to use, the default constructor has no arguments, therefore the x and y properties are initialized using dot notation. Dot notation the syntax for calling a class’s method or for setting a property value for a data element that belongs to the object’s own object class. We set the x value of the location PVector instance using location.x=100; Note that in the code below, the object type is PVector.

PVector location = new PVector();  // declare a new PVector object
location.x= 100;        // initialize the x data element using dot notation
location.y= 120;        // initialize the y data element using dot notation

PVector speed = new PVector(3 , 4 );  // declare and initialize a new PVector object speed has x,y components

location.add(speed)  // use add method to add vector components of speed to location.

8.4. Object Dot Notation

PVector is an object and has both functions and data elements which are associated with that object. As mentioned above, we use the dot notation when accessing and modifying properties of an object. For PVector, x and y are properties or data elements. Below is an example of setting a property value for an instance of a PVector object.:

PVector location = new PVector();
location.x = 100;   // use dot notation to initialize the x property of the location PVector
location.y = 200;

location.x = location.y + 10 ; // dot notation allows accessing and resetting object properties like x and y

ellipse(location.x, location.y, 50,50);  // use dot notation to access the x and y properties of the location PVector object

We also use dot notation when using methods that belong to an object. Methods are functions that belong to an object so they act on that object. We use dot notation to make it clear that we want to use the object’s method, rather than some global function that does not belong to the object’s class. This allows the compiler to understand which function we intend to use. A common example of this would be the function the print() function. It is useful for debugging to be able to print some meaningful information about an object, so when we design an object class, we’ll often either create a print(), display() or a toString() method that belongs to the class so that we can easily access and view data associated with an object. On the other hand, processing provides it’s own print() function, so if PVector had it’s own print() method, then the compiler would need to understand whether we intend to call the processing print() function or the PVector print() method. Dot notation syntax tells the compiler that we want to call the method associated with the object calling the function. Actually PVector has a toString() method, so we could use dot notation to call the method in the following way:

print(location.toString());              // prints '[ 100.0 , 200.0 , 0.0 ]'  which are the values for the x,y,z properties.

8.5. Functions: Pass by Reference

So far, when we’ve created functions, we have only used primitive variable types like int, float, booleans, or literal values. When these values are passed into a function, a copy of the value is passed into the function, so within the function, any modification to a value only affects the local variable. We’ve made the distinction between local and global variables based on the understanding that variables passed into a function are a local copy of a global variable, and so any corresponding global variable isn’t changed when the local function variable is modified. Example of primitive-type function arguments: pass-by-value

float someVal=20;  // declare global variable

void doSomething(float inputVal){
        inputVal += 5;  // modify local variable
}

void doSomethingElse(float someVal){
        someVal += 10;  // modify local variable
}
void setup(){
        doSomething(someVal);   // call the function using someVal as input
        doSomethingElse(someVal);     // call the function using someVal as input

println("someVal " + someVal);  // someVal=20.0 because it is a global and a primitive-type, so pass-by-value appliees
}

This is not the case when using passing objects into a function. For most cases, when we pass an object variable into a function, we actually want to have the changes take place on the object that we pass into the function. Therefore, what is passed into a function is not a copy of a variable, but is a reference or a pointer to the object. here’s an example of pass-by-reference for an PVector object instance

PVector location = new PVector(20,20);   //create and initialize a PVector instance

void doSomething(PVector somePV){
        somePV.x += 5;          //modify the input PVector object x attribute;
}

void setup(){

        doSomething(location);

        println("location.x " + location.x);    // 25.0  location.x was modified due to pass-by-reference
}

So now that we have had experience working with the PVector object, in the next section, let’s create a very simple ball class so we can create ball objects. Then we’ll move on to the example in chapter 8, however where the Learning Processing book creates a car class we will make a fish class.

8.6. Questions:

  1. Which of the following concepts can be represented by a PVector object? location, velocity, friction, acceleration?

  2. What would be the value of location.x in the code below:

    PVector location=new PVector(5/2, 20);
    
  3. What would be the value of location.x in the code below:

    PVector location= new PVector( 5.0/2 , 20);