// COMPLETE WORKING PROCESSING PROGRAM,
// ILLUSTRATING:
// -- ARRAY OF OBJECTS
// -- MAINTAINING SORTED ORDER OF ARRAY OF OBJECTS
// -- SUBCOLLECTIONS
// 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. |