CS 2, Winter 2008
Programming for Interactive Digital Arts

Jan 14: Responding conditionally


Maybe I've reiterated enough by now the need to learn by doing; I hope you are!

Mouse press

In addition to tracking the position of the mouse, Processing also tracks button clicks. The variable mousePressed tells us whether or not the mouse is currently pressed. While we've mostly dealt with numbers (coordinates, sizes), this isn't a number, but rather a boolean, one that can be either true (the mouse is pressed) or false (it isn't).

We can use the state of the mouse to control what we do. The construct for this is a conditional, or if-else statement. Here's an example, making our ellipse draw in white if the mouse is pressed and black if not.

void setup()
{
  size(400,400);
  smooth();
  noStroke();
}

void draw()
{
  if (mousePressed) {
    // It is pressed, so draw in white
    fill(255);
  }
  else {
    // It isn't, so draw in black
    fill(0);
  }
  ellipse(mouseX,mouseY,25,25);
}
screenshot[applet]

(As usual, see the Programming notes section for more discussion of syntax and so forth.) Conditionals are very useful, and there are many things that we can test. For example, we can set the fill based on whether the mouse is on the left or right half of the window:

void setup()
{
  size(400,400);
  smooth();
  noStroke();
}

void draw()
{
  if (mouseX < width/2) {  // Left half of screen; draw black
    fill(0);
  }
  else {                   // Right half of screen; draw white
    fill(255);
  }
  if (mousePressed) {      // Only draw if button is pressed
    ellipse(mouseX,mouseY,25,25);
  }
}
screenshot[applet]

Here the boolean expression is not a single variable (like we saw with mousePressed), but a comparison. It does pretty much what you might expect -- if the x coordinate is less than half the width, the boolean is true; otherwise it is false. Other comparisons (below) include the various inequalities, along with equals (==) and not equals (!=). Try changing the code to use another one.

The variable mouseButton tells us which button is pressed (if one is); its value can be LEFT, MIDDLE, or RIGHT. Here's an example that uses an equality test to draw in black or white for left or right button, and doesn't draw when there's no button.

void setup()
{
  size(400,400);
  smooth();
  noStroke();
}

void draw()
{
  if (mousePressed) {
    if (mouseButton == LEFT) {
      fill(0);
    } 
    else if (mouseButton == RIGHT) {
      fill(255);
    }
    ellipse(mouseX,mouseY,25,25);
  }
}
screenshot[applet]

Note that here when the mousePressed test passes, the body of the "if" is itself an "if". That's totally acceptable, and called nested. Note also that the "else" part of the mouseButton test is itself an if statement, also totally fine, and the way to do things when there are multiple possibilities.

Ever wonder how buttons work in graphical user interfaces? We can build them ourselves, by performing the proper tests. We saw already how to change the color for left half vs. right half. We can test that for any desired x coordinate, including the boundary of a rectangle. We need to make sure that the mouse is more than the left side, less than the left side, more than the top side, and less than the bottom side. The logical AND operator, written &&, combines booleans. Only if both are true does it evaluate to true.

void draw()
{
  if (mouseX>25 && mouseX<75 && mouseY>40 && mouseY<60) { // Inside rectangle
    if (mousePressed) {  // Mouse pressed = red
      fill(255,0,0);
    }
    else {               // Mouse over = yellow
      fill(255,255,0);
    }
  }
  else {                 // Outside rectangle = green
    fill(0,255,0);
  }
  rect(25,40,50,20);
}
screenshot[applet]

We can use these techniques to make our snowperson a little more interactive. We use the dist() function, which computes the distance between two pairs of coordinates, to see if the mouse is within a circle.

void setup() 
{
  size(300,200);
  smooth();
  textFont(loadFont("TrebuchetMS-24.vlw"));
  frameRate(8);
}

void draw() 
{
  background(128);
  // Invariant stuff
  ellipse(130,40,40,40);  // head
  ellipse(130,90,70,70);  // torso
  ellipse(130,160,90,90); // legs?
  strokeWeight(5);
  point(125,35);          // left eye
  point(135,35);          // right eye
  strokeWeight(1);
  triangle(130,40,140,45,130,45);  // nose
  line(100,90,60,50);     // right arm
  line(60,50,65,40);      // right hand
  line(60,50,57,35);
  line(60,50,50,48);
  line(158,76,200,53);    // left arm

  // Stuff that depends on clicks
  if (mousePressed && dist(mouseX,mouseY,130,40) < 20) {
    // Top hat in hand
    rect(40,30,40,5);
    rect(50,10,20,20);
    text("Hi there!",155,25);
  }
  else {
    // Top hat on head
    rect(110,20,40,5);
    rect(120,0,20,20);
  }

  if (mousePressed && dist(mouseX,mouseY,130,90) < 35) {
    // Wiggle left hand  
    line(200,53,200+random(-5,5),42+random(-5,5));
    line(200,53,210+random(-5,5),65+random(-5,5));
  }
  else {
    // Regular left hand
    line(200,53,200,42);
    line(200,53,210,65);
  }

  if (mousePressed && dist(mouseX,mouseY,130,160) < 45) {
    // Rotate spokes by alternating lines
    if (frameCount % 2 == 0) {
      line(130,115,130,205);
      line(85,160,175,160);
    }
    else {
      line(98,191,162,129);
      line(98,129,162,191);
    }
  }
}
screenshot[applet]

Keyboard

Key presses are handled much the same way as mouse clicks. The keyPressed variable (a boolean) tells us whether or not a key has been pressed. Try changing some of the sketches that check for mouse to instead check for key.

We can also see which key has been pressed, with the key variable, which works like the mouseButton variable discussed above. We can see which key was pressed by using equality tests; enclose the letter in single quotes.

void setup()
{
  fill(0,50);
  noStroke();
  background(0);
}

void draw()
{
  if (keyPressed) {
    // Set fill color to be red, green, blue, or black
    // according to key press
    if (key=='r') fill(255,0,0,50);
    else if (key=='g') fill(0,255,0,50);
    else if (key=='b') fill(0,0,255,50);
    else fill(0,50);
  }
  ellipse(mouseX,mouseY,10,10);
}
screenshot[applet]

The keys can also be turned into numbers, by subtracting the letter 'a'. Thus 'a' would be 0, 'b' 1, etc. Then keys can be used more abstractly. In the following sketch, based on Reas and Fry (25-05), we divide the window into 26 bars, one for each letter. We color a bar when the key is pressed.

void draw()
{
  if (keyPressed) {
    fill(random(255),random(255),random(255));
    // The alphabet spans the window, in thin vertical strips 
    rect((key-'a')*width/26.0, 0, width/26.0, height);
  }
}
screenshot[applet]

We use 26.0 instead of just 26 to make sure the division keeps track of the fractional part. We'll talk more about that next time.

Console output

Sometimes in developing a sketch it can be very useful to see, textually, the values that the variables are currently holding. Processing has the ability to "print" things to the console (at the bottom of the window in which you type your sketch). The print() and println() functions do that; the latter forces a new line after printing. To print text, put it in double quotes. To print multiple things, combine them with a plus sign. Calling println() with no parameter simply forces the new line. Here's an example (note that this only works in the Processing environment, not as an applet).

void setup()
{
  frameRate(2);
}

void draw()
{
  print("frame "+frameCount);
  print("  mouse at ("+mouseX+","+mouseY+")");
  if (keyPressed) {
    print("  pressed "+key);
  }
  if (mousePressed) {
    print("  clicked");
  }
  println();
}

Programming notes

Boolean
A boolean is something that is either true or false.
Boolean operation
Just like we add and subtract numbers, we can perform operations on booleans. These include the logical NOT (!), logical AND (&&), and logical OR (||).
a!a
falsetrue
truefalse
  
aba && b
falsefalsefalse
falsetruefalse
truefalsefalse
truetruetrue
  
aba || b
falsefalsefalse
falsetruetrue
truefalsetrue
truetruetrue
Comparison
We can compare numbers to see which is larger than the other. The result of a test is a boolean, indicating whether that statement is true. We can also test for equality:
  • == (equality)    Note that this is a double equals. Single equals means something else entirely, which we'll discuss later; you have to keep these straight or you'll get strange (and frustrating) errors.
  • != (inequality)
Conditional
A conditional executes code only if some boolean expression is true. The basic conditional has the following form:
if (boolean expression) {
  statements to do if the boolean is true
}
else {
  statements to do if the boolean is false
}
If there's only one statement in the body of the "if" or the "else", the curly braces around it can be omitted. The else part is optional -- if we leave it out, then nothing happens if the boolean is false. The else part can also begin another if.

Practice problems

  1. Create a sketch in which randomly colored ellipses are drawn wherever the mouse is, only while the mouse is pressed. [hints]
  2. Modify the sketch so that while ellipses are drawn in the top half, rectangles are drawn in the bottom half. [hints]
  3. Create a stoplight in which the green, yellow, and red lights turn brighter when the mouse is over one of them. [hints]
  4. Modify your stoplight so that when the mouse is anywhere within the case, a police officer head is drawn, and if the mouse is pressed in the red light, he says something. [hints]