commit b827bb4ce6c20a12733023422e5a32a1bfc140d2 Author: Stefano Rossi Date: Thu Jul 10 03:45:41 2025 +0200 first commit diff --git a/Backups_Libraries/G4P V3.5.4.zip b/Backups_Libraries/G4P V3.5.4.zip new file mode 100644 index 0000000..2bf55d5 Binary files /dev/null and b/Backups_Libraries/G4P V3.5.4.zip differ diff --git a/Backups_Libraries/PFrame.zip b/Backups_Libraries/PFrame.zip new file mode 100644 index 0000000..98d4889 Binary files /dev/null and b/Backups_Libraries/PFrame.zip differ diff --git a/GoL3D1/GUI_BUILDER_DATA/gui.ser.2.3 b/GoL3D1/GUI_BUILDER_DATA/gui.ser.2.3 new file mode 100644 index 0000000..2a8be73 Binary files /dev/null and b/GoL3D1/GUI_BUILDER_DATA/gui.ser.2.3 differ diff --git a/GoL3D1/GoL3D1.pde b/GoL3D1/GoL3D1.pde new file mode 100644 index 0000000..fa719ab --- /dev/null +++ b/GoL3D1/GoL3D1.pde @@ -0,0 +1,155 @@ +import com.shigeodayo.pframe.*; +import g4p_controls.*; + +//gestione mouse e rotazione assi +float spostX, spostY, spostZ; //spostamento degli oggeto nel sistema di assi +int mousePressedX, mousePressedY; +float vRot; //velocità di rotazione al pixel + +float rotX, rotY; //variabili rotazione assi +float tempRotX, tempRotY; //variabili temporanee per la somma della rotazione con il drag del mouse +float vSpost; // spostamento negli assi + +float tempSpostX, tempSpostY; //spostamento temporaneo nel sistema +float zoom; //variabile per la gesione della quantità di zoom + +float scalatura; //indica la grandezza di scale() +float scaleIndex; //indica di quanto diminuire scalatura es: 0.1 --> ogni pressione dei tasti su/giu scalatura cambia di 0.1 + +float explode; +int opacita; + +int ogniTot; + +int loneliness; +int overcrowding; +int minNeight, maxNeight; +thVita thv;; + +//istanzio l'array tridimensionale di cellule +Cellula[][][] brodo; + +//lato cubo +int L = 300; +//num Cellule per lato +int numCell = 50; + +public void setup(){ + size(800, 600, P3D); + + //istanzio le alter finestre + createGUI(); + + zoom = 2; + spostX = 0; + spostY = 0; + spostZ = 0; + vSpost = 1; + + explode = 0; + opacita = 51; + + brodo = new Cellula[numCell][numCell][numCell]; + + ogniTot = 2; + + istanziaBrodo(); + + loneliness = 3; + overcrowding = 20; + minNeight = 4; + maxNeight = 15; + + thv = new thVita(); +} + +public void draw(){ + background(255);//cancella tutto + + pushMatrix(); + translate(width/2 + spostX + tempSpostX - (explode/2)*brodo.length, height/2 + spostY + tempSpostY - (explode/2)*brodo.length, -L/2 - (explode/2)*brodo.length);//mi sposto al centro dello schermo + //ruoto il disegno + rotateY(rotX + tempRotX); + rotateX(rotY + tempRotY); + scale(zoom); //imposto la proporzione di disegno + noFill(); + noStroke(); + + + for (int i = 0; i< brodo.length; i++){ + for (int j = 0; j< brodo[0].length; j++){ + for (int k = 0; k< brodo[0][0].length; k++){ + pushMatrix(); + + translate(-L/2 + (L/numCell + explode/2) * i, -L/2 + (L/numCell + explode/2) * j, -L/2 + (L/numCell + explode/2) * k);//mi sposto al centro dello schermo + + if(brodo[i][j][k].isLiving()) { + if(brodo[i][j][k].getWilllive()) fill(0,100,255, opacita); //blu trasparente + else fill(200,100,0, opacita); //blu trasparente + } + else { + noFill(); + } + + box((L/numCell)); + //sphere((L/numCell)/2); + + popMatrix(); + } + } + } + fill(255); + + scalatura = 1; + popMatrix(); + vRot = radians(0.3); //spostamento di 1 px per ogni pixel del mouse +} + +void mouseDragged() { + if (mouseButton == 39) { + tempRotX = (mouseX - mousePressedX) * vRot; + tempRotY = -(mouseY - mousePressedY) * vRot; + } + + if (mouseButton == 37) { + tempSpostX = (mouseX - mousePressedX) * vSpost; + tempSpostY = (mouseY - mousePressedY) * vSpost; + //println(spostX + " " + spostY + " " + tempSpostX + " " + tempSpostY); + } +} + +void mouseReleased() { + //salvo la nuova posizione e resetto le varibili temporanee + rotX += tempRotX; + rotY += tempRotY; + spostX += tempSpostX; + spostY += tempSpostY; + tempSpostX = 0; + tempSpostY = 0; + tempRotX = 0; + tempRotY = 0; +} + +void mouseWheel(MouseEvent event) { + if(zoom - event.getAmount() / 10 > 1) zoom -= (event.getAmount() / 10); + else if(zoom - event.getAmount() / 30 <= 1 && zoom - event.getAmount() / 30 >= 0.1) zoom -= (event.getAmount() / 30); + else zoom = 0.1; +} + +void mousePressed() { + mousePressedX = mouseX; + mousePressedY = mouseY; +} + +public void istanziaBrodo(){ + for (int i = 0; i< brodo.length; i++){ + for (int j = 0; j< brodo[0].length; j++){ + for (int k = 0; k< brodo[0][0].length; k++){ + brodo[i][j][k] = new Cellula(i, j, k); + //1 cellula su boh la faccio vivere in modo random + brodo[i][j][k].setLiving((Math.round(random(ogniTot)) == 0)?true:false); + } + } + } +} + diff --git a/GoL3D1/cellula.pde b/GoL3D1/cellula.pde new file mode 100644 index 0000000..5107b54 --- /dev/null +++ b/GoL3D1/cellula.pde @@ -0,0 +1,35 @@ +public class Cellula{ + private int x, y, z; //posizione nella matriche + private boolean living, willlive; + + + public Cellula(int x, int y, int z){ + this.x = x; + this.y = y; + this.z = z; + + living = false; + willlive = false; + } + + public boolean isLiving(){ + return living; + } + + public boolean getWilllive(){ + return willlive; + } + + public void setLiving(boolean living){ + this.living = living; + } + + public void setWilllive(boolean willlive){ + this.willlive = willlive; + } + + public int[] getCoords(){ + int[] pos = { x, y, z}; + return pos; + } +} diff --git a/GoL3D1/cellularLife.pde b/GoL3D1/cellularLife.pde new file mode 100644 index 0000000..37a13e1 --- /dev/null +++ b/GoL3D1/cellularLife.pde @@ -0,0 +1,82 @@ +//regole GoL 3D: + +// Cells (in this case, cubes) with only 1 or less neighbours die, as if by lonliness. +// If 5 cells surround an empty cell, they breed and fill it. +// If a cell has 8 or more neighbours, it dies from overcrowding. + +public void getFate(){ + int neightboor = 0; + int lastCellindex = numCell-1; + + //calcolo quante cellule vive esistono attorno a ogni cellula e ne imposto il destino + for (int i = 0; i< brodo.length; i++){ + for (int j = 0; j< brodo[0].length; j++){ + for (int k = 0; k< brodo[0][0].length; k++){ + + neightboor = calcolaVicini(i, j, k); + + //println(neightboor); + + //imposto il destino in base alle regole e al num dei vicini + + //se la cella è viva + if(brodo[i][j][k].isLiving()){ + + //muore per solitudine o sovraffollamento + if(neightboor <= loneliness || neightboor >= overcrowding) brodo[i][j][k].setWilllive(false); + + //altrimentio vive + else brodo[i][j][k].setWilllive(true); + + //se è morta + //e ha abbastanza vicini allora nasce + }else if(neightboor >= minNeight && maxNeight <= maxNeight) brodo[i][j][k].setWilllive(true); + + //altrimenti rimane morta + else brodo[i][j][k].setWilllive(false); + + neightboor = 0; + } + } + } +} + +private int calcolaVicini(int i, int j, int k){ + int neight = 0; + short[] signs = new short[3]; + + for (int l = 0; l< 3; l++){ + for (int m = 0; m< 3; m++){ + for (int n = 0; n< 3; n++){ + + if(l == 0) signs[0] = -1; + else if(l == 1) signs[0] = 0; + else signs[0] = 1; + + if(m == 0) signs[1] = -1; + else if(m == 1) signs[1] = 0; + else signs[1] = 1; + + if(n == 0) signs[2] = -1; + else if(n == 1) signs[2] = 0; + else signs[2] = 1; + + try { + if((signs[0] != 0 && signs[1] != 0 && signs[2] != 0) && brodo[i + signs[0]][j + signs[1]][k + signs[2]].isLiving()) neight++; + //println(signs[0] + " " + signs[1] + " " + signs[2]); + }catch(IndexOutOfBoundsException e){} + } + } + } + return neight; +} + +public void setFate(){ + for (int i = 0; i< brodo.length; i++){ + for (int j = 0; j< brodo[0].length; j++){ + for (int k = 0; k< brodo[0][0].length; k++){ + brodo[i][j][k].setLiving(brodo[i][j][k].getWilllive()); + } + } + } +} diff --git a/GoL3D1/gui.pde b/GoL3D1/gui.pde new file mode 100644 index 0000000..7f2f768 --- /dev/null +++ b/GoL3D1/gui.pde @@ -0,0 +1,240 @@ +/* ========================================================= + * ==== WARNING === + * ========================================================= + * The code in this tab has been generated from the GUI form + * designer and care should be taken when editing this file. + * Only add/edit code inside the event handlers i.e. only + * use lines between the matching comment tags. e.g. + + void myBtnEvents(GButton button) { //_CODE_:button1:12356: + // It is safe to enter your event code here + } //_CODE_:button1:12356: + + * Do not rename this tab! + * ========================================================= + */ + +synchronized public void win_draw1(GWinApplet appc, GWinData data) { //_CODE_:window1:677055: + appc.background(255); +} //_CODE_:window1:677055: + +public void csExpl_change1(GCustomSlider source, GEvent event) { //_CODE_:csExpl:499811: + explode = csExpl.getValueF(); +} //_CODE_:csExpl:499811: + +public void csAgg_change1(GCustomSlider source, GEvent event) { //_CODE_:csAgg:315096: + thv.setWait(round((1/csAgg.getValueF())*1000)); +} //_CODE_:csAgg:315096: + +public void pRegole_Click1(GPanel source, GEvent event) { //_CODE_:pRegole:224665: +} //_CODE_:pRegole:224665: + +public void tfLon_change1(GTextField source, GEvent event) { //_CODE_:tfLon:673356: +} //_CODE_:tfLon:673356: + +public void tfCrow_change1(GTextField source, GEvent event) { //_CODE_:tfCrow:374114: +} //_CODE_:tfCrow:374114: + +public void tfMinN_change1(GTextField source, GEvent event) { //_CODE_:tfMinN:658076: +} //_CODE_:tfMinN:658076: + +public void tfMaxN_change1(GTextField source, GEvent event) { //_CODE_:tfMaxN:314532: +} //_CODE_:tfMaxN:314532: + +public void button1_click1(GButton source, GEvent event) { //_CODE_:button1:604408: + ogniTot = int(tfOgniTot.getText()); + + loneliness = int(tfLon.getText()); + overcrowding = int(tfCrow.getText()); + minNeight = int(tfMinN.getText()); + maxNeight = int(tfMaxN.getText()); + + thv.interrupt(); + istanziaBrodo(); + thv = new thVita(); + +} //_CODE_:button1:604408: + +public void tfOgniTotchange1(GTextField source, GEvent event) { //_CODE_:tfOgniTot:934471: +} //_CODE_:tfOgniTot:934471: + +public void csOpa_change1(GCustomSlider source, GEvent event) { //_CODE_:csOpa:589219: + opacita = int(255 * csOpa.getValueI()/100); +} //_CODE_:csOpa:589219: + + + +// Create all the GUI controls. +// autogenerated do not edit +public void createGUI(){ + G4P.messagesEnabled(false); + G4P.setGlobalColorScheme(GCScheme.BLUE_SCHEME); + G4P.setCursor(ARROW); + if(frame != null) + frame.setTitle("Sketch Window"); + window1 = new GWindow(this, "Window title", 0, 0, 338, 424, false, JAVA2D); + window1.addDrawHandler(this, "win_draw1"); + lExplode = new GLabel(window1.papplet, 20, 30, 80, 20); + lExplode.setText("Explode"); + lExplode.setTextBold(); + lExplode.setOpaque(false); + csExpl = new GCustomSlider(window1.papplet, 130, 20, 190, 40, "grey_blue"); + csExpl.setShowValue(true); + csExpl.setShowLimits(true); + csExpl.setLimits(0.0, 0.0, 100.0); + csExpl.setNumberFormat(G4P.DECIMAL, 2); + csExpl.setLocalColorScheme(GCScheme.CYAN_SCHEME); + csExpl.setOpaque(false); + csExpl.addEventHandler(this, "csExpl_change1"); + lAgg = new GLabel(window1.papplet, 10, 80, 100, 30); + lAgg.setText("Aggiornamenti al secondo"); + lAgg.setTextBold(); + lAgg.setOpaque(false); + csAgg = new GCustomSlider(window1.papplet, 130, 70, 190, 50, "grey_blue"); + csAgg.setShowValue(true); + csAgg.setShowLimits(true); + csAgg.setLimits(1.0, 0.3, 5.0); + csAgg.setNbrTicks(5); + csAgg.setNumberFormat(G4P.DECIMAL, 1); + csAgg.setOpaque(false); + csAgg.addEventHandler(this, "csAgg_change1"); + pRegole = new GPanel(window1.papplet, 11, 206, 320, 210, "Regole (su 26 cellule adiacenti)"); + pRegole.setCollapsible(false); + pRegole.setDraggable(false); + pRegole.setText("Regole (su 26 cellule adiacenti)"); + pRegole.setTextBold(); + pRegole.setLocalColorScheme(GCScheme.CYAN_SCHEME); + pRegole.setOpaque(true); + pRegole.addEventHandler(this, "pRegole_Click1"); + lSeViva = new GLabel(window1.papplet, 10, 30, 80, 20); + lSeViva.setTextAlign(GAlign.LEFT, GAlign.MIDDLE); + lSeViva.setText("Se Viva:"); + lSeViva.setTextBold(); + lSeViva.setOpaque(false); + lSeMorta = new GLabel(window1.papplet, 10, 110, 80, 20); + lSeMorta.setTextAlign(GAlign.LEFT, GAlign.MIDDLE); + lSeMorta.setText("Se Morta:"); + lSeMorta.setTextBold(); + lSeMorta.setOpaque(false); + lmuoveSolit = new GLabel(window1.papplet, 10, 50, 170, 20); + lmuoveSolit.setTextAlign(GAlign.LEFT, GAlign.MIDDLE); + lmuoveSolit.setText("Muore se ha meno di"); + lmuoveSolit.setOpaque(false); + label1 = new GLabel(window1.papplet, 240, 50, 80, 20); + label1.setText("vicini"); + label1.setOpaque(false); + label2 = new GLabel(window1.papplet, 10, 70, 170, 20); + label2.setTextAlign(GAlign.LEFT, GAlign.MIDDLE); + label2.setText("Muore se ha più di"); + label2.setOpaque(false); + label3 = new GLabel(window1.papplet, 240, 70, 80, 20); + label3.setText("vicini"); + label3.setOpaque(false); + label4 = new GLabel(window1.papplet, 10, 130, 170, 20); + label4.setTextAlign(GAlign.LEFT, GAlign.MIDDLE); + label4.setText("Vive se ha almeno"); + label4.setOpaque(false); + label5 = new GLabel(window1.papplet, 240, 130, 80, 20); + label5.setText("vicini"); + label5.setOpaque(false); + label6 = new GLabel(window1.papplet, 10, 150, 170, 20); + label6.setTextAlign(GAlign.LEFT, GAlign.MIDDLE); + label6.setText("Vive se ha al massimo"); + label6.setOpaque(false); + label7 = new GLabel(window1.papplet, 240, 150, 80, 20); + label7.setText("vicini"); + label7.setOpaque(false); + tfLon = new GTextField(window1.papplet, 180, 50, 60, 20, G4P.SCROLLBARS_NONE); + tfLon.setText("3"); + tfLon.setLocalColorScheme(GCScheme.CYAN_SCHEME); + tfLon.setOpaque(true); + tfLon.addEventHandler(this, "tfLon_change1"); + tfCrow = new GTextField(window1.papplet, 180, 70, 60, 20, G4P.SCROLLBARS_NONE); + tfCrow.setText("20"); + tfCrow.setLocalColorScheme(GCScheme.CYAN_SCHEME); + tfCrow.setOpaque(true); + tfCrow.addEventHandler(this, "tfCrow_change1"); + tfMinN = new GTextField(window1.papplet, 180, 130, 60, 20, G4P.SCROLLBARS_NONE); + tfMinN.setText("4"); + tfMinN.setLocalColorScheme(GCScheme.CYAN_SCHEME); + tfMinN.setOpaque(true); + tfMinN.addEventHandler(this, "tfMinN_change1"); + tfMaxN = new GTextField(window1.papplet, 180, 150, 60, 20, G4P.SCROLLBARS_NONE); + tfMaxN.setText("15"); + tfMaxN.setLocalColorScheme(GCScheme.CYAN_SCHEME); + tfMaxN.setOpaque(true); + tfMaxN.addEventHandler(this, "tfMaxN_change1"); + button1 = new GButton(window1.papplet, 240, 180, 80, 30); + button1.setText("Riavvia simulazione"); + button1.setTextBold(); + button1.setLocalColorScheme(GCScheme.CYAN_SCHEME); + button1.addEventHandler(this, "button1_click1"); + tfOgniTot = new GTextField(window1.papplet, 180, 180, 60, 20, G4P.SCROLLBARS_NONE); + tfOgniTot.setText("2"); + tfOgniTot.setOpaque(true); + tfOgniTot.addEventHandler(this, "tfOgniTotchange1"); + label8 = new GLabel(window1.papplet, 10, 180, 170, 20); + label8.setTextAlign(GAlign.LEFT, GAlign.MIDDLE); + label8.setText("Fai vivere una cellula ogni"); + label8.setOpaque(false); + lOpac = new GLabel(window1.papplet, 10, -70, 80, 20); + lOpac.setText("Opacità"); + lOpac.setTextBold(); + lOpac.setOpaque(false); + csOpa = new GCustomSlider(window1.papplet, 120, -80, 190, 40, "grey_blue"); + csOpa.setShowValue(true); + csOpa.setShowLimits(true); + csOpa.setLimits(20.0, 0.0, 100.0); + csOpa.setNumberFormat(G4P.INTEGER, 0); + csOpa.setLocalColorScheme(GCScheme.CYAN_SCHEME); + csOpa.setOpaque(false); + csOpa.addEventHandler(this, "csOpa_change1"); + pRegole.addControl(lSeViva); + pRegole.addControl(lSeMorta); + pRegole.addControl(lmuoveSolit); + pRegole.addControl(label1); + pRegole.addControl(label2); + pRegole.addControl(label3); + pRegole.addControl(label4); + pRegole.addControl(label5); + pRegole.addControl(label6); + pRegole.addControl(label7); + pRegole.addControl(tfLon); + pRegole.addControl(tfCrow); + pRegole.addControl(tfMinN); + pRegole.addControl(tfMaxN); + pRegole.addControl(button1); + pRegole.addControl(tfOgniTot); + pRegole.addControl(label8); + pRegole.addControl(lOpac); + pRegole.addControl(csOpa); +} + +// Variable declarations +// autogenerated do not edit +GWindow window1; +GLabel lExplode; +GCustomSlider csExpl; +GLabel lAgg; +GCustomSlider csAgg; +GPanel pRegole; +GLabel lSeViva; +GLabel lSeMorta; +GLabel lmuoveSolit; +GLabel label1; +GLabel label2; +GLabel label3; +GLabel label4; +GLabel label5; +GLabel label6; +GLabel label7; +GTextField tfLon; +GTextField tfCrow; +GTextField tfMinN; +GTextField tfMaxN; +GButton button1; +GTextField tfOgniTot; +GLabel label8; +GLabel lOpac; +GCustomSlider csOpa; + diff --git a/GoL3D1/thread.pde b/GoL3D1/thread.pde new file mode 100644 index 0000000..8cd5632 --- /dev/null +++ b/GoL3D1/thread.pde @@ -0,0 +1,35 @@ +public class thVita extends Thread { + private boolean running; + private int wait; + + public thVita () { + running = true; + this.wait = round((1/csAgg.getValueF())*1000); + this.start(); + } + + boolean isRunning(){ + return this.running; + } + + public void setWait(int wait){ + this.wait = wait; + //println(wait); + } + + void run () { + //apro il socket + while (running) { + try { + //println("agg"); + getFate(); + Thread.sleep(wait); // 1/40 di secondo + setFate(); + } + catch(InterruptedException e) { + running = false; + } + } + } +} + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4002995 --- /dev/null +++ b/LICENSE @@ -0,0 +1,26 @@ +# DON'T BE A DICK PUBLIC LICENSE + +> Version 1.1, December 2016 + +> Copyright (C) 2024 Rossi Stefano + +Everyone is permitted to copy and distribute verbatim or modified +copies of this license document. + +> DON'T BE A DICK PUBLIC LICENSE +> TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +1. Do whatever you like with the original work, just don't be a dick. + + Being a dick includes - but is not limited to - the following instances: + + 1a. Outright copyright infringement - Don't just copy this and change the name. + 1b. Selling the unmodified original with no work done what-so-ever, that's REALLY being a dick. + 1c. Modifying the original work to contain hidden harmful content. That would make you a PROPER dick. + +2. If you become rich through modifications, related works/services, or supporting the original work, +share the love. Only a dick would make loads off this work and not buy the original work's +creator(s) a pint. + +3. Code is provided with no warranty. Using somebody else's code and bitching when it goes wrong makes +you a DONKEY dick. Fix the problem yourself. A non-dick would submit the fix back. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..cb8f40a --- /dev/null +++ b/README.md @@ -0,0 +1,100 @@ +# Game Of life 3D + +This is a 3D version of Conway's Game of Life. + +Always wonder how Conway's game of life would develops in a 3D environnment? now you can! + +- 3D interface (rotate, zoom, and explode) + +- Settings GUI to change GOL3D rules run-time + +- Play/Pause the simulation + +- Color based on future cell state + +It is a zero-player game, meaning that its evolution is determined by its initial state, requiring no further input. + +One interacts with the Game of Life by creating an initial configuration and observing how it evolves. + +In this version you can change the rules of the game and see the simulation in a 3D environment. + +**Note: the demo following gif takes a while to load** + +![gif](images/Gol3d.gif) + +## Running the project + +### Requirements + +- [Java 7](https://www.oracle.com/java/technologies/javase/javase7-archive-downloads.html) + +### Executable + +The project is already compiled and ready to run. You can download the executable for windows and linux here: [releases](https://gitlab.com/stefanorossiti/gameOfLife3D/-/releases) + +## Instructions + +Start the project, you can move the camera with your mouse + +![Scheme](images/3d.png) + +You can change game rules and view settings + +![Scheme](images/menu.png) + +Rules settings translation: + +If alive: + +- dies with less than `{specified}` neighbours + +- dies with more than `{specified}` neighbours + +If dead: + +- dies with more than `{specified}` neighbours + +- dies with less than `{specified}` neighbours + +## Development Requirements + +This program run with [Processing 2.2.2](https://processing.org/). Processing is a **standalone** client. + +You must use the right versions otherwise the progect wont work. + +[Processing 2.2.1](https://processing.org/releases) (search for the correct archieved version) + +### Libraries + +These 2 libraries are needed to add more views to the GUIs. + +- [G4P 3.5.4](https://sourceforge.net/projects/g4p/files/?source=navbar) (search the correct archieved version) +- [PFrame](https://github.com/shigeodayo/PFrame) + +Extract both libraries in a separate folder into sketchbook libraries folder: + +- Default libraries folder: `\Documents\Processing\libraries`. If it doesn't exist, create it. + +![alt text](images/libsPath.png) + +You can now open processing and run the project. + +### Alternative downloads + +I did a backup of the dependencies in case the original links are down. + +[Google Drive](https://drive.google.com/drive/folders/1ik1m29vXTRL1l6UIpJSqgC5Y5LQx7pbR?usp=sharing) + +And the 2 libraries are in the repository itlself in the `Backups_libraries` folder. + +### Run + +Clone the repo, install both libraries and open with processing 2.2.1 the folder `GoL3D`, press play. + +```bash +git clone https://gitlab.com/stefanorossiti/gameOfLife3D.git +``` + +## License + +Released under [DBAD](https://www.dbad-license.org/) license. diff --git a/images/3d.png b/images/3d.png new file mode 100644 index 0000000..472601b Binary files /dev/null and b/images/3d.png differ diff --git a/images/Gol3d.gif b/images/Gol3d.gif new file mode 100644 index 0000000..e3a401f Binary files /dev/null and b/images/Gol3d.gif differ diff --git a/images/libsPath.png b/images/libsPath.png new file mode 100644 index 0000000..da94c45 Binary files /dev/null and b/images/libsPath.png differ diff --git a/images/menu.png b/images/menu.png new file mode 100644 index 0000000..f8fe8c8 Binary files /dev/null and b/images/menu.png differ