CHAPTERS










An Introduction To Programming With Processing








Chapter: Transforms

pushMatrix() and popMatrix()

As transforms effect everything below their implementation within a sketch, due to them transforming the entire coordinate system, there are times when you will find that this is not the effect that you want.
The image below of a cart is made up of three separate images which are rendered in the following order the wheel in the background, the main cart and the wheel in the foreground. If our intention was to rotate the back wheel first, as the back wheel would have to be rendered before any of the other images, we run the risk of having the transforms applied to the back wheel effecting the main cart and the wheel in the foreground as they are rendered after the back wheel, and will therefore be effected by any scale(), translate() and/or rotate() function that is intended for the back wheel. This would create the impression of the cart orbiting around the back wheel, which is obviously not what we where trying to accomplish. Two functions in Processing called pushMatrix() and popMatrix() help us to solve this problem.

Cart Wheels

The template rendering of the cart.pde sketch

The function pushMatrix() when called before a transform is executed will store the current state of the coordinate system. This means that if you call a pushMatrix() function at the start of the draw() structure it will remember the default position of the coordinate system (pre-transform). You are then free to apply transforms to the particular component of the sketch that you would like to scale, rotate and/or translate. Once you are satisfied with the transforms you have performed you can then render the component, for example by means of a call to the image() function. Running the popMatrix() function at this point will then be necessary to restore the coordinate system back to the state it was in when the previous pushMatrix() was called. You can then proceed to run more pushMatrix(), transforms, render, popMatrix() combinations on other components of the sketch which will remain unaffected by the previous transforms. Using pushMatrix() and popMatrix() in this way allows us to apply multiple transforms to a component of a sketch ,and not have those transforms effect other components.

Transform Matrix 01
Transform Matrix 02

Lets have a look at how to use this technique to make the cart move across the Display Window and have the wheels of the cart rotate accordingly.
We'll start as always with a template of what we want the final result should look like:

PImage cart;
PImage wheelFd;
PImage wheelBd;
PImage grass;

void setup(){
  size(800,600);
  cart = loadImage("cartMain.png");
  wheelFd = loadImage("wheelFd.png");
  wheelBd = loadImage("wheelBd.png");
  grass = loadImage("grassLessBlur.jpg");
}
void draw(){
  background(grass);
  imageMode(CENTER);
  image(wheelBd, -75, 25);
  image(cart, 0, 0);
  image(wheelFd, -95, 35);
  //println(mouseY);
}

Out of interest you might be wondering how the X and Y parameters of the image() functions which render the wheels relative to the position of the cart were determined. If you look at the last line you'll see a println() function that has been commented out. By replacing the image() function's X and Y parameters with mouseX then mouseY, respectively and one at a time,  you can run the sketch with the image of the wheel attached to either the mouse's X or Y position. Place the wheel in the position it should be in, the println() function (when uncommented) will print out the position of your mouse, note this value and replace it with the mouseX or mouseY system variable in the wheel's image() function's corresponding X or Y parameter.
As the imageMode() function is set to CENTER in this example you will have to move the main cart image to a location that is not the origin, so that the left and top halves of the image is not obscured by the boundaries of the Display Window. You will then need to subtract the value that you added to the X and Y parameters of the image() function to draw the cart from the respective values printed in the Text Area/debugging console that will be used to place the wheels in the correct locations.
Now that we have a template from which to start let's set up a temporary system that allows the cart to trail after the mouse with a bit of friction, we'll then modify this code to suit the needs of our sketch. This methodological approach to sketching by creating a rough idea then refining it will allow us to see results much sooner in the sketching process, rather than spending a lot of time on a sketch only to find out when it is almost done that the effect we were trying to achieve isn't quite working out.
We'll start by adding the following global variables:

float xPos;
float difX;
int drag = 30;

If you recall from the imageScroll.pde sketch, we used the technique of creating the impression of friction with a similar set of global variables. The only difference is that in this sketch we are now using the same technique to create the impression of friction along the X axis. Next we'll need to add the following statements to the draw() structure after the imageMode() function call:

  image(wheelBd, -75, 25);
  image(cart, 0, 0);
  image(wheelFd, -95, 35);
  difX = mouseX - (xPos + cart.width/2);
  xPos += difX/drag;

Since we want the cart to move with relation to the mouse moving past the end of the cart on the right hand side, we're going to divide the cart.width object variable by 2 and add it to the position of the cart which is now determined by the center of the cart object because we have set imageMode() to CENTER. If imageMode() had been at it's default value of CORNER we would simply use cart.width to determine the right edge of the cart image.
To temporarily link these statements to the rendering of the cart, directly after the previous statements add the following code:

 pushMatrix();
  translate(xPos, 250);
  image(wheelBd, -75, 25);
  image(cart, 0, 0);
  image(wheelFd, -95, 35);
 popMatrix();

Notice that we just added three additional statements. The first new statement is pushMatrix() this tells Processing to store the current transformational data of the coordinate system in memory, we then use the translate() function to to modify the coordinate system along the X axis by the value of the variable xPos and the numerical constant of 250 for the Y parameter. The Y parameter is a numerical constant simply because it will not change throughout the duration that the sketch is running. Next we render the images without any changes to their parameters as all of their positional data remains the same, the transform will create the impression of movement for us. Then finally we use the popMatrix() function to restore the transformational data relating to the coordinate system when pushMatrix() was initially called.
When using pushMatrix() it must always be coupled with popMatrix() or Processing will complain about there being too many calls to pushMatrix() and not enough to popMatrix().


















Brought to you by

www.lyndondaniels.com