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); }
[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); } }
[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); } }
[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); }
[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); } } }
[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); }
[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); } }
[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 false true true false a b a && b false false false false true false true false false true true true a b a || b false false false false true true true false true true true true - 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 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.
if (boolean expression) { statements to do if the boolean is true } else { statements to do if the boolean is false }
Practice problems
- Create a sketch in which randomly colored ellipses are drawn wherever the mouse is, only while the mouse is pressed. [hints]
- Modify the sketch so that while ellipses are drawn in the top half, rectangles are drawn in the bottom half. [hints]
- Create a stoplight in which the green, yellow, and red lights turn brighter when the mouse is over one of them. [hints]
- 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]