Carmen’s Final Project Plan

April 21 Status

Project Idea 1 — L-Systems

Project Idea 2 — CA Systems

April 25 Feature Set

Committed features

Stretch features

April 28 Status

Cool, check out the screen shot:

Game of Life screenshot

Implementation

Library Needed

I needed to add a library called peasy to get the project to go.

Font Needed

Add this font file to the project.

Code

import processing.opengl.*; //hardware acceleration
import peasy.*; //camera library

class Life {
int w, h; //world cube face
int depth; //world extrusion
float density; //used to calculate initial state
int size; //cell size
int[][][][] world; // Indices 1-3 are the coordinates of each cell. The last index indicates the old state :[0] or the new state: [1]
int[] ruleset; //allows modification of the rules of life
boolean oscillateColors = false;
boolean sphereMode = false;

Life(int w, int h, int d, float density, int size, int[] ruleset) {
this.w = w;
this.h = h;
this.depth = d;
this.density = density;
this.size = size;
world = new int[w][h][depth][2];

this.ruleset = ruleset;

randomPopulate();
}

void render() {
fill(0, 100);

for (int i=0; i<w; i+=size) {
  for (int j=0; j<h; j+=size) { //iterates through every cell in the world
    for (int k=0; k<depth; k+=size) {
      //if the present  is alive   or  the present is dead   and the past was just alive
      if ((world[i][j][k][1] == 1) || (world[i][j][k][1] == 0 && world[i][j][k][0] == 1)) {
        world[i][j][k][0]=1;

        if (oscillateColors)stroke(255-i*(2.5*abs(sin(0.1*generations))), 255-j*(2.5*abs(sin((0.1*generations)+PI))), 255-k*(2.5*abs(sin((0.1*generations)+TWO_PI))));
        else 				stroke(255-i*2.5, j*2.5, k*2.5);
        pushMatrix();
        translate(i, j, k);
        sphereDetail(5);
        if (sphereMode) sphere(size);
        else 			box(size);
        popMatrix();
      }

      if (world[i][j][k][1] == -1) {
        world[i][j][k][0] = 0;
      }

      world[i][j][k][1] = 0;
    }
  }
}

for (int i=0; i<w; i+=size) {
  for (int j=0; j<h; j+=size) {
    for (int k=0; k<depth; k+=size) {
      int count = neighbors(i, j, k);

      if (count == ruleset[0] && world[i][j][k][0] == 0) { //death->life rule
        world[i][j][k][1] = 1;
      }

      if ((count < ruleset[1] || count > ruleset[2]) && world[i][j][k][0] == 1) {//overpopulation and lonlieness rule
        world[i][j][k][1] = -1;
      }
    }
  }
}
}

int neighbors(int x, int y, int z) {
return
  world[(x+size) % w][y][z][0] + world[x][(y+size) % h][z][0] +
  world[(x+w-size) % w][y][z][0] + world[x][(y+h-size) % h][z][0] +
  world[(x+size) % w][(y+size) % h][z][0] + world[(x+w-size) % w][(y+size) % h][z][0] +
  world[(x+size) % w][(y+h-size) % h][z][0] + world[(x+w-size) % w][(y+h-size) % h][z][0] +
  world[(x+size) % w][y][(z+size) % depth][0] + world[x][(y+size) % h][(z+size) % depth][0] +
  world[(x+w-size) % w][y][(z+size) % depth][0] + world[x][(y+h-size) % h][(z+size) % depth][0] +
  world[(x+size) % w][(y+size) % h][(z+size) % depth][0] + world[(x+w-size) % w][(y+size) % h][(z+size) % depth][0] +
  world[(x+size) % w][(y+h-size) % h][(z+size) % depth][0] + world[(x+w-size) % w][(y+h-size) % h][(z+size) % depth][0] +
  world[(x+size) % w][y][(z+depth-size) % depth][0] + world[x][(y+size) % h][(z+depth-size) % depth][0] +
  world[(x+w-size) % w][y][(z+depth-size) % depth][0] + world[x][(y+h-size) % h][(z+depth-size) % depth][0] +
  world[(x+size) % w][(y+size) % h][(z+depth-size) % depth][0] + world[(x+w-size) % w][(y+size) % h][(z+depth-size) % depth][0] +
  world[(x+size) % w][(y+h-size) % h][(z+depth-size) % depth][0] + world[(x+w-size) % w][(y+h-size) % h][(z+depth-size) % depth][0];
}

void randomPopulate() {
for (int i=0; i<density*w*h*depth; i+=size) {
  int rW = (int)random(w);
  int rH = (int)random(h);
  int rD = (int)random(depth);
  world[rW][rH][rD][0]=1;
}
}
}

