Looking for more resources about ArrayLists

Thanks for your questions.

Yeah, the boolean variable isDead is part of the class.

It signifies that the cell is dead. Initially isDead is false obviously.

  • isDead plays a role especially when we kill a big cell and replace it with 4 smaller ones (in the function mousePressed()). It is set to true to mark the old big cell as dead.

  • isDead is also used to remove old items from the ArrayList: When isDead it true, we remove (in a backwards for-loop in draw()). The code also works without the removing for-loop (maybe slower since we have more and more dead items in the ArrayList we have to skip).

Why does the code work without the removing for-loop? Iā€™ll explain.

Details

Also for the (very short) time that items in the ArrayList exist that have isDead==true (or when we remove / comment out the removing for-loop), the class ignores dead objects:

  • Dead objects are not displayed anymore (in display()) and
  • they cannot be clicked. So onMouse() returns false when cell is dead. So itā€™s always saying, ā€œNo, I have not been pressedā€.

Usage of return

These lines

    if (isDead) 
        return; //  the oldCell is DEAD and does not display

leave the function draw() before it really begins. But for the rest of the function we know: the cell is alive.

Alternatively you could say if( ! isDead) {.........} and put the entire content of the function in the if clause { ā€¦ }. (Which would give you a code less clear.)

Usage in the function onMouse()

Similarly:

boolean onMouse() { 
    if (isDead) // When TRUE return FALSE state! Yes! Cell is not clicked by mouse (or pretends not to be)
       return false; // leave with false (mouse is never recognized on a dead cell)

So, when the cell is dead, we leave the function fast - with false.
This means a dead cell can never say, ā€œthe mouse has been clicked on meā€.

  • Which is good because otherwise a dead cell could generate 4 children again and again. Not good.

The further lines of onMouse()

Nothing much about.

Now we read this in onMouse():

   // this can return true or false: // I'm not sure what this means? Or why true or false is okay
    return 
      mouseX>x   &&
      mouseX<x+w &&
      mouseY>y   &&
      mouseY<y+w;
  }//method

Well, this just means:

  • return true when mouse is inside and
  • false when mouse is not inside.

Itā€™s a short form of this long form:

  if  ( mouseX>x   &&
      mouseX<x+w &&
      mouseY>y   &&
      mouseY<y+w) 
            return true;    // mouse was inside cell 
            else return false;     // mouse was NOT inside cell 

In the short form we just return the result
of this condition directly:

    ( mouseX>x   &&
      mouseX<x+w &&
      mouseY>y   &&
      mouseY<y+w)

which is doing the same as the long form.

I hope this helps!

Chrisir

4 Likes

Thank you for this very detailed explanation @Chrisir!
Yes this was absolutely helpful!
:slight_smile: :nerd_face:

1 Like

Version in 3D:

3 Likes

This looks GREAT in 3D!! Hadnā€™t thought of thatā€¦
:nerd_face:

1 Like

for anyone interested:

  • Code of the 3D version

Explanation:

  • There is a camera you steer with wasd, cursors and + and - Bit hard to explain. Could be made into a class.
  • You can click the boxes and they grow 4 new boxes. The old box remains but cannot be clicked again.
  • How does the clicking work? It is a picking mechanism: The color underneath the mouse is checked against a unique color that every box has. This unique color is invisible on a PGraphics that gets not displayed. In this PGraphics the check is performed. This is useful because we donā€™t have to do any calculations with mouse position and box position but can just use the colors. We have to use the PGraphics because in the visible graphic, the colors are distorted because we use lights(). The PGraphics has the same size as the visible window and also uses the same camera data of course.
  • We cannot use peasyCam here, then the picking wonā€™t work.

Chrisir



// New version 

ArrayList<GridCell> grid = new ArrayList<GridCell>();

int uniqueColorCounter;

PGraphics pg;

float camAngle;
float camX, camY, camZ, 
  camRadius=520+200+220+900;
float camCenterX, camCenterZ; 

void setup() {

  size(1200, 800, P3D);
  background(255);

  camCenterX = width/2; 
  camCenterZ = - 2000;//-2000

  camY = height/2.0;

  pg=createGraphics(width, height, P3D); 

  final float w = width/4; 
  final float h = w;
  final color WHITE = color(255);
  int depth=0;

  for (float x = 0; x <= width; x+=w) {
    for (float z = -900; z <=0; z+=h) {
      grid.add(
        new GridCell(x, height+320, z - 1630, 
        w, h, 
        WHITE, 
        depth));
    }//for
  }//for
}//func

void draw() {
  background (255);
  lights();

  camX=cos(radians(camAngle)) * camRadius + camCenterX; 
  camZ=sin(radians(camAngle)) * camRadius + camCenterZ; 
  camera(camX, camY, camZ, 
    width/2.0, height/2.0, -450-1630, 
    0, 1, 0);

  displayAllCellsAndRemoveDeadCells();

  keyPressedThroughout();

  // HUD 
  camera();
  noLights();
  fill(0);
  text(grid.size()
    + " cells. Click mouse on a cell to expand cell. Use cursor and wasd (and + -) to move camera.", 
    20, 20);
}//func 

