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 | ||
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 | } |