Chapter 5: Response

  1. Once and Forever
    1. Example 5-1: The draw() Function
    2. Example 5-2: The setup() Function
    3. Example 5-3: Global Variables
  2. Follow
    1. Example 5-4: Track the Mouse
    2. Example 5-5: The Dot Follow You
    3. Example 5-6: Draw Continuously
    4. Example 5-7: Set Line Thickness
    5. Example 5-8: Easing Does it
    6. Example 5-9: Smooth Lines with Easing
  3. Click
    1. Example 5-10: Click the Mouse
    2. Example 5-11: Detect When Not Clicked
    3. Example 5-12: Multiple Mouse Buttons
  4. Location
    1. Example 5-13: Find the Cursor
    2. Example 5-14: The Bounds of a Circle
    3. Example 5-15: The Bounds of a Rectangle
  5. Type
    1. Example 5-16: Tap a Key
    2. Example 5-17: Draw Some Letters
    3. Example 5-18: Check for Specific Keys
    4. Example 5-19: Move the Arrow Keys
  6. Map
    1. Example 5-20: Map Values to a Range
    2. Example 5-21: Mat with the map() Function
  7. Robot 3: Response

Response/Interactivity

The draw() Function

Code that responsds to input from the user (mouse, keyboard, and other devices) must run continuously, seeking out those inputs in real time. Processing has a special function dedicated to this called draw().

PROCESSING

Psuedo-code

void draw() {
   // A bunch of stuff, also known as a block of code
}

Any block of code (that is, lines of code inside the brackets), runs continuously in the Display Window when you run your code in the PDE. Each trip through the draw() function is called a frame. This is a lot like Adobe Animate/Flash! You can even set the frame rate of playback (although the default is 60fps).

Let's try something...

PROCESSING

Example 5-1: The draw() Function

void draw() {
 // Displays the frame count to the Console
 println("I’m drawing");
 println(frameCount);
}

Which will output text into the console window (the window below the editing section of the PDE):

CONSOLE OUTPUT

I'm drawing
1
I'm drawing
2
I'm drawing
3

And so on, forever or until we stop the program.

The setup() Function

The setup() function compliments the draw() function by being run only once at the beginning of a Sketch, and it, well, sets up the Sketch with things like Display Window size.

PROCESSING

Example 5-2: The setup() Function

void setup() {
 println("I’m starting");
}
void draw() {
 println("I’m running");
}

That outputs:

CONSOLE OUTPUT

I'm starting
I'm running
I'm running
I'm running

One More Thing...

While setup() and draw() pair together nicely, there's a GOTCHA rule (codeing is full of these, like forgetting semi-colons) which is: Variables inside functions only work inside that function.

Which means if we set up variables inside the setup() function, draw() won't be able to use them. To get around this, its customary to put the variables you'll need for draw() at the top of the Sketch, and not inside the setup() function. Variables like this are called Global Variables.

PROCESSING

Example 5-3: Global Variable

int x = 280;
int y = -100;
int diameter = 380;

void setup() {
   size(480, 120);
   fill(102);
}

void draw() {
   background(204);
   ellipse(x, y, diameter, diameter);
}
Resulting Display Window

Follow (The Mouse)

