Commit | Line | Data |
---|---|---|
f7f0bfe5 | 1 | class 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 |
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 | ||
64ed63c7 LW |
26 | public final double MIN_ALIVE_PROBABILITY = 0.2; |
27 | public final double MAX_ALIVE_PROBABILITY = 0.9; | |
4861d6c4 | 28 | |
64ed63c7 | 29 | private final SinLFO xPos = new SinLFO(0, model.xMax, 4500); |
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; | |
64ed63c7 LW |
37 | // Hold the new lives |
38 | private List<Boolean> new_lives; | |
f7f0bfe5 | 39 | |
4861d6c4 | 40 | public L8onLife(GLucose glucose) { |
f7f0bfe5 | 41 | super(glucose); |
4861d6c4 LW |
42 | |
43 | //Print debug info about the cubes. | |
44 | //outputCubeInfo(); | |
45 | ||
f7f0bfe5 LW |
46 | initCubeStates(); |
47 | time_since_last_run = 0; | |
48 | any_changes_this_run = false; | |
64ed63c7 | 49 | new_lives = new ArrayList<Boolean>(); |
4861d6c4 LW |
50 | |
51 | addParameter(rateParameter); | |
52 | addParameter(randomParameter); | |
53 | addParameter(deadParameter); | |
54 | addParameter(saturationParameter); | |
f7f0bfe5 | 55 | addModulator(xPos).trigger(); |
f7f0bfe5 LW |
56 | } |
57 | ||
58 | public void run(double deltaMs) { | |
59 | Integer i = 0; | |
4861d6c4 | 60 | CubeState cube_state; |
f7f0bfe5 | 61 | |
4861d6c4 | 62 | any_changes_this_run = false; |
64ed63c7 | 63 | new_lives.clear(); |
f7f0bfe5 LW |
64 | time_since_last_run += deltaMs; |
65 | ||
4861d6c4 LW |
66 | for (Cube cube : model.cubes) { |
67 | cube_state = this.cube_states.get(i); | |
64ed63c7 | 68 | |
4861d6c4 | 69 | if(shouldLightCube(cube_state)) { |
f7f0bfe5 LW |
70 | lightLiveCube(cube); |
71 | } else { | |
72 | lightDeadCube(cube); | |
4861d6c4 LW |
73 | } |
74 | ||
75 | i++; | |
f7f0bfe5 LW |
76 | } |
77 | ||
4861d6c4 LW |
78 | boolean should_randomize_anyway = (randomParameter.getValuef() > 0.5); |
79 | if(should_randomize_anyway || !any_changes_this_run) { | |
f7f0bfe5 | 80 | randomizeCubeStates(); |
64ed63c7 LW |
81 | } else { |
82 | applyNewLives(); | |
f7f0bfe5 LW |
83 | } |
84 | ||
4861d6c4 LW |
85 | if(time_since_last_run >= rateParameter.getValuef()) { |
86 | time_since_last_run = 0; | |
87 | } | |
f7f0bfe5 LW |
88 | } |
89 | ||
4861d6c4 LW |
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 | ||
f7f0bfe5 LW |
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); | |
1c56d2f1 | 134 | alive = true; |
f7f0bfe5 LW |
135 | cube_state = new CubeState(i, alive, neighbors); |
136 | this.cube_states.add(cube_state); | |
137 | ++i; | |
138 | } | |
139 | } | |
140 | ||
4861d6c4 LW |
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()); | |
1c56d2f1 | 144 | |
4861d6c4 LW |
145 | //print("Randomizing cubes! p = " + prob + "\n"); |
146 | ||
147 | for (CubeState cube_state: this.cube_states) { | |
148 | cube_state.alive = (Math.random() <= prob); | |
f7f0bfe5 LW |
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) { | |
4861d6c4 LW |
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 | } | |
f7f0bfe5 LW |
162 | } |
163 | ||
164 | i++; | |
165 | } | |
166 | ||
167 | return neighbors; | |
168 | } | |
169 | ||
4861d6c4 LW |
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; | |
64ed63c7 | 174 | return cube_state.alive; |
4861d6c4 | 175 | } else { |
64ed63c7 LW |
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 | } | |
4861d6c4 LW |
189 | } |
190 | ||
191 | public boolean cycleOfLife(CubeState cube_state) { | |
192 | Integer index = cube_state.index; | |
193 | Integer alive_neighbor_count = countLiveNeighbors(cube_state); | |
f7f0bfe5 | 194 | boolean before_alive = cube_state.alive; |
64ed63c7 | 195 | boolean after_alive = before_alive; |
4861d6c4 | 196 | |
f7f0bfe5 LW |
197 | if(cube_state.alive) { |
198 | if(alive_neighbor_count < 2 || alive_neighbor_count > 3) { | |
64ed63c7 | 199 | after_alive = false; |
f7f0bfe5 | 200 | } else { |
64ed63c7 | 201 | after_alive = true; |
f7f0bfe5 LW |
202 | } |
203 | ||
204 | } else { | |
205 | if(alive_neighbor_count == 3) { | |
64ed63c7 | 206 | after_alive = true; |
f7f0bfe5 | 207 | } else { |
64ed63c7 | 208 | after_alive = false; |
f7f0bfe5 | 209 | } |
64ed63c7 LW |
210 | } |
211 | ||
212 | if(before_alive != after_alive) { | |
4861d6c4 | 213 | any_changes_this_run = true; |
64ed63c7 LW |
214 | } |
215 | ||
216 | return after_alive; | |
f7f0bfe5 LW |
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; | |
4861d6c4 | 231 | } |
f7f0bfe5 | 232 | } |