0eaca3add6cf8cf49acd38457296c52f8f8ed13f
[SugarCubes.git] / L8onWallace.pde
1 class CubeState {
2 // Index of cube in glucose.model.cubes
3 public Integer index;
4 // Boolean which describes if cube is alive.
5 public boolean alive;
6 // List of this cubes neighbors
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
16 class 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.2;
27 public final double MAX_ALIVE_PROBABILITY = 0.9;
28
29 private final SinLFO xPos = new SinLFO(0, model.xMax, 4500);
30
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;
37 // Hold the new lives
38 private List<Boolean> new_lives;
39
40 public L8onLife(GLucose glucose) {
41 super(glucose);
42
43 //Print debug info about the cubes.
44 //outputCubeInfo();
45
46 initCubeStates();
47 time_since_last_run = 0;
48 any_changes_this_run = false;
49 new_lives = new ArrayList<Boolean>();
50
51 addParameter(rateParameter);
52 addParameter(randomParameter);
53 addParameter(deadParameter);
54 addParameter(saturationParameter);
55 addModulator(xPos).trigger();
56 }
57
58 public void run(double deltaMs) {
59 Integer i = 0;
60 CubeState cube_state;
61
62 any_changes_this_run = false;
63 new_lives.clear();
64 time_since_last_run += deltaMs;
65
66 for (Cube cube : model.cubes) {
67 cube_state = this.cube_states.get(i);
68
69 if(shouldLightCube(cube_state)) {
70 lightLiveCube(cube);
71 } else {
72 lightDeadCube(cube);
73 }
74
75 i++;
76 }
77
78 boolean should_randomize_anyway = (randomParameter.getValuef() > 0.5);
79 if(should_randomize_anyway || !any_changes_this_run) {
80 randomizeCubeStates();
81 } else {
82 applyNewLives();
83 }
84
85 if(time_since_last_run >= rateParameter.getValuef()) {
86 time_since_last_run = 0;
87 }
88 }
89
90 public void lightLiveCube(Cube cube) {
91 for (LXPoint p : cube.points) {
92 float hv = max(0, lx.getBaseHuef() - abs(p.x - xPos.getValuef()));
93 colors[p.index] = lx.hsb(
94 hv,
95 saturationParameter.getValuef(),
96 75
97 );
98 }
99 }
100
101 public void lightDeadCube(Cube cube) {
102 for (LXPoint p : cube.points) {
103 float hv = max(0, lx.getBaseHuef() - abs(p.x - xPos.getValuef()));
104 double dead_bright = deadParameter.getValuef() * Math.random();
105
106 colors[p.index] = lx.hsb(
107 hv,
108 saturationParameter.getValuef(),
109 dead_bright
110 );
111 }
112 }
113
114 public void outputCubeInfo() {
115 int i = 0;
116 for (Cube c : model.cubes) {
117 print("Cube " + i + ": " + c.x + "," + c.y + "," + c.z + "\n");
118 ++i;
119 }
120 print("Edgeheight: " + Cube.EDGE_HEIGHT + "\n");
121 print("Edgewidth: " + Cube.EDGE_WIDTH + "\n");
122 print("Channelwidth: " + Cube.CHANNEL_WIDTH + "\n");
123 }
124
125 private void initCubeStates() {
126 List<Integer> neighbors;
127 boolean alive = false;
128 CubeState cube_state;
129 this.cube_states = new LinkedList<CubeState>();
130 Integer i = 0;
131
132 for (Cube c : model.cubes) {
133 neighbors = findCubeNeighbors(c, i);
134 alive = true;
135 cube_state = new CubeState(i, alive, neighbors);
136 this.cube_states.add(cube_state);
137 ++i;
138 }
139 }
140
141 private void randomizeCubeStates() {
142 double prob_range = (1.0 - MIN_ALIVE_PROBABILITY) - (1.0 - MAX_ALIVE_PROBABILITY);
143 double prob = MIN_ALIVE_PROBABILITY + (prob_range * Math.random());
144
145 //print("Randomizing cubes! p = " + prob + "\n");
146
147 for (CubeState cube_state: this.cube_states) {
148 cube_state.alive = (Math.random() <= prob);
149 }
150 }
151
152 public List<Integer> findCubeNeighbors(Cube cube, Integer index) {
153 List<Integer> neighbors = new LinkedList<Integer>();
154 Integer i = 0;
155
156 for (Cube c : model.cubes) {
157 if(index != i) {
158 if(abs(c.x - cube.x) < (Cube.EDGE_WIDTH * 2) && abs(c.y - cube.y) < (Cube.EDGE_HEIGHT * 2)) {
159 //print("Cube " + i + " is a neighbor of " + index + "\n");
160 neighbors.add(i);
161 }
162 }
163
164 i++;
165 }
166
167 return neighbors;
168 }
169
170 public boolean shouldLightCube(CubeState cube_state) {
171 // Respect rate parameter.
172 if(time_since_last_run < rateParameter.getValuef()) {
173 any_changes_this_run = true;
174 return cube_state.alive;
175 } else {
176 boolean new_life = cycleOfLife(cube_state);
177 new_lives.add(new_life);
178 return new_life;
179 }
180 }
181
182 public void applyNewLives() {
183 int index = 0;
184 for(boolean liveliness: new_lives) {
185 CubeState cube_state = this.cube_states.get(index);
186 cube_state.alive = new_lives.get(index);
187 index++;
188 }
189 }
190
191 public boolean cycleOfLife(CubeState cube_state) {
192 Integer index = cube_state.index;
193 Integer alive_neighbor_count = countLiveNeighbors(cube_state);
194 boolean before_alive = cube_state.alive;
195 boolean after_alive = before_alive;
196
197 if(cube_state.alive) {
198 if(alive_neighbor_count < 2 || alive_neighbor_count > 3) {
199 after_alive = false;
200 } else {
201 after_alive = true;
202 }
203
204 } else {
205 if(alive_neighbor_count == 3) {
206 after_alive = true;
207 } else {
208 after_alive = false;
209 }
210 }
211
212 if(before_alive != after_alive) {
213 any_changes_this_run = true;
214 }
215
216 return after_alive;
217 }
218
219 public Integer countLiveNeighbors(CubeState cube_state) {
220 Integer count = 0;
221 CubeState neighbor_cube_state;
222
223 for(Integer neighbor_index: cube_state.neighbors) {
224 neighbor_cube_state = this.cube_states.get(neighbor_index);
225 if(neighbor_cube_state.alive) {
226 count++;
227 }
228 }
229
230 return count;
231 }
232 }