Source Code. Projects. Nerd Stuff. Art Stuff.

Lesson 3: Response

[gn_pullquote align="left"]Code that responds to input from the mouse, keyboard, and other devices has to run continuously. To make this happen, place the lines that update inside a Processing function called draw().[/gn_pullquote] You may have noticed two functions that appear often in many Processing sketches: setup() and draw().

You’ll need the draw() function to create animations, because code within the draw() block runs from top to bottom and repeats until you stop running the sketch. Each trip through draw() is called a frame.

The setup() function, on the other hand, runs only once. Typically, the code defined within this part of the program is used to define the starting values. The first line is usually the size() function, with parameters for the window size (if you don’t include this, the window is defaulted to 100×100 pixels).

Whenever you define variables inside these functions, they are called local variables. In other words, variables declared in draw() are not seen by setup() and vice versa. Where a variable is declared is known as its scope. To define variables that can be used in any scope, declare them outside of these functions. These are called global variables, and can be used anywhere within the program.

To see how these two functions work, consider this example:

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

void draw() {
  ellipse(ouseX, mouseY, 9, 9);
}

 

In this example, each time the code in the draw() block is run, a new circle is drawn to the window. This image was made by moving the mouse around to control the circle’s location. Because the fill is set to be partially transparent, denser black areas show where the mouse spent more time and where it moved slowly. The circles that are spaced farther apart show when the mouse was moving faster.

By adding the background() funciton inside draw(), the window is cleared at the beginning of each frame. This effectively erases any image before it and displays the single, most recent dot. Be sure to place background() at the beginning of the draw() function, before anything is drawn.

The pmouseX and pmouseY variables store the position of the mouse at the previous frame, where the mouse was located during the previous draw() cycle. They can also be use to calculate the speed of the mouse. This is done by measuring the distance between the current and most recent mouse location. If the mouse is moving slowly, the distance is small, but if the mouse starts moving faster, the distance grows. A function called dist() simplifies this calculation, as shown in the following example. Here, the speed of the mouse is used to set the thickness of the drawn line:

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

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

 

Easing

Sometimes you want the values to follow the mouse loosely—to lag behind to create a more fluid motion. This technique is called easing. With easing, there are two values: the current value and the value to move toward. At each step in the program, the current value moves a little closer to the target value. Here’s how we apply easing to the previous example:

float x;
float y;
float px;
float py;
float easing = 0.05

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

void draw() {
  float targetX = mouseX;
  x += (targetX - x) * easing;
  float targetY = mouseY;
  y += (targetY - y) * easing;
  float weight = dist(x, y, px, py);
  strokeWeight(weight);
  line(x, y, px, py);
  py = y;
  px = x
}

 

Map

[gn_pullquote align="left"]When numbers are used to draw to the screen, it’s often useful to convert the values from one range of numbers to another.[/gn_pullquote] The mouseX variable is usually between 0 and the width of the window, but you might want to remap those values to a different range of coordi- nates. You can do this by making calculations like dividing mouseX by a number to reduce its range and then adding or subtracting a number to shift it left or right. Processing has a built-in map() function that’s set to handle this. It converts a variable from one range of numbers to another.

 if() Statements

Like the for() loop discussed in the last lesson, the if() also has a test that is evaluated to true or false:

if (test) {
  statements
}

 

When the test is true, the code inside the block is run; when the test is false, the code inside the block is not run. The computer determines whether the test is true or false by evaluating the expression inside the parentheses.

The == symbol compares the values on the left and right to test whether they are equivalent. This == symbol is different from the assignment operator, the single = symbol. The == symbol asks, “Are these things equal?” and the = symbol sets the value of a variable.

NOTE: It’s a common mistake, even for experienced programmers, to write = in your code when you mean to write ==. The Processing software won’t always warn you when you do this, so be careful.

Click

In addition to the location of the mouse, Processing also keeps track of whether the mouse button is pressed. The mousePressed variable has a different value when the mouse button is pressed and when it is not. The mousePressed variable is a data type called boolean, which means that it has only two possible values: true and false. The value of mousePressed is true when a button is pressed.

In this example, the mousePressed variable is used along with the if statement to determine when a line of code will run and when it won’t.

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

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

 

A single if block gives you the choice of running some code or skipping it. You can extend an if block with an else block, allowing your program to choose between two options. The code inside the else block runs when the value of the if block test is false. For instance, the stroke color for a program can be white when the mouse button is not pressed, and can change to black when the button is pressed:

void setup() {
  size(240, 120);
  smooth();
  stroke(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);
}

Location

An if structure can be used with the mouseX and mouseY values to deter- mine the location of the cursor within the window.

float x;
int offset = 10;

void setup() {
  size(240, 120);
  smooth();
  x = width/2;
}

void draw() {
  background(204);
  if (mouseX > x) {
    x += 0.5;
    offset = -10;
  }
  if (mouseX

 

Type

Processing keeps track of when any key on a keyboard is pressed, as well as the last key pressed. Like the mousePressed variable, the keyPressed variable is true when any key is pressed, and false when no keys are pressed.

For example, this code will draw a second line only when a key is pressed:

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

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

The key variable stores the most recent key that has been pressed. The data type for key is char, which is short for “character.” A char variable can store any single character, which includes letters of the alphabet, numbers, and symbols. This data type is specified by single quotes.

1
 char c = 'A'; // Declares and assigns 'A' to the variable c

Unlike the boolean variable keyPressed, which reverts to false each time a key is released, the key variable keeps its value until the next key is pressed. In the following example, we test for an H or N to be typed. We use the comparison operator, the == symbol, to see if the key value is equal to the characters we’re looking for:

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

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);
}

 

When we watch for H or N to be pressed, we need to check for both the lowercase and uppercase letters in the event that someone hits the Shift key or has the Caps Lock set. We combine the two tests together with a logical OR, the || symbol. If we translate the second if statement in this ex- ample into plain language, it says, “If the ‘h’ key is pressed OR the ‘H’ key is pressed.” Only one of these expressions need be true for the entire test to be true.

Some keys are more difficult to detect, because they aren’t tied to a par- ticular letter. Keys like Shift, Alt, and the arrow keys are coded and require an extra step to figure out if they are pressed. First, we need to check if the key that’s been pressed is a coded key, then we check the code with the keyCode variable to see which key it is. The most frequently used keyCode values are ALT, CONTROL, and SHIFT, as well as the arrow keys, UP, DOWN, LEFT, and RIGHT.

The following example shows how to check for the left or right arrow keys to move a rectangle:

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);
}