Send normalized values back to APC40
[SugarCubes.git] / L8onWallace.pde
CommitLineData
4861d6c4
LW
1class L8onLife extends SCPattern {
2 // Controls the rate of life algorithm ticks, in milliseconds
7782bf8e 3 private BasicParameter rateParameter = new BasicParameter("DELAY", 112.5, 0.0, 1000.0);
7234b0b8 4 // Controls the probability of a mutation in the cycleOfLife
ea4abd09 5 private BasicParameter mutationParameter = new BasicParameter("MUT", 0.000000011, 0.0, 0.1);
4861d6c4
LW
6 // Controls the saturation.
7 private BasicParameter saturationParameter = new BasicParameter("SAT", 90.0, 0.0, 100.0);
8
64ed63c7
LW
9 public final double MIN_ALIVE_PROBABILITY = 0.2;
10 public final double MAX_ALIVE_PROBABILITY = 0.9;
4861d6c4 11
ea4abd09
LW
12 public final float MAX_ALIVE_BRIGHTNESS = 90.0;
13
14 private final SawLFO cubePos = new SawLFO(0, model.cubes.size(), 4000);
2815b690
LW
15
16 class CubeState {
17 // Index of cube in glucose.model.cubes
18 public Integer index;
19 // Boolean which describes if cube is alive.
20 public boolean alive;
ea4abd09
LW
21 // Boolean which describes if strip was just changed;
22 public boolean just_changed;
23 // Current brightness
24 public float current_brightness;
2815b690
LW
25 // List of this cubes neighbors
26 public List<Integer> neighbors;
27
ea4abd09 28 public CubeState(Integer index, boolean alive, float current_brightness, List<Integer> neighbors) {
2815b690
LW
29 this.index = index;
30 this.alive = alive;
ea4abd09 31 this.current_brightness = current_brightness;
2815b690
LW
32 this.neighbors = neighbors;
33 }
34 }
35
4861d6c4
LW
36 // Contains the state of all cubes by index.
37 private List<CubeState> cube_states;
38 // Contains the amount of time since the last cycle of life.
39 private int time_since_last_run;
40 // Boolean describing if life changes were made during the current run.
41 private boolean any_changes_this_run;
64ed63c7
LW
42 // Hold the new lives
43 private List<Boolean> new_lives;
f7f0bfe5 44
4861d6c4 45 public L8onLife(GLucose glucose) {
f7f0bfe5 46 super(glucose);
4861d6c4
LW
47
48 //Print debug info about the cubes.
49 //outputCubeInfo();
50
f7f0bfe5
LW
51 initCubeStates();
52 time_since_last_run = 0;
53 any_changes_this_run = false;
64ed63c7 54 new_lives = new ArrayList<Boolean>();
4861d6c4
LW
55
56 addParameter(rateParameter);
ea4abd09 57 addParameter(mutationParameter);
61f677b8 58 addParameter(saturationParameter);
7234b0b8 59 addModulator(cubePos).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 70 for (Cube cube : model.cubes) {
2815b690 71 cube_state = this.cube_states.get(i);
64ed63c7 72
4861d6c4 73 if(shouldLightCube(cube_state)) {
ea4abd09 74 lightLiveCube(cube, cube_state, deltaMs);
f7f0bfe5 75 } else {
ea4abd09 76 lightDeadCube(cube, cube_state, deltaMs);
4861d6c4
LW
77 }
78
79 i++;
f7f0bfe5
LW
80 }
81
7234b0b8 82 if(!any_changes_this_run) {
f7f0bfe5 83 randomizeCubeStates();
64ed63c7
LW
84 } else {
85 applyNewLives();
f7f0bfe5
LW
86 }
87
4861d6c4
LW
88 if(time_since_last_run >= rateParameter.getValuef()) {
89 time_since_last_run = 0;
90 }
f7f0bfe5
LW
91 }
92
ea4abd09
LW
93 public void lightLiveCube(Cube cube, CubeState cube_state, double deltaMs) {
94 float cube_dist = LXUtils.wrapdistf((float) cube_state.index, cubePos.getValuef(), model.cubes.size());
7234b0b8 95 float hv = (cube_dist / model.cubes.size()) * 360;
ea4abd09
LW
96 float bv = cube_state.current_brightness;
97
98 if(!cube_state.just_changed || deltaMs >= rateParameter.getValuef()) {
99 float bright_prop = min(((float) time_since_last_run / rateParameter.getValuef()), 1.0);
100 bv = min(MAX_ALIVE_BRIGHTNESS, bright_prop * MAX_ALIVE_BRIGHTNESS);
101
102 if(cube_state.current_brightness < bv) {
103 cube_state.current_brightness = bv;
104 } else {
105 bv = cube_state.current_brightness;
106 }
107 }
7234b0b8 108
4861d6c4 109 for (LXPoint p : cube.points) {
4861d6c4
LW
110 colors[p.index] = lx.hsb(
111 hv,
112 saturationParameter.getValuef(),
ea4abd09 113 bv
4861d6c4
LW
114 );
115 }
116 }
117
ea4abd09
LW
118 public void lightDeadCube(Cube cube, CubeState cube_state, double deltaMs) {
119 float cube_dist = LXUtils.wrapdistf((float) cube_state.index, cubePos.getValuef(), model.cubes.size());
120 float hv = (cube_dist / (float) model.cubes.size()) * 360;
121 float bv = cube_state.current_brightness;
122
123 if(!cube_state.just_changed || deltaMs >= rateParameter.getValuef()) {
124 float bright_prop = 1.0 - min(((float) time_since_last_run / rateParameter.getValuef()), 1.0);
125 bv = max(0.0, bright_prop * MAX_ALIVE_BRIGHTNESS);
126
127 if(cube_state.current_brightness > bv) {
128 cube_state.current_brightness = bv;
129 } else {
130 bv = cube_state.current_brightness;
131 }
132 }
7234b0b8 133
4861d6c4 134 for (LXPoint p : cube.points) {
4861d6c4
LW
135 colors[p.index] = lx.hsb(
136 hv,
137 saturationParameter.getValuef(),
ea4abd09 138 bv
4861d6c4
LW
139 );
140 }
141 }
142
f7f0bfe5
LW
143 public void outputCubeInfo() {
144 int i = 0;
145 for (Cube c : model.cubes) {
146 print("Cube " + i + ": " + c.x + "," + c.y + "," + c.z + "\n");
147 ++i;
148 }
149 print("Edgeheight: " + Cube.EDGE_HEIGHT + "\n");
150 print("Edgewidth: " + Cube.EDGE_WIDTH + "\n");
151 print("Channelwidth: " + Cube.CHANNEL_WIDTH + "\n");
152 }
153
154 private void initCubeStates() {
155 List<Integer> neighbors;
156 boolean alive = false;
157 CubeState cube_state;
2815b690 158 this.cube_states = new ArrayList<CubeState>();
ea4abd09 159 float current_brightness = 0.0;
f7f0bfe5
LW
160 Integer i = 0;
161
ea4abd09 162 for (Cube c : model.cubes) {
f7f0bfe5 163 neighbors = findCubeNeighbors(c, i);
1c56d2f1 164 alive = true;
ea4abd09 165 cube_state = new CubeState(i, alive, current_brightness, neighbors);
f7f0bfe5
LW
166 this.cube_states.add(cube_state);
167 ++i;
168 }
169 }
170
4861d6c4
LW
171 private void randomizeCubeStates() {
172 double prob_range = (1.0 - MIN_ALIVE_PROBABILITY) - (1.0 - MAX_ALIVE_PROBABILITY);
173 double prob = MIN_ALIVE_PROBABILITY + (prob_range * Math.random());
1c56d2f1 174
4861d6c4
LW
175 //print("Randomizing cubes! p = " + prob + "\n");
176
177 for (CubeState cube_state: this.cube_states) {
178 cube_state.alive = (Math.random() <= prob);
f7f0bfe5
LW
179 }
180 }
181
182 public List<Integer> findCubeNeighbors(Cube cube, Integer index) {
183 List<Integer> neighbors = new LinkedList<Integer>();
184 Integer i = 0;
185
186 for (Cube c : model.cubes) {
4861d6c4
LW
187 if(index != i) {
188 if(abs(c.x - cube.x) < (Cube.EDGE_WIDTH * 2) && abs(c.y - cube.y) < (Cube.EDGE_HEIGHT * 2)) {
189 //print("Cube " + i + " is a neighbor of " + index + "\n");
190 neighbors.add(i);
191 }
f7f0bfe5
LW
192 }
193
194 i++;
195 }
196
197 return neighbors;
198 }
199
4861d6c4
LW
200 public boolean shouldLightCube(CubeState cube_state) {
201 // Respect rate parameter.
202 if(time_since_last_run < rateParameter.getValuef()) {
203 any_changes_this_run = true;
ea4abd09 204 cube_state.just_changed = false;
64ed63c7 205 return cube_state.alive;
4861d6c4 206 } else {
64ed63c7
LW
207 boolean new_life = cycleOfLife(cube_state);
208 new_lives.add(new_life);
209 return new_life;
210 }
211 }
212
213 public void applyNewLives() {
214 int index = 0;
215 for(boolean liveliness: new_lives) {
216 CubeState cube_state = this.cube_states.get(index);
217 cube_state.alive = new_lives.get(index);
218 index++;
219 }
4861d6c4
LW
220 }
221
222 public boolean cycleOfLife(CubeState cube_state) {
223 Integer index = cube_state.index;
224 Integer alive_neighbor_count = countLiveNeighbors(cube_state);
f7f0bfe5 225 boolean before_alive = cube_state.alive;
64ed63c7 226 boolean after_alive = before_alive;
7234b0b8 227 double mutation = Math.random();
4861d6c4 228
f7f0bfe5
LW
229 if(cube_state.alive) {
230 if(alive_neighbor_count < 2 || alive_neighbor_count > 3) {
64ed63c7 231 after_alive = false;
f7f0bfe5 232 } else {
64ed63c7 233 after_alive = true;
f7f0bfe5
LW
234 }
235
236 } else {
2815b690 237 if(alive_neighbor_count == 2) {
64ed63c7 238 after_alive = true;
f7f0bfe5 239 } else {
64ed63c7 240 after_alive = false;
f7f0bfe5 241 }
64ed63c7
LW
242 }
243
ea4abd09 244 if(mutation <= mutationParameter.getValuef()) {
7234b0b8
LW
245 after_alive = !after_alive;
246 }
247
64ed63c7 248 if(before_alive != after_alive) {
ea4abd09 249 cube_state.just_changed = true;
4861d6c4 250 any_changes_this_run = true;
64ed63c7
LW
251 }
252
253 return after_alive;
f7f0bfe5
LW
254 }
255
256 public Integer countLiveNeighbors(CubeState cube_state) {
257 Integer count = 0;
258 CubeState neighbor_cube_state;
259
260 for(Integer neighbor_index: cube_state.neighbors) {
261 neighbor_cube_state = this.cube_states.get(neighbor_index);
262 if(neighbor_cube_state.alive) {
263 count++;
264 }
265 }
266
267 return count;
4861d6c4 268 }
f7f0bfe5 269}
2815b690
LW
270
271class L8onAutomata extends SCPattern {
2815b690 272 // Controls the rate of life algorithm ticks, in milliseconds
7782bf8e 273 private BasicParameter rateParameter = new BasicParameter("DELAY", 75.0, 0.0, 1000.0);
ea4abd09
LW
274 // Controls the probability of a mutation in the cycleOfStripperLife
275 private BasicParameter mutationParameter = new BasicParameter("MUT", 0.000000011, 0.0, 0.1);
276 // Controls the rate of life algorithm ticks, in milliseconds
277 private BasicParameter saturationParameter = new BasicParameter("SAT", 90.0, 0.0, 100.0);
2815b690 278
ea4abd09 279 private final SawLFO pointPos = new SawLFO(0, model.points.size(), 8000);
2815b690
LW
280
281 public final double MIN_ALIVE_PROBABILITY = 0.2;
282 public final double MAX_ALIVE_PROBABILITY = 0.9;
283
ea4abd09
LW
284 public final float MAX_ALIVE_BRIGHTNESS = 90.0;
285
2815b690
LW
286 class PointState {
287 // Index of cube in glucose.model.cubes
288 public Integer index;
289 // Boolean which describes if cube is alive.
290 public boolean alive;
ea4abd09
LW
291 // Boolean which describes if strip was just changed;
292 public boolean just_changed;
293 // Current brightness
294 public float current_brightness;
2815b690 295
ea4abd09 296 public PointState(Integer index, boolean alive, float current_brightness) {
2815b690
LW
297 this.index = index;
298 this.alive = alive;
ea4abd09
LW
299 this.current_brightness = current_brightness;
300 this.just_changed = false;
2815b690
LW
301 }
302 }
303
304 // Contains the state of all cubes by index.
305 private List<PointState> point_states;
306 // Contains the amount of time since the last cycle of life.
307 private int time_since_last_run;
308 // Boolean describing if life changes were made during the current run.
309 private boolean any_changes_this_run;
310 // Hold the new lives
311 private List<Boolean> new_states;
312
313 public L8onAutomata(GLucose glucose) {
314 super(glucose);
315
316 //Print debug info about the cubes.
317 //outputCubeInfo();
318
319 initPointStates();
320 randomizePointStates();
321 time_since_last_run = 0;
322 any_changes_this_run = false;
323 new_states = new ArrayList<Boolean>();
324
ea4abd09 325 addParameter(mutationParameter);
2815b690 326 addParameter(rateParameter);
ea4abd09 327 addParameter(saturationParameter);
7234b0b8 328 addModulator(pointPos).trigger();
2815b690
LW
329 }
330
331 private void initPointStates() {
ea4abd09 332 boolean alive = true;
2815b690
LW
333 PointState point_state;
334 this.point_states = new ArrayList<PointState>();
335 Integer i = 0;
ea4abd09 336 float current_brightness = 0.0;
2815b690
LW
337
338 for (LXPoint p : model.points) {
ea4abd09 339 point_state = new PointState(i, alive, current_brightness);
2815b690
LW
340 this.point_states.add(point_state);
341 ++i;
342 }
343 }
344
345 public void run(double deltaMs) {
346 Integer i = 0;
347 PointState point_state;
348
349 any_changes_this_run = false;
350 new_states.clear();
351 time_since_last_run += deltaMs;
352
353 for (LXPoint p : model.points) {
354 point_state = this.point_states.get(i);
355
356 if(shouldLightPoint(point_state)) {
ea4abd09 357 lightLivePoint(p, point_state, deltaMs);
2815b690 358 } else {
ea4abd09 359 lightDeadPoint(p, point_state, deltaMs);
2815b690 360 }
2815b690
LW
361 i++;
362 }
363
7234b0b8 364 if(!any_changes_this_run) {
2815b690
LW
365 randomizePointStates();
366 } else {
367 applyNewStates();
368 }
369
370 if(time_since_last_run >= rateParameter.getValuef()) {
371 time_since_last_run = 0;
372 }
373 }
374
ea4abd09
LW
375 public void lightLivePoint(LXPoint p, PointState point_state, double deltaMs) {
376 float point_dist = LXUtils.wrapdistf((float) point_state.index, pointPos.getValuef(), model.points.size());
7234b0b8 377 float hv = (point_dist / model.points.size()) * 360;
ea4abd09
LW
378 float bv = point_state.current_brightness;
379
380 if(deltaMs >= rateParameter.getValuef() || !point_state.just_changed) {
381 float bright_prop = min(((float) time_since_last_run / rateParameter.getValuef()), 1.0);
382 bv = min(MAX_ALIVE_BRIGHTNESS, bright_prop * MAX_ALIVE_BRIGHTNESS);
383
384 if(point_state.current_brightness < bv) {
385 point_state.current_brightness = bv;
386 } else {
387 bv = point_state.current_brightness;
388 }
389 }
7234b0b8 390
2815b690
LW
391 colors[p.index] = lx.hsb(
392 hv,
ea4abd09
LW
393 saturationParameter.getValuef(),
394 bv
2815b690
LW
395 );
396 }
397
ea4abd09
LW
398 public void lightDeadPoint(LXPoint p, PointState point_state, double deltaMs) {
399 float point_dist = LXUtils.wrapdistf((float) point_state.index, pointPos.getValuef(), model.points.size());
400 float hv = (point_dist / model.points.size()) * 360;
401 float bv = point_state.current_brightness;
402
403 if(!point_state.just_changed || deltaMs >= rateParameter.getValuef()) {
404 float bright_prop = 1.0 - min(((float) time_since_last_run / rateParameter.getValuef()), 1.0);
405 bv = max(0.0, bright_prop * MAX_ALIVE_BRIGHTNESS);
406
407 if(point_state.current_brightness > bv) {
408 point_state.current_brightness = bv;
409 } else {
410 bv = point_state.current_brightness;
411 }
412 }
413
2815b690 414 colors[p.index] = lx.hsb(
ea4abd09
LW
415 hv,
416 saturationParameter.getValuef(),
417 bv
2815b690
LW
418 );
419 }
420
421 public boolean shouldLightPoint(PointState point_state) {
422 // Respect rate parameter.
423 if(time_since_last_run < rateParameter.getValuef()) {
424 any_changes_this_run = true;
ea4abd09 425 point_state.just_changed = false;
2815b690
LW
426 return point_state.alive;
427 } else {
428 boolean new_state = cycleOfAutomata(point_state);
429 new_states.add(new_state);
430 return new_state;
431 }
432 }
433
434 public boolean cycleOfAutomata(PointState point_state) {
435 Integer index = point_state.index;
436 Integer alive_neighbor_count = countLiveNeighbors(point_state);
437 boolean before_alive = point_state.alive;
438 boolean after_alive = before_alive;
7234b0b8 439 double mutation = Math.random();
2815b690
LW
440
441 if(point_state.alive) {
442 if(alive_neighbor_count == 1) {
443 after_alive = true;
444 } else {
445 after_alive = false;
446 }
447
448 } else {
449 if(alive_neighbor_count == 1) {
450 after_alive = true;
451 } else {
452 after_alive = false;
453 }
454 }
455
ea4abd09 456 if(mutation < mutationParameter.getValuef()) {
7234b0b8
LW
457 after_alive = !after_alive;
458 }
459
2815b690
LW
460 if(before_alive != after_alive) {
461 any_changes_this_run = true;
ea4abd09 462 point_state.just_changed = true;
2815b690
LW
463 }
464
465 return after_alive;
466 }
467
468 public int countLiveNeighbors(PointState point_state) {
2815b690 469 int count = 0;
ea4abd09
LW
470
471 if (point_state.index > 0) {
472 PointState before_neighbor = point_states.get(point_state.index - 1);
2815b690
LW
473 if(before_neighbor.alive) {
474 count++;
475 }
476 }
477
ea4abd09
LW
478 if (point_state.index < (point_states.size() - 1)) {
479 PointState after_neighbor = point_states.get(point_state.index + 1);
2815b690
LW
480 if(after_neighbor.alive) {
481 count++;
482 }
483 }
484
485 return count;
486 }
487
488 private void applyNewStates() {
489 int index = 0;
490 for(boolean new_state: new_states) {
491 PointState point_state = this.point_states.get(index);
492 point_state.alive = new_states.get(index);
493 index++;
494 }
495 }
496
497 private void randomizePointStates() {
498 double prob_range = (1.0 - MIN_ALIVE_PROBABILITY) - (1.0 - MAX_ALIVE_PROBABILITY);
499 double prob = MIN_ALIVE_PROBABILITY + (prob_range * Math.random());
500
7234b0b8 501 //print("Randomizing points! p = " + prob + "\n");
2815b690
LW
502
503 for (PointState point_state: this.point_states) {
504 point_state.alive = (Math.random() <= prob);
ea4abd09 505 point_state.just_changed = true;
2815b690
LW
506 }
507 }
7782bf8e
LW
508}
509
ea4abd09 510class L8onStripLife extends SCPattern {
7782bf8e 511 // Controls the rate of life algorithm ticks, in milliseconds
8004bace 512 private BasicParameter rateParameter = new BasicParameter("DELAY", 112.5, 1.0, 1000.0);
7234b0b8 513 // Controls the probability of a mutation in the cycleOfStripperLife
ea4abd09 514 private BasicParameter mutationParameter = new BasicParameter("MUT", 0.000000011, 0.0, 0.1);
7782bf8e
LW
515 // Controls the saturation.
516 private BasicParameter saturationParameter = new BasicParameter("SAT", 90.0, 0.0, 100.0);
517
f01b5ada 518 public final double MIN_ALIVE_PROBABILITY = 0.4;
7782bf8e
LW
519 public final double MAX_ALIVE_PROBABILITY = 0.9;
520
ea4abd09 521 public final float MAX_ALIVE_BRIGHTNESS = 90.0;
8004bace 522
ea4abd09 523 private final SawLFO stripPos = new SawLFO(0, model.strips.size(), 8000);
7782bf8e
LW
524
525 class StripState {
526 // Index of strip in glucose.model.strips
527 public Integer index;
528 // Boolean which describes if strip is alive.
529 public boolean alive;
8004bace
LW
530 // Boolean which describes if strip was just changed;
531 public boolean just_changed;
532 // Current brightness
533 public float current_brightness;
7782bf8e
LW
534 // List of this cubes neighbors
535 public List<Integer> neighbors;
536
8004bace 537 public StripState(Integer index, boolean alive, float current_brightness, List<Integer> neighbors) {
7782bf8e
LW
538 this.index = index;
539 this.alive = alive;
ea4abd09 540 this.current_brightness = current_brightness;
7782bf8e 541 this.neighbors = neighbors;
ea4abd09 542 this.just_changed = false;
7782bf8e
LW
543 }
544 }
545
546 // Contains the state of all cubes by index.
547 private List<StripState> strip_states;
548 // Contains the amount of time since the last cycle of life.
549 private int time_since_last_run;
550 // Boolean describing if life changes were made during the current run.
551 private boolean any_changes_this_run;
552 // Hold the new lives
553 private List<Boolean> new_lives;
554
ea4abd09 555 public L8onStripLife(GLucose glucose) {
7782bf8e
LW
556 super(glucose);
557
558 //Print debug info about the strips.
559 //outputStripInfo();
560
561 initStripStates();
562 randomizeStripStates();
563 time_since_last_run = 0;
564 any_changes_this_run = false;
565 new_lives = new ArrayList<Boolean>();
566
567 addParameter(rateParameter);
ea4abd09 568 addParameter(mutationParameter);
7782bf8e
LW
569 addParameter(saturationParameter);
570
7234b0b8 571 addModulator(stripPos).trigger();
7782bf8e
LW
572 }
573
574 public void run(double deltaMs) {
575 Integer i = 0;
576 StripState strip_state;
577
578 any_changes_this_run = false;
579 new_lives.clear();
580 time_since_last_run += deltaMs;
581
582 for (Strip strip : model.strips) {
583 strip_state = this.strip_states.get(i);
584
585 if(shouldLightStrip(strip_state)) {
8004bace 586 lightLiveStrip(strip, strip_state, deltaMs);
7782bf8e 587 } else {
8004bace 588 lightDeadStrip(strip, strip_state, deltaMs);
7782bf8e 589 }
7782bf8e
LW
590 i++;
591 }
592
7234b0b8 593 if(!any_changes_this_run) {
7782bf8e
LW
594 randomizeStripStates();
595 } else {
596 applyNewLives();
597 }
598
599 if(time_since_last_run >= rateParameter.getValuef()) {
600 time_since_last_run = 0;
601 }
602 }
603
8004bace 604 public void lightLiveStrip(Strip strip, StripState strip_state, double deltaMs) {
ea4abd09 605 float strip_dist = LXUtils.wrapdistf((float) strip_state.index, stripPos.getValuef(), model.strips.size());
7234b0b8 606 float hv = (strip_dist / model.strips.size()) * 360;
8004bace
LW
607 float bv = strip_state.current_brightness;
608
ea4abd09 609 if(deltaMs >= rateParameter.getValuef() || !strip_state.just_changed) {
8004bace
LW
610 float bright_prop = min(((float) time_since_last_run / rateParameter.getValuef()), 1.0);
611 bv = min(MAX_ALIVE_BRIGHTNESS, bright_prop * MAX_ALIVE_BRIGHTNESS);
612
8004bace
LW
613 if(strip_state.current_brightness < bv) {
614 strip_state.current_brightness = bv;
615 } else {
616 bv = strip_state.current_brightness;
617 }
8004bace 618 }
7234b0b8 619
7782bf8e 620 for (LXPoint p : strip.points) {
7782bf8e
LW
621 colors[p.index] = lx.hsb(
622 hv,
623 saturationParameter.getValuef(),
8004bace 624 bv
7782bf8e
LW
625 );
626 }
627 }
628
8004bace 629 public void lightDeadStrip(Strip strip, StripState strip_state, double deltaMs) {
ea4abd09
LW
630 float strip_dist = LXUtils.wrapdistf((float) strip_state.index, stripPos.getValuef(), model.strips.size());
631 float hv = (strip_dist / model.strips.size()) * 360;
632 float bv = strip_state.current_brightness;
8004bace
LW
633
634 if(!strip_state.just_changed || deltaMs >= rateParameter.getValuef()) {
635 float bright_prop = 1.0 - min(((float) time_since_last_run / rateParameter.getValuef()), 1.0);
636 bv = max(0.0, bright_prop * MAX_ALIVE_BRIGHTNESS);
637
8004bace
LW
638 if(strip_state.current_brightness > bv) {
639 strip_state.current_brightness = bv;
640 } else {
641 bv = strip_state.current_brightness;
642 }
8004bace 643 }
2815b690 644
7234b0b8 645 for (LXPoint p : strip.points) {
7782bf8e
LW
646 colors[p.index] = lx.hsb(
647 hv,
648 saturationParameter.getValuef(),
8004bace 649 bv
7782bf8e
LW
650 );
651 }
652 }
653
654 public void outputStripInfo() {
655 int i = 0;
656 for (Strip strip : model.strips) {
657 print("Strip " + i + ": " + strip.cx + "," + strip.cy + "," + strip.cz + "\n");
658 ++i;
659 }
660 }
661
662 private void initStripStates() {
663 List<Integer> neighbors;
664 boolean alive = false;
ea4abd09 665 float current_brightness = 0.0;
7782bf8e
LW
666 StripState strip_state;
667 this.strip_states = new ArrayList<StripState>();
668 Integer i = 0;
669
670 int total_neighbors = 0;
671
672 for (Strip strip : model.strips) {
673 neighbors = findStripNeighbors(strip, i);
674 alive = true;
8004bace 675 strip_state = new StripState(i, alive, current_brightness, neighbors);
7782bf8e
LW
676 this.strip_states.add(strip_state);
677
678 total_neighbors += neighbors.size();
679 ++i;
680 }
681
682 float average_neighbor_count = (float) total_neighbors / (float) model.strips.size();
683 //print("Average neighbor count: " + average_neighbor_count + "\n");
684 }
685
686 private void randomizeStripStates() {
687 double prob_range = (1.0 - MIN_ALIVE_PROBABILITY) - (1.0 - MAX_ALIVE_PROBABILITY);
688 double prob = MIN_ALIVE_PROBABILITY + (prob_range * Math.random());
689
690 //print("Randomizing strips! p = " + prob + "\n");
691
692 for (StripState strip_state : this.strip_states) {
693 strip_state.alive = (Math.random() <= prob);
ea4abd09 694 strip_state.just_changed = true;
7782bf8e
LW
695 }
696 }
697
698 public List<Integer> findStripNeighbors(Strip strip, Integer index) {
699 List<Integer> neighbors = new LinkedList<Integer>();
700 Integer i = 0;
701 int neighbor_count = 0;
702 double distance = 0.0;
703
704 for (Strip s : model.strips) {
705 if( (int)index != (int)i ) {
706 distance = Math.sqrt( Math.pow((s.cx - strip.cx), 2) + Math.pow((s.cy - strip.cy), 2) + Math.pow((s.cz - strip.cz), 2) );
707
708 if(distance < ( (double) Cube.EDGE_WIDTH) ) {
709 //print("Strip " + i + " is a neighbor of " + index + "\n");
710 neighbors.add(i);
711 }
712 }
713 i++;
714 }
715
716 return neighbors;
717 }
718
719 public boolean shouldLightStrip(StripState strip_state) {
720 // Respect rate parameter.
721 if(time_since_last_run < rateParameter.getValuef()) {
722 any_changes_this_run = true;
8004bace 723 strip_state.just_changed = false;
7782bf8e
LW
724 return strip_state.alive;
725 } else {
726 boolean new_life = cycleOfStripperLife(strip_state);
727 new_lives.add(new_life);
728 return new_life;
729 }
730 }
731
732 public void applyNewLives() {
733 int index = 0;
734 for(boolean liveliness: new_lives) {
735 StripState strip_state = this.strip_states.get(index);
736 strip_state.alive = new_lives.get(index);
737 index++;
738 }
739 }
740
741 public boolean cycleOfStripperLife(StripState strip_state) {
7782bf8e
LW
742 Integer alive_neighbor_count = countLiveNeighbors(strip_state);
743 boolean before_alive = strip_state.alive;
744 boolean after_alive = before_alive;
7234b0b8 745 double mutation = Math.random();
7782bf8e
LW
746
747 if(strip_state.alive) {
748 if(alive_neighbor_count < 2 || alive_neighbor_count > 6) {
749 after_alive = false;
750 } else {
751 after_alive = true;
752 }
753
754 } else {
755 if(alive_neighbor_count == 5) {
756 after_alive = true;
757 } else {
758 after_alive = false;
759 }
760 }
761
ea4abd09 762 if(mutation < mutationParameter.getValuef()) {
7234b0b8
LW
763 after_alive = !after_alive;
764 }
765
7782bf8e
LW
766 if(before_alive != after_alive) {
767 any_changes_this_run = true;
8004bace 768 strip_state.just_changed = true;
7782bf8e
LW
769 }
770
771 return after_alive;
772 }
773
774 public Integer countLiveNeighbors(StripState strip_state) {
775 Integer count = 0;
776 StripState neighbor_strip_state;
777
778 for(Integer neighbor_index: strip_state.neighbors) {
779 neighbor_strip_state = this.strip_states.get(neighbor_index);
780 if(neighbor_strip_state.alive) {
781 count++;
782 }
783 }
784
785 return count;
786 }
2815b690 787}