PFont f;
PeasyCam camera;
ArrayList lives;
Life life;
ManagedButtons lifeRuleSelector;
Button[] buttons = new Button[3];
ArrayList<int[]> ruleSets = new ArrayList<int[]>();

int generations;
int sf = 3;


void reset() {
int[] tempRuleSet;
if (millis() > 5000) tempRuleSet = life.ruleset;
else 				 tempRuleSet = ruleSets.get(0);
Life tempLife = new Life(sf*80, sf*80, sf*80, 0.135, 5, tempRuleSet);
life = tempLife;
generations = 0;
}

void setup() {
size(1000, 800, P3D);
frameRate(15);

f = createFont("ArcadeClassic.otf", 25);
textFont(f, 25);

initButtons();
initRules();

translate((80*sf/2), (80*sf/2), (80*sf/2));
camera = new PeasyCam(this, 400);
camera.setWheelScale(0.5);

reset();
}

void draw() {
background(0);
generations++;

rotateX(radians(45));
rotateZ(radians(45));

life.render();

drawHUD();
printArray(life.ruleset);
}

void keyPressed() {
if (keyPressed == true && key == ' ') {
// reset();
thread("reset");
}
if (keyPressed == true && key == 'c') {
life.oscillateColors = !life.oscillateColors;
}
if (keyPressed == true && (key == 's' || key == 'b')) {
life.sphereMode = !life.sphereMode;
}
}

void mousePressed() {
// for (Button b: buttons) b.poll();
lifeRuleSelector.poll();

Button activeButton = null;
for (Button b : buttons) {
if (b.state) {
  activeButton = b;
  break;
} else activeButton = null;
}

if 		(activeButton == buttons[0]) life.ruleset = ruleSets.get(0);
else if (activeButton == buttons[1]) life.ruleset = ruleSets.get(1);
else if (activeButton == buttons[2]) life.ruleset = ruleSets.get(2);
}

void drawHUD() {
camera.beginHUD();
fill(0);
//stroke(0);
rect(-30, height-130, width+100, height);
if (millis() < 17000) rect(-30, -30, width+500, 100);

lifeRuleSelector.display();
fill(255);
text("Generations: " + generations, width-150, height-60);

if (millis() < 17000) {
pushMatrix();
textSize(17);
text("drag  to  rotate.", 1000/4, 30);
text("double  click  to  reset  camera", 1000-1000/4, 30);
text("press  s  for  spheres", 125, 60);
text("press  c  to  change  colors", 375, 60);
text("press  space  to  reset  life", 625, 60);
text("press  b  for  boxes", 875, 60);
popMatrix();
}
camera.endHUD();
noFill();
}

void initButtons() {
buttons[0] = new Button(true, 100, height-50, 15, f, "4-12-13");
buttons[1] = new Button(false, 330, height-50, 15, f, "6-2-4");
buttons[2] = new Button(false, 560, height-50, 15, f, "2-3-3");
lifeRuleSelector = new ManagedButtons(buttons, true, buttons[0], 0, height-130, width-200, 130, true, color(0));
}

void initRules() {
int[] rule4_12_13 = {4, 12, 13};
int[] rule6_2_4 = {6, 2, 4};
int[] rule2_3_3 = {2, 3, 3};
ruleSets.add(rule4_12_13);
ruleSets.add(rule6_2_4);
ruleSets.add(rule2_3_3);
}

