CPSC 120: Lab Assignment 4

Due at 2:00 pm on Thursday, 02/20/2014

Home | Calendar | Announcements | Assignments | Example Code | Notes

Overview

The goal of this assignment is to solidify your skill with defining methods and effectively using calls to those methods in various contexts. You will construct a surprisingly substantial program, based on the bubbles.pde program from the Example Code and the two sketches your made for Lab 3.

There are three problems, and all of them should be put in a single Processing sketch, titled lab4.

Begin by studying the Tutorial below. It will give you the framework you need to successfully complete the exercises.

Tutorial #1: Abstraction and Method Definitions

"Abstraction" refers to the practice of removing irrelevant details from a task, in order to create something that can be reused in many contexts. You benefit from this every time you use a microwave (without comprehending the physics of dielectric heating), start a car (with no consideration of automotive construction), or shampoo your hair (regardless of what you know about shampoo chemistry).

In programming, you are used to this from the Processing library methods used in drawing sketches. For example, you use the ellipse() method, by providing four numbers: the (x,y) coordinates, the width, and the height, in that order. You are comfortable with the idea that when you use

ellipse(250,176, 99, 65);

a corresponding ellipse is drawn in the program's window. At no point do you need to think about how this ellipse is drawn: the details of the ellipse() method are totally irrelevant to the task of actually using the method.

Method definition uses another kind of abstraction: the values of the parameters. For example, let's say we have this sketch:

void setup() {
  size(800, 800); 
}

void draw() {
    background(255);
    drawRedRect(mouseX,mouseY,mouseX + 100, mouseY + 90);
}

Within draw(), we don't need to know anything about the definition of drawRedRect(): the only thing we need to know is that the four values specify the top left and bottom right corners of a red rectangle that is drawn in the window.

On the other hand, in the definition of drawRedRect, the fact that we're using it in the draw() method with the mouseX and mouseY values (plus two others) is irrelevant! It is enough to give names to those four values and trust that the values are filled in every time the method runs. This time, the abstraction is over the values that will be provided by users of the method, and it's what we do every time we define a method with parameters:

void drawRedRect(int xa, int ya, int xb, int yb) {
  noStroke();
  fill(255, 0, 0);
  rectMode(CORNERS);
  rect(xa, ya, xb, yb);
}

Combine this definition with the setup() and draw() methods above, and try out the resulting program!

Tutorial #2: Using abstraction in method definitions

Consider this simple program:

void setup() {
  size(500, 500);
  background(127);

  rectMode(CORNERS);
  stroke(0);
  fill(255);
  rect(100, 200, 350, 400);
  noStroke();
  fill(255,0,0);
  rect(135,220,315,380);
}

This draws a white rectangle with a proportionally-sized red rectangle inside of it: the sides begin 35 pixels in from the white rectangle, while the top and bottom are 20 pixels in. Why these numbers? Because 35 is 1/10 of the difference between the leftmost and rightmost x-coordinates of the white rectangle, while 20 is 1/10 the difference between the top and bottom edges. This picture should make the pattern clear:

Now let's rewrite this program in a way that captures the pattern:

void setup() {
  size(500, 500);
  background(127);

  int x1 = 100, y1 = 200, x2 = 350, y2 = 400;
  
  float wid = x2 - x1, hgt = y2 - y1;
  float xoffset = wid/10.0;
  float yoffset = hgt/10.0;
  
  rectMode(CORNERS);
  stroke(0);
  fill(255);
  rect(x1, y1, x2, y2);
  noStroke();
  fill(255,0,0);
  rect(x1+xoffset,y1+yoffset,x2-xoffset,y2-yoffset);
}

This does exactly the same thing as the first example, but now it's easy to change the location and size of the rectangle. If, for example, we want to draw this from (55,60) to (490, 130), all we need to do is change the values assigned to the pair of (x,y) coordinates:

  int x1 = 55, y1 = 60, x2 = 490, y2 = 130;

Try it!

We now have a chunk of code that is the same every time and a portion that changes only in the initial values it assigns to variables. This is exactly what happens when you build a method definition:

void drawPicture(int x1, int y1, int x2, int y2) {
  float wid = x2 - x1, hgt = y2 - y1;
  float xoffset = wid/10.0;
  float yoffset = hgt/10.0;
  
  rectMode(CORNERS);
  stroke(0);
  fill(255);
  rect(x1, y1, x2, y2);
  noStroke();
  fill(255,0,0);
  rect(x1+xoffset,y1+yoffset,x2-xoffset,y2-yoffset); 
}

Only now, we have something far, far more flexible:

int xleft;
int ytop;

void setup() {
  size(500, 500);
  background(127);
}

void draw() {  }

void mousePressed() {
  noFill();
  xleft = mouseX;
  ytop = mouseY;
}

void mouseDragged() {
  stroke(255);
  rectMode(CORNERS);
  rect(xleft, ytop, mouseX, mouseY);
}

void mouseReleased() {
  drawPicture(xleft, ytop, mouseX, mouseY);
}

Combine this code with the drawPicture() definition in a single file. It's a neat effect.

Your Job

Part One

Begin by recalling your mondrian and circle drawings from Lab #3.

You are to write a program with two methods: mondrian and retroCircs. Each one should take four int values. A call to mondrian(x1,y1,x2,y2) draws your Lab 3 Mondrian design in the rectangle bounded by (x1,y1) and (x2,y2), with all components drawn proportional to this rectangle. Similarly, a call to retroCircs(x1,y1,x2,y2) draws your circle design, with and proprtional to the rectangle defined by (x1,y1) and (x2,y2).

Part Two

Modify the example program above to draw either your Mondrian or circle design, whenever the user clicks, drags, and releases the mouse.

Bonus excellence

You can switch back and forth between the two designs by adding a global variable that determines which drawing to use, defining a keyPressed that switches values of this variable, andmodifying the mouseReleased method to use this value. Begin by declaring a global int variable, which you initialize to 0. Let's call it "useMondrian". Then make these modifications:

void keyPressed() {
    if (useMondrian == 0) {
        useMondrian = 1;
    else {
        useMondrian = 0;
    }
}

void mouseReleased() {
    if (useMondrian == 0) { ... }
}

Save your sketch in the lab4 folder of your turn-in directory.

Extra Credit

Add a bubbles() method, based on the bubbles.pde program, on the Example Code page.


Turn In

Make a folder in your turn in directory named "lab3". Copy both of your solutions in to this folder. Consult the Linux tutorial from Lab #0 if you need a reminder of the details.


Standards

John H. E. Lasseter