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 functions, an important tool for modularity and reuse. Declaring a function bundles a series of related instructions (such as the instructions to draw a complex shape) into one package distinct from the rest of the program, and adding parameters allows the same basic steps to be reused with different values without having to rewrite the steps themselves. Both of these aspects of functions become increasingly important as programs get larger and more complex.
Several of the exercises are based on traditional quilt patterns. These kinds of quilts are an excellent real-world example of modularity and abstraction — first individual pieces of fabric are assembled into blocks, then the blocks are assembled to create the whole quilt.
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 must document any help received and any outside resources used.
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, and 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.
Also remember that the goal of the exercises and of this course is learning the process of creating programs, and that what you hand in for a grade is a reflection of your engagement of that process. 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.
You may not shortcut to a solution by copying code (except as specifically authorized in instructions) or working with others to write code together. You may not be in possession of someone else's program or solution before you have handed in your own. See the full collaboration policy for more on this.
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 lab4a, lab4b, and lab4c 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 function definitions and class (as well as the previous topics of sketch structure, variables, and animation)?
Keep incremental development in mind — even though the exercises list the "create drawing function(s)" step before the "complete the sketch" step, you can still apply incremental development. Start with a simple function body — draw just one of the shapes you'll need, for example — and make one call to the function from draw(). Gradually build up the body of the function; once it draws the complete pattern, add a second call to it in draw() (and so on). You can also start without parameters — have the function draw the shape in a fixed spot — and then add parameters once you have that working.
Also remember how to work out coordinates — draw pictures and label what you know! This is especially important when variables and parameters are involved because you need to work out expressions to calculate positions and sizes rather than just figuring out the correct number.
Comment function definitions. Each function should have a comment immediately before it describing the function's purpose (what does it draw?) and each of its parameters. For example:
// draw a snowman // (x,y) is position of the bottom center void drawSnowman ( int x, int y ) { ... }
Functions should have one (and only one) purpose. A drawing function draws something — it should contain all of the statements necessary for drawing that something, but only statements actually for drawing that something — it should not contain statements for drawing other things or statements not for drawing. In particular, updating animation variables is not drawing and should go in draw(), not one of your drawing functions.
Avoid having more parameters than necessary. For example, a single parameter for the size of a square or circle is sufficient — you don't need separate width and height parameters because the width is always the same as the height for those shapes.
Let's say that width is 600 — what is the value of 2/3*width? You expect it to be 400, but this expression actually evaluates to 0! This is because the division operator / works differently depending on the type of its operands. When used with floats, as in 2.0/3.0 the result is as expected — 2.0/3.0 = 0.66667. But when used with ints, the result is the number of whole times the denominator divides the numerator — 3/2 = 1, 600/140 = 4, and 2/3 = 0. (Another way to think about it is that integer division does the division but then drops the decimal part e.g. 3/2 = 1.5, which is 1 when the 0.5 is dropped.)
To avoid surprises, here are two guidelines:
If you want the result to be an integer, write the expression so the multiplication(s) happen first e.g. 2/3*width can be rewritten as 2*width/3. This still results in integer division, but the result is no longer 0 — 2*width happens first, then 1200/3 = 400.
If you want the result to have a decimal point, introduce floats in the numerator and/or denominator of the division. For example, rewrite 2/3*width as 2.0/3.0*width. Now the result is 400.0, because 0.67*600 = 400.0. If only integer variables are involved (e.g. width/height), multiply either the numerator or denominator by 1.0: (1.0*width)/height. (The parens ensure that the multiplication happens before the division.)
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 lab4a which draws the hourglass quilt shown.
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 lab4a.
Copy and paste or type the following into your sketch, then run it to see what it does.
void setup () { size(400, 400); } void draw () { background(255); // vertical block stroke(0); fill(237, 57, 57); triangle(0, 0, 200, 0, 100, 100); triangle(0, 200, 100, 100, 200, 200); // horizontal block stroke(0); fill(61, 224, 195); triangle(200, 0, 300, 100, 200, 200); triangle(400, 0, 300, 100, 400, 200); }
Define functions drawVerticalBlock and drawHorizontalBlock to draw the vertical (red) and horizontal (green) variations of the hourglass block. Move the drawing code for each block from draw to the body of the appropriate function, and call the functions from draw. Also include comments for each function. Run the sketch to make sure it still works the same as it did.
Add parameters to the drawVerticalBlock function so that the block can be drawn in any position and update the body the function to utilize those parameters. Also update the function comments and call accordingly. Run the sketch to make sure it still works the same as it did.
Add a second call drawVerticalBlock to draw the other vertical (red) block. Also include comments for each function. Run the sketch to make sure the second block is positioned correctly.
Repeat the previous two steps with the drawHorizontalBlock function — add parameters for the position, updating the body and call accordingly, and then add a second call to complete the quilt pattern.
The goal in this exercise is to create a sketch named lab4b which draws the quilt shown.
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 lab4b.
Write a function drawBlock which draws the quilt block pattern shown. It should have parameters for the position and size of the block (the block will always be a square, so you only need a single parameter for the size and not separate parameters for width and height) and the color of the center diamond shape. (You can choose the two colors to use for the triangles — you do not need to match the colors shown, but you should match the pattern so that things that are the same color in the example are the same color in your block.) Use a quad (look it up in the Processing API to see how to use it) to draw the center diamond shape. Include comments describing the function and its parameters.
Test your function by calling it from draw.
Complete the sketch to produce a quilt similar to the one shown using your drawBlock function. You can choose whatever colors you want, but the pattern should match (three different center diamond colors, and things with the same color in the picture have the same color in your sketch) and the blocks must be arranged in a 3x3 grid with a border around the outside of the quilt.
The goal in this exercise is to create a sketch named lab4c which draws the quilt shown. (This is a traditional pattern known as Drunkard's Path.)
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 lab4c.
Write four functions drawDrunkardUR, drawDrunkardUL, drawDrunkardLR, and drawDrunkardLL to draw the four patterns shown. (The naming scheme indicates where the arc is — UR for upper right, LL for lower left, etc.) Each function should take the position and size of the pattern and the two colors used as parameters. (Assume that the block will always fill a square region, so only one parameter is needed for the size.) Note that the radius of the arc is two-thirds the size of the rectangle. Include comments describing each function and its parameters.
Test your functions by calling them from draw.
Write a function drawDrunkardBlock which takes the position, the size (again assume that it will always be a square), and two colors as parameters and draws the block shown. Use the functions you wrote in the previous step to draw each of the 16 pattern squares — don't draw rectangles and arcs directly in drawDrunkardBlock! Include comments describing the function and its parameters.
Test your function by calling it from draw.
Complete the sketch to produce a quilt similar to the one shown using your drawDrunkardBlock function. (The blocks are arranged in a 2x2 grid.) You can choose whatever colors you want.
Create a sketch of your own design. (Name this sketch lab4d.) 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 use at least 30 shapes. (That's 30 shape-drawing commands, not 30 different kinds of shapes. Note that a function which draws five shapes only contributes five shapes to the total even if the function is called multiple times — this is a count of different statements that you write to draw shapes, not how many shapes appear on the screen.)
You must have at least four different compound things built out of three or more shapes each, with a function to draw each kind of thing. For example, if you have a car and a (three-shape) tree and a snowman in your scene, you must create a car-drawing function, a tree-drawing function, and a snowman-drawing function.
You must demonstrate variation in position, size, and color using functions with parameters. This means that you'll need multiple instances of at least one of your compound things and will need to add parameters to at least one of your drawing functions as a result — your sketch might contain two otherwise-identical cars in different positions, for example, and thus should have a single car-drawing function with parameters for the position rather than separate drawing functions for each car. The three properties (position, size, and color) can be covered together or in other combinations — a red car, a blue car, a large snowman, and a small snowman all in different positions covers all three properties, as does just a large red car and a small blue car in different positions.
Each function must have a descriptive name related to its purpose (e.g. drawTree would be a good name for a tree-drawing function) and a comment immediately before the function describing the purpose of the function and what each of its parameters are for. For example:
// draw a tree // (x,y) is the position of the bottom center of the tree trunk void drawTree ( int x, int y ) { ... }
At least two instances of one of the compound things must be interactive and/or animated in different ways. For example, two cars could each move in different directions and at different speeds, or one car could move while another changes color, or one car could be animated in some way while the other tracks the mouse, or one car follows the mouse's x coordinate while another follows the mouse's y coordinate. Note: do not use mouseX, mouseY, or animation variables in the body of your drawing functions! Instead, add parameters for whatever is changing and pass in the desired value. For example, to have a tree that follows the mouse around, define drawTree with parameters for the position:
void drawTree ( int x, int y ) { ... }
and then call it with drawTree(mouseX,mouseY).
(It is legal to use mouseX, mouseY, and animation variables inside function bodies, but it is better programming style in many cases not to.)
As with all sketches, your name and a description of what your scene is a picture of must be included in a comment at the beginning of the sketch and your code must be properly formatted. (Use auto-format!)
You can earn extra credit by going substantially beyond the required elements. Add to your sketch for #4 and include a brief description of what you've done for extra credit in a comment at the beginning of your sketch. Some possibilities:
Lots more stuff — more shapes, more more compound shapes (drawn with parameterized functions).
Build up more complex shapes by creating drawing functions which call other drawing functions (as in #3). Incorporate some additional complexity — functions which call functions which call functions (etc) or animated elements (such as a windmill with rotating blades).
Draw a quilt like the one shown as an element in or as the background for your scene. Make use of parameterized functions to save work! (Very little credit will be given if you just draw lots of individual rectangles and triangles, or if you have lots of very similar functions when a single parameterized one would do.) A clever way to handle the color variations would be to add/subtract small random amounts to the components of a base color. (Look up random() in the Processing API.) To avoid flickering effects when random() is used in active mode, put the statement
randomSeed(0);
at the beginning of draw().
More creative and challenging elements will earn more points.