//---------------------------------------------------------------------------------

void keyPressedThroughout() {
  if ( ! keyPressed) 
    return;

  if (key=='w')
    camCenterZ++;
  else if (key=='s')
    camCenterZ--;

  else if (key=='a')
    camCenterX--;
  else if (key=='d')
    camCenterX++;

  else if (key=='+')
    camY--;
  else if (key=='-')
    camY++;

  if (keyCode==LEFT) 
    camAngle++;
  else if (keyCode==RIGHT) 
    camAngle--;
  //----
  else if (keyCode==UP) 
    camRadius-=2;
  else if (keyCode==DOWN) 
    camRadius+=2;
}

void keyReleased() {
  key=0;
}

//---------------------------------------------------------------------------------

void mousePressed() {
  // search clicked cell:
  for (GridCell oldCell : grid) {  // short form of for-loop here
    // check mouse 
    if ( oldCell.onMouse() ) {
      // replace cell with 4 cells 
      grid.add(oldCell.getUpperLeftCorner());   // upper left corner (old pos)
      grid.add(oldCell.getUpperRightCorner());  // upper right corner 
      grid.add(oldCell.getLowerLeftCorner());   // lower left corner 
      grid.add(oldCell.getLowerRightCorner());  // lower right corner
      // don't kill old cell but mark as not suitable for mouse
      oldCell.canGetMouse=false;
      // leave here (we found ONE cell, that's enough)
      return;
    }//if
  }//for
}//func 

//---------------------------------------------------------------------------------

color getRandomColor() {
  return
    color(random(255), random(255), random(255));
}//func

void displayAllCellsAndRemoveDeadCells() {
  // display all cells
  pg.beginDraw(); 
  pg.background(255);
  pg. camera(camX, camY, camZ, 
    width/2.0, height/2.0, -450-1630, 
    0, 1, 0);

  for (GridCell currentCell : grid) { // use short form of for-loop here
    currentCell.display();
  }//for
  pg.endDraw();

  // backward for-loop to remove dead ones here !! 
  // can't use short form of for-loop here
  // remove dead ones (you need to have a conventional for-loop here, backwards)
  for (int i=grid.size()-1; i>=0; i--) {
    if (grid.get(i).isDead) 
      grid.remove(i);
  }//for
}


//====================================================================================================
//MAIN GRID CLASS //////////////////////////////////

class GridCell {

  float x, y, z; // pos
  float w, h; // size 
  boolean isDead = false, 
    canGetMouse=true; 
  color col1     = color(255);
  color uniqueColor;  // used for invisible pg

  int depth=0; // doesn't really have a function 

  // constr 
  GridCell(float x_, float y_, float z_, 
    float w_, float h_, 
    color col1_, 
    int depth_) {
    x = x_;
    y = y_;
    z = z_;

    w = w_;
    h = h_;

    col1  = col1_;
    depth = depth_;

    uniqueColor = color(uniqueColorCounter);
    uniqueColorCounter++;
  } // constr 

  void display() {
    if (isDead)
      return; // leave when it's dead - no displaying 

    pushMatrix();
    stroke(0);
    fill(col1);
    translate(x, y, z);
    box(w, h, h);
    popMatrix();

    // draw pg parallel 
    pg. pushMatrix();
    pg. translate(x, y, z);
    pg. noLights(); 
    pg. noStroke(); 
    pg. fill(uniqueColor); 
    pg. box(w, h, h);
    pg. popMatrix();
  }//method

  boolean onMouse() { 
    if (! canGetMouse)
      return false; // leave with false (mouse is never recognized on a dead cell)

    // this can return true or false: 
    return 
      pg.get(mouseX, mouseY)==uniqueColor;
  }//method

  //--- make child cells (all with w/2) 

  GridCell getUpperLeftCorner() {  
    // upper left corner (old pos) 
    return new GridCell(x-h/4, y-h/2-h/4, z-h/4, 
      w/2, h/2, getRandomColor(), 
      depth+1);
  } //method     

  GridCell getUpperRightCorner() {
    // upper right corner (x changed)
    return new GridCell(x+w/2-h/4, y-h/2-h/4, z-h/4, 
      w/2, h/2, getRandomColor(), 
      depth+1);
  } //method

  GridCell getLowerLeftCorner() {
    // lower left corner (y changed) 
    return new GridCell(x-h/4, y-h/2-h/4, z+w/2-h/4, 
      w/2, h/2, getRandomColor(), 
      depth+1);
  } //method

  GridCell getLowerRightCorner() {
    // lower right corner (x and y changed)
    return  new GridCell(x+w/2-h/4, y-h/2-h/4, z+w/2-h/4, 
      w/2, h/2, getRandomColor(), 
      depth+1);
  } //method
  // 
  // ---
  //
}//class 
//

4 Likes