CPSC 120 | Principles of Computer Science | Fall 2024 |
Labs are due at the start of lab. It is OK if you show up to lab and copy your files to the handin directory at the very beginning of lab, but this should take at most a couple of minutes and you should not spend the next lab period finishing up the previous week's lab. I will check timestamps, and files handed in more than five minutes after the start of lab will be considered late.
See the policy on late work and extensions.
This week's lab deals with animation — things that move (or change in other ways) on their own.
Successfully completing this lab means that you are able to:
Labs are a chance to practice and gain understanding. You may get help in office hours, at Teaching Fellows, and from other students and may use other materials (such as reference books or websites) but the course materials (including provided links or references to documentation and other materials) along with office hours and the Teaching Fellows should be your primary resources. Always start with these!
You may not, however, copy or be in possession of someone else's program or solution before you have handed in your own and you may not write code collaboratively with another student. You must document any help received and any outside resources used. See the full collaboration policy for more on this.
Be careful not to rely too much on others — things often look easy (or at least easier) when someone else does it. (You don't want the exam to be where you discover this!) You should always make the first attempt at doing something yourself.
Also keep in mind that the goal of the exercises is learning the process of creating programs, not obtaining any particular program. When you get help, also ask about that process — if you're stuck on what to do next, ask not only what to do next but also how one knows that's what to do, or if your program isn't working correctly, ask not only what's wrong and how to fix it but also how to track down the problem for yourself. And finally, make sure that you always fully understand the help you received — you should be able to explain your solution to someone else, and should never just write down code that someone else wrote or told you to write without be able to explain what it does and why.
To hand in your work:
Make sure that your name and a short description of the sketch are included in a comment at the beginning of each sketch.
Make sure that you've auto-formatted each sketch.
Copy the entire lab3a, lab3b, and lab3c directories from your sketchbook (~/cs120/sketchbook) to your handin directory (found inside /classes/cs120/handin).
Before you start writing code, remind yourself of:
All of these things have been covered in class, and the slides and in-class exercises handouts posted on the schedule page are the places to find this information. It is also a good idea to review the examples from class and make sure you understand the role of each line of code in the sketch — what does it do? Why is it there? How does it relate to the patterns for sketch structure, variables, and animation?
Then, as you begin each exercise, remember decomposition and incremental development! You might draw the elements of the scene that don't change first, then draw animated elements with their initial characteristics (position, size, color, etc), and then finally add in the animation variables to make things change.
The first two exercises guide you through this process, but you'll need to do it for yourself in #3 (and in future sketches) — be thinking about how those steps exemplify the process.
Often the question "what changes from one frame to the next?" can have multiple answers. For example, for the three-circle animation from Wednesday's in-class exercises, both of the following are correct answers:
However, the second version is simpler — realizing that the relative position of each of the three circles remains fixed with respect to the other circles means that we don't need separate x and y coordinate variables for each circle. Two variables (one x coordinate variable and one y coordinate variable) suffice because the positions of the other two circles can be computed from the position of one.
This is one aspect of representation — how many different concepts are there, and thus how many different variables are needed? The positions of the three circles aren't really three separate concepts because there is a fixed relationship between the values. Representations with fewer variables (within reason) are preferred — less typing and fewer chances for errors.
A second aspect of representation is what exactly the variable(s) stand for. "The position of the circle pattern" is still vague, since the pattern has height and thus spans many x values and many y values when it is drawn. So, what position(s) would be convenient? One choice is a convenient point for one of the shapes within the pattern — ellipses can be drawn in CENTER or CORNER mode, so a reasonable choice would be to have the variables represent the center or corner of one of the circles. Another choice is a convenient point for the pattern as a whole, such as the lower left corner of an imaginary box around the whole pattern. How to decide? Pick what seems simplest or most intuitive to you, then make sure to name the variable appropriately and/or add a descriptive comment for the declaration so that it is clear to the reader what you chose.
Use descriptive (but brief) variable names that address what the variable is for. For example, variables for positions should generally include x or y in the name to distinguish x coordinates from y coordinates, variables for sizes should include w and h or width and height, and variables for colors should include r, g, b or red, green, blue. If you have several variables for the same sort of thing (such as two variables for x coordinates), include something in the name to distinguish one from the other e.g. carX and cloudX. Other than in the specific cases of coordinates and RGB color values (and one or two other situations), one-letter variable names are rarely good choices.
Comment your variable declarations to provide further information about the variable that is not apparent from its name. For example:
int y; // y coordinate of the center of the circle pattern
It is OK to provide redundant information in the comment (that y is a y coordinate is already apparent from the name and so could be omitted from the comment), but you should not leave out information. The following are all insufficient:
int y; int y; // y coordinate int y; // for the circle pattern int centerY; int circleY;
Additional examples of appropriate name and comment combinations:
int y; // center of the circle pattern int centerY; // for the circle pattern int centerY; // center of the circle pattern int circleCenterY;
Writing the comment serves two important purposes:
(important) It helps someone reading your code (which might be you, a month or a week or even a day after you initially wrote the sketch) understand what the variable is for and thus what's going on in the code without having to have overly long variable names.
(even more important) It makes you have to know precisely what the variable is for. Proper initialization, usage, and updating of a variable is impossible if you don't know what the variable is for (or have only a vague notion) — and is often quite easy if you do know what the variable is for. Having to write down this purpose means you have to have thought it.
You can declare two or more variables of the same type in a single statement, for example:
int x, y; // center of the circle pattern int r, g, b; // color of the bottom left circle
This is convenient and appropriate when the variables are aspects of the same thing (such as x and y coordinates for a position or r, g, b components for a color) and a single comment can be usefully written about all of the variables together.
Initialize your animation variables in setup(), after the drawing window has been opened. It is legal to declare and initialize a variable in one step e.g.
int y = 375; // y coordinate of the center of the circle pattern
but doing this for animation variables opens you up to doing the perfectly reasonable looking
int y = height-25; // y coordinate of the center of the circle pattern
The problem with this is that the system variables width and height only have correct values after the drawing window has been opened, and variable declarations and initializations at the beginning of the sketch are executed before the body of setup(). (Unfortunately, using width and height before the drawing window is open is not illegal so you won't get an error message or a warning — instead the values will be taken to be 0 and your animation variables will be initialized to unexpected values.) Always putting animation variable initializations in setup() (after opening the drawing window) avoids this problem.
Pay attention to details. If something asks for the edge of a circle to be just touching the bottom edge of the drawing window, the edge of the circle should be just touching the bottom edge of the drawing window. That means you'll need to calculate the correct position of the center or top edge (depending on your setting for ellipseMode) to achieve that.
Processing provides two system variables width and height for the width and height of the drawing window, respectively. (Note that you can only use these values after the window has been opened with size.)
Use these variables to design more flexible sketches. For example, if you want an ellipse to be centered at the lower right corner of a 300x400 window, you could write
ellipseMode(CENTER); ellipse(300,400,50,50);
However, if you change the size of the window but want the ellipse to still be in the lower right corner, you'll have to change both the size command and the coordinates where you've drawn the ellipse. A simpler solution is to instead write
ellipseMode(CENTER); ellipse(width,height,50,50);
This will always center the ellipse at the lower right corner no matter what size the window is, so changing the window size means only change the size command.
Note that this doesn't mean you should always use width and height — think about whether you want something at particular coordinates no matter what size the window is or whether you want it at the same relative position e.g. lower right corner or center. Use width and height only in the latter case.
Exercise 2 involves drawing arcs (part of a circle). You may be familiar with measuring angles in degrees — a complete circle is 360 degrees, half a circle is 180 degrees, and so forth.
Angles can also be specified in radians rather than degrees — a whole circle is 2π radians. The pictures below show how degrees and radians relate to each other:
Processing uses radians rather than degrees, so when you work with angles (such as with the arc command, which you'll need in exercise #2 below) you'll need to either use radians directly or convert from degrees to radians.
Important note: Processing measures angles going clockwise rather than counterclockwise, so what is shown above as 135 degrees or 3π/4 radians would be -135 or 360-135 = 225 degrees or -3π/4 = 2π-3π/4 = 5π/4 radians in Processing.
To work with radians directly, you'll need to express the value π (pi). Processing defines constants PI, HALF_PI, QUARTER_PI, and TWO_PI. This means you can just write PI, HALF_PI, etc when you want those values, and you can use them in expressions such as PI/3 (60 degrees) or 7*PI/6 (210 degrees). For example, the documentation for arc includes the following:
size(400,400); arc(200, 200, 320, 320, 0, PI+QUARTER_PI, PIE); |
This uses radians directly. (PI+QUARTER_PI = 5π/4)
Alternatively, to convert from degrees to radians, use
radians(deg)
where deg is the value in degrees. For example:
size(400,400); arc(200, 200, 320, 320, radians(0), radians(225), PIE); |
Put your name and a description of the sketch in comments at the beginning of each sketch. Also don't forget to Auto Format your code before handing it in. (Refer back to lab 2 for more on Auto Format if needed.)
The goal in this exercise is to create a sketch named lab3a which works like the example shown — in particular, there should be an animated cloud which moves from left to right across the drawing window at a reasonable speed. Clicking the mouse resets the cloud to its starting position. Include all of the elements shown — cloud, building, grass. You don't need to match the sizes, positions, and colors of the example exactly, but you should aim for something close. (In particular, the cloud should be made up of at least three ellipses and should be a very light gray, not white.) The initial position of the cloud should just be outside the window to the left — it should take a few moments (but not very long) to appear. The cloud should pass behind the building. |
To create this sketch:
Create a new sketch, add your name and a description of the sketch in comments at the beginning, and save your sketch as lab3a.
Set up the right program structure:
Does this sketch contain elements that change over time, or is it just a fixed picture? If elements change over time, this is an active sketch. If nothing changes, it is a static sketch.
Copy the appropriate template (active or static) into your sketch. Refer back to the slides from Monday's class (in particular, the one labeled "Active Mode vs Static Mode) for these templates.
Start with the fixed elements of the sketch — write code to open a drawing window, make the blue background, and draw the grass and building. Put these elements under the appropriate comment in the template. Keep in mind that "draw one frame" includes clearing the background as well as drawing the desired elements.
Add a simplified version of the cloud — a single ellipse that moves across the window (passing behind the building). This involves change (the ellipse moves), so ask yourself the animation questions below. Refer to the slides from Monday's class (in particular, the last slide with the animation questions and the rectangle-moving-right example) for how the answers to these questions get translated into code.
What changes from one frame to the next?
Each value that changes will need a variable. For each variable:
What kind of value is it? For whole numbers, the type is int. For numbers with decimal points, the type is float. Write the code to declare the variable accordingly. (Refer to the Naming and Commenting section above regarding naming your variable.)
What's the starting value? (The cloud should be just outside the window to the left.) Write the code to initialize the variable accordingly.
How does the value change? (The cloud should move right.) Write the code to update the variable accordingly.
Finally, write the code to use the variable(s) — draw the ellipse.
Finish the cloud — add the other two ellipses. Review the Representation section above — do you need more animation variables for the whole cloud?
Make the cloud's position reset when the mouse is clicked in the drawing window. Review the material from last week about interaction for this.
If you haven't already, Auto Format your code before handing it in. See lab 2 for information about Auto Format.
The goal in this exercise is to create a sketch named lab3b which works like the example shown below — in particular, there should be a two-color wheel which rolls at a reasonable speed along the bottom of the drawing window. Design your sketch so that the wheel starts in the lower left corner of the window and moves along the bottom no matter what size the window is. In addition, the rotational speed and horizontal motion of the wheel should be matched so that the wheel appears to be rolling rather than spinning or sliding. You do not need to make the wheel reset when it reaches the right edge of the window as shown in the demo; it is fine for the wheel to just disappear off the right side of the window.
To create this sketch:
Create a new sketch, add your name and a description of the sketch in comments at the beginning, and save your sketch as lab3b.
Set up the right program structure — copy the right template for an active or a static sketch. Refer back to this step in #1 if you don't remember what to do.
Start with just the wheel without any movement or spinning — put it in the lower left corner of the window, just touching the left side and bottom of the window. (Refer to the section about width and height above for how to ensure placement in the lower left corner no matter what size the window is.) Use arcs for the four wedges of the circle. (Look up arc in the Processing API if you aren't sure how to use it, and refer to the About Radians section above for how to work with angles in Processing.)
Make the wheel move to the right. This will be similar to making the cloud move in #1 — refer back there for the animation questions and how to translate the answers into code.
Make the wheel spin. This involves change, so again go through the animation questions. For rotation, what changes will be the starting angle of each arc. How many variables will you need? Review the section about Representation above and how you made the three ellipses of the cloud move together in #1.
Make the wheel roll. To make the wheel look like it is rolling rather than spinning or sliding, you need to match the amount that it moves to the right in each frame to the amount that the wheel turns in each frame. You might know that the circumference of a circle is 2πr, where r is the radius of the circle. From this you can work out that if a wheel turns by the angle a (in radians) in each step, it rolls a distance of a*r. Plug in the amount that you update the wheel's angle by and the wheel's size for a and r respectively to figure out the proper amount for updating the wheel's position. If you are working with angles in degrees, you'll need to convert the update angle to radians first.
If you haven't already, Auto Format your code before handing it in. See lab 2 for information about Auto Format.
Create an animated sketch of your own design. (Name this sketch lab3c.) What the sketch depicts is up to you (here's a chance to be creative!) but for full credit it must include the following required elements:
The scene must be original and created by you for this exercise. While you might be inspired by an example from class (such as the simple car) or another exercise in this (or a previous) lab, you may not copy code from other exercises in this lab or other labs, examples or solutions in the textbook or from class, or other sources even if you then make some changes — create your own version (such as a fancier car) from scratch.
The scene must be recognizable as something. The intent is that you should deliberately choose positions and colors for the shapes — simply drawing a bunch of shapes at whatever location they happen to end up at is not acceptable. Simplifying things (like making a tree out of a rectangle and a triangle) is fine.
You must include comments in your sketch identifying what thing each drawing command or group of drawing commands is associated with. For example, the comment
// cloudwould be appropriate just before the commands that set colors and draw the shapes for a cloud. Also use blank lines to separate the drawing commands for different elements in the scene.
You must use at least 30 shapes. (That's 30 shape-drawing commands, not 30 different kinds of shapes.)
You must use at least five different colors.
You must have at least four different compound things built out of three or more shapes each. "Compound thing" just means that you use several shapes to depict one thing; the cloud from #1 is an example (three shapes).
You must have at least four things which are animated in some way (moving, changing size, changing color/opacity, etc), at least two of which must be compound things and at least two of which must involve a property other than position. Choose reasonable rates of change — for example, something that is moving should move fast enough that you can tell it is moving, but not so quickly that it disappears off the screen right away. Also consider representation — try to avoid using unnecessary animation variables!
Clicking the mouse should reset the animation to the beginning so that it appears to start over.
To create this sketch, use the same process as for #1 and #2 — create a new sketch with the right name, use the template for the right program structure, work on building up the parts of the picture that don't change, and then add the animated parts one at a time, working through the animation questions for each.
You can earn extra credit by going substantially beyond the required elements. Some possibilities:
More stuff — lots more shapes, animation, interaction.
Section 4.6 introduced random(). Make use of random() in an interesting way. (The "if you have time" section of the handout from Wednesday's in-class exercises addresses how to use random colors without a flickering effect.)
More complex animation, such as a compound thing that grows or shrinks (keep the number of variables to a minimum!) or something that speeds up or slows down or something that moves in a curve or other complex path (not just a straight line).
Allow the user to control aspects of the sketch by pressing keys — increase or decrease the speed of something by pressing '+' or '-', or change the color of something to red, green, or blue by pressing 'r', 'g', or 'b', or reset the cloud by pressing 'c' and the wheel by pressing 'w', or ...
Include a brief description of what you've done for extra credit in a comment at the beginning of your sketch(es). More creative and challenging elements will earn more points.