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