Cool, check out the screen shot:
I needed to add a library called peasy to get the project to go.
Add this font file to the project.
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
}
}