Programming for Interactive Digital Arts
CS 2, Fall 2023

5. Responding conditionally

Testing the mouse and key

We saw last time how to do something exactly at the "event" when the mouse is pressed, by defining a mousePressed() function. Suppose though that we want to write a scribbling program, where ellipses are drawn wherever the mouse is, but only when a mouse button is held down. That is, each frame we want to check if the mouse is pressed, and if so, draw an ellipse. Here's the translation of that English sentence into Processing.

[sketch1]
function setup()
{
  createCanvas(400,400);
  noStroke();
}

function draw()
{
  if (mouseIsPressed) {
    // It is pressed, so draw a white ellipse
    fill(255);
    ellipse(mouseX,mouseY,25,25);
  }
}

The variable mousePressed (yes, the same name as the function) 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).

To do something (or not) depending on whether or not a boolean is true, we use a construct called a conditional, or if-else statement. (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:

[sketch2]
function setup()
{
  createCanvas(400,400);
  noStroke();
}

function draw()
{
  if (mouseX < width/2) {  // Left half of screen; draw black
    fill(0);
  }
  else {                   // Right half of screen; draw white
    fill(255);
  }
  if (mouseIsPressed) {      // Only draw if button is pressed
    ellipse(mouseX,mouseY,25,25);
  }
}

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 equality (==) and inequality (!=). 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.

[sketch3]
function setup()
{
  createCanvas(400,400);
  noStroke();
}

function draw()
{
  if (mouseIsPressed) {
    if (mouseButton == LEFT) {
      fill(0);
    } 
    else if (mouseButton == RIGHT) {
      fill(255);
    }
    ellipse(mouseX,mouseY,25,25);
  }
}

Note that here when the mousePressed test passes, the body of the "if" is itself an "if". That's totally acceptable, and called nested. In fact, it would make sense to nest the fill color in the previous sketch -- no need to set it unless we know the button is pressed. 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. The outcome of one question leads to the next question (kind of like in the game of 20 questions). Proper indentation is a must for this to be readable!

There are plenty of other variations on this theme. For example, rather than testing the mouse press, we could set the fill color either black or white at random:

if (random(1) < 0.5) {
  fill(0);
} 
else {
  fill(255);
}

Key presses are handled much the same way as mouse clicks. Just as the keyPressed() function handles key press events, the keyPressed variable (also a boolean) tells us whether or not a key has been pressed, and we can test it inside the draw body. I can't think of too many reasons to do that, though, without knowing which key is pressed. To do so in such a case, as well as in the keyPressed() function, we check the key variable, which works much like the mouseButton variable. This variable tells us which key was pressed. We can compare it against different letters (enclosed in single quotes) with equality tests, and do different things depending on which test passes.

Key presses are useful to control aspects of a sketch. In the following example, keys set the color to be drawn. Note that if there's only one statement following the "if", we can omit the curly braces.

[sketch4]
function setup()
{
  createCanvas(400,400);
  noStroke();
  background(0);
}

function draw()
{
  if(mouseIsPressed) ellipse(mouseX,mouseY,25,25);
}

function keyPressed()
{
  // Set fill color to be red, green, blue, or black
  // according to key press
  if (key=='r') fill(255,0,0);
  else if (key=='g') fill(0,255,0);
  else if (key=='b') fill(0,0,255);
  else fill(255);
}

State and conditionals

Let us combine conditionals and state by changing the random wanderer from last time. First, let us introduce another variable holding the current shade of the ball, along with one telling us whether we should increase or decrease the shade. Once the shade gets too low, we'll increase until it gets too high, and then we'll decrease. We'll do the same type of thing with the size, but take smaller steps.

[sketch5]
// The current coordinates of the ball, starting at the center (in setup)
var x, y;
// The current color of the ball, and whether it's increasing or decreasing
var gray=255, dgray=-1;
// The current size of the ball, and how much it's increasing or decreasing
var sz=5, dsz=0.1;

function setup()
{
  createCanvas(400,400);
  noStroke();
  background(0);
  x=width/2; y=height/2;
}

function draw()
{
  background(0,20);
  fill(gray);
  ellipse(x,y,sz,sz);
  // Move the position by random steps in x and y
  x += random(-10,10);
  y += random(-10,10);
  // Increase/decrease the color, changing direction at the extremes
  gray += dgray;
  // too large or too small, go the other way
  if (gray <= 128) dgray = 1;
  else if(gray >= 255) dgray = -1;
  // Increase/decrease the size, changing direction at the extremes
  sz += dsz;
  // if too large or too small, go the other way
  if (sz <= 2 || sz >= 15) dsz = -dsz;
}

A few details. The shorthand x+=random(-2,2) is equivalent to x=x+random(-2,2). Similarly, while the example doesn't use it, x++ is equivalent to x=x+1. The two ways of changing direction here (explicitly set the variable vs. negate it) are equivalent. Note that in one case, we combine two test into one using the Logical OR (||) operator. This is true whether either of the test is true; more on this below. Also note that when we're using floating point numbers, it's best not to test for exact equality but for the appropriate inequality.

If we wanted the keep the wanderer from going off-screen, we could add code like this, just after giving x its new value:

if (x < 0) x = 0;
else if (x > width) x = width;

Since it's common to want to constrain something to be within a range, Processing provides the constrain() function, which returns the value (first parameter) if it's between the minimum (second) and maximum (third), or else the minimum or maximum. So we could instead write the following, which is exactly equivalent to the "if" construct above:

x = constrain(x,0,width);

Graphical user interfaces

If you ever wonder how buttons work in graphical user interfaces, this part of the lecture will answer your question. We can build a button 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 and y coordinates, the point is inside a rectangle specifying the button shape. We need to make sure that the mouse is more than the left side, less than the right side, more than the top side, and less than the bottom side. The Logical AND (&&) operator combines booleans returning true if and only if both its arguments are true.

[sketch6]
function draw()
{
  if (mouseX>25 && mouseX<75 && mouseY>40 && mouseY<60)
    // Inside rectangle, so make it yellow
    fill(255,255,0);
  else
    // Outside rectangle, so make it green
    fill(0,255,0);
  rect(25,40,50,20);
}

That gives us a "mouseover" effect. To make it into a button, either "on" or "off", we set a boolean variable accordingly, each time the mouse is pressed in the rectangle. We draw the rectangle differently depending on the state of the variable.

[sketch7]
// Dartmouth CS 2, Winter 2009, Chris Bailey-Kellogg
// Notes 5 | Sketch 7

var buttonOn = false;

function draw()
{
  if (buttonOn)
    // On, so make it red
    fill(255,0,0);
  else if (mouseX>25 && mouseX<75 && mouseY>40 && mouseY<60)
    // Inside rectangle, so make it yellow
    fill(255,255,0);
  else
    // Outside rectangle, so make it green
    fill(0,255,0);
  rect(25,40,50,20);
}

function mousePressed()
{
  if (mouseX>25 && mouseX<75 && mouseY>40 && mouseY<60)
    // Button inside rectangle, so invert button
    buttonOn = !buttonOn;
}

Note the use of the Logical NOT (!) operator, which gives the opposite value (true for false, and vice versa).

Programming notes

Boolean
A boolean is something that is either true or false.
Boolean operators
Just like we add and subtract numbers, we can combine booleans with Logical AND (&&) and Logical OR (||) or invert their values with Logical NOT (!) which inverts a single boolean.
aba && b
falsefalsefalse
falsetruefalse
truefalsefalse
truetruetrue
aba || b
falsefalsefalse
falsetruetrue
truefalsetrue
truetruetrue
a!a
falsetrue
truefalse
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 if (another boolean expression) {
  statements to do if the first boolean is false and the second 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. There can be as many "else if" parts as we like (including none).