class Button {
boolean state;
float x, y, w;
String title;
PFont f;
//w should be even for it to look good
Button(boolean initialState, float x, float y, float w, PFont f, String title) {
this.x = x;
this.y = y;
this.w = w;
this.f = f;
state = initialState;
this.title = title;
}

void poll() { //must be called in mousePressed() in main program
if ((mouseX > x-w) && (mouseX < x+w) && (mouseY > y-w) && (mouseY < y+w)) {
  state = !state;
}
}

boolean hovered() {
if ((mouseX > x-w) && (mouseX < x+w) && (mouseY > y-w) && (mouseY < y+w)) {
  return true;
}
return false;
}

//default method -> black and white button
void display() {

//button box
pushMatrix();
rectMode(RADIUS);
stroke(255);
if (hovered()) {
  fill(255, 200);
} else {
  fill(255);
}
rect(x, y, w, w);
rectMode(CORNER);
popMatrix();

//draw title
pushMatrix();
fill(255);
textAlign(CENTER);
textFont(f);
text(title, x, y-(w*2));
popMatrix();

if (state) {
  pushMatrix();
  ellipseMode(CENTER);
  fill(0);
  ellipse(x, y, w, w);
  //ellipse(x+(w/2), y+(w/2), w/2, w/2);
  popMatrix();
}
}

//draws everything based on a background
void display(color scheme) {
float ir = 255 - red(scheme);
float ig = 255 - green(scheme);
float ib = 255 - blue(scheme);
color invScheme = color(ir, ig, ib);
//button box
pushMatrix();
rectMode(RADIUS);
stroke(255);
if (hovered()) {
  fill(invScheme, 200);
} else {
  fill(invScheme);
}
rect(x, y, w, w);
rectMode(CORNER);
popMatrix();

//draw title
pushMatrix();
fill(invScheme);
textAlign(CENTER);
textFont(f);
text(title, x, y-(w*2));
popMatrix();

if (state) {
  pushMatrix();
  ellipseMode(CENTER);
  fill(scheme);
  ellipse(x, y, w, w);
  //ellipse(x+(w/2), y+(w/2), w/2, w/2);
  popMatrix();
}
}
}

class ButtonController { //used to manage a set of buttons with less execuation code and create a single-true button array
Button[] buttons; //need to pass in a pre-initialized array.
boolean radioManagement;

//need to pass a pre-initialized array, pass true if you want a single-true array, pass if there if you want an on-state for launch
ButtonController(Button[] buttons, boolean s, Button defaultOn) {
this.buttons = buttons;
radioManagement = s;

for (Button b : buttons) { //normalizes buttons to a single one turned on at the start
  if (b == defaultOn) {
    b.state = true;
  } else {
    b.state = false;
  }
}
}

//use this consturctor if none or more than one should start turned on. If more than one, it should be managed when the buttons are initiailized
ButtonController(Button[] buttons, boolean s) {
this.buttons = buttons;
radioManagement = s;
}

void display() {
for (Button b : buttons) b.display();
}

void display(color scheme) {
for (Button b : buttons) b.display(scheme);
}

void poll() { //must be called in mousePressed()
if (radioManagement) { //if you want a single-true array
  Button currentActive = null;

  //find the currently-on button and assign it to currentActive
  for (Button b : buttons) {
    if (b.state == true) {
      currentActive = b;
      break;
    } else currentActive = null; //if none of them are on, itʼs null and the execution logic is not carried out
  }

  for (Button b : buttons) b.poll(); //at the end of this function, the currently-on and the newly-on button will be active

  for (Button b : buttons) {
    //if the button is not the currentActive and it is turned on (thus two+ are on) and currentActive exists then set currentActive to false
    //it no work
    if ((b != currentActive) && (b.state) && (currentActive != null)) {
      currentActive.state = false;
    }
  }
} else {
  for (Button b : buttons) b.poll();
}
}
}

class ManagedButtons extends ButtonController {
boolean opaqueBackground;
int x, y, w, h;
color scheme;

//used if you want a default-on array
ManagedButtons(Button[] buttons, boolean radioManagement, Button defaultOn, int x, int y, int w, int h, boolean o, color scheme) {
super(buttons, radioManagement, defaultOn); //inits the button controller

opaqueBackground = o;
this.scheme = scheme;

this.x = x;
this.w = w;
this.y = y;
this.h = h;

assignPositions();
}

//use if you donʼt want a default-on state
ManagedButtons(Button[] buttons, boolean radioManagement, int x, int y, int w, int h, boolean o, color scheme) {
super(buttons, radioManagement); //inits the button controller

opaqueBackground = o;
this.scheme = scheme;

this.x = x;
this.w = w;
this.y = y;
this.h = h;

assignPositions();
}

void assignPositions() {
//the width given to each button
float sliceWidth = w/buttons.length;
for (int i = 0; i < buttons.length; i++) {
  Button b = buttons[i];
  b.x = x + (i * sliceWidth) + (sliceWidth/2); //pop it ahead the number of slices appropriate, then put it in the middle of its slice
  b.y = y + (h/2); //draw it in the center of the rect height-wise
}
}

void display() {
if (opaqueBackground) { //draw the background rect
  noStroke();
  fill(scheme);
  rect(x, y, w, h);
}
super.display(scheme); //display using the color scheme
}
}