Our first foray into writing interactive programs in which you can make what's going on in the display window respond to the mouse or keyboard while it is running.
By the end of this lesson, you should be able to:
This lesson will take us one week to complete, 9 - 15 June 2021. This lesson involves two graded items:
If you have any questions, please post them to our Questions? discussion forum (not e-mail) in Canvas. I will check that discussion forum daily to respond. While you are there, feel free to post your own responses if you, too, are able to help out a classmate.
The articles we'll read for this week's discussion are linked from the Lesson 4 discussion board in Canvas.
Coxon, S.V., 2012, Innovative allies: Spatial and creative abilities, Gifted Child Today, 35, p. 277-284.
Mayer, R.E., and V.K. Sims, 1994, For Whom Is a Picture Worth a Thousand Words? Extensions of a Dual-Coding Theory of Multimedia Learning, Journal of Educational Psychology, 86, p. 389-401.
The first paper discusses the fact that contemporary school curricula do not specifically address spatial visualization skills but that innovative teachers could quite easily incorporate activities that speak to these skills while still meeting the various standards of learning. Teaching kids some computer programming is specifically mentioned. If anybody wants to, go to the program called "Scratch [1]" and play around.
The second paper is by educational psychologists who are trying to discover what kind of person benefits from what kind of educational presentation. The idea is that while technology has enabled us to produce whiz-bang animations and graphics, research into whether these techniques actually help people learn hasn't quite caught up yet. So they ran some experiments to try to figure this out.
As you read, consider the following questions, which we will discuss as a class:
Once you have finished the readings, engage in a class discussion that will take place over the entire week devoted to Lesson 4. This discussion will require you to participate multiple times over that period.
You will be graded on the quality of your participation. See the grading rubric [2] for specifics.
Another fun thing to do is write programs that involve interactivity via the keyboard and the mouse. Processing stores the position of the cursor as long as the cursor is inside the display window. It stores that position inside the variables mouseX and mouseY. It also stores the most recent previous position of the cursor in the variables pmouseX and pmouseY. You can take advantage of this by telling your program to do various things depending on where the cursor is.
This program draws a square and circle to the screen. The circle follows the cursor around the display window. Both the circle and square change color depending on the cursor's position.
color bg = color ( 220 , 220 , 200 ); void setup () { background (bg); size ( 200 , 200 ); } void draw () { strokeWeight ( 1 ); fill ( 48 , 107 , 198 , 100 ); if ( mouseX >= 100 && mouseY < 100 ) { fill ( 242 , 99 , 42 , 100 ); } if ( mouseX < 100 && mouseY >= 100 ) { fill ( 146 , 21 , 175 , 100 ); } rect ( 40 , 40 , 40 , 40 ); ellipse ( mouseX , mouseY , 50 , 50 ); } |
Below is another example program that uses mouseClicked(). mouseClicked() is a separate function written in its own block after draw(). In this program, clicking inside one of the squares will make the other square change color.
// Click within a square to change // the color of the other square. color value = color ( 255 , 0 , 0 ); color value2 = color ( 0 , 255 , 0 ); void setup () { size ( 200 , 200 ); background ( 255 ); } void draw () { fill (value); rect ( 25 , 25 , 50 , 50 ); fill (value2); rect ( 75 , 75 , 50 , 50 ); } // The mouseClicked function is called after a mouse button is pressed // and then released. void mouseClicked() { //if the cursor is inside the top square and the color of the bottom //square is green . . . if ( mouseX < 75 && mouseY < 75 && mouseX > 25 && mouseY > 25 && value2 == color ( 0 , 255 , 0 )) { value2 = color ( 0 , 255 , 255 ); } // If the cursor is inside the top square and the color of the bottom // square is aqua . . . else if ( mouseX < 75 && mouseY < 75 && mouseX > 25 && mouseY > 25 && value2 == color ( 0 , 255 , 255 )) { value2 = color ( 0 , 255 , 0 ); } //if the cursor is inside the bottom square and the color of the top //square is red . . . else if ( mouseX < 125 && mouseY < 125 && mouseX > 75 && mouseY > 75 && value == color ( 255 , 0 , 0 )) { value = color ( 255 , 0 , 255 ); } //if the cursor is inside the bottom square and the color of the top //square is pink . . . else if ( mouseX < 125 && mouseY < 125 && mouseX > 75 && mouseY > 75 && value == color ( 255 , 0 , 255 )){ value = color ( 255 , 0 , 0 ); } //tell me if I haven't satisfied any of the above possibilities else { println ( "You have to click inside a square for something to happen!" ); } } |
You already saw an example of the if-else structure back in Lesson 3, but I'm going to repeat the explanation here anyway. Bare bones it looks like:
if else if else if else if else |
inside the mouseClicked() function.
The way it works is like this:
if (test is true ) { do something; } else if (first test was false , then test if this test is true and if it is) { do some other thing; } else if (first two tests were false , test this one) { do some other thing; } else if (first three tests were falst, test this one) { do some other thing; } else { none of the above possibilities were true , so do this by default ; } |
You can write as many else ifs as you want to in between the first if and the final else. The final else does not include a parenthetical test because it is there to mop up. It will execute its commands by default when none of the tests above it have been satisfied.
Screenshot of the text of the program and a still from the program running in which clicking the mouse in certain regions changes the fill color of the squares.
Your turn to play around with mouse inputs:
4.1 Draw a series of points in a trail following the mouse position.
4.2 Draw a shape that follows the mouse but does not leave a trail.
4.3 Draw a shape that follows the mouse but in reverse (reverse x and y or reverse the response to changes in directions of x and y)
4.4 Draw a shape that changes size depending on the mouse position.
4.5 Draw three different shapes of three different colors to the screen and have them move in an interesting relationship to the mouse when the mouse is moved, and have them change color when the mouse is clicked.
Here's another example of user input that involves both the mouse and the keyboard. This program draws a line on the screen that follows the mouse if you also hold down the "r" key or the "k" key. See the demonstration of pmouseX and pmouseY and also the variable keyPressed.
/* Move your mouse across the screen while holding down "r" or "k" key to draw a line that follows the mouse */ // set the background color color bg = color ( 220 , 220 , 200 ); // define a shade of red that I like rd = color ( 242 , 42 , 48 ); void setup () { size ( 200 , 200 ); background (bg); } void draw () { strokeWeight ( 2 ); //keyPressed is a boolean variable. //That means it can either be "true" or false" //Capitals are different from lowercase. //I wrote the if test this way so in case the //user has caps lock on, it will still work if (( keyPressed == true ) && ( key == 'r' || key == 'R' )) { stroke (rd); line ( mouseX , mouseY , pmouseX , pmouseY ); } if (( keyPressed == true ) && ( key == 'k' || key == 'K' )) { stroke ( 0 ); line ( mouseX , mouseY , pmouseX , pmouseY ); } } |
Here's a program that combines keyboard input with moving a shape across the screen, so we are reviewing bits of Lesson 3 while adding Lesson 4 skills here:
//move a shape around the screen according to keyboard inputs int x= 100 ; int y= 100 ; void setup (){ size ( 200 , 200 ); pixelDensity( 2 ); stroke ( 0 ); ellipse (x,y, 10 , 10 ); } void draw (){ //stroke(0); //ellipse(x,y,10,10); if (( keyPressed == true ) && ( key == 'u' || key == 'U' )){ stroke ( 255 , 0 , 0 ); y--; } else if ( keyPressed == true && ( key == 'd' || key == 'D' )){ stroke ( 0 , 255 , 0 ); y++; } else if ( keyPressed == true && ( key == 'r' || key == 'R' )){ stroke ( 0 , 0 , 255 ); x++; } else if ( keyPressed == true && ( key == 'l' || key == 'L' )){ stroke ( 255 , 255 , 0 ); x--; } else { ellipse (x,y, 10 , 10 ); } } |
The beginning position is just a black circle drawn to the middle of the display window. It doesn't do anything unless you type the u, d, l, or r keys, in which case the circle jumps up, down, left, or right, and changes color. If no key is being pressed, the circle just sits still in its most recent position with its most recent color.
4.6 Make a virtual Etch-a-Sketch in which you use the keyboard to draw a black line on a grey background.
The random function is a fun way to produce unpredictable results in a drawing. It works two ways. The first way is that you specify one number inside the parentheses, such as random(100), and a float variable will be created between zero and the number you wrote, in this case 100. The second way is that you specify two numbers inside the parentheses, and a float variable will be created between the two numbers. For example, random(2,5) will output a float between 2 and 5.
Test out the code below to draw circles on the display window, for example:
// draw several circles to the screen in unpredictable places void setup () { size ( 400 , 400 ); } void draw () { float x = random ( width ); // A number between 0 and width float y = random ( height ); // A number between 0 and height float rad = random ( 20 ); // A number between 0 and 20 ellipse (x, y, rad, rad); // Draw the ellipse using the created numbers. // On the next loop through draw, new numbers // will be created. } |
The code above continuously draws circles to the screen in a variety of places because each time it loops through draw(), the x and y values are reset to a random value in between 0 and the number specified, which is width for x and height for y. The radius of each circle is a randomly generated number between 0 and 20.
We can modify that code in a fun and more visually interesting way by also choosing the color of each circle with the random() function, like this:
// Draw several circles to the screen in unpredictable places. // Make their colors unpredictable, too. void setup () { size ( 400 , 400 ); } void draw () { float x = random ( width ); // A number between 0 and width float y = random ( height ); // A number between 0 and height float rad = random ( 20 ); // A number between 0 and 20 fill ( random ( 255 ), random ( 255 ), random ( 255 )); //random color ellipse (x, y, rad, rad); //Draw the ellipse using the created numbers. //On the next loop through draw, new numbers //will be created. } |
The only difference between the two programs is that I added a line that sets the fill() in the second program. Now every time the program loops through draw() each value of red, green, and blue is randomized, too. The default is for random() to produce a float, but you can make it an int instead by enclosing the call to random() inside int(). For example, int x = int(random(4)) will initialize the variable x and set it to an integer between 0 and 4.
Below is a screencast of me walking you through the randomly colored circles program (0:37).
4.7: Modify the program above so that the aspect ratio of each ellipse is also randomized
4.8: Modify the program above so that the top half of the screen gets filled with randomly-placed rectangles and the bottom half gets filled with randomly-placed circles
Submit exercise 4.8 to its assignment dropbox in Canvas. Name your file lastname4_8.pde
In Exercise 4.8 I want to see correct use of the random() function.
We’ve already learned how to write a for loop to make a repetitive sequence of commands more compact to write. Another great utility that can save writing is a function.
You’ve already worked with some functions that are intrinsic to Processing, such as random() that you just learned.
Functions take arguments to control their behavior. For example, the ellipse() function draws an ellipse. ellipse() takes four arguments. They are: 1. the x-coordinate, 2. the y-coordinate, 3. the width of the ellipse, and 4. the height of the ellipse. So, when you want to draw an ellipse, you have to specify these four things in that order.
You can create your own functions and name them yourself. Here are some examples that show you why you’d want to do this and how to do it.
This program draws a figure to the screen in a not-so-smart way by setting each shape explicitly with numbers. I included what the house looks like in the screenshot.
//draw a house in the dumbest possible way void setup () { size ( 200 , 200 ); // We are just drawing a static figure. // Therefore, noLoop tells draw to just run once. noLoop (); } void draw () { fill ( 46 , 136 , 242 ); rect ( 90 , 100 , 40 , 40 ); fill ( 0 ); triangle ( 80 , 100 , 110 , 70 , 140 , 100 ); fill ( 93 , 50 , 10 ); rect ( 100 , 120 , 10 , 20 ); fill ( 242 , 213 , 46 ); rect ( 95 , 105 , 10 , 10 ); rect ( 115 , 105 , 10 , 10 ); line ( 100 , 105 , 100 , 115 ); line ( 95 , 110 , 105 , 110 ); line ( 120 , 105 , 120 , 115 ); line ( 115 , 110 , 125 , 110 ); } |
Now, let’s be a little smarter and use some variables to set the positions of the shapes that make up the figure relative to each other, like this:
// Draw a house. Use variables. int x = 90 ; int y = 100 ; void setup () { size ( 200 , 200 ); // We are just drawing a static figure. // Therefore, noLoop tells draw to just run once. noLoop (); } void draw () { fill ( 46 , 136 , 242 ); rect (x, y, 40 , 40 ); fill ( 0 ); triangle (x - 10 , y, x + 20 , y - 30 , x + 50 , y); fill ( 93 , 50 , 10 ); rect (x + 10 , y + 20 , 10 , 20 ); fill ( 242 , 213 , 46 ) rect (x + 5 , y + 5 , 10 , 10 ); rect (x + 25 , y + 5 , 10 , 10 ); line (x + 10 , y + 5 , x + 10 , y + 15 ); line (x + 5 , y + 10 , x + 15 , y + 10 ); line (x + 30 , y + 5 , x + 30 , y + 15 ); line (x + 25 , y + 10 , x + 35 , y + 10 ); } |
The example above draws the exact same house as before, but now we can control where the house goes just by changing x and y instead of going into each line and recalculating. (Recall this concept from Lesson 2.) There are 15 lines of code inside draw(). What if we wanted to draw two houses? We could declare two more variables and write those same 15 lines again, but then our code inside draw() will be 30 lines long. If we wanted ten houses our code would suddenly be 150 lines long. That would be really annoying. If we write a function that has the 15 lines needed to make the house inside it, then we can just call the function with one line of code, so we only have to write those 15 lines one time.
Here’s an example below in which I define the house() function. You can make up any name for your own functions but don’t make up a name that Processing already uses, such as ellipse() or something like that. My house() function will take two arguments: the x and y position.
// Draw a house using a function. void setup () { size ( 200 , 200 ); // We are just drawing a static figure. // Therefore, noLoop tells draw to just run once. noLoop (); } void draw () { // The house function needs two arguments, x and y. house( 90 , 100 ); } // Here is the house function. // It is written outside of draw. // The two arguments it will take are written inside parentheses. void house( int x, int y) { fill ( 46 , 136 , 242 ); rect (x, y, 40 , 40 ); fill ( 0 ); triangle (x - 10 , y, x + 20 , y - 30 , x + 50 , y); fill ( 93 , 50 , 10 ); rect (x + 10 , y + 20 , 10 , 20 ); fill ( 242 , 213 , 46 ) rect (x + 5 , y + 5 , 10 , 10 ); rect (x + 25 , y + 5 , 10 , 10 ); line (x + 10 , y + 5 , x + 10 , y + 15 ); line (x + 5 , y + 10 , x + 15 , y + 10 ); line (x + 30 , y + 5 , x + 30 , y + 15 ); line (x + 25 , y + 10 , x + 35 , y + 10 ); } |
The program above draws the exact same house as in the previous two programs, and at first glance it doesn’t look like we have gained too much from creating a function. However, now we can make a bunch of houses very easily -- see below:
The program below draws four houses in different places on the screen, but I only had to write the function containing the details of the house one time.
// Draw multiple houses using a function. void setup () { size ( 200 , 200 ); // We are just drawing a static figure. // Therefore, noLoop tells draw to just run once. noLoop (); } void draw () { // I'm calling the house function four times with different x // and y values to make four houses. house( 90 , 100 ); house( 40 , 40 ); house( 100 , 150 ); house( 10 , 60 ); } // But down here I only have to write the house function once. void house( int x, int y) { fill ( 46 , 136 , 242 ); rect (x, y, 40 , 40 ); fill ( 0 ); triangle (x - 10 , y, x + 20 , y - 30 , x + 50 , y); fill ( 93 , 50 , 10 ); rect (x + 10 , y + 20 , 10 , 20 ); fill ( 242 , 213 , 46 ) rect (x + 5 , y + 5 , 10 , 10 ); rect (x + 25 , y + 5 , 10 , 10 ); line (x + 10 , y + 5 , x + 10 , y + 15 ); line (x + 5 , y + 10 , x + 15 , y + 10 ); line (x + 30 , y + 5 , x + 30 , y + 15 ); line (x + 25 , y + 10 , x + 35 , y + 10 ); } |
Now that you’ve got the basic idea, it’s pretty easy to add something to your function. For example, what if we wanted to choose the color of each house, too? We can add that as an argument to the function, like this:
// Draw a small neighborhood with variety in color void setup () { size ( 200 , 200 ); // We are just drawing a static figure. // Therefore, noLoop tells draw to just run once. noLoop (); } void draw () { // The call to house() needs four arguments now. // The arguments are x position, y position, siding color, and roof color. house( 90 , 100 , color ( 46 , 136 , 242 ), color ( 255 )); house( 40 , 40 , color ( 250 , 91 , 221 ), color ( 200 )); house( 100 , 150 , color ( 232 , 192 , 150 ), color ( 100 )); house( 10 , 60 , color ( 148 , 229 , 149 ), color ( 0 )); } // See how I've added the third and fourth arguments at the beginning inside // the parentheses where the arguments are listed? void house( int x, int y, color c, color r) { fill (c); rect (x, y, 40 , 40 ); fill (r); triangle (x - 10 , y, x + 20 , y - 30 , x + 50 , y); fill ( 93 , 50 , 10 ); rect (x + 10 , y + 20 , 10 , 20 ); fill ( 242 , 213 , 46 ) rect (x + 5 , y + 5 , 10 , 10 ); rect (x + 25 , y + 5 , 10 , 10 ); line (x + 10 , y + 5 , x + 10 , y + 15 ); line (x + 5 , y + 10 , x + 15 , y + 10 ); line (x + 30 , y + 5 , x + 30 , y + 15 ); line (x + 25 , y + 10 , x + 35 , y + 10 ); } |
So now that we have made this function, if we ever want to draw some houses in another program, we can just copy and paste this function in without worrying about what each line does. All we have to think about is where to put the house and what color to make it.
4.9 Take your custom shape from previous exercises (or make a new one) and write it as a function.
4.10 Modify your program in 4.9 so that your function is drawn to the screen with a random color scheme.
4.11 Choice!: Modify your program in 4.9 so that your shape is drawn to the display window at the position where you click your mouse. (You’ll have to get rid of noLoop() to make this work)
OR modify your program so that your shape follows your mouse around the display window
OR modify your program so that the display window fills up with your shape in randomized locations. (NOTE: why not do them all? But you only have to submit one. See below)
4.12 Review: Modify your program so that your shape moves back and forth across the screen. (You’ll have to get rid of noLoop() for this one, too.)
Turn in Exercise 4.11 and 4.12 to their assignment dropboxes in Canvas.
In Exercise 4.11 I want to see your shape written as a function and plotted with whichever new skill appeals to you. In Exercise 4.12 I want to see your shape written as a function, and correct use of if to move it back and forth, recalling how to do that from Lesson 3.
We've progressed pretty far at this point. You can make interactive art with your computer!
4.1 Draw a series of points in a trail following the mouse position.
4.2 Draw a shape that follows the mouse but does not leave a trail.
4.3 Draw a shape that follows the mouse but in reverse (reverse x and y or reverse the response to changes in directions of x and y)
4.4 Draw a shape that changes size depending on the mouse position.
4.5 Draw three different shapes of three different colors to the screen and have them move in an interesting relationship to the mouse when the mouse is moved, and have them change color when the mouse is clicked.
4.6 Make a virtual Etch-a-Sketch in which you use the keyboard to draw a black line on a grey background.
4.7 Modify my "random circles" program so that the aspect ratio of each ellipse is also randomized
4.8 Modify my "random circles" program so that the top half of the screen gets filled with randomly-placed rectangles and the bottom half gets filled with randomly-placed circles.
4.9 Take your custom shape from previous exercises (or make a new one) and write it as a function.
4.10 Modify your program in 4.9 so that your function is drawn to the screen with a random color scheme.
4.11 Choice!: Modify your program in 4.9 so that your shape is drawn to the display window at the position where you click your mouse.
OR modify your program so that your shape follows your mouse around the display window
OR modify your program so that the display window fills up with your shape in randomized locations. (NOTE: why not do them all? But you only have to submit one. See below)
4.12 Review: Modify your program so that your shape moves back and forth across the screen.
You have reached the end of Lesson 4! Double-check the to-do list on the Lesson 4 Overview page to make sure you have completed all of the activities listed there before you begin Lesson 5. You should have submitted three programs to the dropbox: exercises 4.8, one of the 4.11 options, and 4.12. Also, you should have been participating all week in the reading discussion.