Code
// COMPLETE WORKING PROCESSING PROGRAM,
// ILLUSTRATING:
// -- ARRAY OF OBJECTS
// -- MAINTAINING SORTED ORDER OF ARRAY OF OBJECTS
// -- SUBCOLLECTIONS.
// See keyPressed() for documentation!
// Global instances of Refrigerator objects:
Refrigerator mainFridge;
Refrigerator miniFridge;
void setup(){
size(450, 400);
// Create the instance of the main Refrigerator object.
// The mini Refrigerator is null for now.
// Question for you: where does the miniFridge get created?
mainFridge = new Refrigerator();
miniFridge = null;
}
void draw(){
background(180);
// Request the main Refrigerator to draw itself.
// I have modified the render() method to take location & size.
mainFridge.render(50,50,150,300);
// Since the mini Refrigerator does not exist at the start
// of the program, only draw it once it has been created.
if (miniFridge != null){
miniFridge.render(250,180,150,170);
}
}
int miniFridgeType = 0; // just a little hack
void keyPressed(){
switch(key){
case 'o':
mainFridge.openDoor();
break;
case 'c':
mainFridge.closeDoor();
break;
case '-':
mainFridge.removeItem();
break;
case '+':
mainFridge.addItem();
break;
case 'f':
// When we press the 'f' key,
// Create the mini-fridge.
// For this fridge, we ONLY select those foods of a certain "type"
// (triangle, circle, square).
miniFridgeType++;
miniFridgeType%=3;
miniFridge = mainFridge.makeSubcollectionRefrigerator (miniFridgeType);
break;
case 'x':
// When we press the 'x' key,
// delete whichever item is mouse-selected (in the main fridge),
// ans shift the remaining items down one position.
int indexOfSelectedItem = mainFridge.getIndexOfSelectedItem();
if (indexOfSelectedItem != -1){
mainFridge.removeSpecificItem(indexOfSelectedItem);
}
break;
case 'v':
// when we press the 'v' key,
// insert a new FoodItem into the main fridge.
// This FoodItem is generated with a random tallness,
// and it will have to be inserted in the correct position
// in the array (maintaining sorted order!).
// All inserted FoodItems are RED, so you can easily spot them.
float tallness = 10 + random(35);
color chroma = color(255,0,0);
int type = (int)random(0,3);
FoodItem F = new FoodItem(tallness, chroma, type);
mainFridge.insertAndSortNewFoodItem (F);
break;
}
}
//===============================================
class Refrigerator {
// FIELDS
int nFoodItems;
FoodItem foodItems[];
boolean bDoorOpen;
//---------------------------
// METHODS
Refrigerator(){
bDoorOpen = false;
nFoodItems = 0;
int initialFoodItemArraySize = 10;
foodItems = new FoodItem[initialFoodItemArraySize];
// ...individual food items will be created (new'd) when they're added.
// that's one way to do it.
}
//---------------------------
void render(int x, int y, int w, int h){
// IF THE DOOR IS CLOSED, SHOW THE OUTSIDE OF THE FRIDGE.
if (bDoorOpen == false){
stroke(0);
strokeWeight(1);
fill(255,240,230);
rect(x,y,w,h);
fill(180,180,190);
rect(x+5,y+h/2-40,5,60);
// render silly kid's drawing
pushMatrix();
translate(50,50);
rotate(radians(-15.0));
translate(x,y);
fill(255);
rect(0,0,36,36);
smooth();
ellipse(18,18,27,27);
arc(18,18,20,20, radians(45),radians(135));
ellipse(12,14,6,6);
ellipse(24,14,6,6);
popMatrix();
}
else {
// BUT IF THE DOOR IS OPEN, SHOW THE STUFF INSIDE.
stroke(0);
strokeWeight(1);
// Draw the fridge enclosure
fill(200,200,208);
rect(x,y,w,h);
fill(240,240,255);
rect(x+5,y+5,w-10,h-10);
// draw the food items at specified locations.
// Compute some positions to cleverly position things.
for (int i=0; i<nFoodItems; i++){
float foodx = x + 25 + (i%6)*20;
float foody = y + h - 10 - (60*(i/6));
foodItems[i].draw(foodx,foody);
}
}
}
//---------------------------
// ACCESSOR METHODS
boolean isDoorOpen(){
return bDoorOpen;
}
int getNFoodItems(){
return nFoodItems;
}
// SIMPLE MUTATOR METHODS
void closeDoor(){
bDoorOpen = false;
}
void openDoor(){
bDoorOpen = true;
}
//---------------------------
Refrigerator makeSubcollectionRefrigerator (int subselectionFoodType){
// Here, we ask this Refrigerator to create
// a "subcollection" Refrigerator (such as a cooler or mini-fridge).
// We know (from its signature) that this method *must*
// return a Refrigerator. So declare and create this right away!
// And don't forget to return it at the end of the method.
Refrigerator outputFridge = new Refrigerator();
// Open its door so we can add food to it (ha!)
outputFridge.openDoor();
// Search through my foodItems array for items which meet the criterion.
// Add those items to the outputFridge.
// (Note that "criterion" in this case means, subselectionFoodType.
// But the criterion could be measuring a continuous property instead --
// For example, find me all FoodItems taller than.... etc.)
for (int i=0; i<nFoodItems; i++){
if (foodItems[i].getType() == subselectionFoodType){
outputFridge.addItem(foodItems[i]);
}
}
// Return the outputFridge.
return outputFridge;
}
//---------------------------
// ADDITEM 1: pass in an actual FoodItem object.
void addItem (FoodItem F){
if (bDoorOpen){
foodItems[nFoodItems] = F;
nFoodItems++;
if (nFoodItems >= foodItems.length){
expandFoodItemArray();
}
}
else {
System.out.println("Door must be open to add an item.");
}
}
//---------------------------
// ADDITEM 2: pass in properties for a new FoodItem to be made from
void addItem (float tallness, color chroma, int type){
if (bDoorOpen){
foodItems[nFoodItems] = new FoodItem(tallness, chroma, type);
nFoodItems++;
if (nFoodItems >= foodItems.length){
expandFoodItemArray();
}
}
else {
System.out.println("Door must be open to add an item.");
}
}
//---------------------------
void addItem(){
// ADDITEM 3: pass in nothing (no arguments);
// Adds a randomly-generated item to the end of the array,
// expanding the array if necessary (if it gets too full).
if (bDoorOpen){
// If the door is open,
// generate the properties of a new FoodItem
// This is code for adding a new fooditem to the end.
// It makes sure that this new food item is 4 pixels taller than
// what is currently the last item in the array.
float tallness = 10;
if (nFoodItems > 0){
tallness = foodItems[nFoodItems-1].getTallness() + 4.0;
}
// generate a color and food type for
// what will be the newly added FoodItem
color chroma = color(random(100,255), random(128,255), random(0,100));
int type = (int)random(0,3);
// .. create and add that FoodItem to the foodItems array.
foodItems[nFoodItems] = new FoodItem(tallness, chroma, type);
nFoodItems++;
// Deal with what happens when the user fills up the array.
// If the number of food items has exceeded the size of the array,
// Expand the array!
if (nFoodItems >= foodItems.length){
expandFoodItemArray();
}
}
else {
System.out.println("Door must be open to add an item.");
}
}
//---------------------------
void insertAndSortNewFoodItem (FoodItem inputF){
// Some FoodItem, inputF, is being "inserted" into the array.
// We are asked to make sure that we maintain "sorted order",
// in this case, of the "tallness" of the foodItems.
if (bDoorOpen){
// Only insert new food if the door is open, right?
// Now fetch the tallness of the inputF FoodItem.
// Note the use of the accessor method.
float tallnessOfInputF = inputF.getTallness();
// Search for the index at which to insert this foodItem,
// to maintain the sorted order of the foodItem array.
// This could also be accomplished with a while{} loop.
int indexAtWhichToInsertInputF = 0;
for (int i=0; i<nFoodItems; i++){
if (foodItems[i].getTallness() < tallnessOfInputF){
indexAtWhichToInsertInputF++;
}
}
// It's possible we might need to enlarge the size of the array.
if (nFoodItems >= foodItems.length){
expandFoodItemArray();
}
// Shift all elements taller than inputF,
// one position further down the array.
for (int i=nFoodItems; i>indexAtWhichToInsertInputF; i--){
foodItems[i] = foodItems[i-1];
}
// Insert the inputF. Note how simple this is.
// Then, don't forget to increase the nFoodItems!
foodItems[indexAtWhichToInsertInputF] = inputF;
nFoodItems++;
}
else {
System.out.println("Door must be open to insert an item.");
}
}
//---------------------------
void removeItem(){
if (bDoorOpen){
if (nFoodItems > 0){
nFoodItems--;
}
}
else {
System.out.println("Door must be open to remove an item");
}
}
//---------------------------
void removeSpecificItem (int indexFromWhichToRemoveFoodItem){
// To remove a specific item from the foodItems array,
// copy all higher-numbered FoodItems down one position in the array.
if ((indexFromWhichToRemoveFoodItem >= 0) &&
(indexFromWhichToRemoveFoodItem < nFoodItems)){
for (int i=indexFromWhichToRemoveFoodItem; i<nFoodItems; i++){
foodItems[i] = foodItems[i+1];
}
nFoodItems--;
}
}
//---------------------------
int getIndexOfSelectedItem(){
// Loop over all of my FoodItems,
// and return the array-index of whichever one
// reports that the mouse is hovering over it.
int output = -1;
for (int i=0; i<nFoodItems; i++){
if (foodItems[i].isMouseHovering()){
output = i;
}
}
return output;
}
//---------------------------
void expandFoodItemArray(){
// Make a new array ("biggerArray") which
// holds twice as much stuff as foodItems.
FoodItem biggerArray[] = new FoodItem[ foodItems.length * 2];
// Copy all of our FoodItems into that bigger array.
for (int i=0; i<foodItems.length; i++){
biggerArray[i] = foodItems[i];
}
// Re-assign foodItems to be that bigger array.
foodItems = biggerArray;
}
} // End of Refrigerator Class.
//=====================================================
//=====================================================
class FoodItem {
float posx, posy;
float tallness;
color chroma;
int type;
//-----------------------------------
// Constructor for a FoodItem.
// All necessary variables are passed in.
FoodItem (float tallness, color chroma, int type){
this.tallness = tallness;
this.chroma = chroma;
this.type = type;
}
//-----------------------------------
// How a FoodItem should draw itself.
// Note that a position is passed in.
void draw (float x, float y){
posx = x;
posy = y;
smooth();
stroke(0);
if (isMouseHovering()){
strokeWeight(4);
}
else {
strokeWeight(1);
}
fill(chroma);
switch(type){
case 0: // RECT
rect(posx-6,posy-tallness, 12,tallness);
break;
case 1: // ELLIPSE
ellipse(posx,posy-tallness/2, 12,tallness);
break;
case 2: // TRIANGLE
triangle(posx-6,posy, posx+6,posy, posx,posy-tallness);
break;
}
noSmooth();
}
//-----------------------------------
// ACCESSOR METHODS.
int getType(){
return type;
}
float getTallness(){
return tallness;
}
boolean isMouseHovering(){
return (
( mouseX > posx-6) &&
(mouseX < posx+6) &&
(mouseY < posy) &&
(mouseY > posy-tallness));
}
} // End of FoodItem Class.
0905 - A Subcollection: Your Fridge makes a sub-Fridge
Statement:Hey, this is your last regular homework for the course. The most important aspect of this assignment is that it will form crucial rehearsal for the final exam.
Extend the functionality of your Refrigerator class with a "factory" method, which is able to produce a subcollection of the items in your fridge.
For a reasonable metaphor: Suppose you are going on a picnic which some friends who are strict circuphages. (That means they only eat circles.) Ask your fridge object to make a small sub-fridge (like a cooler) which only contains the circular FoodItems from your fridge.
Hints: you will need to make a factory method within the Refrigerator class that returns a Refrigerator (of course), and which takes as an argument some kind of "condition" parameter. That condition could be the "food type" (triangle, circle, square), or (alternatively) it could be the food size ("tallness"), etcetera. Your applet will also need to assign the result of this factory function to a second Refigerator object, and display it next to the main fridge.
For more information on how to do this, see the blog entry about factory methods" and the blog entry about sub-collections. All of the answers to doing this lie therein.