By having a running draw() loop, we can track interactions with the computer, like Mouse movement. Processing can access the current mouse x and y coordinates with the environmental variables, mouseX and mouseY (note the capital X and Y, it won't work without the proper capitalization). Here we'll just draw an ellipse to the Display Window 65 times per second (theoretically):

PROCESSING

Example 5-4: Track The Mouse

void setup() {
   size(480, 120);
   fill(0, 102);
   noStroke();
}

void draw() {
   ellipse(mouseX, mouseY, 9, 9);
}
Resulting Display Window

Cool tricks: One Dot Alone

In the previous example the ellipse is drawn over and over and leaves a trail. How could we make it so the ellipse doesn't leave a trail?

PROCESSING

Example 5-5: The Dot Follows You

void setup() {
   size(480, 120);
   fill(0, 102);
   noStroke();
}

void draw() {
    background(204);   
    ellipse(mouseX, mouseY, 9, 9);
}
Resulting Display Window

Cool tricks: Connect the Dots For Continuous Drawing

So here instead of drawing an ellipse every frame, we'll draw a line whose starting x,y is the current mouseX and mouseY and whose ending x,y is the previous mouseX and mouseY from a 65th of a second ago, which Processing has a special environmental variable for: pmouseX and pmouseY.

PROCESSING

Example 5-6: Draw Continuously

void setup() {
   size(480, 120);
   strokeWeight(4);
   fill(0, 102);
}

void draw() {
    line(mouseX, mouseY, pmouseX, pmouseY);   
}
Resulting Display Window

New Function: dist()

Processing has a lot of built-in functions to make our coding lives easier. Half the fun of using Processing is discovering what cool little widgets lie within. This time, let's try dist() (short for distance). It takes two x/y locations and returns the distance between them. Try this:

PROCESSING

Psuedo Code

float myDistance = dist(1,1,10,10);
println(myDistance);

Interesting, but what to do with it? How about if we use the value to change the size of the ellipse we're drawing with some draw() code? Since the faster you move the mouse, the further apart the points are, you can use that measurement to set the size of strokeWeight():

PROCESSING

Example 5-7: Set Line Thickness

void setup() {
 size(480, 120);
 stroke(0, 102);
}

void draw() { 
 float weight = dist(mouseX, mouseY, pmouseX, pmouseY);
 strokeWeight(weight);
 line(mouseX, mouseY, pmouseX, pmouseY);
}
Resulting Display Window

Or you could use dist() to change the opacity of the line, or the grayscale color...

New Concept: Easing

Sometimes we need to smooth out the motion of an object, otherwise it just jumps around and feels jerky. In coding this is called "easing" and is accomplished thusly:

PROCESSING

Example 5-8: Easing Does It

float xVal;
float easingVal = 0.1;

void setup() {
  size(500, 100);
}

void draw() {
   float targetX = mouseX;
   xVal += (targetX - xVal) * easingVal;
   ellipse(xVal, height/2, 12, 12);
   println(targetX + " : " + xVal);
}
Resulting Display Window

A couple of new things in this. We use float in declaring our variables because it's a number that resolves to numbers that need a decimal place. We also declare the xVal variable without assigning it a value. There's no need to because it gets set in the draw() block of code.

There's also some new methods revealed in the println(targetX + " : " + ) function. println() will output what's between the two parenthesi

s to the console window in the PDE following a few rules. You can string values together in the output by putting plus signs between the values, as in:

println(valueA + valueB + valueC)

Which would output whatever those three values are to the console. But usually that's not useful because you need a space or other text between the outputted values. To insert text between values you use "some text", with a fairly standard technique of using ", " to put a comma and space between the values or " : " to put a space-colon-space between the values.

Let's try a version that eases on both the x and y axis:

PROCESSING

Exercise 5-8: Easing Does It Variation I

float xVal;
float yVal;
float easingVal = 0.1;

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

void draw() {
   float targetX = mouseX;
   float targetY = mouseY;
   xVal += (targetX - xVal) * easingVal;
   yVal += (targetY - yVal) * easingVal;
   ellipse(xVal, yVal, 12, 12);
   println(targetX + ":" + xVal + ", " + targetY + ":" + targetX);
}
Resulting Display Window

Now let's modify it to not leave trails of ellipses when you drag the mouse around. Simple enough, in Processing you simply refresh the Display Window's background color with background(NN); where NN is the grayscale number of the color.

PROCESSING

Exercise 5-8: Easing Does It Variation II

float xVal;
float yVal;
float easingVal = 0.1;

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

void draw() {
   background(220);
   float targetX = mouseX;
   float targetY = mouseY;
   xVal += (targetX - xVal) * easingVal;
   yVal += (targetY - yVal) * easingVal;
   ellipse(xVal, yVal, 12, 12);
   println(targetX + ":" + xVal + ", " + targetY + ":" + targetX);
}

Smooth Lines with Easing

Now that we have a technique for easing, or what is known in coding terms as an algorithm, we can apply it to other problems as well, like the coarse and chunky lines from the previous sketch.

PROCESSING

Exercise 5-9: Smooth Lines with Easing

float currentX;
float currentY; 
float previousX;
float previousY;
float easingVal = 0.08;

void setup(){
  size(480, 120);
  stroke(0, 102);
}

void draw() {
  float targetX = mouseX;
  currentX += (targetX - currentX) * easingVal;
  float targetY = mouseY;
  currentY += (targetY - currentY) * easingVal;
  float weightVal = dist(currentX, currentY, previousX, previousY);
  strokeWeight(weightVal);
  line(currentX, currentY, previousX, previousY);
  previousX = currentX;
  previousY = currentY;
}
Resulting Display Window

You can get a lot of variation in this by modifying the easingVal variable.

Click The Mouse

Now let's get into if/then statement (a close cousin to if/then/else). The Display Window can detect when a mouse click occurs inside itself. The environmental variable mousePressed holds a boolean value of true or false that reveals the current status of the mouse button when it's inside the Document Window. Note that it will be true as long as the mouse button is held down, not just at the moment it's pressed.

Let's try it...

PROCESSING

Example 5-10: Click The Mouse

void setup() {
   size(240, 120);
   strokeWeight(30);
}

void draw() {
   background(204);
   stroke(102);
   line(40, 0, 70, height);
   if (mousePressed == true) {
      stroke(0);
   }
   line(0, 70, width, 50);
}
Resulting Display Window Resulting Display Window

And here's that bizarre == test, which tests to see if something is true or not by one value being equal to another value.

Interestingly, you could also do the test simply as:

Example 5-10: Click The Mouse Decontstructed

PROCESSING

Example 5-11: Detect When Not Clicked

if (mousePressed) {
  stroke(0);
}

This works because the if test is looking to see if what's between the parantheses is true or false. So boolean variables like mousePressed will return true or false, which the if() test can evaluate as true or false.

A Word Of Caution

The == test compares the values on the left and right to test whether they are equivalent.
IT IS NOT THE SAME AS = !

Review of Logical Operators for if() tests

A > B is A greater than B
A < B is A less than B
A >= B is A greater or equal to B
A <= B is A less than or equal to B
A == B is A equal to B.
Yes, that's right, two = signs, not one. One equal sign assigns a value and doesn't test two values.
A != B is A not equal to B
Believe it or not, this can come in really handy

Detect When Not Clicked

A Single if/then block gives you the choice of running some code or not running it. But to run two different blocks of code based on the true or false test, you can use an if/then/else block. When the if test is true, it does one block of code, but when it's false it does a different block of code. Thusly...

PROCESSING

Example 5-11:Detect When Not Clicked

void setup() {
   size(240, 120);
   strokeWeight(30);
}

void draw() {
   background(204);
   stroke(102);
   line(40, 0, 70, height);
   if (mousePressed == true) {
      stroke(0);
   } else {
     stroke(255);
   }
   line(0, 70, width, 50);
}
Resulting Display Window Resulting Display Window

Multiple Mouse Buttons

While mousePressed gives us the status of any mouse button, the environment variable mouseButton returns a value of either LEFT or RIGHT, which can be tested...

PROCESSING

Example 5-12: Multiple Mouse Buttons

void setup() {
   size(120, 120);
   strokeWeight(30);
}

void draw() {
   background(204);
   stroke(102);
   line(40, 0, 70, height);
   if (mousePressed == true) {
     if (mouseButton == LEFT) {
        stroke(255);
      } else {
        stroke(0);
      }
   }
   line(0, 70, width, 50);
}
Resulting Display Window: No mouseButton Click Resulting Display Window Resulting Display Window: RIGHT mouseButton Click

The use of if/then/else blocks are very powerful, and are a lot of what coding is about. Here's some flow diagrams of how they can work:

These flow diagrams correspond to the code on the right:

Flow Diagram: If/Then Test
if (test) {
  statements
}
Flow Diagram: If/Then/Else Test
if (test) {
  statements 1
} else {
  statements 2
}
Flow Diagram: If/Then/Else If Test
if (test 1) {
  statements 1
} else if (test 2) {
  statements 2
}

We'll play a lot more with these blocks! Let's try one more variation just for grins...

PROCESSING SKETCH

Example 5-12: Variations

void setup() {
   size(120, 120);
   strokeWeight(30);
}

void draw() {
   background(204);
   stroke(102);
   line(40, 0, 70, height);
   if (mousePressed == true) {
      if (mouseButton == LEFT) {
         stroke(255)
      } else if (mouseButton == RIGHT) {
         stroke(0);
      } else { // This will detect if the center button is pressed
	     stroke(#ff0000); 
   }
   line(0, 70, width, 50);
}
Resulting Display Window: No mouseButton Resulting Display Window: LEFT mouseButton Resulting Display Window: RIGHT mouseButton Resulting Display Window: Neither RIGHT nor LEFT mouseButton

If we flowchart the above, this is how it would look (logic-wise):

Flow Chart: If/Then/Else If/Else
 

Location

We now know lots of stuff about the mouse, like if a button is pressed, which button is pressed, and where is the pointer relative to the Display Window, which we get from mouseX and mouseY. We can test the mouse location and respond to it with an if statement. Our text does this interesting trick:

PROCESSING

Example 5-13: Find the Cursor

float xVal;
int offsetVal = 10;

void setup() {
   size(240,120);
   xVal = width/2; // xVal is in the center
}

void draw() {
   background(204);
   if (mouseX > xVal) {
      xVal += 0.5;
      offsetVal = -10;
   }
   if (mouseX < xVal) {
      xVal -= 0.5;
      offsetVal = 10;
   }
   // Draw arrow left or right depending on "offsetVal" 
   line(xVal, 0, xVal, height);
   line(mouseX, mouseY, mouseX + offsetVal, mouseY - 10);
   line(mouseX, mouseY, mouseX + offsetVal, mouseY + 10);
   line(mouseX, mouseY, mouseX + (offsetVal*3), mouseY);
}

Finding the edges of a circle

To find whether or not the mouse is inside a circle, we need to do some fancy jiggery-pokery with the dist() function. We can get the distance from the center of the circle and test to see if it's less than radius of the circle. If it is, it's inside!

PROCESSING

Example 5-14: The Bounds of a Circle

int xVal = 120;
int yVal = 120;
int myRadius = 50;

void setup() {
  size(240, 240);
  ellipseMode(RADIUS); // Draws ellipse from center out
}
void draw() {
  float myDistance = dist(mouseX, mouseY, xVal, yVal);
  if (myDistance < myRadius) {
    fill(0);
  } else {
    fill(255);
  }
  ellipse(xVal, yVal, myRadius, myRadius);
}
Resulting Display Window: No Mouse Over Resulting Display Window: Mouse Over

Let's beef that up a bit by making the circle change size when it's rolled over by the mouse

 

PROCESSING

Example 5-14: The Bounds of a Circle Variation

int xVal = 120;
int yVal = 120;
int myRadius = 10;

void setup() {
  size(240, 240);
  ellipseMode(RADIUS); // Draws ellipse from center out
}
void draw() {
  background(204);
  float myDistance = dist(mouseX, mouseY, xVal, yVal);
  if (myDistance < myRadius) {
    myRadius++;
    fill(0);
  } else {
    fill(255);
  }
  ellipse(xVal, yVal, myRadius, myRadius);
}
Resulting Display Window: No Mouse Over Resulting Display Window: Mouse Over

Note that this only works with perfect circles, and won't work with an ellipse whose width and height are not equal. The math for that is crazy hard.

 

Finding the bounds of a rectangle

We'll use a different approach to find the boundind edges of a rectangle. For that, you have to check for multiple conditions to be true, which will introduce us to some new logic for our if() statement.

The logic operator we'll use is &&, which is a logical and. It's only true when what on either side of the && is true. Like so:

Value Test Value Result
true && true true
true && false false
false && true false
false && false false
Value Test Value Test Value Result
true && true && true true
true && true && false false
true && false && false false
false && false && false false
etc.
 

PROCESSING

Example 5-15: The Bounds of a Rectangle

int myWidth = 80;
int myHeight = 60;
int xVal = 80;
int yVal = 30;
void setup() {
  size(240, 120);
}
void draw() {
  background(204);
  // the && test means both sides 
  // of the && test must be true.
  // In this case, all four test have to be true
  // for the if statement to resolve to true
  if ((mouseX > xVal) && 
      (mouseX < xVal + myWidth) &&
      (mouseY > yVal) && 
      (mouseY < yVal + myHeight)) {
    fill(0);
  } else {
    fill(255);
  }
  rect(xVal, yVal, myWidth, myHeight);
}
Resulting Display Window: No Mouse Over Resulting Display Window: Mouse Over
Resulting Display Window: Mouse Greater Than Left Edge of Rect

mouseX > xVal

Resulting Display Window: Mouse Less Then Right Edge of Rect

mouseX < xVal + myWidth

Resulting Display Window: Mouse Greater Than Top Edge of Rect

mouseY > yVal

Resulting Display Window: Mouse Less Then Bottom Edge of Rect

mouseY < yVal + myHeight

Resulting Display Window: Mouse Inside Rect

(mouseX > xVal) &&
(mouseX < xVal + myWidth) &&
(mouseY > yVal) &&
(mouseY < yVal + myHeight)

Type: Keyboard Interactions

Processing can get input from the keyboard as well as the mouse. The keyPressed environmental variable tells Processing that key has been pressed in the form of a boolean datatype (true or false).

PROCESSING

Example 5-16: Tap a Key

void setup() {
  size(240, 120);
}

void draw() {
  background(204);
  line(20, 20, 220, 100);
  if (keyPressed) {
    line(220, 20, 20, 100);
  }
  println(keyPressed);
}

The char variable data type

keyPressed like mousePressed will tell us if something has been pressed or not. To find out what key was actually pressed, we use the environmental variable key. It is a variable with the data type of char. You declare and assign a char variable thusly:

char myChar = 'M'; // Declares and assigns 'M' to the variable myChar

But these two misguided attempts at assigning a char variable will fail:

char myChar = "M"; // Double quotes are used for strings

char myChar = M; // would imply that M is a variable

Unlike the boolean variable keyPressed, which reverts to false each time a key is released, the key variable holds its value until another key is pressed.

In the following code introduces the textSize() function to set the size of the letters we're going to display in the Display Window, the textAlign() function to set the alignment of the text (centered, in this case), and the text() function to display the letter.

PROCESSING

Example 5-17: Draw Some Letters

void setup() {
  size(120,120);
  background(0);
  textSize(64);
  textAlign(CENTER);
}

void draw() {
  background(0);
  text(key, 60, 80);
}
Resulting Display Window

Notice that some keys produce a blank screen (like the spacebar) and others produce a white rectangle (like the arrow keys). Also, notice what happens when you use the shift key for a capital letter...

Checking for Specific Keys

The char type variable can be tested to see what character it holds, but you have to use the single-quote convention for the logic test

This sketch also introduces another logic operator like &&, the double vertical line. The vertical line character is called a pipe. Two in succession create the logic operator of or, as in this or that.

PROCESSING

Example 5-18: Checking for Specific Keys

void setup() {
  size(120,120);
  strokeWeight(10); 
}

void draw() {
  background(204);
  if(keyPressed) {
    if((key == 'h') || (key == 'H')){
      line(30, 60, 90, 60);
    }
    if((key == 'n') || (key == 'N')) {
      line(30, 20, 90, 100); 
    }
  }
  line(30, 20, 30, 100);
  line(90, 20, 90, 100);
}
Resulting Display Window: Not H or N Keys Resulting Display Window: H Key Resulting Display Window: N Key
Value Test Value Result
true || true true
true || false true
false || true true
false || false false

The logic or operator works as shown in the table on the left.

 

Detecting Odd-Ball Keys

Some keys are harder to detect beause they aren't tied to a particular letter, these include:

Processing identifies these keys as CODED, which can be tested for with key == CODED. Some of the above keys have special signifiers when they're pressed, like UP, DOWN, LEFT, RIGHT, which can be tested for as well with keyCode. Let's write some code for detecting the arrow keys:

PROCESSING

Example 5-19: Move with the Arrow Keys

int x = 215;

void setup() {
 size(480, 120);
}

void draw() {
 if (keyPressed && (key == CODED)) { // If it’s a coded key
   if (keyCode == LEFT) { // If it’s the left arrow
     x--;
   } else if (keyCode == RIGHT) { // If it’s the right arrow
     x++;
   }
 }
   rect(x, 45, 50, 50);
}
Resulting Display Window

Now let's make it work on all arrow keys

PROCESSING

Example 5-19 (extended)

int xVal = 215;
int yVal = xVal;

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

void draw() {
 if (keyPressed && (key == CODED)) { // If it’s a coded key
   if (keyCode == LEFT) { // If it’s the left arrow
     xVal--;
   } else if (keyCode == RIGHT) { // If it’s the right arrow
     xVal++;
   }
   if (keyCode == UP) { // If it’s the up arrow
     yVal--;
   } else if (keyCode == DOWN) { // If it’s the down arrow
     yVal++;
   }
 }
 rect(xVal, yVal, 50, 50);
}

Remapping values with Map

Sometimes you need to take one range of values and re-map them to another range. This happens a lot in coding, where sometimes you have some events, like the possible horizontal position of a slider button, that's different than the range of values you need to control the position of a video. For this, you use the map() function.

First, this example does some re-mapping, but without the map function:

PROCESSING

Example 5-20: Map Value to a Range

void setup() {
  size(240, 120);
  strokeWeight(12);
}

void draw() {
  background(204);
  stroke(102);
  line(mouseX, 0, mouseX, height); // White line
  stroke(0);
  float xVal = mouseX/2 + 60; // Remap mouseX
  line(xVal, 0, xVal, height); // Black line
}
Resulting Display Window: Mouse Left of Center Resulting Display Window: Mouse Right of Center

Now let's use the map() function to do our re-mapping for us. It uses the syntax of map(value, start1, stop1, start2, stop2), where value is the value you want to re-map, start1 and stop1 is the range you're mapping from, and start2 and stop2 is the range you're mapping to. For example, here's how the map() function would re-map in a given set of parameters:

Value start1 stop1 start2 stop2 result
map( 10, 0, 10, 0, 1000 ) = 1000
map( 5, 0, 10, 0, 1000 ) = 500
map( 5, 0, 10, 500, 1000 ) = 750
map( 5, 0, 10, 100, 500 ) = 300

Here we'll use the map function to re-map the mouse x location from the starting range of 0 to the width of the Display Window to a range between 60 and 180. It's the same code as the previous example, but we use the map() function to re-map the numbers instead of mouseX/2 + 60.

PROCESSING

Example 5-21: Map with the map() Function

void setup() {
  size(240, 120);
  strokeWeight(12);
}
void draw() {
  background(204);
  stroke(102);
  line(mouseX, 0, mouseX, height); // Gray line
  stroke(0);
  float xVal = map(mouseX, 0, width, 60, 180);
  line(xVal, 0, xVal, height); // Black line
}
mouseX value:  0 10 20 30 40 ... 200 210 220 230 240 (the width)
map( ) output: 60 65 70 75 80 ... 160 165 170 175 180

Responsive Robot

Here's we'll use the variables from Robot 2 and change them while the program runs so the shapes respond to the mouse.

PROCESSING

Robot 3: Repsonse

float xVal = 60;      // xVal-coordinate
float yVal = 440;     // yVal-coordinate
int radius = 45;      // Head Radius
int bodyHeight = 160; // BodyVal Height
int neckHeight = 70;  // Neck Height

float easing = 0.04;

void setup() {
  size(360, 480);
  ellipseMode(RADIUS);
}

void draw() {
  strokeWeight(2);

  int targetX = mouseX;
  xVal += (targetX - xVal) * easing;
  
  if (mousePressed) {
    neckHeight = 16;
    bodyHeight = 90;
  } else {
    neckHeight = 70;
    bodyHeight = 160;
  }
  
  float neckY = yVal - bodyHeight - neckHeight - radius;
  
  background(0, 153, 204);
  
  // Neck
  stroke(255);
  line(xVal+12, yVal-bodyHeight, xVal+12, neckY);
  
  // Antennae
  line(xVal+12, neckY, xVal-18, neckY-43);
  line(xVal+12, neckY, xVal+42, neckY-99);
  line(xVal+12, neckY, xVal+78, neckY+15);
 
  // Body
  noStroke();
  fill(255, 204, 0);
  ellipse(xVal, yVal-33, 33, 33);
  fill(0);
  rect(xVal-45, yVal-bodyHeight, 90, bodyHeight-33);
  
  // Head
  fill(0);
  ellipse(xVal+12, neckY, radius, radius);
  fill(255);
  ellipse(xVal+24, neckY-6, 14, 14);
  fill(0);
  ellipse(xVal+24, neckY-6, 3, 3);
}
Previous Chapter
Next Chapter