Life Refinements
[SugarCubes.git] / L8onWallace.pde
CommitLineData
f7f0bfe5 1class CubeState {
4861d6c4 2 // Index of cube in glucose.model.cubes
f7f0bfe5 3 public Integer index;
4861d6c4 4 // Boolean which describes if cube is alive.
f7f0bfe5 5 public boolean alive;
4861d6c4 6 // List of this cubes neighbors
f7f0bfe5
LW
7 public List<Integer> neighbors;
8
9 public CubeState(Integer index, boolean alive, List<Integer> neighbors) {
10 this.index = index;
11 this.alive = alive;
12 this.neighbors = neighbors;
13 }
14}
15
4861d6c4
LW
16class L8onLife extends SCPattern {
17 // Controls the rate of life algorithm ticks, in milliseconds
18 private BasicParameter rateParameter = new BasicParameter("RATE", 122.5, 0.0, 2000.0);
19 // Controls if the cubes should be randomized even if something changes. Set above 0.5 to randomize cube aliveness.
20 private BasicParameter randomParameter = new BasicParameter("RAND", 0.0);
21 // Controls the brightness of dead cubes.
22 private BasicParameter deadParameter = new BasicParameter("DEAD", 25.0, 0.0, 100.0);
23 // Controls the saturation.
24 private BasicParameter saturationParameter = new BasicParameter("SAT", 90.0, 0.0, 100.0);
25
26 public final double MIN_ALIVE_PROBABILITY = 0.1;
27 public final double MAX_ALIVE_PROBABILITY = 0.8;
28
29 private final SinLFO xPos = new SinLFO(0, model.xMax, 3500);
f7f0bfe5 30
4861d6c4
LW
31 // Contains the state of all cubes by index.
32 private List<CubeState> cube_states;
33 // Contains the amount of time since the last cycle of life.
34 private int time_since_last_run;
35 // Boolean describing if life changes were made during the current run.
36 private boolean any_changes_this_run;
f7f0bfe5 37
4861d6c4 38 public L8onLife(GLucose glucose) {
f7f0bfe5 39 super(glucose);
4861d6c4
LW
40
41 //Print debug info about the cubes.
42 //outputCubeInfo();
43
f7f0bfe5
LW
44 initCubeStates();
45 time_since_last_run = 0;
46 any_changes_this_run = false;
4861d6c4
LW
47
48 addParameter(rateParameter);
49 addParameter(randomParameter);
50 addParameter(deadParameter);
51 addParameter(saturationParameter);
f7f0bfe5 52 addModulator(xPos).trigger();
f7f0bfe5
LW
53 }
54
55 public void run(double deltaMs) {
56 Integer i = 0;
4861d6c4 57 CubeState cube_state;
f7f0bfe5 58
4861d6c4 59 any_changes_this_run = false;
f7f0bfe5
LW
60 time_since_last_run += deltaMs;
61
4861d6c4
LW
62 for (Cube cube : model.cubes) {
63 cube_state = this.cube_states.get(i);
f7f0bfe5 64
4861d6c4 65 if(shouldLightCube(cube_state)) {
f7f0bfe5
LW
66 lightLiveCube(cube);
67 } else {
68 lightDeadCube(cube);
4861d6c4
LW
69 }
70
71 i++;
f7f0bfe5
LW
72 }
73
4861d6c4
LW
74 boolean should_randomize_anyway = (randomParameter.getValuef() > 0.5);
75 if(should_randomize_anyway || !any_changes_this_run) {
f7f0bfe5
LW
76 randomizeCubeStates();
77 }
78
4861d6c4
LW
79 if(time_since_last_run >= rateParameter.getValuef()) {
80 time_since_last_run = 0;
81 }
f7f0bfe5
LW
82 }
83
4861d6c4
LW
84 public void lightLiveCube(Cube cube) {
85 for (LXPoint p : cube.points) {
86 float hv = max(0, lx.getBaseHuef() - abs(p.x - xPos.getValuef()));
87 colors[p.index] = lx.hsb(
88 hv,
89 saturationParameter.getValuef(),
90 75
91 );
92 }
93 }
94
95 public void lightDeadCube(Cube cube) {
96 for (LXPoint p : cube.points) {
97 float hv = max(0, lx.getBaseHuef() - abs(p.x - xPos.getValuef()));
98 double dead_bright = deadParameter.getValuef() * Math.random();
99
100 colors[p.index] = lx.hsb(
101 hv,
102 saturationParameter.getValuef(),
103 dead_bright
104 );
105 }
106 }
107
f7f0bfe5
LW
108 public void outputCubeInfo() {
109 int i = 0;
110 for (Cube c : model.cubes) {
111 print("Cube " + i + ": " + c.x + "," + c.y + "," + c.z + "\n");
112 ++i;
113 }
114 print("Edgeheight: " + Cube.EDGE_HEIGHT + "\n");
115 print("Edgewidth: " + Cube.EDGE_WIDTH + "\n");
116 print("Channelwidth: " + Cube.CHANNEL_WIDTH + "\n");
117 }
118
119 private void initCubeStates() {
120 List<Integer> neighbors;
121 boolean alive = false;
122 CubeState cube_state;
123 this.cube_states = new LinkedList<CubeState>();
124 Integer i = 0;
125
126 for (Cube c : model.cubes) {
127 neighbors = findCubeNeighbors(c, i);
1c56d2f1 128 alive = true;
f7f0bfe5
LW
129 cube_state = new CubeState(i, alive, neighbors);
130 this.cube_states.add(cube_state);
131 ++i;
132 }
133 }
134
4861d6c4
LW
135 private void randomizeCubeStates() {
136 double prob_range = (1.0 - MIN_ALIVE_PROBABILITY) - (1.0 - MAX_ALIVE_PROBABILITY);
137 double prob = MIN_ALIVE_PROBABILITY + (prob_range * Math.random());
1c56d2f1 138
4861d6c4
LW
139 //print("Randomizing cubes! p = " + prob + "\n");
140
141 for (CubeState cube_state: this.cube_states) {
142 cube_state.alive = (Math.random() <= prob);
f7f0bfe5
LW
143 }
144 }
145
146 public List<Integer> findCubeNeighbors(Cube cube, Integer index) {
147 List<Integer> neighbors = new LinkedList<Integer>();
148 Integer i = 0;
149
150 for (Cube c : model.cubes) {
4861d6c4
LW
151 if(index != i) {
152 if(abs(c.x - cube.x) < (Cube.EDGE_WIDTH * 2) && abs(c.y - cube.y) < (Cube.EDGE_HEIGHT * 2)) {
153 //print("Cube " + i + " is a neighbor of " + index + "\n");
154 neighbors.add(i);
155 }
f7f0bfe5
LW
156 }
157
158 i++;
159 }
160
161 return neighbors;
162 }
163
4861d6c4
LW
164 public boolean shouldLightCube(CubeState cube_state) {
165 // Respect rate parameter.
166 if(time_since_last_run < rateParameter.getValuef()) {
167 any_changes_this_run = true;
168 return cube_state.alive;
169 } else {
170 return cycleOfLife(cube_state);
171 }
172 }
173
174 public boolean cycleOfLife(CubeState cube_state) {
175 Integer index = cube_state.index;
176 Integer alive_neighbor_count = countLiveNeighbors(cube_state);
f7f0bfe5 177 boolean before_alive = cube_state.alive;
4861d6c4 178
f7f0bfe5
LW
179 if(cube_state.alive) {
180 if(alive_neighbor_count < 2 || alive_neighbor_count > 3) {
181 cube_state.alive = false;
182 } else {
183 cube_state.alive = true;
184 }
185
186 } else {
187 if(alive_neighbor_count == 3) {
188 cube_state.alive = true;
189 } else {
4861d6c4 190 cube_state.alive = false;
f7f0bfe5
LW
191 }
192 }
193
194 this.cube_states.set(index, cube_state);
4861d6c4 195
f7f0bfe5 196 if(before_alive != cube_state.alive) {
4861d6c4 197 any_changes_this_run = true;
f7f0bfe5 198 }
4861d6c4 199
f7f0bfe5
LW
200 return cube_state.alive;
201 }
202
203 public Integer countLiveNeighbors(CubeState cube_state) {
204 Integer count = 0;
205 CubeState neighbor_cube_state;
206
207 for(Integer neighbor_index: cube_state.neighbors) {
208 neighbor_cube_state = this.cube_states.get(neighbor_index);
209 if(neighbor_cube_state.alive) {
210 count++;
211 }
212 }
213
214 return count;
4861d6c4 215 }
f7f0bfe5 216}