GCC Code Coverage Report


Directory: ./
File: overlays/wled/FX.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 5522 6093 90.6%
Functions: 258 264 97.7%
Branches: 5801 10270 56.5%

Line Branch Exec Source
1 /*
2 WS2812FX.cpp contains all effect methods
3 Harm Aldick - 2016
4 www.aldick.org
5
6 Copyright (c) 2016 Harm Aldick
7 Licensed under the EUPL v. 1.2 or later
8 Adapted from code originally licensed under the MIT license
9
10 Modified heavily for WLED
11 */
12
13 #include <ctime>
14
15 #include "wled.h"
16 #include "FX.h"
17 #include "fcn_declare.h"
18
19 #if !(defined(WLED_DISABLE_PARTICLESYSTEM2D) && defined(WLED_DISABLE_PARTICLESYSTEM1D))
20 #include "FXparticleSystem.h"
21 #ifdef ESP8266
22 #if !defined(WLED_DISABLE_PARTICLESYSTEM2D) && !defined(WLED_DISABLE_PARTICLESYSTEM1D)
23 #error ESP8266 does not support 1D and 2D particle systems simultaneously. Please disable one of them.
24 #endif
25 #endif
26 #else
27 #define WLED_PS_DONT_REPLACE_FX
28 #endif
29
30 //////////////
31 // DEV INFO //
32 //////////////
33 /*
34 information for FX metadata strings: https://kno.wled.ge/interfaces/json-api/#effect-metadata
35
36 Audio Reactive: use the following code to pass usermod variables to effect
37
38 uint8_t *binNum = (uint8_t*)&SEGENV.aux1, *maxVol = (uint8_t*)(&SEGENV.aux1+1); // just in case assignment
39 bool samplePeak = false;
40 float FFT_MajorPeak = 1.0;
41 uint8_t *fftResult = nullptr;
42 float *fftBin = nullptr;
43 um_data_t *um_data = getAudioData();
44 volumeSmth = *(float*) um_data->u_data[0];
45 volumeRaw = *(float*) um_data->u_data[1];
46 fftResult = (uint8_t*) um_data->u_data[2];
47 samplePeak = *(uint8_t*) um_data->u_data[3];
48 FFT_MajorPeak = *(float*) um_data->u_data[4];
49 my_magnitude = *(float*) um_data->u_data[5];
50 maxVol = (uint8_t*) um_data->u_data[6]; // requires UI element (SEGMENT.customX?), changes source element
51 binNum = (uint8_t*) um_data->u_data[7]; // requires UI element (SEGMENT.customX?), changes source element
52 fftBin = (float*) um_data->u_data[8];
53 */
54
55 #define IBN 5100
56 // paletteBlend: 0 - wrap when moving, 1 - always wrap, 2 - never wrap, 3 - none (undefined)
57 #define PALETTE_SOLID_WRAP (strip().paletteBlend == 1 || strip().paletteBlend == 3)
58 #define PALETTE_MOVING_WRAP !(strip().paletteBlend == 2 || (strip().paletteBlend == 0 && SEGMENT.speed == 0))
59
60 #define indexToVStrip(index, stripNr) ((index) | (int((stripNr)+1)<<16))
61
62 // a few constants needed for AudioReactive effects
63 // for 22Khz sampling
64 #define MAX_FREQUENCY 11025 // sample frequency / 2 (as per Nyquist criterion)
65 #define MAX_FREQ_LOG10 4.04238f // log10(MAX_FREQUENCY)
66 // for 20Khz sampling
67 //#define MAX_FREQUENCY 10240
68 //#define MAX_FREQ_LOG10 4.0103f
69 // for 10Khz sampling
70 //#define MAX_FREQUENCY 5120
71 //#define MAX_FREQ_LOG10 3.71f
72
73 // effect utility functions
74 15022 uint8_t sin_gap(uint16_t in) {
75
2/2
✓ Branch 0 taken 5514 times.
✓ Branch 1 taken 9508 times.
15022 if (in & 0x100) return 0;
76 9508 return sin8_t(in + 192); // correct phase shift of sine so that it starts and stops at 0
77 }
78
79 18108 uint16_t triwave16(uint16_t in) {
80
2/2
✓ Branch 0 taken 12405 times.
✓ Branch 1 taken 5703 times.
18108 if (in < 0x8000) return in *2;
81 5703 return 0xFFFF - (in - 0x8000)*2;
82 }
83
84 /*
85 * Generates a tristate square wave w/ attac & decay
86 * @param x input value 0-255
87 * @param pulsewidth 0-127
88 * @param attdec attack & decay, max. pulsewidth / 2
89 * @returns signed waveform value
90 */
91 23 int8_t tristate_square8(uint8_t x, uint8_t pulsewidth, uint8_t attdec) {
92 23 int8_t a = 127;
93
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if (x > 127) {
94 5 a = -127;
95 5 x -= 127;
96 }
97
98
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (x < attdec) { //inc to max
99 ✗ return (int16_t) x * a / attdec;
100 }
101
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 9 times.
23 else if (x < pulsewidth - attdec) { //max
102 14 return a;
103 }
104
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 else if (x < pulsewidth) { //dec to 0
105 ✗ return (int16_t) (pulsewidth - x) * a / attdec;
106 }
107 9 return 0;
108 }
109
110 1400 static um_data_t* getAudioData() {
111 um_data_t *um_data;
112
2/4
✓ Branch 0 taken 1400 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1400 times.
✗ Branch 3 not taken.
1400 if (!UsermodManager::getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
113 // add support for no audio
114
3/6
✓ Branch 0 taken 1400 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1400 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1400 times.
✗ Branch 5 not taken.
1400 um_data = simulateSound(SEGMENT.soundSim);
115 }
116 1400 return um_data;
117 }
118
119 // effect functions
120
121 /*
122 * No blinking. Just plain old static light.
123 */
124 282 uint16_t mode_static(void) {
125 282 SEGMENT.fill(SEGCOLOR(0));
126
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 282 times.
282 return strip().isOffRefreshRequired() ? FRAMETIME : 350;
127 }
128 static const char _data_FX_MODE_STATIC[] PROGMEM = "Solid";
129
130
131 /*
132 * Blink/strobe function
133 * Alternate between color1 and color2
134 * if(strobe == true) then create a strobe effect
135 */
136 221 uint16_t blink(uint32_t color1, uint32_t color2, bool strobe, bool do_palette) {
137 221 uint32_t cycleTime = (255 - SEGMENT.speed)*20;
138 221 uint32_t onTime = FRAMETIME;
139
2/2
✓ Branch 0 taken 175 times.
✓ Branch 1 taken 46 times.
221 if (!strobe) onTime += ((cycleTime * SEGMENT.intensity) >> 8);
140 221 cycleTime += FRAMETIME*2;
141 221 uint32_t it = strip().now / cycleTime;
142 221 uint32_t rem = strip().now % cycleTime;
143
144 221 bool on = false;
145 221 if (it != SEGENV.step //new iteration, force on state for one frame, even if set time is too brief
146
6/6
✓ Branch 0 taken 192 times.
✓ Branch 1 taken 29 times.
✓ Branch 2 taken 73 times.
✓ Branch 3 taken 119 times.
✓ Branch 4 taken 102 times.
✓ Branch 5 taken 119 times.
221 || rem <= onTime) {
147 102 on = true;
148 }
149
150 221 SEGENV.step = it; //save previous iteration
151
152
2/2
✓ Branch 0 taken 102 times.
✓ Branch 1 taken 119 times.
221 uint32_t color = on ? color1 : color2;
153
4/4
✓ Branch 0 taken 102 times.
✓ Branch 1 taken 119 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 94 times.
221 if (color == color1 && do_palette)
154 {
155
2/2
✓ Branch 0 taken 3243 times.
✓ Branch 1 taken 8 times.
3251 for (unsigned i = 0; i < SEGLEN; i++) {
156
2/4
✓ Branch 0 taken 3243 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3243 times.
3243 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
157 }
158 221 } else SEGMENT.fill(color);
159
160 221 return FRAMETIME;
161 }
162
163
164 /*
165 * Normal blinking. Intensity sets duty cycle.
166 */
167 ✗ uint16_t mode_blink(void) {
168 ✗ return blink(SEGCOLOR(0), SEGCOLOR(1), false, true);
169 }
170 static const char _data_FX_MODE_BLINK[] PROGMEM = "Blink@!,Duty cycle;!,!;!;01";
171
172
173 /*
174 * Classic Blink effect. Cycling through the rainbow.
175 */
176 175 uint16_t mode_blink_rainbow(void) {
177 175 return blink(SEGMENT.color_wheel(SEGENV.call & 0xFF), SEGCOLOR(1), false, false);
178 }
179 static const char _data_FX_MODE_BLINK_RAINBOW[] PROGMEM = "Blink Rainbow@Frequency,Blink duration;!,!;!;01";
180
181
182 /*
183 * Classic Strobe effect.
184 */
185 23 uint16_t mode_strobe(void) {
186 23 return blink(SEGCOLOR(0), SEGCOLOR(1), true, true);
187 }
188 static const char _data_FX_MODE_STROBE[] PROGMEM = "Strobe@!;!,!;!;01";
189
190
191 /*
192 * Classic Strobe effect. Cycling through the rainbow.
193 */
194 23 uint16_t mode_strobe_rainbow(void) {
195 23 return blink(SEGMENT.color_wheel(SEGENV.call & 0xFF), SEGCOLOR(1), true, false);
196 }
197 static const char _data_FX_MODE_STROBE_RAINBOW[] PROGMEM = "Strobe Rainbow@!;,!;!;01";
198
199
200 /*
201 * Color wipe function
202 * LEDs are turned on (color1) in sequence, then turned off (color2) in sequence.
203 * if (bool rev == true) then LEDs are turned off in reverse order
204 */
205 92 uint16_t color_wipe(bool rev, bool useRandomColors) {
206
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 92 times.
92 if (SEGLEN <= 1) return mode_static();
207 92 uint32_t cycleTime = 750 + (255 - SEGMENT.speed)*150;
208 92 uint32_t perc = strip().now % cycleTime;
209 92 unsigned prog = (perc * 65535) / cycleTime;
210 92 bool back = (prog > 32767);
211
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 32 times.
92 if (back) {
212 60 prog -= 32767;
213
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 39 times.
60 if (SEGENV.step == 0) SEGENV.step = 1;
214 } else {
215
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 if (SEGENV.step == 2) SEGENV.step = 3; //trigger color change
216 }
217
218
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 46 times.
92 if (useRandomColors) {
219
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 36 times.
46 if (SEGENV.call == 0) {
220 10 SEGENV.aux0 = hw_random8();
221 10 SEGENV.step = 3;
222 }
223
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 39 times.
46 if (SEGENV.step == 1) { //if flag set, change to new random color
224 7 SEGENV.aux1 = get_random_wheel_index(SEGENV.aux0);
225 7 SEGENV.step = 2;
226 }
227
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 36 times.
46 if (SEGENV.step == 3) {
228 10 SEGENV.aux0 = get_random_wheel_index(SEGENV.aux1);
229 10 SEGENV.step = 0;
230 }
231 }
232
233 92 unsigned ledIndex = (prog * SEGLEN) >> 15;
234 92 uint16_t rem = (prog * SEGLEN) * 2; //mod 0xFFFF by truncating
235 92 rem /= (SEGMENT.intensity +1);
236
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 45 times.
92 if (rem > 255) rem = 255;
237
238
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 46 times.
92 uint32_t col1 = useRandomColors? SEGMENT.color_wheel(SEGENV.aux1) : SEGCOLOR(1);
239
2/2
✓ Branch 0 taken 30044 times.
✓ Branch 1 taken 92 times.
30136 for (unsigned i = 0; i < SEGLEN; i++)
240 {
241
4/4
✓ Branch 0 taken 15022 times.
✓ Branch 1 taken 15022 times.
✓ Branch 2 taken 13312 times.
✓ Branch 3 taken 1710 times.
30044 unsigned index = (rev && back)? SEGLEN -1 -i : i;
242
4/6
✓ Branch 0 taken 15022 times.
✓ Branch 1 taken 15022 times.
✓ Branch 2 taken 15022 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 15022 times.
30044 uint32_t col0 = useRandomColors? SEGMENT.color_wheel(SEGENV.aux0) : SEGMENT.color_from_palette(index, true, PALETTE_SOLID_WRAP, 0);
243
244
2/2
✓ Branch 0 taken 11899 times.
✓ Branch 1 taken 18145 times.
30044 if (i < ledIndex)
245 {
246
2/2
✓ Branch 0 taken 9913 times.
✓ Branch 1 taken 1986 times.
11899 SEGMENT.setPixelColor(index, back? col1 : col0);
247 } else
248 {
249
2/2
✓ Branch 0 taken 17187 times.
✓ Branch 1 taken 958 times.
18145 SEGMENT.setPixelColor(index, back? col0 : col1);
250
6/6
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 18053 times.
✓ Branch 2 taken 60 times.
✓ Branch 3 taken 32 times.
✓ Branch 4 taken 60 times.
✓ Branch 5 taken 32 times.
18145 if (i == ledIndex) SEGMENT.setPixelColor(index, color_blend(back? col0 : col1, back? col1 : col0, uint8_t(rem)));
251 }
252 }
253 92 return FRAMETIME;
254 }
255
256
257 /*
258 * Lights all LEDs one after another.
259 */
260 23 uint16_t mode_color_wipe(void) {
261 23 return color_wipe(false, false);
262 }
263 static const char _data_FX_MODE_COLOR_WIPE[] PROGMEM = "Wipe@!,!;!,!;!";
264
265
266 /*
267 * Lights all LEDs one after another. Turns off opposite
268 */
269 23 uint16_t mode_color_sweep(void) {
270 23 return color_wipe(true, false);
271 }
272 static const char _data_FX_MODE_COLOR_SWEEP[] PROGMEM = "Sweep@!,!;!,!;!";
273
274
275 /*
276 * Turns all LEDs after each other to a random color.
277 * Then starts over with another color.
278 */
279 23 uint16_t mode_color_wipe_random(void) {
280 23 return color_wipe(false, true);
281 }
282 static const char _data_FX_MODE_COLOR_WIPE_RANDOM[] PROGMEM = "Wipe Random@!;;!";
283
284
285 /*
286 * Random color introduced alternating from start and end of strip().
287 */
288 23 uint16_t mode_color_sweep_random(void) {
289 23 return color_wipe(true, true);
290 }
291 static const char _data_FX_MODE_COLOR_SWEEP_RANDOM[] PROGMEM = "Sweep Random@!;;!";
292
293
294 /*
295 * Lights all LEDs up in one random color. Then switches them
296 * to the next random color.
297 */
298 23 uint16_t mode_random_color(void) {
299 23 uint32_t cycleTime = 200 + (255 - SEGMENT.speed)*50;
300 23 uint32_t it = strip().now / cycleTime;
301 23 uint32_t rem = strip().now % cycleTime;
302 23 unsigned fadedur = (cycleTime * SEGMENT.intensity) >> 8;
303
304 23 uint32_t fade = 255;
305
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 4 times.
23 if (fadedur) {
306 19 fade = (rem * 255) / fadedur;
307
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 13 times.
19 if (fade > 255) fade = 255;
308 }
309
310
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if (SEGENV.call == 0) {
311 5 SEGENV.aux0 = hw_random8();
312 5 SEGENV.step = 2;
313 }
314
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 16 times.
23 if (it != SEGENV.step) //new color
315 {
316 7 SEGENV.aux1 = SEGENV.aux0;
317 7 SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0); //aux0 will store our random color wheel index
318 7 SEGENV.step = it;
319 }
320
321 23 SEGMENT.fill(color_blend(SEGMENT.color_wheel(SEGENV.aux1), SEGMENT.color_wheel(SEGENV.aux0), uint8_t(fade)));
322 23 return FRAMETIME;
323 }
324 static const char _data_FX_MODE_RANDOM_COLOR[] PROGMEM = "Random Colors@!,Fade time;;!;01";
325
326
327 /*
328 * Lights every LED in a random color. Changes all LED at the same time
329 * to new random colors.
330 */
331 46 uint16_t mode_dynamic(void) {
332
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
46 if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed
333
334
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 36 times.
46 if(SEGENV.call == 0) {
335 //SEGMENT.fill(BLACK);
336
2/2
✓ Branch 0 taken 3414 times.
✓ Branch 1 taken 10 times.
3424 for (unsigned i = 0; i < SEGLEN; i++) SEGENV.data[i] = hw_random8();
337 }
338
339 46 uint32_t cycleTime = 50 + (255 - SEGMENT.speed)*15;
340 46 uint32_t it = strip().now / cycleTime;
341
6/6
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 23 times.
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 15 times.
✓ Branch 5 taken 31 times.
46 if (it != SEGENV.step && SEGMENT.speed != 0) //new color
342 {
343
2/2
✓ Branch 0 taken 5974 times.
✓ Branch 1 taken 15 times.
5989 for (unsigned i = 0; i < SEGLEN; i++) {
344
2/2
✓ Branch 0 taken 5056 times.
✓ Branch 1 taken 918 times.
5974 if (hw_random8() <= SEGMENT.intensity) SEGENV.data[i] = hw_random8(); // random color index
345 }
346 15 SEGENV.step = it;
347 }
348
349
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 23 times.
46 if (SEGMENT.check1) {
350
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
351 7511 SEGMENT.blendPixelColor(i, SEGMENT.color_wheel(SEGENV.data[i]), 16);
352 }
353 } else {
354
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
355 7511 SEGMENT.setPixelColor(i, SEGMENT.color_wheel(SEGENV.data[i]));
356 }
357 }
358 46 return FRAMETIME;
359 }
360 static const char _data_FX_MODE_DYNAMIC[] PROGMEM = "Dynamic@!,!,,,,Smooth;;!";
361
362
363 /*
364 * effect "Dynamic" with smooth color-fading
365 */
366 23 uint16_t mode_dynamic_smooth(void) {
367 23 bool old = SEGMENT.check1;
368 23 SEGMENT.check1 = true;
369 23 mode_dynamic();
370 23 SEGMENT.check1 = old;
371 23 return FRAMETIME;
372 }
373 static const char _data_FX_MODE_DYNAMIC_SMOOTH[] PROGMEM = "Dynamic Smooth@!,!;;!";
374
375
376 /*
377 * Does the "standby-breathing" of well known i-Devices.
378 */
379 23 uint16_t mode_breath(void) {
380 23 unsigned var = 0;
381 23 unsigned counter = (strip().now * ((SEGMENT.speed >> 3) +10)) & 0xFFFFU;
382 23 counter = (counter >> 2) + (counter >> 4); //0-16384 + 0-2048
383
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 6 times.
23 if (counter < 16384) {
384
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 4 times.
17 if (counter > 8192) counter = 8192 - (counter - 8192);
385 17 var = sin16_t(counter) / 103; //close to parabolic in range 0-8192, max val. 23170
386 }
387
388 23 uint8_t lum = 30 + var;
389
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
390
2/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7511 times.
7511 SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), lum));
391 }
392
393 23 return FRAMETIME;
394 }
395 static const char _data_FX_MODE_BREATH[] PROGMEM = "Breathe@!;!,!;!;01";
396
397
398 /*
399 * Fades the LEDs between two colors
400 */
401 23 uint16_t mode_fade(void) {
402 23 unsigned counter = (strip().now * ((SEGMENT.speed >> 3) +10));
403 23 uint8_t lum = triwave16(counter) >> 8;
404
405
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
406
2/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7511 times.
7511 SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), lum));
407 }
408
409 23 return FRAMETIME;
410 }
411 static const char _data_FX_MODE_FADE[] PROGMEM = "Fade@!;!,!;!;01";
412
413
414 /*
415 * Scan mode parent function
416 */
417 46 uint16_t scan(bool dual) {
418
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
46 if (SEGLEN <= 1) return mode_static();
419 46 uint32_t cycleTime = 750 + (255 - SEGMENT.speed)*150;
420 46 uint32_t perc = strip().now % cycleTime;
421 46 int prog = (perc * 65535) / cycleTime;
422 46 int size = 1 + ((SEGMENT.intensity * SEGLEN) >> 9);
423 46 int ledIndex = (prog * ((SEGLEN *2) - size *2)) >> 16;
424
425
1/2
✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
46 if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1));
426
427 46 int led_offset = ledIndex - (SEGLEN - size);
428 46 led_offset = abs(led_offset);
429
430
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 23 times.
46 if (dual) {
431
2/2
✓ Branch 0 taken 1893 times.
✓ Branch 1 taken 23 times.
1916 for (int j = led_offset; j < led_offset + size; j++) {
432 1893 unsigned i2 = SEGLEN -1 -j;
433
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 1893 times.
✓ Branch 2 taken 1893 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1893 times.
1893 SEGMENT.setPixelColor(i2, SEGMENT.color_from_palette(i2, true, PALETTE_SOLID_WRAP, (SEGCOLOR(2))? 2:0));
434 }
435 }
436
437
2/2
✓ Branch 0 taken 3786 times.
✓ Branch 1 taken 46 times.
3832 for (int j = led_offset; j < led_offset + size; j++) {
438
2/4
✓ Branch 0 taken 3786 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3786 times.
3786 SEGMENT.setPixelColor(j, SEGMENT.color_from_palette(j, true, PALETTE_SOLID_WRAP, 0));
439 }
440
441 46 return FRAMETIME;
442 }
443
444
445 /*
446 * Runs a single pixel back and forth.
447 */
448 23 uint16_t mode_scan(void) {
449 23 return scan(false);
450 }
451 static const char _data_FX_MODE_SCAN[] PROGMEM = "Scan@!,# of dots,,,,,Overlay;!,!,!;!";
452
453
454 /*
455 * Runs two pixel back and forth in opposite directions.
456 */
457 23 uint16_t mode_dual_scan(void) {
458 23 return scan(true);
459 }
460 static const char _data_FX_MODE_DUAL_SCAN[] PROGMEM = "Scan Dual@!,# of dots,,,,,Overlay;!,!,!;!";
461
462
463 /*
464 * Cycles all LEDs at once through a rainbow.
465 */
466 51 uint16_t mode_rainbow(void) {
467 51 unsigned counter = (strip().now * ((SEGMENT.speed >> 2) +2)) & 0xFFFF;
468 51 counter = counter >> 8;
469
470
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 47 times.
51 if (SEGMENT.intensity < 128){
471 4 SEGMENT.fill(color_blend(SEGMENT.color_wheel(counter),WHITE,uint8_t(128-SEGMENT.intensity)));
472 } else {
473 47 SEGMENT.fill(SEGMENT.color_wheel(counter));
474 }
475
476 51 return FRAMETIME;
477 }
478 static const char _data_FX_MODE_RAINBOW[] PROGMEM = "Colorloop@!,Saturation;;!;01";
479
480
481 /*
482 * Cycles a rainbow over the entire string of LEDs.
483 */
484 228 uint16_t mode_rainbow_cycle(void) {
485 228 unsigned counter = (strip().now * ((SEGMENT.speed >> 2) +2)) & 0xFFFF;
486 228 counter = counter >> 8;
487
488
2/2
✓ Branch 0 taken 104617 times.
✓ Branch 1 taken 228 times.
104845 for (unsigned i = 0; i < SEGLEN; i++) {
489 //intensity/29 = 0 (1/16) 1 (1/8) 2 (1/4) 3 (1/2) 4 (1) 5 (2) 6 (4) 7 (8) 8 (16)
490 104617 uint8_t index = (i * (16 << (SEGMENT.intensity /29)) / SEGLEN) + counter;
491 104617 SEGMENT.setPixelColor(i, SEGMENT.color_wheel(index));
492 }
493
494 228 return FRAMETIME;
495 }
496 static const char _data_FX_MODE_RAINBOW_CYCLE[] PROGMEM = "Rainbow@!,Size;;!";
497
498
499 /*
500 * Alternating pixels running function.
501 */
502 69 static uint16_t running(uint32_t color1, uint32_t color2, bool theatre = false) {
503
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 23 times.
69 int width = (theatre ? 3 : 1) + (SEGMENT.intensity >> 4); // window
504 69 uint32_t cycleTime = 50 + (255 - SEGMENT.speed);
505 69 uint32_t it = strip().now / cycleTime;
506 69 bool usePalette = color1 == SEGCOLOR(0);
507
508
2/2
✓ Branch 0 taken 22533 times.
✓ Branch 1 taken 69 times.
22602 for (unsigned i = 0; i < SEGLEN; i++) {
509 22533 uint32_t col = color2;
510
4/6
✓ Branch 0 taken 15022 times.
✓ Branch 1 taken 7511 times.
✓ Branch 2 taken 15022 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 15022 times.
22533 if (usePalette) color1 = SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0);
511
2/2
✓ Branch 0 taken 15022 times.
✓ Branch 1 taken 7511 times.
22533 if (theatre) {
512
2/2
✓ Branch 0 taken 2228 times.
✓ Branch 1 taken 12794 times.
15022 if ((i % width) == SEGENV.aux0) col = color1;
513 } else {
514 7511 int pos = (i % (width<<1));
515
7/8
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6937 times.
✓ Branch 3 taken 574 times.
✓ Branch 4 taken 3782 times.
✓ Branch 5 taken 3155 times.
✓ Branch 6 taken 3782 times.
✓ Branch 7 taken 3729 times.
7511 if ((pos < SEGENV.aux0-width) || ((pos >= SEGENV.aux0) && (pos < SEGENV.aux0+width))) col = color1;
516 }
517 22533 SEGMENT.setPixelColor(i,col);
518 }
519
520
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 34 times.
69 if (it != SEGENV.step) {
521
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 12 times.
35 SEGENV.aux0 = (SEGENV.aux0 +1) % (theatre ? width : (width<<1));
522 35 SEGENV.step = it;
523 }
524 69 return FRAMETIME;
525 }
526
527
528 /*
529 * Theatre-style crawling lights.
530 * Inspired by the Adafruit examples.
531 */
532 23 uint16_t mode_theater_chase(void) {
533 23 return running(SEGCOLOR(0), SEGCOLOR(1), true);
534 }
535 static const char _data_FX_MODE_THEATER_CHASE[] PROGMEM = "Theater@!,Gap size;!,!;!";
536
537
538 /*
539 * Theatre-style crawling lights with rainbow effect.
540 * Inspired by the Adafruit examples.
541 */
542 23 uint16_t mode_theater_chase_rainbow(void) {
543 23 return running(SEGMENT.color_wheel(SEGENV.step), SEGCOLOR(1), true);
544 }
545 static const char _data_FX_MODE_THEATER_CHASE_RAINBOW[] PROGMEM = "Theater Rainbow@!,Gap size;,!;!";
546
547
548 /*
549 * Running lights effect with smooth sine transition base.
550 */
551 69 static uint16_t running_base(bool saw, bool dual=false) {
552 69 unsigned x_scale = SEGMENT.intensity >> 2;
553 69 uint32_t counter = (strip().now * SEGMENT.speed) >> 9;
554
555
2/2
✓ Branch 0 taken 22533 times.
✓ Branch 1 taken 69 times.
22602 for (unsigned i = 0; i < SEGLEN; i++) {
556 22533 unsigned a = i*x_scale - counter;
557
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 15022 times.
22533 if (saw) {
558 7511 a &= 0xFF;
559
2/2
✓ Branch 0 taken 2366 times.
✓ Branch 1 taken 5145 times.
7511 if (a < 16)
560 {
561 2366 a = 192 + a*8;
562 } else {
563 5145 a = map(a,16,255,64,192);
564 }
565 7511 a = 255 - a;
566 }
567
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 15022 times.
22533 uint8_t s = dual ? sin_gap(a) : sin8_t(a);
568
2/4
✓ Branch 0 taken 22533 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 22533 times.
22533 uint32_t ca = color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), s);
569
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 15022 times.
22533 if (dual) {
570 7511 unsigned b = (SEGLEN-1-i)*x_scale - counter;
571 7511 uint8_t t = sin_gap(b);
572
2/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7511 times.
7511 uint32_t cb = color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 2), t);
573 7511 ca = color_blend(ca, cb, uint8_t(127));
574 }
575 22533 SEGMENT.setPixelColor(i, ca);
576 }
577
578 69 return FRAMETIME;
579 }
580
581
582 /*
583 * Running lights in opposite directions.
584 * Idea: Make the gap width controllable with a third slider in the future
585 */
586 23 uint16_t mode_running_dual(void) {
587 23 return running_base(false, true);
588 }
589 static const char _data_FX_MODE_RUNNING_DUAL[] PROGMEM = "Running Dual@!,Wave width;L,!,R;!";
590
591
592 /*
593 * Running lights effect with smooth sine transition.
594 */
595 23 uint16_t mode_running_lights(void) {
596 23 return running_base(false);
597 }
598 static const char _data_FX_MODE_RUNNING_LIGHTS[] PROGMEM = "Running@!,Wave width;!,!;!";
599
600
601 /*
602 * Running lights effect with sawtooth transition.
603 */
604 23 uint16_t mode_saw(void) {
605 23 return running_base(true);
606 }
607 static const char _data_FX_MODE_SAW[] PROGMEM = "Saw@!,Width;!,!;!";
608
609
610 /*
611 * Blink several LEDs in random colors on, reset, repeat.
612 * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
613 */
614 23 uint16_t mode_twinkle(void) {
615 23 SEGMENT.fade_out(224);
616
617 23 uint32_t cycleTime = 20 + (255 - SEGMENT.speed)*5;
618 23 uint32_t it = strip().now / cycleTime;
619
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 15 times.
23 if (it != SEGENV.step)
620 {
621 8 unsigned maxOn = map(SEGMENT.intensity, 0, 255, 1, SEGLEN); // make sure at least one LED is on
622
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (SEGENV.aux0 >= maxOn)
623 {
624 ✗ SEGENV.aux0 = 0;
625 ✗ SEGENV.aux1 = hw_random(); //new seed for our PRNG
626 }
627 8 SEGENV.aux0++;
628 8 SEGENV.step = it;
629 }
630
631 23 unsigned PRNG16 = SEGENV.aux1;
632
633
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 23 times.
52 for (unsigned i = 0; i < SEGENV.aux0; i++)
634 {
635 29 PRNG16 = (uint16_t)(PRNG16 * 2053) + 13849; // next 'random' number
636 29 uint32_t p = (uint32_t)SEGLEN * (uint32_t)PRNG16;
637 29 unsigned j = p >> 16;
638
2/4
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 29 times.
29 SEGMENT.setPixelColor(j, SEGMENT.color_from_palette(j, true, PALETTE_SOLID_WRAP, 0));
639 }
640
641 23 return FRAMETIME;
642 }
643 static const char _data_FX_MODE_TWINKLE[] PROGMEM = "Twinkle@!,!;!,!;!;;m12=0"; //pixels
644
645
646 /*
647 * Dissolve function
648 */
649 46 uint16_t dissolve(uint32_t color) {
650 46 unsigned dataSize = sizeof(uint32_t) * SEGLEN;
651
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
46 if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
652 46 uint32_t* pixels = reinterpret_cast<uint32_t*>(SEGENV.data);
653
654
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 36 times.
46 if (SEGENV.call == 0) {
655
2/2
✓ Branch 0 taken 3414 times.
✓ Branch 1 taken 10 times.
3424 for (unsigned i = 0; i < SEGLEN; i++) pixels[i] = SEGCOLOR(1);
656 10 SEGENV.aux0 = 1;
657 }
658
659
2/2
✓ Branch 0 taken 1040 times.
✓ Branch 1 taken 46 times.
1086 for (unsigned j = 0; j <= SEGLEN / 15; j++) {
660
2/2
✓ Branch 0 taken 512 times.
✓ Branch 1 taken 528 times.
1040 if (hw_random8() <= SEGMENT.intensity) {
661
2/2
✓ Branch 0 taken 4006 times.
✓ Branch 1 taken 388 times.
4394 for (size_t times = 0; times < 10; times++) { //attempt to spawn a new pixel 10 times
662 4006 unsigned i = hw_random16(SEGLEN);
663
1/2
✓ Branch 0 taken 4006 times.
✗ Branch 1 not taken.
4006 if (SEGENV.aux0) { //dissolve to primary/palette
664
2/2
✓ Branch 0 taken 124 times.
✓ Branch 1 taken 3882 times.
4006 if (pixels[i] == SEGCOLOR(1)) {
665
4/6
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 61 times.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 63 times.
124 pixels[i] = color == SEGCOLOR(0) ? SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0) : color;
666 124 break; //only spawn 1 new pixel per frame per 50 LEDs
667 }
668 } else { //dissolve to secondary
669 ✗ if (pixels[i] != SEGCOLOR(1)) {
670 ✗ pixels[i] = SEGCOLOR(1);
671 ✗ break;
672 }
673 }
674 }
675 }
676 }
677 // fix for #4401
678
2/2
✓ Branch 0 taken 15022 times.
✓ Branch 1 taken 46 times.
15068 for (unsigned i = 0; i < SEGLEN; i++) SEGMENT.setPixelColor(i, pixels[i]);
679
680
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
46 if (SEGENV.step > (255 - SEGMENT.speed) + 15U) {
681 ✗ SEGENV.aux0 = !SEGENV.aux0;
682 ✗ SEGENV.step = 0;
683 } else {
684 46 SEGENV.step++;
685 }
686
687 46 return FRAMETIME;
688 }
689
690
691 /*
692 * Blink several LEDs on and then off
693 */
694 23 uint16_t mode_dissolve(void) {
695
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 return dissolve(SEGMENT.check1 ? SEGMENT.color_wheel(hw_random8()) : SEGCOLOR(0));
696 }
697 static const char _data_FX_MODE_DISSOLVE[] PROGMEM = "Dissolve@Repeat speed,Dissolve speed,,,,Random;!,!;!";
698
699
700 /*
701 * Blink several LEDs on and then off in random colors
702 */
703 23 uint16_t mode_dissolve_random(void) {
704 23 return dissolve(SEGMENT.color_wheel(hw_random8()));
705 }
706 static const char _data_FX_MODE_DISSOLVE_RANDOM[] PROGMEM = "Dissolve Rnd@Repeat speed,Dissolve speed;,!;!";
707
708
709 /*
710 * Blinks one LED at a time.
711 * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
712 */
713 23 uint16_t mode_sparkle(void) {
714
3/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7511 times.
✓ Branch 3 taken 23 times.
7534 if (!SEGMENT.check2) for (unsigned i = 0; i < SEGLEN; i++) {
715
2/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7511 times.
7511 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1));
716 }
717 23 uint32_t cycleTime = 10 + (255 - SEGMENT.speed)*2;
718 23 uint32_t it = strip().now / cycleTime;
719
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 13 times.
23 if (it != SEGENV.step)
720 {
721 10 SEGENV.aux0 = hw_random16(SEGLEN); // aux0 stores the random led index
722 10 SEGENV.step = it;
723 }
724
725 23 SEGMENT.setPixelColor(SEGENV.aux0, SEGCOLOR(0));
726 23 return FRAMETIME;
727 }
728 static const char _data_FX_MODE_SPARKLE[] PROGMEM = "Sparkle@!,,,,,,Overlay;!,!;!;;m12=0";
729
730
731 /*
732 * Lights all LEDs in the color. Flashes single col 1 pixels randomly. (List name: Sparkle Dark)
733 * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
734 */
735 23 uint16_t mode_flash_sparkle(void) {
736
3/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7511 times.
✓ Branch 3 taken 23 times.
7534 if (!SEGMENT.check2) for (unsigned i = 0; i < SEGLEN; i++) {
737
2/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7511 times.
7511 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
738 }
739
740
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 12 times.
23 if (strip().now - SEGENV.aux0 > SEGENV.step) {
741
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 7 times.
11 if(hw_random8((255-SEGMENT.intensity) >> 4) == 0) {
742 4 SEGMENT.setPixelColor(hw_random16(SEGLEN), SEGCOLOR(1)); //flash
743 }
744 11 SEGENV.step = strip().now;
745 11 SEGENV.aux0 = 255-SEGMENT.speed;
746 }
747 23 return FRAMETIME;
748 }
749 static const char _data_FX_MODE_FLASH_SPARKLE[] PROGMEM = "Sparkle Dark@!,!,,,,,Overlay;Bg,Fx;!;;m12=0";
750
751
752 /*
753 * Like flash sparkle. With more flash.
754 * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
755 */
756 23 uint16_t mode_hyper_sparkle(void) {
757
3/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7511 times.
✓ Branch 3 taken 23 times.
7534 if (!SEGMENT.check2) for (unsigned i = 0; i < SEGLEN; i++) {
758
2/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7511 times.
7511 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
759 }
760
761
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 12 times.
23 if (strip().now - SEGENV.aux0 > SEGENV.step) {
762
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 6 times.
11 if (hw_random8((255-SEGMENT.intensity) >> 4) == 0) {
763 5 int len = max(1, (int)SEGLEN/3);
764
2/2
✓ Branch 0 taken 687 times.
✓ Branch 1 taken 5 times.
692 for (int i = 0; i < len; i++) {
765 687 SEGMENT.setPixelColor(hw_random16(SEGLEN), SEGCOLOR(1));
766 }
767 }
768 11 SEGENV.step = strip().now;
769 11 SEGENV.aux0 = 255-SEGMENT.speed;
770 }
771 23 return FRAMETIME;
772 }
773 static const char _data_FX_MODE_HYPER_SPARKLE[] PROGMEM = "Sparkle+@!,!,,,,,Overlay;Bg,Fx;!;;m12=0";
774
775
776 /*
777 * Strobe effect with different strobe count and pause, controlled by speed.
778 */
779 23 uint16_t mode_multi_strobe(void) {
780
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
781
2/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7511 times.
7511 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1));
782 }
783
784 23 SEGENV.aux0 = 50 + 20*(uint16_t)(255-SEGMENT.speed);
785 23 unsigned count = 2 * ((SEGMENT.intensity / 10) + 1);
786
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 1 times.
23 if(SEGENV.aux1 < count) {
787
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 13 times.
22 if((SEGENV.aux1 & 1) == 0) {
788 9 SEGMENT.fill(SEGCOLOR(0));
789 9 SEGENV.aux0 = 15;
790 } else {
791 13 SEGENV.aux0 = 50;
792 }
793 }
794
795
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 7 times.
23 if (strip().now - SEGENV.aux0 > SEGENV.step) {
796 16 SEGENV.aux1++;
797
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (SEGENV.aux1 > count) SEGENV.aux1 = 0;
798 16 SEGENV.step = strip().now;
799 }
800
801 23 return FRAMETIME;
802 }
803 static const char _data_FX_MODE_MULTI_STROBE[] PROGMEM = "Strobe Mega@!,!;!,!;!;01";
804
805
806 /*
807 * Android loading circle
808 */
809 56 uint16_t mode_android(void) {
810
811
2/2
✓ Branch 0 taken 1138 times.
✓ Branch 1 taken 56 times.
1194 for (unsigned i = 0; i < SEGLEN; i++) {
812
2/4
✓ Branch 0 taken 1138 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1138 times.
1138 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1));
813 }
814
815
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 43 times.
56 if (SEGENV.aux1 > (SEGMENT.intensity*SEGLEN)/255)
816 {
817 13 SEGENV.aux0 = 1;
818 } else
819 {
820
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 14 times.
43 if (SEGENV.aux1 < 2) SEGENV.aux0 = 0;
821 }
822
823 56 unsigned a = SEGENV.step & 0xFFFFU;
824
825
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 17 times.
56 if (SEGENV.aux0 == 0)
826 {
827
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 27 times.
39 if (SEGENV.call %3 == 1) {a++;}
828 27 else {SEGENV.aux1++;}
829 } else
830 {
831 17 a++;
832
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 6 times.
17 if (SEGENV.call %3 != 1) SEGENV.aux1--;
833 }
834
835
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 52 times.
56 if (a >= SEGLEN) a = 0;
836
837
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 11 times.
56 if (a + SEGENV.aux1 < SEGLEN)
838 {
839
2/2
✓ Branch 0 taken 74 times.
✓ Branch 1 taken 45 times.
119 for (unsigned i = a; i < a+SEGENV.aux1; i++) {
840 74 SEGMENT.setPixelColor(i, SEGCOLOR(0));
841 }
842 } else
843 {
844
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 11 times.
29 for (unsigned i = a; i < SEGLEN; i++) {
845 18 SEGMENT.setPixelColor(i, SEGCOLOR(0));
846 }
847
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 11 times.
14 for (unsigned i = 0; i < SEGENV.aux1 - (SEGLEN -a); i++) {
848 3 SEGMENT.setPixelColor(i, SEGCOLOR(0));
849 }
850 }
851 56 SEGENV.step = a;
852
853 56 return 3 + ((8 * (uint32_t)(255 - SEGMENT.speed)) / SEGLEN);
854 }
855 static const char _data_FX_MODE_ANDROID[] PROGMEM = "Android@!,Width;!,!;!;;m12=1"; //vertical
856
857
858 /*
859 * color chase function.
860 * color1 = background color
861 * color2 and color3 = colors of two adjacent leds
862 */
863 92 static uint16_t chase(uint32_t color1, uint32_t color2, uint32_t color3, bool do_palette) {
864 92 uint16_t counter = strip().now * ((SEGMENT.speed >> 2) + 1);
865 92 uint16_t a = (counter * SEGLEN) >> 16;
866
867 92 bool chase_random = (SEGMENT.mode == FX_MODE_CHASE_RANDOM);
868
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 69 times.
92 if (chase_random) {
869
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 22 times.
23 if (a < SEGENV.step) //we hit the start again, choose new color for Chase random
870 {
871 1 SEGENV.aux1 = SEGENV.aux0; //store previous random color
872 1 SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0);
873 }
874 23 color1 = SEGMENT.color_wheel(SEGENV.aux0);
875 }
876 92 SEGENV.step = a;
877
878 // Use intensity setting to vary chase up to 1/2 string length
879 92 unsigned size = 1 + ((SEGMENT.intensity * SEGLEN) >> 10);
880
881 92 uint16_t b = a + size; //"trail" of chase, filled with color1
882
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 77 times.
92 if (b > SEGLEN) b -= SEGLEN;
883 92 uint16_t c = b + size;
884
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 81 times.
92 if (c > SEGLEN) c -= SEGLEN;
885
886 //background
887
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 69 times.
92 if (do_palette)
888 {
889
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
890
2/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7511 times.
7511 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1));
891 }
892 69 } else SEGMENT.fill(color1);
893
894 //if random, fill old background between a and end
895
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 69 times.
92 if (chase_random)
896 {
897 23 color1 = SEGMENT.color_wheel(SEGENV.aux1);
898
2/2
✓ Branch 0 taken 3952 times.
✓ Branch 1 taken 23 times.
3975 for (unsigned i = a; i < SEGLEN; i++)
899 3952 SEGMENT.setPixelColor(i, color1);
900 }
901
902 //fill between points a and b with color2
903
2/2
✓ Branch 0 taken 77 times.
✓ Branch 1 taken 15 times.
92 if (a < b)
904 {
905
2/2
✓ Branch 0 taken 3181 times.
✓ Branch 1 taken 77 times.
3258 for (unsigned i = a; i < b; i++)
906 3181 SEGMENT.setPixelColor(i, color2);
907 } else {
908
2/2
✓ Branch 0 taken 265 times.
✓ Branch 1 taken 15 times.
280 for (unsigned i = a; i < SEGLEN; i++) //fill until end
909 265 SEGMENT.setPixelColor(i, color2);
910
2/2
✓ Branch 0 taken 358 times.
✓ Branch 1 taken 15 times.
373 for (unsigned i = 0; i < b; i++) //fill from start until b
911 358 SEGMENT.setPixelColor(i, color2);
912 }
913
914 //fill between points b and c with color2
915
2/2
✓ Branch 0 taken 81 times.
✓ Branch 1 taken 11 times.
92 if (b < c)
916 {
917
2/2
✓ Branch 0 taken 3020 times.
✓ Branch 1 taken 81 times.
3101 for (unsigned i = b; i < c; i++)
918 3020 SEGMENT.setPixelColor(i, color3);
919 } else {
920
2/2
✓ Branch 0 taken 438 times.
✓ Branch 1 taken 11 times.
449 for (unsigned i = b; i < SEGLEN; i++) //fill until end
921 438 SEGMENT.setPixelColor(i, color3);
922
2/2
✓ Branch 0 taken 346 times.
✓ Branch 1 taken 11 times.
357 for (unsigned i = 0; i < c; i++) //fill from start until c
923 346 SEGMENT.setPixelColor(i, color3);
924 }
925
926 92 return FRAMETIME;
927 }
928
929
930 /*
931 * Bicolor chase, more primary color.
932 */
933 23 uint16_t mode_chase_color(void) {
934
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 return chase(SEGCOLOR(1), (SEGCOLOR(2)) ? SEGCOLOR(2) : SEGCOLOR(0), SEGCOLOR(0), true);
935 }
936 static const char _data_FX_MODE_CHASE_COLOR[] PROGMEM = "Chase@!,Width;!,!,!;!";
937
938
939 /*
940 * Primary running followed by random color.
941 */
942 23 uint16_t mode_chase_random(void) {
943
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 return chase(SEGCOLOR(1), (SEGCOLOR(2)) ? SEGCOLOR(2) : SEGCOLOR(0), SEGCOLOR(0), false);
944 }
945 static const char _data_FX_MODE_CHASE_RANDOM[] PROGMEM = "Chase Random@!,Width;!,,!;!";
946
947
948 /*
949 * Primary, secondary running on rainbow.
950 */
951 23 uint16_t mode_chase_rainbow(void) {
952 23 unsigned color_sep = 256 / SEGLEN;
953
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 10 times.
23 if (color_sep == 0) color_sep = 1; // correction for segments longer than 256 LEDs
954 23 unsigned color_index = SEGENV.call & 0xFF;
955 23 uint32_t color = SEGMENT.color_wheel(((SEGENV.step * color_sep) + color_index) & 0xFF);
956
957 23 return chase(color, SEGCOLOR(0), SEGCOLOR(1), false);
958 }
959 static const char _data_FX_MODE_CHASE_RAINBOW[] PROGMEM = "Chase Rainbow@!,Width;!,!;!";
960
961
962 /*
963 * Primary running on rainbow.
964 */
965 23 uint16_t mode_chase_rainbow_white(void) {
966 23 uint16_t n = SEGENV.step;
967 23 uint16_t m = (SEGENV.step + 1) % SEGLEN;
968 23 uint32_t color2 = SEGMENT.color_wheel(((n * 256 / SEGLEN) + (SEGENV.call & 0xFF)) & 0xFF);
969 23 uint32_t color3 = SEGMENT.color_wheel(((m * 256 / SEGLEN) + (SEGENV.call & 0xFF)) & 0xFF);
970
971 23 return chase(SEGCOLOR(0), color2, color3, false);
972 }
973 static const char _data_FX_MODE_CHASE_RAINBOW_WHITE[] PROGMEM = "Rainbow Runner@!,Size;Bg;!";
974
975
976 /*
977 * Red - Amber - Green - Blue lights running
978 */
979 23 uint16_t mode_colorful(void) {
980 23 unsigned numColors = 4; //3, 4, or 5
981 23 uint32_t cols[9]{0x00FF0000,0x00EEBB00,0x0000EE00,0x000077CC};
982
9/14
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 19 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 19 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 19 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 19 times.
✓ Branch 12 taken 4 times.
✓ Branch 13 taken 19 times.
23 if (SEGMENT.intensity > 160 || SEGMENT.palette) { //palette or color
983
3/6
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 if (!SEGMENT.palette) {
984 4 numColors = 3;
985
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 4 times.
16 for (size_t i = 0; i < 3; i++) cols[i] = SEGCOLOR(i);
986 } else {
987 ✗ unsigned fac = 80;
988 ✗ if (SEGMENT.palette == 52) {numColors = 5; fac = 61;} //C9 2 has 5 colors
989 ✗ for (size_t i = 0; i < numColors; i++) {
990 ✗ cols[i] = SEGMENT.color_from_palette(i*fac, false, true, 255);
991 }
992 }
993
4/6
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 15 times.
19 } else if (SEGMENT.intensity < 80) //pastel (easter) colors
994 {
995 4 cols[0] = 0x00FF8040;
996 4 cols[1] = 0x00E5D241;
997 4 cols[2] = 0x0077FF77;
998 4 cols[3] = 0x0077F0F0;
999 }
1000
2/2
✓ Branch 0 taken 65 times.
✓ Branch 1 taken 23 times.
88 for (size_t i = numColors; i < numColors*2 -1U; i++) cols[i] = cols[i-numColors];
1001
1002
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 uint32_t cycleTime = 50 + (8 * (uint32_t)(255 - SEGMENT.speed));
1003
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 uint32_t it = strip().now / cycleTime;
1004
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 9 times.
✓ Branch 5 taken 14 times.
23 if (it != SEGENV.step)
1005 {
1006
6/10
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
9 if (SEGMENT.speed > 0) SEGENV.aux0++;
1007
6/10
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 8 times.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
9 if (SEGENV.aux0 >= numColors) SEGENV.aux0 = 0;
1008
2/4
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
9 SEGENV.step = it;
1009 }
1010
1011
2/2
✓ Branch 0 taken 2056 times.
✓ Branch 1 taken 23 times.
2079 for (unsigned i = 0; i < SEGLEN; i+= numColors)
1012 {
1013
7/12
✓ Branch 0 taken 7540 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7540 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7540 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 7540 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 7540 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 7540 times.
✓ Branch 11 taken 2056 times.
9596 for (unsigned j = 0; j < numColors; j++) SEGMENT.setPixelColor(i + j, cols[SEGENV.aux0 + j]);
1014 }
1015
1016
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
46 return FRAMETIME;
1017 }
1018 static const char _data_FX_MODE_COLORFUL[] PROGMEM = "Colorful@!,Saturation;1,2,3;!";
1019
1020
1021 /*
1022 * Emulates a traffic light.
1023 */
1024 23 uint16_t mode_traffic_light(void) {
1025
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
1026
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i=0; i < SEGLEN; i++)
1027
2/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7511 times.
7511 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1));
1028 23 uint32_t mdelay = 500;
1029
2/2
✓ Branch 0 taken 2495 times.
✓ Branch 1 taken 23 times.
2518 for (unsigned i = 0; i < SEGLEN-2 ; i+=3)
1030 {
1031
3/5
✓ Branch 0 taken 567 times.
✓ Branch 1 taken 1418 times.
✓ Branch 2 taken 510 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
2495 switch (SEGENV.aux0)
1032 {
1033 567 case 0: SEGMENT.setPixelColor(i, 0x00FF0000); mdelay = 150 + (100 * (uint32_t)(255 - SEGMENT.speed));break;
1034 1418 case 1: SEGMENT.setPixelColor(i, 0x00FF0000); mdelay = 150 + (20 * (uint32_t)(255 - SEGMENT.speed)); SEGMENT.setPixelColor(i+1, 0x00EECC00); break;
1035 510 case 2: SEGMENT.setPixelColor(i+2, 0x0000FF00); mdelay = 150 + (100 * (uint32_t)(255 - SEGMENT.speed));break;
1036 ✗ case 3: SEGMENT.setPixelColor(i+1, 0x00EECC00); mdelay = 150 + (20 * (uint32_t)(255 - SEGMENT.speed));break;
1037 }
1038 }
1039
1040
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if (strip().now - SEGENV.step > mdelay)
1041 {
1042 5 SEGENV.aux0++;
1043
5/6
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 4 times.
5 if (SEGENV.aux0 == 1 && SEGMENT.intensity > 140) SEGENV.aux0 = 2; //skip Red + Amber, to get US-style sequence
1044
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (SEGENV.aux0 > 3) SEGENV.aux0 = 0;
1045 5 SEGENV.step = strip().now;
1046 }
1047
1048 23 return FRAMETIME;
1049 }
1050 static const char _data_FX_MODE_TRAFFIC_LIGHT[] PROGMEM = "Traffic Light@!,US style;,!;!";
1051
1052
1053 /*
1054 * Sec flashes running on prim.
1055 */
1056 #define FLASH_COUNT 4
1057 23 uint16_t mode_chase_flash(void) {
1058
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
1059 23 unsigned flash_step = SEGENV.call % ((FLASH_COUNT * 2) + 1);
1060
1061
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
1062
2/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7511 times.
7511 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
1063 }
1064
1065 23 unsigned delay = 10 + ((30 * (uint16_t)(255 - SEGMENT.speed)) / SEGLEN);
1066
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 if(flash_step < (FLASH_COUNT * 2)) {
1067
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 10 times.
23 if(flash_step % 2 == 0) {
1068 13 unsigned n = SEGENV.step;
1069 13 unsigned m = (SEGENV.step + 1) % SEGLEN;
1070 13 SEGMENT.setPixelColor( n, SEGCOLOR(1));
1071 13 SEGMENT.setPixelColor( m, SEGCOLOR(1));
1072 13 delay = 20;
1073 } else {
1074 10 delay = 30;
1075 }
1076 } else {
1077 ✗ SEGENV.step = (SEGENV.step + 1) % SEGLEN;
1078 }
1079 23 return delay;
1080 }
1081 static const char _data_FX_MODE_CHASE_FLASH[] PROGMEM = "Chase Flash@!;Bg,Fx;!";
1082
1083
1084 /*
1085 * Prim flashes running, followed by random color.
1086 */
1087 23 uint16_t mode_chase_flash_random(void) {
1088
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
1089 23 unsigned flash_step = SEGENV.call % ((FLASH_COUNT * 2) + 1);
1090
1091
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 for (int i = 0; i < SEGENV.aux1; i++) {
1092 ✗ SEGMENT.setPixelColor(i, SEGMENT.color_wheel(SEGENV.aux0));
1093 }
1094
1095 23 unsigned delay = 1 + ((10 * (uint16_t)(255 - SEGMENT.speed)) / SEGLEN);
1096
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 if(flash_step < (FLASH_COUNT * 2)) {
1097 23 unsigned n = SEGENV.aux1;
1098 23 unsigned m = (SEGENV.aux1 + 1) % SEGLEN;
1099
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 10 times.
23 if(flash_step % 2 == 0) {
1100 13 SEGMENT.setPixelColor( n, SEGCOLOR(0));
1101 13 SEGMENT.setPixelColor( m, SEGCOLOR(0));
1102 13 delay = 20;
1103 } else {
1104 10 SEGMENT.setPixelColor( n, SEGMENT.color_wheel(SEGENV.aux0));
1105 10 SEGMENT.setPixelColor( m, SEGCOLOR(1));
1106 10 delay = 30;
1107 }
1108 } else {
1109 ✗ SEGENV.aux1 = (SEGENV.aux1 + 1) % SEGLEN;
1110
1111 ✗ if (SEGENV.aux1 == 0) {
1112 ✗ SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0);
1113 }
1114 }
1115 23 return delay;
1116 }
1117 static const char _data_FX_MODE_CHASE_FLASH_RANDOM[] PROGMEM = "Chase Flash Rnd@!;!,!;!";
1118
1119
1120 /*
1121 * Alternating color/sec pixels running.
1122 */
1123 23 uint16_t mode_running_color(void) {
1124 23 return running(SEGCOLOR(0), SEGCOLOR(1));
1125 }
1126 static const char _data_FX_MODE_RUNNING_COLOR[] PROGMEM = "Chase 2@!,Width;!,!;!";
1127
1128
1129 /*
1130 * Random colored pixels running. ("Stream")
1131 */
1132 123 uint16_t mode_running_random(void) {
1133 123 uint32_t cycleTime = 25 + (3 * (uint32_t)(255 - SEGMENT.speed));
1134 123 uint32_t it = strip().now / cycleTime;
1135
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 117 times.
123 if (SEGENV.call == 0) SEGENV.aux0 = hw_random(); // random seed for PRNG on start
1136
1137 123 unsigned zoneSize = ((255-SEGMENT.intensity) >> 4) +1;
1138 123 uint16_t PRNG16 = SEGENV.aux0;
1139
1140 123 unsigned z = it % zoneSize;
1141
4/4
✓ Branch 0 taken 115 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 101 times.
123 bool nzone = (!z && (it & 0xFFFF) != SEGENV.aux1);
1142
2/2
✓ Branch 0 taken 12511 times.
✓ Branch 1 taken 123 times.
12634 for (int i=SEGLEN-1; i >= 0; i--) {
1143
4/4
✓ Branch 0 taken 12497 times.
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 7490 times.
✓ Branch 3 taken 5007 times.
12511 if (nzone || z >= zoneSize) {
1144 7504 unsigned lastrand = PRNG16 >> 8;
1145 7504 int16_t diff = 0;
1146
2/2
✓ Branch 0 taken 10399 times.
✓ Branch 1 taken 7504 times.
17903 while (abs(diff) < 42) { // make sure the difference between adjacent colors is big enough
1147 10399 PRNG16 = (uint16_t)(PRNG16 * 2053) + 13849; // next zone, next 'random' number
1148 10399 diff = (PRNG16 >> 8) - lastrand;
1149 }
1150
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 7490 times.
7504 if (nzone) {
1151 14 SEGENV.aux0 = PRNG16; // save next starting seed
1152 14 nzone = false;
1153 }
1154 7504 z = 0;
1155 }
1156 12511 SEGMENT.setPixelColor(i, SEGMENT.color_wheel(PRNG16 >> 8));
1157 12511 z++;
1158 }
1159
1160 123 SEGENV.aux1 = it & 0xFFFF;
1161 123 return FRAMETIME;
1162 }
1163 static const char _data_FX_MODE_RUNNING_RANDOM[] PROGMEM = "Stream@!,Zone size;;!";
1164
1165
1166 /*
1167 * K.I.T.T.
1168 */
1169 46 uint16_t mode_larson_scanner(void) {
1170
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
46 if (SEGLEN <= 1) return mode_static();
1171
1172 46 const unsigned speed = FRAMETIME * map(SEGMENT.speed, 0, 255, 96, 2); // map into useful range
1173 46 const unsigned pixels = SEGLEN / speed; // how many pixels to advance per frame
1174
1175 46 SEGMENT.fade_out(255-SEGMENT.intensity);
1176
1177
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
46 if (SEGENV.step > strip().now) return FRAMETIME; // we have a pause
1178
1179 46 unsigned index = SEGENV.aux1 + pixels;
1180 // are we slow enough to use frames per pixel?
1181
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 8 times.
46 if (pixels == 0) {
1182 38 const unsigned frames = speed / SEGLEN; // how many frames per 1 pixel
1183
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 2 times.
38 if (SEGENV.step++ < frames) return FRAMETIME;
1184 2 SEGENV.step = 0;
1185 2 index++;
1186 }
1187
1188
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (index > SEGLEN) {
1189
1190 ✗ SEGENV.aux0 = !SEGENV.aux0; // change direction
1191 ✗ SEGENV.aux1 = 0; // reset position
1192 // set delay
1193 ✗ if (SEGENV.aux0 || SEGMENT.check2) SEGENV.step = strip().now + SEGMENT.custom1 * 25; // multiply by 25ms
1194 ✗ else SEGENV.step = 0;
1195
1196 } else {
1197
1198 // paint as many pixels as needed
1199
2/2
✓ Branch 0 taken 82 times.
✓ Branch 1 taken 10 times.
92 for (unsigned i = SEGENV.aux1; i < index; i++) {
1200
1/2
✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
82 unsigned j = (SEGENV.aux0) ? i : SEGLEN - 1 - i;
1201
2/4
✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 82 times.
82 uint32_t c = SEGMENT.color_from_palette(j, true, PALETTE_SOLID_WRAP, 0);
1202 82 SEGMENT.setPixelColor(j, c);
1203
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 41 times.
82 if (SEGMENT.check1) {
1204
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 41 times.
41 SEGMENT.setPixelColor(SEGLEN - 1 - j, SEGCOLOR(2) ? SEGCOLOR(2) : c);
1205 }
1206 }
1207 10 SEGENV.aux1 = index;
1208 }
1209 10 return FRAMETIME;
1210 }
1211 static const char _data_FX_MODE_LARSON_SCANNER[] PROGMEM = "Scanner@!,Trail,Delay,,,Dual,Bi-delay;!,!,!;!;;m12=0,c1=0";
1212
1213 /*
1214 * Creates two Larson scanners moving in opposite directions
1215 * Custom mode by Keith Lord: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/DualLarson.h
1216 */
1217 23 uint16_t mode_dual_larson_scanner(void){
1218 23 SEGMENT.check1 = true;
1219 23 return mode_larson_scanner();
1220 }
1221 static const char _data_FX_MODE_DUAL_LARSON_SCANNER[] PROGMEM = "Scanner Dual@!,Trail,Delay,,,Dual,Bi-delay;!,!,!;!;;m12=0,c1=0";
1222
1223 /*
1224 * Firing comets from one end. "Lighthouse"
1225 */
1226 23 uint16_t mode_comet(void) {
1227
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
1228 23 unsigned counter = (strip().now * ((SEGMENT.speed >>2) +1)) & 0xFFFF;
1229 23 unsigned index = (counter * SEGLEN) >> 16;
1230
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if (SEGENV.call == 0) SEGENV.aux0 = index;
1231
1232 23 SEGMENT.fade_out(SEGMENT.intensity);
1233
1234
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 23 times.
23 SEGMENT.setPixelColor( index, SEGMENT.color_from_palette(index, true, PALETTE_SOLID_WRAP, 0));
1235
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 9 times.
23 if (index > SEGENV.aux0) {
1236
2/2
✓ Branch 0 taken 144 times.
✓ Branch 1 taken 14 times.
158 for (unsigned i = SEGENV.aux0; i < index ; i++) {
1237
2/4
✓ Branch 0 taken 144 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 144 times.
144 SEGMENT.setPixelColor( i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
1238 }
1239
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 9 times.
9 } else if (index < SEGENV.aux0 && index < 10) {
1240 ✗ for (unsigned i = 0; i < index ; i++) {
1241 ✗ SEGMENT.setPixelColor( i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
1242 }
1243 }
1244 23 SEGENV.aux0 = index++;
1245
1246 23 return FRAMETIME;
1247 }
1248 static const char _data_FX_MODE_COMET[] PROGMEM = "Lighthouse@!,Fade rate;!,!;!";
1249
1250 /*
1251 * Fireworks function.
1252 */
1253 46 uint16_t mode_fireworks() {
1254
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
46 if (SEGLEN <= 1) return mode_static();
1255
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 10 times.
46 const uint16_t width = SEGMENT.is2D() ? SEG_W : SEGLEN;
1256 46 const uint16_t height = SEG_H;
1257
1258
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 36 times.
46 if (SEGENV.call == 0) {
1259 10 SEGENV.aux0 = UINT16_MAX;
1260 10 SEGENV.aux1 = UINT16_MAX;
1261 }
1262 46 SEGMENT.fade_out(128);
1263
1264 46 uint8_t x = SEGENV.aux0%width, y = SEGENV.aux0/width; // 2D coordinates stored in upper and lower byte
1265
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 23 times.
46 if (!SEGENV.step) {
1266 // fireworks mode (blur flares)
1267 23 bool valid1 = (SEGENV.aux0 < width*height);
1268 23 bool valid2 = (SEGENV.aux1 < width*height);
1269 23 uint32_t sv1 = 0, sv2 = 0;
1270
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
23 if (valid1) sv1 = SEGMENT.is2D() ? SEGMENT.getPixelColorXY(x, y) : SEGMENT.getPixelColor(SEGENV.aux0); // get spark color
1271
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
23 if (valid2) sv2 = SEGMENT.is2D() ? SEGMENT.getPixelColorXY(x, y) : SEGMENT.getPixelColor(SEGENV.aux1);
1272 23 SEGMENT.blur(16); // used in mode_rain()
1273
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
23 if (valid1) { if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(x, y, sv1); else SEGMENT.setPixelColor(SEGENV.aux0, sv1); } // restore spark color after blur
1274
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
23 if (valid2) { if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(x, y, sv2); else SEGMENT.setPixelColor(SEGENV.aux1, sv2); } // restore old spark color after blur
1275 }
1276
1277
2/2
✓ Branch 0 taken 106 times.
✓ Branch 1 taken 46 times.
152 for (int i=0; i<max(1, width/20); i++) {
1278
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 100 times.
106 if (hw_random8(129 - (SEGMENT.intensity >> 1)) == 0) {
1279 6 uint16_t index = hw_random16(width*height);
1280 6 x = index % width;
1281 6 y = index / width;
1282 6 uint32_t col = SEGMENT.color_from_palette(hw_random8(), false, false, 0);
1283
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(x, y, col);
1284 3 else SEGMENT.setPixelColor(index, col);
1285 6 SEGENV.aux1 = SEGENV.aux0; // old spark
1286 6 SEGENV.aux0 = index; // remember where spark occurred
1287 }
1288 }
1289 46 return FRAMETIME;
1290 }
1291 static const char _data_FX_MODE_FIREWORKS[] PROGMEM = "Fireworks@,Frequency;!,!;!;12;ix=192,pal=11";
1292
1293 //Twinkling LEDs running. Inspired by https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Rain.h
1294 23 uint16_t mode_rain() {
1295
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
1296 23 const unsigned width = SEG_W;
1297 23 const unsigned height = SEG_H;
1298 23 SEGENV.step += FRAMETIME;
1299
6/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 7 times.
✓ Branch 4 taken 11 times.
✓ Branch 5 taken 12 times.
23 if (SEGENV.call && SEGENV.step > SPEED_FORMULA_L) {
1300 11 SEGENV.step = 1;
1301
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 2 times.
11 if (SEGMENT.is2D()) {
1302 //uint32_t ctemp[width];
1303 //for (int i = 0; i<width; i++) ctemp[i] = SEGMENT.getPixelColorXY(i, height-1);
1304 9 SEGMENT.move(6, 1, true); // move all pixels down
1305 //for (int i = 0; i<width; i++) SEGMENT.setPixelColorXY(i, 0, ctemp[i]); // wrap around
1306 9 SEGENV.aux0 = (SEGENV.aux0 % width) + (SEGENV.aux0 / width + 1) * width;
1307 9 SEGENV.aux1 = (SEGENV.aux1 % width) + (SEGENV.aux1 / width + 1) * width;
1308 } else {
1309 //shift all leds left
1310 2 uint32_t ctemp = SEGMENT.getPixelColor(0);
1311
2/2
✓ Branch 0 taken 298 times.
✓ Branch 1 taken 2 times.
300 for (unsigned i = 0; i < SEGLEN - 1; i++) {
1312 298 SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1));
1313 }
1314 2 SEGMENT.setPixelColor(SEGLEN -1, ctemp); // wrap around
1315 2 SEGENV.aux0++; // increase spark index
1316 2 SEGENV.aux1++;
1317 }
1318
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 10 times.
11 if (SEGENV.aux0 == 0) SEGENV.aux0 = UINT16_MAX; // reset previous spark position
1319
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 10 times.
11 if (SEGENV.aux1 == 0) SEGENV.aux0 = UINT16_MAX; // reset previous spark position
1320
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 10 times.
11 if (SEGENV.aux0 >= width*height) SEGENV.aux0 = 0; // ignore
1321
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (SEGENV.aux1 >= width*height) SEGENV.aux1 = 0;
1322 }
1323 23 return mode_fireworks();
1324 }
1325 static const char _data_FX_MODE_RAIN[] PROGMEM = "Rain@!,Spawning rate;!,!;!;12;ix=128,pal=0";
1326
1327 /*
1328 * Fire flicker function
1329 */
1330 23 uint16_t mode_fire_flicker(void) {
1331 23 uint32_t cycleTime = 40 + (255 - SEGMENT.speed);
1332 23 uint32_t it = strip().now / cycleTime;
1333
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 12 times.
23 if (SEGENV.step == it) return FRAMETIME;
1334
1335 12 byte w = (SEGCOLOR(0) >> 24);
1336 12 byte r = (SEGCOLOR(0) >> 16);
1337 12 byte g = (SEGCOLOR(0) >> 8);
1338 12 byte b = (SEGCOLOR(0) );
1339
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 byte lum = (SEGMENT.palette == 0) ? MAX(w, MAX(r, MAX(g, b))) : 255;
1340 12 lum /= (((256-SEGMENT.intensity)/16)+1);
1341
2/2
✓ Branch 0 taken 4438 times.
✓ Branch 1 taken 12 times.
4450 for (unsigned i = 0; i < SEGLEN; i++) {
1342 4438 byte flicker = hw_random8(lum);
1343
1/2
✓ Branch 0 taken 4438 times.
✗ Branch 1 not taken.
4438 if (SEGMENT.palette == 0) {
1344 4438 SEGMENT.setPixelColor(i, MAX(r - flicker, 0), MAX(g - flicker, 0), MAX(b - flicker, 0), MAX(w - flicker, 0));
1345 } else {
1346 ✗ SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0, 255 - flicker));
1347 }
1348 }
1349
1350 12 SEGENV.step = it;
1351 12 return FRAMETIME;
1352 }
1353 static const char _data_FX_MODE_FIRE_FLICKER[] PROGMEM = "Fire Flicker@!,!;!;!;01";
1354
1355
1356 /*
1357 * Gradient run base function
1358 */
1359 46 uint16_t gradient_base(bool loading) {
1360
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
46 if (SEGLEN <= 1) return mode_static();
1361 46 uint16_t counter = strip().now * ((SEGMENT.speed >> 2) + 1);
1362 46 uint16_t pp = (counter * SEGLEN) >> 16;
1363
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 36 times.
46 if (SEGENV.call == 0) pp = 0;
1364 int val; //0 = sec 1 = pri
1365 46 int brd = 1 + loading ? SEGMENT.intensity/2 : SEGMENT.intensity/4;
1366 //if (brd < 1) brd = 1;
1367 46 int p1 = pp-SEGLEN;
1368 46 int p2 = pp+SEGLEN;
1369
1370
2/2
✓ Branch 0 taken 15022 times.
✓ Branch 1 taken 46 times.
15068 for (int i = 0; i < (int)SEGLEN; i++) {
1371
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 7511 times.
15022 if (loading) {
1372
2/2
✓ Branch 0 taken 3525 times.
✓ Branch 1 taken 3986 times.
7511 val = abs(((i>pp) ? p2:pp) - i);
1373 } else {
1374 7511 val = min(abs(pp-i),min(abs(p1-i),abs(p2-i)));
1375 }
1376
2/2
✓ Branch 0 taken 1865 times.
✓ Branch 1 taken 13157 times.
15022 val = (brd > val) ? (val * 255) / brd : 255;
1377
2/4
✓ Branch 0 taken 15022 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 15022 times.
15022 SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(0), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1), uint8_t(val)));
1378 }
1379
1380 46 return FRAMETIME;
1381 }
1382
1383
1384 /*
1385 * Gradient run
1386 */
1387 23 uint16_t mode_gradient(void) {
1388 23 return gradient_base(false);
1389 }
1390 static const char _data_FX_MODE_GRADIENT[] PROGMEM = "Gradient@!,Spread;!,!;!;;ix=16";
1391
1392
1393 /*
1394 * Gradient run with hard transition
1395 */
1396 23 uint16_t mode_loading(void) {
1397 23 return gradient_base(true);
1398 }
1399 static const char _data_FX_MODE_LOADING[] PROGMEM = "Loading@!,Fade;!,!;!;;ix=16";
1400
1401 /*
1402 * Two dots running
1403 */
1404 23 uint16_t mode_two_dots() {
1405
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
1406 23 unsigned delay = 1 + (FRAMETIME<<3) / SEGLEN; // longer segments should change faster
1407 23 uint32_t it = strip().now / map(SEGMENT.speed, 0, 255, delay<<4, delay);
1408 23 unsigned offset = it % SEGLEN;
1409 23 unsigned width = ((SEGLEN*(SEGMENT.intensity+1))>>9); //max width is half the strip
1410
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (!width) width = 1;
1411
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(2));
1412 23 const uint32_t color1 = SEGCOLOR(0);
1413
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 const uint32_t color2 = (SEGCOLOR(1) == SEGCOLOR(2)) ? color1 : SEGCOLOR(1);
1414
2/2
✓ Branch 0 taken 1883 times.
✓ Branch 1 taken 23 times.
1906 for (unsigned i = 0; i < width; i++) {
1415 1883 unsigned indexR = (offset + i) % SEGLEN;
1416 1883 unsigned indexB = (offset + i + (SEGLEN>>1)) % SEGLEN;
1417 1883 SEGMENT.setPixelColor(indexR, color1);
1418 1883 SEGMENT.setPixelColor(indexB, color2);
1419 }
1420 23 return FRAMETIME;
1421 }
1422 static const char _data_FX_MODE_TWO_DOTS[] PROGMEM = "Two Dots@!,Dot size,,,,,Overlay;1,2,Bg;!";
1423
1424
1425 /*
1426 * Fairy, inspired by https://www.youtube.com/watch?v=zeOw5MZWq24
1427 */
1428 //4 bytes
1429 typedef struct Flasher {
1430 uint16_t stateStart;
1431 uint8_t stateDur;
1432 bool stateOn;
1433 } flasher;
1434
1435 #define FLASHERS_PER_ZONE 6
1436 #define MAX_SHIMMER 92
1437
1438 23 uint16_t mode_fairy() {
1439 //set every pixel to a 'random' color from palette (using seed so it doesn't change between frames)
1440
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 uint16_t PRNG16 = 5100 + strip().getCurrSegmentId();
1441
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
1442 7511 PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number
1443
6/12
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7511 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7511 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 7511 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 7511 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 7511 times.
✗ Branch 11 not taken.
7511 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(PRNG16 >> 8, false, false, 0));
1444 }
1445
1446 //amount of flasher pixels depending on intensity (0: none, 255: every LED)
1447
5/8
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 19 times.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
23 if (SEGMENT.intensity == 0) return FRAMETIME;
1448
2/4
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
19 unsigned flasherDistance = ((255 - SEGMENT.intensity) / 28) +1; //1-10
1449 19 unsigned numFlashers = (SEGLEN / flasherDistance) +1;
1450
1451 19 unsigned dataSize = sizeof(flasher) * numFlashers;
1452
4/10
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 19 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 19 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
19 if (!SEGENV.allocateData(dataSize)) return FRAMETIME; //allocation failed
1453
2/4
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
19 Flasher* flashers = reinterpret_cast<Flasher*>(SEGENV.data);
1454
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 unsigned now16 = strip().now & 0xFFFF;
1455
1456 //Up to 11 flashers in one brightness zone, afterwards a new zone for every 6 flashers
1457 19 unsigned zones = numFlashers/FLASHERS_PER_ZONE;
1458
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 14 times.
19 if (!zones) zones = 1;
1459 19 unsigned flashersInZone = numFlashers/zones;
1460 uint8_t flasherBri[FLASHERS_PER_ZONE*2 -1];
1461
1462
2/2
✓ Branch 0 taken 455 times.
✓ Branch 1 taken 19 times.
474 for (unsigned z = 0; z < zones; z++) {
1463 455 unsigned flasherBriSum = 0;
1464 455 unsigned firstFlasher = z*flashersInZone;
1465
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 436 times.
455 if (z == zones-1) flashersInZone = numFlashers-(flashersInZone*(zones-1));
1466
1467
2/2
✓ Branch 0 taken 2747 times.
✓ Branch 1 taken 455 times.
3202 for (unsigned f = firstFlasher; f < firstFlasher + flashersInZone; f++) {
1468 2747 unsigned stateTime = uint16_t(now16 - flashers[f].stateStart);
1469 //random on/off time reached, switch state
1470
2/2
✓ Branch 0 taken 793 times.
✓ Branch 1 taken 1954 times.
2747 if (stateTime > flashers[f].stateDur * 10) {
1471 793 flashers[f].stateOn = !flashers[f].stateOn;
1472
2/2
✓ Branch 0 taken 652 times.
✓ Branch 1 taken 141 times.
793 if (flashers[f].stateOn) {
1473
2/4
✓ Branch 0 taken 652 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 652 times.
✗ Branch 3 not taken.
652 flashers[f].stateDur = 12 + hw_random8(12 + ((255 - SEGMENT.speed) >> 2)); //*10, 250ms to 1250ms
1474 } else {
1475
2/4
✓ Branch 0 taken 141 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 141 times.
✗ Branch 3 not taken.
141 flashers[f].stateDur = 20 + hw_random8(6 + ((255 - SEGMENT.speed) >> 2)); //*10, 250ms to 1250ms
1476 }
1477 //flashers[f].stateDur = 51 + hw_random8(2 + ((255 - SEGMENT.speed) >> 1));
1478 793 flashers[f].stateStart = now16;
1479
2/2
✓ Branch 0 taken 141 times.
✓ Branch 1 taken 652 times.
793 if (stateTime < 255) {
1480 141 flashers[f].stateStart -= 255 -stateTime; //start early to get correct bri
1481 141 flashers[f].stateDur += 26 - stateTime/10;
1482 141 stateTime = 255 - stateTime;
1483 } else {
1484 652 stateTime = 0;
1485 }
1486 }
1487
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2747 times.
2747 if (stateTime > 255) stateTime = 255; //for flasher brightness calculation, fades in first 255 ms of state
1488 //flasherBri[f - firstFlasher] = (flashers[f].stateOn) ? 255-SEGMENT.gamma8((510 - stateTime) >> 1) : SEGMENT.gamma8((510 - stateTime) >> 1);
1489
2/2
✓ Branch 0 taken 2598 times.
✓ Branch 1 taken 149 times.
2747 flasherBri[f - firstFlasher] = (flashers[f].stateOn) ? stateTime : 255 - (stateTime >> 0);
1490 2747 flasherBriSum += flasherBri[f - firstFlasher];
1491 }
1492 //dim factor, to create "shimmer" as other pixels get less voltage if a lot of flashers are on
1493 455 unsigned avgFlasherBri = flasherBriSum / flashersInZone;
1494 455 unsigned globalPeakBri = 255 - ((avgFlasherBri * MAX_SHIMMER) >> 8); //183-255, suitable for 1/5th of LEDs flashers
1495
1496
2/2
✓ Branch 0 taken 2747 times.
✓ Branch 1 taken 455 times.
3202 for (unsigned f = firstFlasher; f < firstFlasher + flashersInZone; f++) {
1497 2747 uint8_t bri = (flasherBri[f - firstFlasher] * globalPeakBri) / 255;
1498 2747 PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number
1499 2747 unsigned flasherPos = f*flasherDistance;
1500
7/14
✓ Branch 0 taken 2747 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2747 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2747 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2747 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2747 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2747 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 2747 times.
✗ Branch 13 not taken.
2747 SEGMENT.setPixelColor(flasherPos, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(PRNG16 >> 8, false, false, 0), bri));
1501
6/6
✓ Branch 0 taken 2740 times.
✓ Branch 1 taken 2732 times.
✓ Branch 2 taken 2725 times.
✓ Branch 3 taken 15 times.
✓ Branch 4 taken 2725 times.
✓ Branch 5 taken 2747 times.
5472 for (unsigned i = flasherPos+1; i < flasherPos+flasherDistance && i < SEGLEN; i++) {
1502 2725 PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number
1503
6/12
✓ Branch 0 taken 2725 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2725 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2725 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2725 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2725 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2725 times.
✗ Branch 11 not taken.
2725 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(PRNG16 >> 8, false, false, 0, globalPeakBri));
1504 }
1505 }
1506 }
1507
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 return FRAMETIME;
1508 }
1509 static const char _data_FX_MODE_FAIRY[] PROGMEM = "Fairy@!,# of flashers;!,!;!";
1510
1511
1512 /*
1513 * Fairytwinkle. Like Colortwinkle, but starting from all lit and not relying on strip().getPixelColor
1514 * Warning: Uses 4 bytes of segment data per pixel
1515 */
1516 23 uint16_t mode_fairytwinkle() {
1517 23 unsigned dataSize = sizeof(flasher) * SEGLEN;
1518
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
1519 23 Flasher* flashers = reinterpret_cast<Flasher*>(SEGENV.data);
1520 23 unsigned now16 = strip().now & 0xFFFF;
1521 23 uint16_t PRNG16 = 5100 + strip().getCurrSegmentId();
1522
1523 23 unsigned riseFallTime = 400 + (255-SEGMENT.speed)*3;
1524 23 unsigned maxDur = riseFallTime/100 + ((255 - SEGMENT.intensity) >> 2) + 13 + ((255 - SEGMENT.intensity) >> 1);
1525
1526
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned f = 0; f < SEGLEN; f++) {
1527 7511 uint16_t stateTime = now16 - flashers[f].stateStart;
1528 //random on/off time reached, switch state
1529
2/2
✓ Branch 0 taken 1707 times.
✓ Branch 1 taken 5804 times.
7511 if (stateTime > flashers[f].stateDur * 100) {
1530 1707 flashers[f].stateOn = !flashers[f].stateOn;
1531 1707 bool init = !flashers[f].stateDur;
1532
1/2
✓ Branch 0 taken 1707 times.
✗ Branch 1 not taken.
1707 if (flashers[f].stateOn) {
1533 1707 flashers[f].stateDur = riseFallTime/100 + ((255 - SEGMENT.intensity) >> 2) + hw_random8(12 + ((255 - SEGMENT.intensity) >> 1)) +1;
1534 } else {
1535 ✗ flashers[f].stateDur = riseFallTime/100 + hw_random8(3 + ((255 - SEGMENT.speed) >> 6)) +1;
1536 }
1537 1707 flashers[f].stateStart = now16;
1538 1707 stateTime = 0;
1539
1/2
✓ Branch 0 taken 1707 times.
✗ Branch 1 not taken.
1707 if (init) {
1540 1707 flashers[f].stateStart -= riseFallTime; //start lit
1541 1707 flashers[f].stateDur = riseFallTime/100 + hw_random8(12 + ((255 - SEGMENT.intensity) >> 1)) +5; //fire up a little quicker
1542 1707 stateTime = riseFallTime;
1543 }
1544 }
1545
3/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 125 times.
✓ Branch 3 taken 7386 times.
7511 if (flashers[f].stateOn && flashers[f].stateDur > maxDur) flashers[f].stateDur = maxDur; //react more quickly on intensity change
1546
2/2
✓ Branch 0 taken 5804 times.
✓ Branch 1 taken 1707 times.
7511 if (stateTime > riseFallTime) stateTime = riseFallTime; //for flasher brightness calculation, fades in first 255 ms of state
1547 7511 unsigned fadeprog = 255 - ((stateTime * 255) / riseFallTime);
1548
1/2
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
7511 uint8_t flasherBri = (flashers[f].stateOn) ? 255-gamma8(fadeprog) : gamma8(fadeprog);
1549 7511 unsigned lastR = PRNG16;
1550 7511 unsigned diff = 0;
1551
2/2
✓ Branch 0 taken 14004 times.
✓ Branch 1 taken 7511 times.
21515 while (diff < 0x4000) { //make sure colors of two adjacent LEDs differ enough
1552 14004 PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number
1553
2/2
✓ Branch 0 taken 7250 times.
✓ Branch 1 taken 6754 times.
14004 diff = (PRNG16 > lastR) ? PRNG16 - lastR : lastR - PRNG16;
1554 }
1555 7511 SEGMENT.setPixelColor(f, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(PRNG16 >> 8, false, false, 0), flasherBri));
1556 }
1557 23 return FRAMETIME;
1558 }
1559 static const char _data_FX_MODE_FAIRYTWINKLE[] PROGMEM = "Fairytwinkle@!,!;!,!;!;;m12=0"; //pixels
1560
1561
1562 /*
1563 * Tricolor chase function
1564 */
1565 23 uint16_t tricolor_chase(uint32_t color1, uint32_t color2) {
1566 23 uint32_t cycleTime = 50 + ((255 - SEGMENT.speed)<<1);
1567 23 uint32_t it = strip().now / cycleTime; // iterator
1568 23 unsigned width = (1 + (SEGMENT.intensity>>4)); // value of 1-16 for each colour
1569 23 unsigned index = it % (width*3);
1570
1571
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++, index++) {
1572
2/2
✓ Branch 0 taken 845 times.
✓ Branch 1 taken 6666 times.
7511 if (index > (width*3)-1) index = 0;
1573
1574 7511 uint32_t color = color1;
1575
4/6
✓ Branch 0 taken 2461 times.
✓ Branch 1 taken 5050 times.
✓ Branch 2 taken 2461 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2461 times.
7511 if (index > (width<<1)-1) color = SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1);
1576
2/2
✓ Branch 0 taken 2553 times.
✓ Branch 1 taken 2497 times.
5050 else if (index > width-1) color = color2;
1577
1578 7511 SEGMENT.setPixelColor(SEGLEN - i -1, color);
1579 }
1580 23 return FRAMETIME;
1581 }
1582
1583
1584 /*
1585 * Tricolor chase mode
1586 */
1587 23 uint16_t mode_tricolor_chase(void) {
1588 23 return tricolor_chase(SEGCOLOR(2), SEGCOLOR(0));
1589 }
1590 static const char _data_FX_MODE_TRICOLOR_CHASE[] PROGMEM = "Chase 3@!,Size;1,2,3;!";
1591
1592
1593 /*
1594 * ICU mode
1595 */
1596 23 uint16_t mode_icu(void) {
1597 23 unsigned dest = SEGENV.step & 0xFFFF;
1598 23 unsigned space = (SEGMENT.intensity >> 3) +2;
1599
1600
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1));
1601
1602 23 byte pindex = map(dest, 0, SEGLEN-SEGLEN/space, 0, 255);
1603 23 uint32_t col = SEGMENT.color_from_palette(pindex, false, false, 0);
1604
1605 23 SEGMENT.setPixelColor(dest, col);
1606 23 SEGMENT.setPixelColor(dest + SEGLEN/space, col);
1607
1608
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if(SEGENV.aux0 == dest) { // pause between eye movements
1609
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if(hw_random8(6) == 0) { // blink once in a while
1610 ✗ SEGMENT.setPixelColor(dest, SEGCOLOR(1));
1611 ✗ SEGMENT.setPixelColor(dest + SEGLEN/space, SEGCOLOR(1));
1612 ✗ return 200;
1613 }
1614 5 SEGENV.aux0 = hw_random16(SEGLEN-SEGLEN/space);
1615 5 return 1000 + hw_random16(2000);
1616 }
1617
1618
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 if(SEGENV.aux0 > SEGENV.step) {
1619 18 SEGENV.step++;
1620 18 dest++;
1621 ✗ } else if (SEGENV.aux0 < SEGENV.step) {
1622 ✗ SEGENV.step--;
1623 ✗ dest--;
1624 }
1625
1626 18 SEGMENT.setPixelColor(dest, col);
1627 18 SEGMENT.setPixelColor(dest + SEGLEN/space, col);
1628
1629 18 return SPEED_FORMULA_L;
1630 }
1631 static const char _data_FX_MODE_ICU[] PROGMEM = "ICU@!,!,,,,,Overlay;!,!;!";
1632
1633
1634 /*
1635 * Custom mode by Aircoookie. Color Wipe, but with 3 colors
1636 */
1637 23 uint16_t mode_tricolor_wipe(void) {
1638 23 uint32_t cycleTime = 1000 + (255 - SEGMENT.speed)*200;
1639 23 uint32_t perc = strip().now % cycleTime;
1640 23 unsigned prog = (perc * 65535) / cycleTime;
1641 23 unsigned ledIndex = (prog * SEGLEN * 3) >> 16;
1642 23 unsigned ledOffset = ledIndex;
1643
1644
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++)
1645 {
1646
2/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7511 times.
7511 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 2));
1647 }
1648
1649
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 10 times.
23 if(ledIndex < SEGLEN) { //wipe from 0 to 1
1650
2/2
✓ Branch 0 taken 2391 times.
✓ Branch 1 taken 13 times.
2404 for (unsigned i = 0; i < SEGLEN; i++)
1651 {
1652
2/2
✓ Branch 0 taken 983 times.
✓ Branch 1 taken 1408 times.
2391 SEGMENT.setPixelColor(i, (i > ledOffset)? SEGCOLOR(0) : SEGCOLOR(1));
1653 }
1654
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
10 } else if (ledIndex < SEGLEN*2) { //wipe from 1 to 2
1655 5 ledOffset = ledIndex - SEGLEN;
1656
2/2
✓ Branch 0 taken 1304 times.
✓ Branch 1 taken 5 times.
1309 for (unsigned i = ledOffset +1; i < SEGLEN; i++)
1657 {
1658 1304 SEGMENT.setPixelColor(i, SEGCOLOR(1));
1659 }
1660 } else //wipe from 2 to 0
1661 {
1662 5 ledOffset = ledIndex - SEGLEN*2;
1663
2/2
✓ Branch 0 taken 2474 times.
✓ Branch 1 taken 5 times.
2479 for (unsigned i = 0; i <= ledOffset; i++)
1664 {
1665 2474 SEGMENT.setPixelColor(i, SEGCOLOR(0));
1666 }
1667 }
1668
1669 23 return FRAMETIME;
1670 }
1671 static const char _data_FX_MODE_TRICOLOR_WIPE[] PROGMEM = "Tri Wipe@!;1,2,3;!";
1672
1673
1674 /*
1675 * Fades between 3 colors
1676 * Custom mode by Keith Lord: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/TriFade.h
1677 * Modified by Aircoookie
1678 */
1679 23 uint16_t mode_tricolor_fade(void) {
1680 23 unsigned counter = strip().now * ((SEGMENT.speed >> 3) +1);
1681 23 uint16_t prog = (counter * 768) >> 16;
1682
1683 23 uint32_t color1 = 0, color2 = 0;
1684 23 unsigned stage = 0;
1685
1686
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if(prog < 256) {
1687 ✗ color1 = SEGCOLOR(0);
1688 ✗ color2 = SEGCOLOR(1);
1689 ✗ stage = 0;
1690
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 } else if(prog < 512) {
1691 ✗ color1 = SEGCOLOR(1);
1692 ✗ color2 = SEGCOLOR(2);
1693 ✗ stage = 1;
1694 } else {
1695 23 color1 = SEGCOLOR(2);
1696 23 color2 = SEGCOLOR(0);
1697 23 stage = 2;
1698 }
1699
1700 23 byte stp = prog; // % 256
1701
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
1702 uint32_t color;
1703
1/2
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
7511 if (stage == 2) {
1704
2/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7511 times.
7511 color = color_blend(SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 2), color2, stp);
1705 ✗ } else if (stage == 1) {
1706 ✗ color = color_blend(color1, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 2), stp);
1707 } else {
1708 ✗ color = color_blend(color1, color2, stp);
1709 }
1710 7511 SEGMENT.setPixelColor(i, color);
1711 }
1712
1713 23 return FRAMETIME;
1714 }
1715 static const char _data_FX_MODE_TRICOLOR_FADE[] PROGMEM = "Tri Fade@!;1,2,3;!";
1716
1717 #ifdef WLED_PS_DONT_REPLACE_FX
1718 /*
1719 * Creates random comets
1720 * Custom mode by Keith Lord: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/MultiComet.h
1721 */
1722 #define MAX_COMETS 8
1723 23 uint16_t mode_multi_comet(void) {
1724 23 uint32_t cycleTime = 10 + (uint32_t)(255 - SEGMENT.speed);
1725 23 uint32_t it = strip().now / cycleTime;
1726
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 13 times.
23 if (SEGENV.step == it) return FRAMETIME;
1727
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 if (!SEGENV.allocateData(sizeof(uint16_t) * MAX_COMETS)) return mode_static(); //allocation failed
1728
1729 13 SEGMENT.fade_out(SEGMENT.intensity/2 + 128);
1730
1731 13 uint16_t* comets = reinterpret_cast<uint16_t*>(SEGENV.data);
1732
1733
2/2
✓ Branch 0 taken 104 times.
✓ Branch 1 taken 13 times.
117 for (unsigned i=0; i < MAX_COMETS; i++) {
1734
1/2
✓ Branch 0 taken 104 times.
✗ Branch 1 not taken.
104 if(comets[i] < SEGLEN) {
1735 104 unsigned index = comets[i];
1736
1/2
✓ Branch 0 taken 104 times.
✗ Branch 1 not taken.
104 if (SEGCOLOR(2) != 0)
1737 {
1738
4/6
✓ Branch 0 taken 52 times.
✓ Branch 1 taken 52 times.
✓ Branch 2 taken 52 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 52 times.
104 SEGMENT.setPixelColor(index, i % 2 ? SEGMENT.color_from_palette(index, true, PALETTE_SOLID_WRAP, 0) : SEGCOLOR(2));
1739 } else
1740 {
1741 ✗ SEGMENT.setPixelColor(index, SEGMENT.color_from_palette(index, true, PALETTE_SOLID_WRAP, 0));
1742 }
1743 104 comets[i]++;
1744 } else {
1745 ✗ if(!hw_random16(SEGLEN)) {
1746 ✗ comets[i] = 0;
1747 }
1748 }
1749 }
1750
1751 13 SEGENV.step = it;
1752 13 return FRAMETIME;
1753 }
1754 static const char _data_FX_MODE_MULTI_COMET[] PROGMEM = "Multi Comet@!,Fade;!,!;!;1";
1755 #undef MAX_COMETS
1756 #endif // WLED_PS_DONT_REPLACE_FX
1757
1758 /*
1759 * Running random pixels ("Stream 2")
1760 * Custom mode by Keith Lord: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/RandomChase.h
1761 */
1762 23 uint16_t mode_random_chase(void) {
1763
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if (SEGENV.call == 0) {
1764 5 SEGENV.step = RGBW32(random8(), random8(), random8(), 0);
1765 5 SEGENV.aux0 = random16();
1766 }
1767 23 unsigned prevSeed = random16_get_seed(); // save seed so we can restore it at the end of the function
1768 23 uint32_t cycleTime = 25 + (3 * (uint32_t)(255 - SEGMENT.speed));
1769 23 uint32_t it = strip().now / cycleTime;
1770 23 uint32_t color = SEGENV.step;
1771 23 random16_set_seed(SEGENV.aux0);
1772
1773
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (int i = SEGLEN -1; i >= 0; i--) {
1774
2/2
✓ Branch 0 taken 6403 times.
✓ Branch 1 taken 1108 times.
7511 uint8_t r = random8(6) != 0 ? (color >> 16 & 0xFF) : random8();
1775
2/2
✓ Branch 0 taken 6221 times.
✓ Branch 1 taken 1290 times.
7511 uint8_t g = random8(6) != 0 ? (color >> 8 & 0xFF) : random8();
1776
2/2
✓ Branch 0 taken 6186 times.
✓ Branch 1 taken 1325 times.
7511 uint8_t b = random8(6) != 0 ? (color & 0xFF) : random8();
1777 7511 color = RGBW32(r, g, b, 0);
1778 7511 SEGMENT.setPixelColor(i, color);
1779
6/6
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 7488 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 15 times.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 7503 times.
7511 if (i == SEGLEN -1U && SEGENV.aux1 != (it & 0xFFFFU)) { //new first color in next frame
1780 8 SEGENV.step = color;
1781 8 SEGENV.aux0 = random16_get_seed();
1782 }
1783 }
1784
1785 23 SEGENV.aux1 = it & 0xFFFF;
1786
1787 23 random16_set_seed(prevSeed); // restore original seed so other effects can use "random" PRNG
1788 23 return FRAMETIME;
1789 }
1790 static const char _data_FX_MODE_RANDOM_CHASE[] PROGMEM = "Stream 2@!;;";
1791
1792
1793 //7 bytes
1794 typedef struct Oscillator {
1795 uint16_t pos;
1796 uint8_t size;
1797 int8_t dir;
1798 uint8_t speed;
1799 } oscillator;
1800
1801 /*
1802 / Oscillating bars of color, updated with standard framerate
1803 */
1804 23 uint16_t mode_oscillate(void) {
1805 23 constexpr unsigned numOscillators = 3;
1806 23 constexpr unsigned dataSize = sizeof(oscillator) * numOscillators;
1807
1808
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
1809
1810 23 Oscillator* oscillators = reinterpret_cast<Oscillator*>(SEGENV.data);
1811
1812
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if (SEGENV.call == 0)
1813 {
1814 5 oscillators[0] = {(uint16_t)(SEGLEN/4), (uint8_t)(SEGLEN/8), 1, 1};
1815 5 oscillators[1] = {(uint16_t)(SEGLEN/4*3), (uint8_t)(SEGLEN/8), 1, 2};
1816 5 oscillators[2] = {(uint16_t)(SEGLEN/4*2), (uint8_t)(SEGLEN/8), -1, 1};
1817 }
1818
1819 23 uint32_t cycleTime = 20 + (2 * (uint32_t)(255 - SEGMENT.speed));
1820 23 uint32_t it = strip().now / cycleTime;
1821
1822
2/2
✓ Branch 0 taken 69 times.
✓ Branch 1 taken 23 times.
92 for (unsigned i = 0; i < numOscillators; i++) {
1823 // if the counter has increased, move the oscillator by the random step
1824
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 39 times.
69 if (it != SEGENV.step) oscillators[i].pos += oscillators[i].dir * oscillators[i].speed;
1825 69 oscillators[i].size = SEGLEN/(3+SEGMENT.intensity/8);
1826
4/6
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 23 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 69 times.
69 if((oscillators[i].dir == -1) && (oscillators[i].pos > SEGLEN << 1)) { // use integer overflow
1827 ✗ oscillators[i].pos = 0;
1828 ✗ oscillators[i].dir = 1;
1829 // make bigger steps for faster speeds
1830 ✗ oscillators[i].speed = SEGMENT.speed > 100 ? hw_random8(2, 4):hw_random8(1, 3);
1831 }
1832
4/6
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 46 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 69 times.
69 if((oscillators[i].dir == 1) && (oscillators[i].pos >= (SEGLEN - 1))) {
1833 ✗ oscillators[i].pos = SEGLEN - 1;
1834 ✗ oscillators[i].dir = -1;
1835 ✗ oscillators[i].speed = SEGMENT.speed > 100 ? hw_random8(2, 4):hw_random8(1, 3);
1836 }
1837 }
1838
1839
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
1840 7511 uint32_t color = BLACK;
1841
2/2
✓ Branch 0 taken 22533 times.
✓ Branch 1 taken 7511 times.
30044 for (unsigned j = 0; j < numOscillators; j++) {
1842
4/4
✓ Branch 0 taken 13783 times.
✓ Branch 1 taken 8750 times.
✓ Branch 2 taken 5185 times.
✓ Branch 3 taken 8598 times.
22533 if((int)i >= (int)oscillators[j].pos - oscillators[j].size && i <= oscillators[j].pos + oscillators[j].size) {
1843
2/2
✓ Branch 0 taken 3485 times.
✓ Branch 1 taken 1700 times.
5185 color = (color == BLACK) ? SEGCOLOR(j) : color_blend(color, SEGCOLOR(j), uint8_t(128));
1844 }
1845 }
1846 7511 SEGMENT.setPixelColor(i, color);
1847 }
1848
1849 23 SEGENV.step = it;
1850 23 return FRAMETIME;
1851 }
1852 static const char _data_FX_MODE_OSCILLATE[] PROGMEM = "Oscillate";
1853
1854
1855 //TODO
1856 23 uint16_t mode_lightning(void) {
1857
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
1858 23 unsigned ledstart = hw_random16(SEGLEN); // Determine starting location of flash
1859 23 unsigned ledlen = 1 + hw_random16(SEGLEN -ledstart); // Determine length of flash (not to go beyond NUM_LEDS-1)
1860 23 uint8_t bri = 255/hw_random8(1, 3);
1861
1862
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if (SEGENV.aux1 == 0) //init, leader flash
1863 {
1864 5 SEGENV.aux1 = hw_random8(4, 4 + SEGMENT.intensity/20); //number of flashes
1865 5 SEGENV.aux1 *= 2;
1866
1867 5 bri = 52; //leader has lower brightness
1868 5 SEGENV.aux0 = 200; //200ms delay after leader
1869 }
1870
1871
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1));
1872
1873
5/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGENV.aux1 > 3 && !(SEGENV.aux1 & 0x01)) { //flash on even number >2
1874
2/2
✓ Branch 0 taken 183 times.
✓ Branch 1 taken 5 times.
188 for (unsigned i = ledstart; i < ledstart + ledlen; i++)
1875 {
1876
2/4
✓ Branch 0 taken 183 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 183 times.
183 SEGMENT.setPixelColor(i,SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0, bri));
1877 }
1878 5 SEGENV.aux1--;
1879
1880 5 SEGENV.step = strip().now;
1881 //return hw_random8(4, 10); // each flash only lasts one frame/every 24ms... originally 4-10 milliseconds
1882 } else {
1883
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if (strip().now - SEGENV.step > SEGENV.aux0) {
1884 ✗ SEGENV.aux1--;
1885 ✗ if (SEGENV.aux1 < 2) SEGENV.aux1 = 0;
1886
1887 ✗ SEGENV.aux0 = (50 + hw_random8(100)); //delay between flashes
1888 ✗ if (SEGENV.aux1 == 2) {
1889 ✗ SEGENV.aux0 = (hw_random8(255 - SEGMENT.speed) * 100); // delay between strikes
1890 }
1891 ✗ SEGENV.step = strip().now;
1892 }
1893 }
1894 23 return FRAMETIME;
1895 }
1896 static const char _data_FX_MODE_LIGHTNING[] PROGMEM = "Lightning@!,!,,,,,Overlay;!,!;!";
1897
1898 // combined function from original pride and colorwaves
1899 46 uint16_t mode_colorwaves_pride_base(bool isPride2015) {
1900 46 unsigned duration = 10 + SEGMENT.speed;
1901 46 unsigned sPseudotime = SEGENV.step;
1902 46 unsigned sHue16 = SEGENV.aux0;
1903
1904
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 23 times.
46 uint8_t sat8 = isPride2015 ? beatsin88_t(87, 220, 250) : 255;
1905 46 unsigned brightdepth = beatsin88_t(341, 96, 224);
1906 46 unsigned brightnessthetainc16 = beatsin88_t(203, (25 * 256), (40 * 256));
1907 46 unsigned msmultiplier = beatsin88_t(147, 23, 60);
1908
1909 46 unsigned hue16 = sHue16;
1910
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 23 times.
46 unsigned hueinc16 = isPride2015 ? beatsin88_t(113, 1, 3000) :
1911 23 beatsin88_t(113, 60, 300) * SEGMENT.intensity * 10 / 255;
1912
1913 46 sPseudotime += duration * msmultiplier;
1914 46 sHue16 += duration * beatsin88_t(400, 5, 9);
1915 46 unsigned brightnesstheta16 = sPseudotime;
1916
1917
2/2
✓ Branch 0 taken 15022 times.
✓ Branch 1 taken 46 times.
15068 for (unsigned i = 0; i < SEGLEN; i++) {
1918 15022 hue16 += hueinc16;
1919 uint8_t hue8;
1920
1921
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 7511 times.
15022 if (isPride2015) {
1922 7511 hue8 = hue16 >> 8;
1923 } else {
1924 7511 unsigned h16_128 = hue16 >> 7;
1925
2/2
✓ Branch 0 taken 2489 times.
✓ Branch 1 taken 5022 times.
7511 hue8 = (h16_128 & 0x100) ? (255 - (h16_128 >> 1)) : (h16_128 >> 1);
1926 }
1927
1928 15022 brightnesstheta16 += brightnessthetainc16;
1929 15022 unsigned b16 = sin16_t(brightnesstheta16) + 32768;
1930 15022 unsigned bri16 = (uint32_t)((uint32_t)b16 * (uint32_t)b16) / 65536;
1931 15022 uint8_t bri8 = (uint32_t)(((uint32_t)bri16) * brightdepth) / 65536;
1932 15022 bri8 += (255 - brightdepth);
1933
1934
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 7511 times.
15022 if (isPride2015) {
1935 7511 CRGB newcolor = CHSV(hue8, sat8, bri8);
1936
3/6
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7511 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7511 times.
✗ Branch 5 not taken.
7511 SEGMENT.blendPixelColor(i, newcolor, 64);
1937 } else {
1938
2/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7511 times.
7511 SEGMENT.blendPixelColor(i, SEGMENT.color_from_palette(hue8, false, PALETTE_SOLID_WRAP, 0, bri8), 128);
1939 }
1940 }
1941
1942 46 SEGENV.step = sPseudotime;
1943 46 SEGENV.aux0 = sHue16;
1944
1945 46 return FRAMETIME;
1946 }
1947
1948 // Pride2015
1949 // Animated, ever-changing rainbows.
1950 // by Mark Kriegsman: https://gist.github.com/kriegsman/964de772d64c502760e5
1951 23 uint16_t mode_pride_2015(void) {
1952 23 return mode_colorwaves_pride_base(true);
1953 }
1954 static const char _data_FX_MODE_PRIDE_2015[] PROGMEM = "Pride 2015@!;;";
1955
1956 // ColorWavesWithPalettes by Mark Kriegsman: https://gist.github.com/kriegsman/8281905786e8b2632aeb
1957 // This function draws color waves with an ever-changing,
1958 // widely-varying set of parameters, using a color palette.
1959 23 uint16_t mode_colorwaves() {
1960 23 return mode_colorwaves_pride_base(false);
1961 }
1962 static const char _data_FX_MODE_COLORWAVES[] PROGMEM = "Colorwaves@!,Hue;!;!;;pal=26";
1963
1964
1965 //eight colored dots, weaving in and out of sync with each other
1966 23 uint16_t mode_juggle(void) {
1967
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
23 if (SEGLEN <= 1) return mode_static();
1968
1969
5/10
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 23 times.
✗ Branch 9 not taken.
23 SEGMENT.fadeToBlackBy(192 - (3*SEGMENT.intensity/4));
1970 CRGB fastled_col;
1971 23 byte dothue = 0;
1972
2/2
✓ Branch 0 taken 184 times.
✓ Branch 1 taken 23 times.
207 for (int i = 0; i < 8; i++) {
1973
3/6
✓ Branch 0 taken 184 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 184 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 184 times.
✗ Branch 5 not taken.
184 int index = 0 + beatsin88_t((16 + SEGMENT.speed)*(i + 7), 0, SEGLEN -1);
1974
3/6
✓ Branch 0 taken 184 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 184 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 184 times.
✗ Branch 5 not taken.
184 fastled_col = CRGB(SEGMENT.getPixelColor(index));
1975
3/8
✓ Branch 0 taken 184 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 184 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 184 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
184 fastled_col |= (SEGMENT.palette==0)?CHSV(dothue, 220, 255):CRGB(ColorFromPalette(SEGPALETTE, dothue, 255));
1976
3/6
✓ Branch 0 taken 184 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 184 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 184 times.
✗ Branch 5 not taken.
184 SEGMENT.setPixelColor(index, fastled_col);
1977 184 dothue += 32;
1978 }
1979
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 return FRAMETIME;
1980 }
1981 static const char _data_FX_MODE_JUGGLE[] PROGMEM = "Juggle@!,Trail;;!;;sx=64,ix=128";
1982
1983
1984 174 uint16_t mode_palette() {
1985 // Set up some compile time constants so that we can handle integer and float based modes using the same code base.
1986 #ifdef ESP8266
1987 using mathType = int32_t;
1988 using wideMathType = int64_t;
1989 using angleType = unsigned;
1990 constexpr mathType sInt16Scale = 0x7FFF;
1991 constexpr mathType maxAngle = 0x8000;
1992 constexpr mathType staticRotationScale = 256;
1993 constexpr mathType animatedRotationScale = 1;
1994 constexpr int16_t (*sinFunction)(uint16_t) = &sin16_t;
1995 constexpr int16_t (*cosFunction)(uint16_t) = &cos16_t;
1996 #else
1997 using mathType = float;
1998 using wideMathType = float;
1999 using angleType = float;
2000 174 constexpr mathType sInt16Scale = 1.0f;
2001 174 constexpr mathType maxAngle = M_PI / 256.0;
2002 174 constexpr mathType staticRotationScale = 1.0f;
2003 174 constexpr mathType animatedRotationScale = M_TWOPI / double(0xFFFF);
2004 174 constexpr float (*sinFunction)(float) = &sin_t;
2005 174 constexpr float (*cosFunction)(float) = &cos_t;
2006 #endif
2007 174 const bool isMatrix = strip().isMatrix;
2008 174 const int cols = SEG_W;
2009
2/2
✓ Branch 0 taken 169 times.
✓ Branch 1 taken 5 times.
174 const int rows = isMatrix ? SEG_H : strip().getActiveSegmentsNum();
2010
2011 174 const int inputShift = SEGMENT.speed;
2012 174 const int inputSize = SEGMENT.intensity;
2013 174 const int inputRotation = SEGMENT.custom1;
2014 174 const bool inputAnimateShift = SEGMENT.check1;
2015 174 const bool inputAnimateRotation = SEGMENT.check2;
2016 174 const bool inputAssumeSquare = SEGMENT.check3;
2017
2018
1/2
✓ Branch 0 taken 174 times.
✗ Branch 1 not taken.
174 const angleType theta = (!inputAnimateRotation) ? ((inputRotation + 128) * maxAngle / staticRotationScale) : (((strip().now * ((inputRotation >> 4) +1)) & 0xFFFF) * animatedRotationScale);
2019 174 const mathType sinTheta = sinFunction(theta);
2020 174 const mathType cosTheta = cosFunction(theta);
2021
2022 174 const mathType maxX = std::max(1, cols-1);
2023 174 const mathType maxY = std::max(1, rows-1);
2024 // Set up some parameters according to inputAssumeSquare, so that we can handle anamorphic mode using the same code base.
2025
1/2
✓ Branch 0 taken 174 times.
✗ Branch 1 not taken.
174 const mathType maxXIn = inputAssumeSquare ? maxX : mathType(1);
2026
1/2
✓ Branch 0 taken 174 times.
✗ Branch 1 not taken.
174 const mathType maxYIn = inputAssumeSquare ? maxY : mathType(1);
2027
1/2
✓ Branch 0 taken 174 times.
✗ Branch 1 not taken.
174 const mathType maxXOut = !inputAssumeSquare ? maxX : mathType(1);
2028
1/2
✓ Branch 0 taken 174 times.
✗ Branch 1 not taken.
174 const mathType maxYOut = !inputAssumeSquare ? maxY : mathType(1);
2029 174 const mathType centerX = sInt16Scale * maxXOut / mathType(2);
2030 174 const mathType centerY = sInt16Scale * maxYOut / mathType(2);
2031 // The basic idea for this effect is to rotate a rectangle that is filled with the palette along one axis, then map our
2032 // display to it, to find what color a pixel should have.
2033 // However, we want a) no areas of solid color (in front of or behind the palette), and b) we want to make use of the full palette.
2034 // So the rectangle needs to have exactly the right size. That size depends on the rotation.
2035 // This scale computation here only considers one dimension. You can think of it like the rectangle is always scaled so that
2036 // the left and right most points always match the left and right side of the display.
2037 174 const mathType scale = std::abs(sinTheta) + (std::abs(cosTheta) * maxYOut / maxXOut);
2038 // 2D simulation:
2039 // If we are dealing with a 1D setup, we assume that each segment represents one line on a 2-dimensional display.
2040 // The function is called once per segments, so we need to handle one line at a time.
2041
2/2
✓ Branch 0 taken 169 times.
✓ Branch 1 taken 5 times.
174 const int yFrom = isMatrix ? 0 : strip().getCurrSegmentId();
2042
2/2
✓ Branch 0 taken 169 times.
✓ Branch 1 taken 5 times.
174 const int yTo = isMatrix ? maxY : yFrom;
2043
2/2
✓ Branch 0 taken 2644 times.
✓ Branch 1 taken 174 times.
2818 for (int y = yFrom; y <= yTo; ++y) {
2044 // translate, scale, rotate
2045 2644 const mathType ytCosTheta = mathType((wideMathType(cosTheta) * wideMathType(y * sInt16Scale - centerY * maxYIn))/wideMathType(maxYIn * scale));
2046
2/2
✓ Branch 0 taken 84823 times.
✓ Branch 1 taken 2644 times.
87467 for (int x = 0; x < cols; ++x) {
2047 // translate, scale, rotate
2048 84823 const mathType xtSinTheta = mathType((wideMathType(sinTheta) * wideMathType(x * sInt16Scale - centerX * maxXIn))/wideMathType(maxXIn * scale));
2049 // Map the pixel coordinate to an imaginary-rectangle-coordinate.
2050 // The y coordinate doesn't actually matter, as our imaginary rectangle is filled with the palette from left to right,
2051 // so all points at a given x-coordinate have the same color.
2052 84823 const mathType sourceX = xtSinTheta + ytCosTheta + centerX;
2053 // The computation was scaled just right so that the result should always be in range [0, maxXOut], but enforce this anyway
2054 // to account for imprecision. Then scale it so that the range is [0, 255], which we can use with the palette.
2055 84823 int colorIndex = (std::min(std::max(sourceX, mathType(0)), maxXOut * sInt16Scale) * wideMathType(255)) / (sInt16Scale * maxXOut);
2056 // inputSize determines by how much we want to scale the palette:
2057 // values < 128 display a fraction of a palette,
2058 // values > 128 display multiple palettes.
2059
2/2
✓ Branch 0 taken 82775 times.
✓ Branch 1 taken 2048 times.
84823 if (inputSize <= 128) {
2060 82775 colorIndex = (colorIndex * inputSize) / 128;
2061 } else {
2062 // Linear function that maps colorIndex 128=>1, 256=>9.
2063 // With this function every full palette repetition is exactly 16 configuration steps wide.
2064 // That allows displaying exactly 2 repetitions for example.
2065 2048 colorIndex = ((inputSize - 112) * colorIndex) / 16;
2066 }
2067 // Finally, shift the palette a bit.
2068
2/4
✓ Branch 0 taken 84823 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 84823 times.
✗ Branch 3 not taken.
84823 const int paletteOffset = (!inputAnimateShift) ? (inputShift) : (((strip().now * ((inputShift >> 3) +1)) & 0xFFFF) >> 8);
2069 84823 colorIndex -= paletteOffset;
2070
3/6
✓ Branch 0 taken 84823 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 84823 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 84823 times.
✗ Branch 5 not taken.
84823 const uint32_t color = SEGMENT.color_wheel((uint8_t)colorIndex);
2071
2/2
✓ Branch 0 taken 84073 times.
✓ Branch 1 taken 750 times.
84823 if (isMatrix) {
2072
3/6
✓ Branch 0 taken 84073 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 84073 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 84073 times.
✗ Branch 5 not taken.
84073 SEGMENT.setPixelColorXY(x, y, color);
2073 } else {
2074
3/6
✓ Branch 0 taken 750 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 750 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 750 times.
✗ Branch 5 not taken.
750 SEGMENT.setPixelColor(x, color);
2075 }
2076 }
2077 }
2078 174 return FRAMETIME;
2079 }
2080 static const char _data_FX_MODE_PALETTE[] PROGMEM = "Palette@Shift,Size,Rotation,,,Animate Shift,Animate Rotation,Anamorphic;;!;12;ix=112,c1=0,o1=1,o2=0,o3=1";
2081
2082 #ifdef WLED_PS_DONT_REPLACE_FX
2083 // WLED limitation: Analog Clock overlay will NOT work when Fire2012 is active
2084 // Fire2012 by Mark Kriegsman, July 2012
2085 // as part of "Five Elements" shown here: http://youtu.be/knWiGsmgycY
2086 ////
2087 // This basic one-dimensional 'fire' simulation works roughly as follows:
2088 // There's a underlying array of 'heat' cells, that model the temperature
2089 // at each point along the line. Every cycle through the simulation,
2090 // four steps are performed:
2091 // 1) All cells cool down a little bit, losing heat to the air
2092 // 2) The heat from each cell drifts 'up' and diffuses a little
2093 // 3) Sometimes randomly new 'sparks' of heat are added at the bottom
2094 // 4) The heat from each cell is rendered as a color into the leds array
2095 // The heat-to-color mapping uses a black-body radiation approximation.
2096 //
2097 // Temperature is in arbitrary units from 0 (cold black) to 255 (white hot).
2098 //
2099 // This simulation scales it self a bit depending on SEGLEN; it should look
2100 // "OK" on anywhere from 20 to 100 LEDs without too much tweaking.
2101 //
2102 // I recommend running this simulation at anywhere from 30-100 frames per second,
2103 // meaning an interframe delay of about 10-35 milliseconds.
2104 //
2105 // Looks best on a high-density LED setup (60+ pixels/meter).
2106 //
2107 //
2108 // There are two main parameters you can play with to control the look and
2109 // feel of your fire: COOLING (used in step 1 above) (Speed = COOLING), and SPARKING (used
2110 // in step 3 above) (Effect Intensity = Sparking).
2111 23 uint16_t mode_fire_2012() {
2112
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
2113 23 const unsigned strips = SEGMENT.nrOfVStrips();
2114
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (!SEGENV.allocateData(strips * SEGLEN)) return mode_static(); //allocation failed
2115 23 byte* heat = SEGENV.data;
2116
2117 23 const uint32_t it = strip().now >> 5; //div 32
2118
2119 struct virtualStrip {
2120 456 static void runStrip(uint16_t stripNr, byte* heat, uint32_t it) {
2121
2122
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 451 times.
456 const uint8_t ignition = MAX(3,SEGLEN/10); // ignition area: 10% of segment length or minimum 3 pixels
2123
2124 // Step 1. Cool down every cell a little
2125
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 456 times.
7967 for (unsigned i = 0; i < SEGLEN; i++) {
2126
1/2
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
7511 uint8_t cool = (it != SEGENV.step) ? hw_random8((((20 + SEGMENT.speed/3) * 16) / SEGLEN)+2) : hw_random8(4);
2127
2/2
✓ Branch 0 taken 1428 times.
✓ Branch 1 taken 6083 times.
7511 uint8_t minTemp = (i<ignition) ? (ignition-i)/4 + 16 : 0; // should not become black in ignition area
2128
2/2
✓ Branch 0 taken 6000 times.
✓ Branch 1 taken 1511 times.
7511 uint8_t temp = qsub8(heat[i], cool);
2129
2/2
✓ Branch 0 taken 1015 times.
✓ Branch 1 taken 6496 times.
7511 heat[i] = temp<minTemp ? minTemp : temp;
2130 }
2131
2132
1/2
✓ Branch 0 taken 456 times.
✗ Branch 1 not taken.
456 if (it != SEGENV.step) {
2133 // Step 2. Heat from each cell drifts 'up' and diffuses a little
2134
2/2
✓ Branch 0 taken 6599 times.
✓ Branch 1 taken 456 times.
7055 for (int k = SEGLEN -1; k > 1; k--) {
2135 6599 heat[k] = (heat[k - 1] + (heat[k - 2]<<1) ) / 3; // heat[k-2] multiplied by 2
2136 }
2137
2138 // Step 3. Randomly ignite new 'sparks' of heat near the bottom
2139
2/2
✓ Branch 0 taken 261 times.
✓ Branch 1 taken 195 times.
456 if (hw_random8() <= SEGMENT.intensity) {
2140 261 uint8_t y = hw_random8(ignition);
2141 261 uint8_t boost = (17+SEGMENT.custom3) * (ignition - y/2) / ignition; // integer math!
2142
2/2
✓ Branch 0 taken 104 times.
✓ Branch 1 taken 157 times.
522 heat[y] = qadd8(heat[y], hw_random8(96+2*boost,207+boost));
2143 }
2144 }
2145
2146 // Step 4. Map from heat cells to LED colors
2147
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 456 times.
7967 for (unsigned j = 0; j < SEGLEN; j++) {
2148 7511 SEGMENT.setPixelColor(indexToVStrip(j, stripNr), ColorFromPalette(SEGPALETTE, min(heat[j], byte(240)), 255, NOBLEND));
2149 }
2150 456 }
2151 };
2152
2153
2/2
✓ Branch 0 taken 456 times.
✓ Branch 1 taken 23 times.
479 for (unsigned stripNr=0; stripNr<strips; stripNr++)
2154 456 virtualStrip::runStrip(stripNr, &heat[stripNr * SEGLEN], it);
2155
2156
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
23 if (SEGMENT.is2D()) {
2157 18 uint8_t blurAmount = SEGMENT.custom2 >> 2;
2158
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 14 times.
18 if (blurAmount > 48) blurAmount += blurAmount-48; // extra blur when slider > 192 (bush burn)
2159
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 14 times.
18 if (blurAmount < 16) SEGMENT.blurCols(SEGMENT.custom2 >> 1); // no side-burn when slider < 64 (faster)
2160 14 else SEGMENT.blur(blurAmount);
2161 }
2162
2163
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 if (it != SEGENV.step)
2164 23 SEGENV.step = it;
2165
2166 23 return FRAMETIME;
2167 }
2168 static const char _data_FX_MODE_FIRE_2012[] PROGMEM = "Fire 2012@Cooling,Spark rate,,2D Blur,Boost;;!;1;pal=35,sx=64,ix=160,m12=1,c2=128"; // bars
2169 #endif // WLED_PS_DONT_REPLACE_FX
2170
2171 // colored stripes pulsing at a defined Beats-Per-Minute (BPM)
2172 23 uint16_t mode_bpm() {
2173 23 uint32_t stp = (strip().now / 20) & 0xFF;
2174 23 uint8_t beat = beatsin8_t(SEGMENT.speed, 64, 255);
2175
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
2176
2/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7511 times.
7511 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(stp + (i * 2), false, PALETTE_SOLID_WRAP, 0, beat - stp + (i * 10)));
2177 }
2178
2179 23 return FRAMETIME;
2180 }
2181 static const char _data_FX_MODE_BPM[] PROGMEM = "Bpm@!;!;!;;sx=64";
2182
2183
2184 23 uint16_t mode_fillnoise8() {
2185
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if (SEGENV.call == 0) SEGENV.step = hw_random();
2186
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
2187 7511 unsigned index = perlin8(i * SEGLEN, SEGENV.step + i * SEGLEN);
2188
2/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7511 times.
7511 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0));
2189 }
2190 23 SEGENV.step += beatsin8_t(SEGMENT.speed, 1, 6); //10,1,4
2191
2192 23 return FRAMETIME;
2193 }
2194 static const char _data_FX_MODE_FILLNOISE8[] PROGMEM = "Fill Noise@!;!;!";
2195
2196
2197 23 uint16_t mode_noise16_1() {
2198 23 unsigned scale = 320; // the "zoom factor" for the noise
2199 23 SEGENV.step += (1 + SEGMENT.speed/16);
2200
2201
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
2202 7511 unsigned shift_x = beatsin8_t(11); // the x position of the noise field swings @ 17 bpm
2203 7511 unsigned shift_y = SEGENV.step/42; // the y position becomes slowly incremented
2204 7511 unsigned real_x = (i + shift_x) * scale; // the x position of the noise field swings @ 17 bpm
2205 7511 unsigned real_y = (i + shift_y) * scale; // the y position becomes slowly incremented
2206 7511 uint32_t real_z = SEGENV.step; // the z position becomes quickly incremented
2207 7511 unsigned noise = perlin16(real_x, real_y, real_z) >> 8; // get the noise data and scale it down
2208 7511 unsigned index = sin8_t(noise * 3); // map LED color based on noise data
2209
2210
2/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7511 times.
7511 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0));
2211 }
2212
2213 23 return FRAMETIME;
2214 }
2215 static const char _data_FX_MODE_NOISE16_1[] PROGMEM = "Noise 1@!;!;!;;pal=20";
2216
2217
2218 23 uint16_t mode_noise16_2() {
2219 23 unsigned scale = 1000; // the "zoom factor" for the noise
2220 23 SEGENV.step += (1 + (SEGMENT.speed >> 1));
2221
2222
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
2223 7511 unsigned shift_x = SEGENV.step >> 6; // x as a function of time
2224 7511 uint32_t real_x = (i + shift_x) * scale; // calculate the coordinates within the noise field
2225 7511 unsigned noise = perlin16(real_x, 0, 4223) >> 8; // get the noise data and scale it down
2226 7511 unsigned index = sin8_t(noise * 3); // map led color based on noise data
2227
2228
2/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7511 times.
7511 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0, noise));
2229 }
2230
2231 23 return FRAMETIME;
2232 }
2233 static const char _data_FX_MODE_NOISE16_2[] PROGMEM = "Noise 2@!;!;!;;pal=43";
2234
2235
2236 23 uint16_t mode_noise16_3() {
2237 23 unsigned scale = 800; // the "zoom factor" for the noise
2238 23 SEGENV.step += (1 + SEGMENT.speed);
2239
2240
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
2241 7511 unsigned shift_x = 4223; // no movement along x and y
2242 7511 unsigned shift_y = 1234;
2243 7511 uint32_t real_x = (i + shift_x) * scale; // calculate the coordinates within the noise field
2244 7511 uint32_t real_y = (i + shift_y) * scale; // based on the precalculated positions
2245 7511 uint32_t real_z = SEGENV.step*8;
2246 7511 unsigned noise = perlin16(real_x, real_y, real_z) >> 8; // get the noise data and scale it down
2247 7511 unsigned index = sin8_t(noise * 3); // map led color based on noise data
2248
2249
2/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7511 times.
7511 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0, noise));
2250 }
2251
2252 23 return FRAMETIME;
2253 }
2254 static const char _data_FX_MODE_NOISE16_3[] PROGMEM = "Noise 3@!;!;!;;pal=35";
2255
2256
2257 //https://github.com/aykevl/ledstrip-spark/blob/master/ledstrip().ino
2258 23 uint16_t mode_noise16_4() {
2259 23 uint32_t stp = (strip().now * SEGMENT.speed) >> 7;
2260
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
2261 7511 int index = perlin16(uint32_t(i) << 12, stp);
2262
2/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7511 times.
7511 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0));
2263 }
2264 23 return FRAMETIME;
2265 }
2266 static const char _data_FX_MODE_NOISE16_4[] PROGMEM = "Noise 4@!;!;!;;pal=26";
2267
2268
2269 //based on https://gist.github.com/kriegsman/5408ecd397744ba0393e
2270 23 uint16_t mode_colortwinkle() {
2271 23 unsigned dataSize = (SEGLEN+7) >> 3; //1 bit per LED
2272
4/10
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 23 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
23 if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
2273
2274 CRGBW col, prev;
2275
4/10
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
23 fract8 fadeUpAmount = strip().getBrightness()>28 ? 8 + (SEGMENT.speed>>2) : 68-strip().getBrightness();
2276
4/10
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
23 fract8 fadeDownAmount = strip().getBrightness()>28 ? 8 + (SEGMENT.speed>>3) : 68-strip().getBrightness();
2277
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
2278
3/6
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7511 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7511 times.
✗ Branch 5 not taken.
7511 CRGBW cur = SEGMENT.getPixelColor(i);
2279 7511 prev = cur;
2280 7511 unsigned index = i >> 3;
2281 7511 unsigned bitNum = i & 0x07;
2282
2/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7511 times.
✗ Branch 3 not taken.
7511 bool fadeUp = bitRead(SEGENV.data[index], bitNum);
2283
2284
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7511 times.
7511 if (fadeUp) {
2285 ✗ CRGBW incrementalColor = color_fade(cur, fadeUpAmount, true);
2286 ✗ col = color_add(cur, incrementalColor);
2287
2288 ✗ if (col.r == 255 || col.g == 255 || col.b == 255) {
2289 ✗ bitWrite(SEGENV.data[index], bitNum, false);
2290 }
2291
2292 ✗ if (cur == prev) { //fix "stuck" pixels
2293 ✗ color_add(col, col);
2294 ✗ SEGMENT.setPixelColor(i, col);
2295 }
2296 ✗ else SEGMENT.setPixelColor(i, col);
2297 }
2298 else {
2299
1/2
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
15022 col = color_fade(cur, 255 - fadeDownAmount);
2300
3/6
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7511 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7511 times.
✗ Branch 5 not taken.
15022 SEGMENT.setPixelColor(i, col);
2301 }
2302 }
2303
2304
2/2
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 23 times.
191 for (unsigned j = 0; j <= SEGLEN / 50; j++) {
2305
4/6
✓ Branch 0 taken 168 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 168 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 93 times.
✓ Branch 5 taken 75 times.
168 if (hw_random8() <= SEGMENT.intensity) {
2306
2/2
✓ Branch 0 taken 465 times.
✓ Branch 1 taken 93 times.
558 for (unsigned times = 0; times < 5; times++) { //attempt to spawn a new pixel 5 times
2307 465 int i = hw_random16(SEGLEN);
2308
4/8
✓ Branch 0 taken 465 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 465 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 465 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 465 times.
465 if (SEGMENT.getPixelColor(i) == 0) {
2309 ✗ unsigned index = i >> 3;
2310 ✗ unsigned bitNum = i & 0x07;
2311 ✗ bitWrite(SEGENV.data[index], bitNum, true);
2312 ✗ SEGMENT.setPixelColor(i, ColorFromPalette(SEGPALETTE, hw_random8(), 64, NOBLEND));
2313 ✗ break; //only spawn 1 new pixel per frame per 50 LEDs
2314 }
2315 }
2316 }
2317 }
2318 23 return FRAMETIME_FIXED;
2319 }
2320 static const char _data_FX_MODE_COLORTWINKLE[] PROGMEM = "Colortwinkles@Fade speed,Spawn speed;;!;;m12=0"; //pixels
2321
2322
2323 //Calm effect, like a lake at night
2324 23 uint16_t mode_lake() {
2325 23 unsigned sp = SEGMENT.speed/10;
2326 23 int wave1 = beatsin8_t(sp +2, -64,64);
2327 23 int wave2 = beatsin8_t(sp +1, -64,64);
2328 23 int wave3 = beatsin8_t(sp +2, 0,80);
2329
2330
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++)
2331 {
2332 7511 int index = cos8_t((i*15)+ wave1)/2 + cubicwave8((i*23)+ wave2)/2;
2333
2/2
✓ Branch 0 taken 6247 times.
✓ Branch 1 taken 1264 times.
7511 uint8_t lum = (index > wave3) ? index - wave3 : 0;
2334 7511 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, false, 0, lum));
2335 }
2336
2337 23 return FRAMETIME;
2338 }
2339 static const char _data_FX_MODE_LAKE[] PROGMEM = "Lake@!;Fx;!";
2340
2341
2342 // meteor effect & meteor smooth (merged by @dedehai)
2343 // send a meteor from begining to to the end of the strip with a trail that randomly decays.
2344 // adapted from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectMeteorRain
2345 23 uint16_t mode_meteor() {
2346
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
2347
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed
2348 23 const bool meteorSmooth = SEGMENT.check3;
2349 23 byte* trail = SEGENV.data;
2350
2351 23 const unsigned meteorSize = 1 + SEGLEN / 20; // 5%
2352 uint16_t meteorstart;
2353
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if(meteorSmooth) meteorstart = map((SEGENV.step >> 6 & 0xFF), 0, 255, 0, SEGLEN -1);
2354 else {
2355 23 unsigned counter = strip().now * ((SEGMENT.speed >> 2) + 8);
2356 23 meteorstart = (counter * SEGLEN) >> 16;
2357 }
2358
2359
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 const int max = SEGMENT.palette==5 || !SEGMENT.check1 ? 240 : 255;
2360 // fade all leds to colors[1] in LEDs one step
2361
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
2362 uint32_t col;
2363
2/2
✓ Branch 0 taken 3732 times.
✓ Branch 1 taken 3779 times.
7511 if (hw_random8() <= 255 - SEGMENT.intensity) {
2364
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3732 times.
3732 if(meteorSmooth) {
2365 ✗ if (trail[i] > 0) {
2366 ✗ int change = trail[i] + 4 - hw_random8(24); //change each time between -20 and +4
2367 ✗ trail[i] = constrain(change, 0, max);
2368 }
2369 ✗ col = SEGMENT.check1 ? SEGMENT.color_from_palette(i, true, false, 0, trail[i]) : SEGMENT.color_from_palette(trail[i], false, true, 255);
2370 }
2371 else {
2372 3732 trail[i] = scale8(trail[i], 128 + hw_random8(127));
2373 3732 int index = trail[i];
2374 3732 int idx = 255;
2375
2/4
✓ Branch 0 taken 3732 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3732 times.
✗ Branch 3 not taken.
3732 int bri = SEGMENT.palette==35 || SEGMENT.palette==36 ? 255 : trail[i];
2376
1/2
✓ Branch 0 taken 3732 times.
✗ Branch 1 not taken.
3732 if (!SEGMENT.check1) {
2377 3732 idx = 0;
2378 3732 index = map(i,0,SEGLEN,0,max);
2379 3732 bri = trail[i];
2380 }
2381 3732 col = SEGMENT.color_from_palette(index, false, false, idx, bri); // full brightness for Fire
2382 }
2383 3732 SEGMENT.setPixelColor(i, col);
2384 }
2385 }
2386
2387 // draw meteor
2388
2/2
✓ Branch 0 taken 388 times.
✓ Branch 1 taken 23 times.
411 for (unsigned j = 0; j < meteorSize; j++) {
2389 388 unsigned index = (meteorstart + j) % SEGLEN;
2390
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 388 times.
388 if(meteorSmooth) {
2391 ✗ trail[index] = max;
2392 ✗ uint32_t col = SEGMENT.check1 ? SEGMENT.color_from_palette(index, true, false, 0, trail[index]) : SEGMENT.color_from_palette(trail[index], false, true, 255);
2393 ✗ SEGMENT.setPixelColor(index, col);
2394 }
2395 else{
2396 388 int idx = 255;
2397 388 int i = trail[index] = max;
2398
1/2
✓ Branch 0 taken 388 times.
✗ Branch 1 not taken.
388 if (!SEGMENT.check1) {
2399 388 i = map(index,0,SEGLEN,0,max);
2400 388 idx = 0;
2401 }
2402 388 uint32_t col = SEGMENT.color_from_palette(i, false, false, idx, 255); // full brightness
2403 388 SEGMENT.setPixelColor(index, col);
2404 }
2405 }
2406
2407 23 SEGENV.step += SEGMENT.speed +1;
2408 23 return FRAMETIME;
2409 }
2410 static const char _data_FX_MODE_METEOR[] PROGMEM = "Meteor@!,Trail,,,,Gradient,,Smooth;;!;1";
2411
2412
2413 //Railway Crossing / Christmas Fairy lights
2414 23 uint16_t mode_railway() {
2415
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
2416 23 unsigned dur = (256 - SEGMENT.speed) * 40;
2417 23 uint16_t rampdur = (dur * SEGMENT.intensity) >> 8;
2418
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 22 times.
23 if (SEGENV.step > dur)
2419 {
2420 //reverse direction
2421 1 SEGENV.step = 0;
2422 1 SEGENV.aux0 = !SEGENV.aux0;
2423 }
2424 23 unsigned pos = 255;
2425
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 4 times.
23 if (rampdur != 0)
2426 {
2427 19 unsigned p0 = (SEGENV.step * 255) / rampdur;
2428
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 if (p0 < 255) pos = p0;
2429 }
2430
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 21 times.
23 if (SEGENV.aux0) pos = 255 - pos;
2431
2/2
✓ Branch 0 taken 3758 times.
✓ Branch 1 taken 23 times.
3781 for (unsigned i = 0; i < SEGLEN; i += 2)
2432 {
2433 3758 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(255 - pos, false, false, 255)); // do not use color 1 or 2, always use palette
2434
2/2
✓ Branch 0 taken 3753 times.
✓ Branch 1 taken 5 times.
3758 if (i < SEGLEN -1)
2435 {
2436 3753 SEGMENT.setPixelColor(i + 1, SEGMENT.color_from_palette(pos, false, false, 255)); // do not use color 1 or 2, always use palette
2437 }
2438 }
2439 23 SEGENV.step += FRAMETIME;
2440 23 return FRAMETIME;
2441 }
2442 static const char _data_FX_MODE_RAILWAY[] PROGMEM = "Railway@!,Smoothness;1,2;!;;pal=3";
2443
2444
2445 //Water ripple
2446 //propagation velocity from speed
2447 //drop rate from intensity
2448
2449 //4 bytes
2450 typedef struct Ripple {
2451 uint8_t state;
2452 uint8_t color;
2453 uint16_t pos;
2454 } ripple;
2455
2456 #ifdef ESP8266
2457 #define MAX_RIPPLES 56
2458 #else
2459 #define MAX_RIPPLES 100
2460 #endif
2461 46 static uint16_t ripple_base(uint8_t blurAmount = 0) {
2462 46 unsigned maxRipples = min(1 + (int)(SEGLEN >> 2), MAX_RIPPLES); // 56 max for 16 segment ESP8266
2463 46 unsigned dataSize = sizeof(ripple) * maxRipples;
2464
2465
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
46 if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
2466
2467 46 Ripple* ripples = reinterpret_cast<Ripple*>(SEGENV.data);
2468
2469 //draw wave
2470
2/2
✓ Branch 0 taken 3040 times.
✓ Branch 1 taken 46 times.
3086 for (unsigned i = 0; i < maxRipples; i++) {
2471 3040 unsigned ripplestate = ripples[i].state;
2472
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 3029 times.
3040 if (ripplestate) {
2473 11 unsigned rippledecay = (SEGMENT.speed >> 4) +1; //faster decay if faster propagation
2474 11 unsigned rippleorigin = ripples[i].pos;
2475 11 uint32_t col = SEGMENT.color_from_palette(ripples[i].color, false, false, 255);
2476 11 unsigned propagation = ((ripplestate/rippledecay - 1) * (SEGMENT.speed + 1));
2477 11 int propI = propagation >> 8;
2478 11 unsigned propF = propagation & 0xFF;
2479
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 2 times.
11 unsigned amp = (ripplestate < 17) ? triwave8((ripplestate-1)*8) : map(ripplestate,17,255,255,2);
2480
2481 #ifndef WLED_DISABLE_2D
2482
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 6 times.
11 if (SEGMENT.is2D()) {
2483 5 propI /= 2;
2484 5 unsigned cx = rippleorigin >> 8;
2485 5 unsigned cy = rippleorigin & 0xFF;
2486 5 unsigned mag = scale8(sin8_t((propF>>2)), amp);
2487
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
5 if (propI > 0) SEGMENT.drawCircle(cx, cy, propI, color_blend(SEGMENT.getPixelColorXY(cx + propI, cy), col, mag), true);
2488 } else
2489 #endif
2490 {
2491 6 int left = rippleorigin - propI -1;
2492 6 int right = rippleorigin + propI +2;
2493
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 6 times.
30 for (int v = 0; v < 4; v++) {
2494 24 uint8_t mag = scale8(cubicwave8((propF>>2) + v * 64), amp);
2495 24 SEGMENT.setPixelColor(left + v, color_blend(SEGMENT.getPixelColor(left + v), col, mag)); // TODO
2496 24 SEGMENT.setPixelColor(right - v, color_blend(SEGMENT.getPixelColor(right - v), col, mag)); // TODO
2497 }
2498 }
2499 11 ripplestate += rippledecay;
2500
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 ripples[i].state = (ripplestate > 254) ? 0 : ripplestate;
2501 } else {//randomly create new wave
2502
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 3024 times.
3029 if (hw_random16(IBN + 10000) <= (SEGMENT.intensity >> (SEGMENT.is2D()*3))) {
2503 5 ripples[i].state = 1;
2504
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 ripples[i].pos = SEGMENT.is2D() ? ((hw_random8(SEG_W)<<8) | (hw_random8(SEG_H))) : hw_random16(SEGLEN);
2505 5 ripples[i].color = hw_random8(); //color
2506 }
2507 }
2508 }
2509 46 SEGMENT.blur(blurAmount);
2510 46 return FRAMETIME;
2511 }
2512 #undef MAX_RIPPLES
2513
2514
2515 23 uint16_t mode_ripple(void) {
2516
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
2517
5/6
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 19 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 19 times.
23 if(SEGMENT.custom1 || SEGMENT.check2) // blur or overlay
2518 4 SEGMENT.fade_out(250);
2519 else
2520 19 SEGMENT.fill(SEGCOLOR(1));
2521
2522 23 return ripple_base(SEGMENT.custom1>>1);
2523 }
2524 static const char _data_FX_MODE_RIPPLE[] PROGMEM = "Ripple@!,Wave #,Blur,,,,Overlay;,!;!;12;c1=0";
2525
2526
2527 23 uint16_t mode_ripple_rainbow(void) {
2528
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
2529
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if (SEGENV.call ==0) {
2530 5 SEGENV.aux0 = hw_random8();
2531 5 SEGENV.aux1 = hw_random8();
2532 }
2533
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 22 times.
23 if (SEGENV.aux0 == SEGENV.aux1) {
2534 1 SEGENV.aux1 = hw_random8();
2535
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 13 times.
22 } else if (SEGENV.aux1 > SEGENV.aux0) {
2536 9 SEGENV.aux0++;
2537 } else {
2538 13 SEGENV.aux0--;
2539 }
2540 23 SEGMENT.fill(color_blend(SEGMENT.color_wheel(SEGENV.aux0),BLACK,uint8_t(235)));
2541 23 return ripple_base();
2542 }
2543 static const char _data_FX_MODE_RIPPLE_RAINBOW[] PROGMEM = "Ripple Rainbow@!,Wave #;;!;12";
2544
2545
2546 // TwinkleFOX by Mark Kriegsman: https://gist.github.com/kriegsman/756ea6dcae8e30845b5a
2547 //
2548 // TwinkleFOX: Twinkling 'holiday' lights that fade in and out.
2549 // Colors are chosen from a palette. Read more about this effect using the link above!
2550 15022 static CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat)
2551 {
2552 // Overall twinkle speed (changed)
2553
2/4
✓ Branch 0 taken 15022 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15022 times.
✗ Branch 3 not taken.
15022 unsigned ticks = ms / SEGENV.aux0;
2554 15022 unsigned fastcycle8 = uint8_t(ticks);
2555 15022 uint16_t slowcycle16 = (ticks >> 8) + salt;
2556
1/2
✓ Branch 0 taken 15022 times.
✗ Branch 1 not taken.
15022 slowcycle16 += sin8_t(slowcycle16);
2557 15022 slowcycle16 = (slowcycle16 * 2053) + 1384;
2558 15022 uint8_t slowcycle8 = (slowcycle16 & 0xFF) + (slowcycle16 >> 8);
2559
2560 // Overall twinkle density.
2561 // 0 (NONE lit) to 8 (ALL lit at once).
2562 // Default is 5.
2563
2/4
✓ Branch 0 taken 15022 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15022 times.
✗ Branch 3 not taken.
15022 unsigned twinkleDensity = (SEGMENT.intensity >> 5) +1;
2564
2565 15022 unsigned bright = 0;
2566
2/2
✓ Branch 0 taken 9141 times.
✓ Branch 1 taken 5881 times.
15022 if (((slowcycle8 & 0x0E)/2) < twinkleDensity) {
2567 9141 unsigned ph = fastcycle8;
2568 // This is like 'triwave8', which produces a
2569 // symmetrical up-and-down triangle sawtooth waveform, except that this
2570 // function produces a triangle wave with a faster attack and a slower decay
2571
2/2
✓ Branch 0 taken 4573 times.
✓ Branch 1 taken 4568 times.
9141 if (cat) //twinklecat, variant where the leds instantly turn on
2572 {
2573 4573 bright = 255 - ph;
2574 } else { //vanilla twinklefox
2575
2/2
✓ Branch 0 taken 1610 times.
✓ Branch 1 taken 2958 times.
4568 if (ph < 86) {
2576 1610 bright = ph * 3;
2577 } else {
2578 2958 ph -= 86;
2579 2958 bright = 255 - (ph + (ph/2));
2580 }
2581 }
2582 }
2583
2584 15022 unsigned hue = slowcycle8 - salt;
2585 CRGB c;
2586
2/2
✓ Branch 0 taken 9110 times.
✓ Branch 1 taken 5912 times.
15022 if (bright > 0) {
2587
1/2
✓ Branch 0 taken 9110 times.
✗ Branch 1 not taken.
9110 c = ColorFromPalette(SEGPALETTE, hue, bright, NOBLEND);
2588
3/6
✓ Branch 0 taken 9110 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9110 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 9110 times.
✗ Branch 5 not taken.
9110 if (!SEGMENT.check1) {
2589 // This code takes a pixel, and if its in the 'fading down'
2590 // part of the cycle, it adjusts the color a little bit like the
2591 // way that incandescent bulbs fade toward 'red' as they dim.
2592
2/2
✓ Branch 0 taken 4469 times.
✓ Branch 1 taken 4641 times.
9110 if (fastcycle8 >= 128)
2593 {
2594 4469 unsigned cooling = (fastcycle8 - 128) >> 4;
2595
2/2
✓ Branch 0 taken 2681 times.
✓ Branch 1 taken 1788 times.
4469 c.g = qsub8(c.g, cooling);
2596
2/2
✓ Branch 0 taken 2157 times.
✓ Branch 1 taken 2312 times.
8938 c.b = qsub8(c.b, cooling * 2);
2597 }
2598 }
2599 } else {
2600 c = CRGB::Black;
2601 }
2602 15022 return c;
2603 }
2604
2605 // This function loops over each pixel, calculates the
2606 // adjusted 'clock' that this pixel should use, and calls
2607 // "CalculateOneTwinkle" on each pixel. It then displays
2608 // either the twinkle color of the background color,
2609 // whichever is brighter.
2610 46 static uint16_t twinklefox_base(bool cat)
2611 {
2612 // "PRNG16" is the pseudorandom number generator
2613 // It MUST be reset to the same starting value each time
2614 // this function is called, so that the sequence of 'random'
2615 // numbers that it generates is (paradoxically) stable.
2616 46 uint16_t PRNG16 = 11337;
2617
2618 // Calculate speed
2619
8/14
✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 38 times.
✓ Branch 5 taken 8 times.
✓ Branch 6 taken 38 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 38 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 38 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 38 times.
✗ Branch 13 not taken.
46 if (SEGMENT.speed > 100) SEGENV.aux0 = 3 + ((255 - SEGMENT.speed) >> 3);
2620
4/8
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
8 else SEGENV.aux0 = 22 + ((100 - SEGMENT.speed) >> 1);
2621
2622 // Set up the background color, "bg".
2623 46 CRGB bg = CRGB(SEGCOLOR(1));
2624 46 unsigned bglight = bg.getAverageLight();
2625
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
46 if (bglight > 64) {
2626 bg.nscale8_video(16); // very bright, so scale to 1/16th
2627
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 30 times.
46 } else if (bglight > 16) {
2628 bg.nscale8_video(64); // not that bright, so scale to 1/4th
2629 } else {
2630 bg.nscale8_video(86); // dim, scale to 1/3rd.
2631 }
2632
2633 46 unsigned backgroundBrightness = bg.getAverageLight();
2634
2635
2/2
✓ Branch 0 taken 15022 times.
✓ Branch 1 taken 46 times.
15068 for (unsigned i = 0; i < SEGLEN; i++) {
2636
2637 15022 PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number
2638 15022 unsigned myclockoffset16= PRNG16; // use that number as clock offset
2639 15022 PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number
2640 // use that number as clock speed adjustment factor (in 8ths, from 8/8ths to 23/8ths)
2641 15022 unsigned myspeedmultiplierQ5_3 = ((((PRNG16 & 0xFF)>>4) + (PRNG16 & 0x0F)) & 0x0F) + 0x08;
2642
1/2
✓ Branch 0 taken 15022 times.
✗ Branch 1 not taken.
15022 uint32_t myclock30 = (uint32_t)((strip().now * myspeedmultiplierQ5_3) >> 3) + myclockoffset16;
2643 15022 unsigned myunique8 = PRNG16 >> 8; // get 'salt' value for this pixel
2644
2645 // We now have the adjusted 'clock' for this pixel, now we call
2646 // the function that computes what color the pixel should be based
2647 // on the "brightness = f( time )" idea.
2648
1/2
✓ Branch 0 taken 15022 times.
✗ Branch 1 not taken.
15022 CRGB c = twinklefox_one_twinkle(myclock30, myunique8, cat);
2649
2650 15022 unsigned cbright = c.getAverageLight();
2651 15022 int deltabright = cbright - backgroundBrightness;
2652
5/6
✓ Branch 0 taken 9771 times.
✓ Branch 1 taken 5251 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 9771 times.
✓ Branch 4 taken 5251 times.
✓ Branch 5 taken 9771 times.
15022 if (deltabright >= 32 || (!bg)) {
2653 // If the new pixel is significantly brighter than the background color,
2654 // use the new color.
2655
3/6
✓ Branch 0 taken 5251 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5251 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5251 times.
✗ Branch 5 not taken.
5251 SEGMENT.setPixelColor(i, c);
2656
2/2
✓ Branch 0 taken 3190 times.
✓ Branch 1 taken 6581 times.
9771 } else if (deltabright > 0) {
2657 // If the new pixel is just slightly brighter than the background color,
2658 // mix a blend of the new color and the background color
2659
4/8
✓ Branch 0 taken 3190 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3190 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3190 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3190 times.
✗ Branch 7 not taken.
3190 SEGMENT.setPixelColor(i, color_blend(RGBW32(bg.r,bg.g,bg.b,0), RGBW32(c.r,c.g,c.b,0), uint8_t(deltabright * 8)));
2660 } else {
2661 // if the new pixel is not at all brighter than the background color,
2662 // just use the background color.
2663
3/6
✓ Branch 0 taken 6581 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6581 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6581 times.
✗ Branch 5 not taken.
6581 SEGMENT.setPixelColor(i, bg);
2664 }
2665 }
2666
1/2
✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
92 return FRAMETIME;
2667 }
2668
2669
2670 23 uint16_t mode_twinklefox()
2671 {
2672 23 return twinklefox_base(false);
2673 }
2674 static const char _data_FX_MODE_TWINKLEFOX[] PROGMEM = "Twinklefox@!,Twinkle rate,,,,Cool;!,!;!";
2675
2676
2677 23 uint16_t mode_twinklecat()
2678 {
2679 23 return twinklefox_base(true);
2680 }
2681 static const char _data_FX_MODE_TWINKLECAT[] PROGMEM = "Twinklecat@!,Twinkle rate,,,,Cool;!,!;!";
2682
2683
2684 23 uint16_t mode_halloween_eyes()
2685 {
2686 enum eyeState : uint8_t {
2687 initializeOn = 0,
2688 on,
2689 blink,
2690 initializeOff,
2691 off,
2692
2693 count
2694 };
2695 struct EyeData {
2696 eyeState state;
2697 uint8_t color;
2698 uint16_t startPos;
2699 // duration + endTime could theoretically be replaced by a single endTime, however we would lose
2700 // the ability to end the animation early when the user reduces the animation time.
2701 uint16_t duration;
2702 uint32_t startTime;
2703 uint32_t blinkEndTime;
2704 };
2705
2706
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
2707
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
23 const unsigned maxWidth = strip().isMatrix ? SEG_W : SEGLEN;
2708
6/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 13 times.
✓ Branch 5 taken 5 times.
23 const unsigned HALLOWEEN_EYE_SPACE = MAX(2, strip().isMatrix ? SEG_W>>4: SEGLEN>>5);
2709 23 const unsigned HALLOWEEN_EYE_WIDTH = HALLOWEEN_EYE_SPACE/2;
2710 23 unsigned eyeLength = (2*HALLOWEEN_EYE_WIDTH) + HALLOWEEN_EYE_SPACE;
2711
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (eyeLength >= maxWidth) return mode_static(); //bail if segment too short
2712
2713
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (!SEGENV.allocateData(sizeof(EyeData))) return mode_static(); //allocation failed
2714 23 EyeData& data = *reinterpret_cast<EyeData*>(SEGENV.data);
2715
2716
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1)); //fill background
2717
2718 23 data.state = static_cast<eyeState>(data.state % eyeState::count);
2719 23 unsigned duration = max(uint16_t{1u}, data.duration);
2720 23 const uint32_t elapsedTime = strip().now - data.startTime;
2721
2722
4/7
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
23 switch (data.state) {
2723 6 case eyeState::initializeOn: {
2724 // initialize the eyes-on state:
2725 // - select eye position and color
2726 // - select a duration
2727 // - immediately switch to eyes on state.
2728
2729 6 data.startPos = hw_random16(0, maxWidth - eyeLength - 1);
2730 6 data.color = hw_random8();
2731
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 if (strip().isMatrix) SEGMENT.offset = hw_random16(SEG_H-1); // a hack: reuse offset since it is not used in matrices
2732 6 duration = 128u + hw_random16(SEGMENT.intensity*64u);
2733 6 data.duration = duration;
2734 6 data.state = eyeState::on;
2735 [[fallthrough]];
2736 }
2737 7 case eyeState::on: {
2738 // eyes-on steate:
2739 // - fade eyes in for some time
2740 // - keep eyes on until the pre-selected duration is over
2741 // - randomly switch to the blink (sub-)state, and initialize it with a blink duration (more precisely, a blink end time stamp)
2742 // - never switch to the blink state if the animation just started or is about to end
2743
2744 7 unsigned start2ndEye = data.startPos + HALLOWEEN_EYE_WIDTH + HALLOWEEN_EYE_SPACE;
2745 // If the user reduces the input while in this state, limit the duration.
2746 7 duration = min(duration, (128u + (SEGMENT.intensity * 64u)));
2747
2748 7 constexpr uint32_t minimumOnTimeBegin = 1024u;
2749 7 constexpr uint32_t minimumOnTimeEnd = 1024u;
2750 7 const uint32_t fadeInAnimationState = elapsedTime * uint32_t{256u * 8u} / duration;
2751 7 const uint32_t backgroundColor = SEGCOLOR(1);
2752 7 const uint32_t eyeColor = SEGMENT.color_from_palette(data.color, false, false, 0);
2753 7 uint32_t c = eyeColor;
2754
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (fadeInAnimationState < 256u) {
2755 ✗ c = color_blend(backgroundColor, eyeColor, uint8_t(fadeInAnimationState));
2756
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2 times.
7 } else if (elapsedTime > minimumOnTimeBegin) {
2757
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 const uint32_t remainingTime = (elapsedTime >= duration) ? 0u : (duration - elapsedTime);
2758
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (remainingTime > minimumOnTimeEnd) {
2759 ✗ if (hw_random8() < 4u)
2760 {
2761 ✗ c = backgroundColor;
2762 ✗ data.state = eyeState::blink;
2763 ✗ data.blinkEndTime = strip().now + hw_random8(8, 128);
2764 }
2765 }
2766 }
2767
2768
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 if (c != backgroundColor) {
2769 // render eyes
2770
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 7 times.
15 for (unsigned i = 0; i < HALLOWEEN_EYE_WIDTH; i++) {
2771
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 if (strip().isMatrix) {
2772 6 SEGMENT.setPixelColorXY(data.startPos + i, (unsigned)SEGMENT.offset, c);
2773 6 SEGMENT.setPixelColorXY(start2ndEye + i, (unsigned)SEGMENT.offset, c);
2774 } else {
2775 2 SEGMENT.setPixelColor(data.startPos + i, c);
2776 2 SEGMENT.setPixelColor(start2ndEye + i, c);
2777 }
2778 }
2779 }
2780 7 break;
2781 }
2782 ✗ case eyeState::blink: {
2783 // eyes-on but currently blinking state:
2784 // - wait until the blink time is over, then switch back to eyes-on
2785
2786 ✗ if (strip().now >= data.blinkEndTime) {
2787 ✗ data.state = eyeState::on;
2788 }
2789 ✗ break;
2790 }
2791 5 case eyeState::initializeOff: {
2792 // initialize eyes-off state:
2793 // - select a duration
2794 // - immediately switch to eyes-off state
2795
2796 5 const unsigned eyeOffTimeBase = SEGMENT.speed*128u;
2797 5 duration = eyeOffTimeBase + hw_random16(eyeOffTimeBase);
2798 5 data.duration = duration;
2799 5 data.state = eyeState::off;
2800 [[fallthrough]];
2801 }
2802 16 case eyeState::off: {
2803 // eyes-off state:
2804 // - not much to do here
2805
2806 // If the user reduces the input while in this state, limit the duration.
2807 16 const unsigned eyeOffTimeBase = SEGMENT.speed*128u;
2808 16 duration = min(duration, (2u * eyeOffTimeBase));
2809 16 break;
2810 }
2811 ✗ case eyeState::count: {
2812 // Can't happen, not an actual state.
2813 ✗ data.state = eyeState::initializeOn;
2814 ✗ break;
2815 }
2816 }
2817
2818
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 17 times.
23 if (elapsedTime > duration) {
2819 // The current state duration is over, switch to the next state.
2820
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 switch (data.state) {
2821 5 case eyeState::initializeOn:
2822 case eyeState::on:
2823 case eyeState::blink:
2824 5 data.state = eyeState::initializeOff;
2825 5 break;
2826 1 case eyeState::initializeOff:
2827 case eyeState::off:
2828 case eyeState::count:
2829 default:
2830 1 data.state = eyeState::initializeOn;
2831 1 break;
2832 }
2833 6 data.startTime = strip().now;
2834 }
2835
2836 23 return FRAMETIME;
2837 }
2838 static const char _data_FX_MODE_HALLOWEEN_EYES[] PROGMEM = "Halloween Eyes@Eye off time,Eye on time,,,,,Overlay;!,!;!;12";
2839
2840
2841 //Speed slider sets amount of LEDs lit, intensity sets unlit
2842 23 uint16_t mode_static_pattern()
2843 {
2844 23 unsigned lit = 1 + SEGMENT.speed;
2845 23 unsigned unlit = 1 + SEGMENT.intensity;
2846 23 bool drawingLit = true;
2847 23 unsigned cnt = 0;
2848
2849
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
2850
4/6
✓ Branch 0 taken 4088 times.
✓ Branch 1 taken 3423 times.
✓ Branch 2 taken 4088 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 4088 times.
7511 SEGMENT.setPixelColor(i, (drawingLit) ? SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0) : SEGCOLOR(1));
2851 7511 cnt++;
2852
4/4
✓ Branch 0 taken 4088 times.
✓ Branch 1 taken 3423 times.
✓ Branch 2 taken 2076 times.
✓ Branch 3 taken 5435 times.
7511 if (cnt >= ((drawingLit) ? lit : unlit)) {
2853 2076 cnt = 0;
2854 2076 drawingLit = !drawingLit;
2855 }
2856 }
2857
2858 23 return FRAMETIME;
2859 }
2860 static const char _data_FX_MODE_STATIC_PATTERN[] PROGMEM = "Solid Pattern@Fg size,Bg size;Fg,!;!;;pal=0";
2861
2862
2863 23 uint16_t mode_tri_static_pattern()
2864 {
2865 23 unsigned segSize = (SEGMENT.intensity >> 5) +1;
2866 23 unsigned currSeg = 0;
2867 23 unsigned currSegCount = 0;
2868
2869
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
2870
2/2
✓ Branch 0 taken 2548 times.
✓ Branch 1 taken 4963 times.
7511 if ( currSeg % 3 == 0 ) {
2871 2548 SEGMENT.setPixelColor(i, SEGCOLOR(0));
2872
2/2
✓ Branch 0 taken 2486 times.
✓ Branch 1 taken 2477 times.
4963 } else if( currSeg % 3 == 1) {
2873 2486 SEGMENT.setPixelColor(i, SEGCOLOR(1));
2874 } else {
2875 2477 SEGMENT.setPixelColor(i, SEGCOLOR(2));
2876 }
2877 7511 currSegCount += 1;
2878
2/2
✓ Branch 0 taken 2984 times.
✓ Branch 1 taken 4527 times.
7511 if (currSegCount >= segSize) {
2879 2984 currSeg +=1;
2880 2984 currSegCount = 0;
2881 }
2882 }
2883
2884 23 return FRAMETIME;
2885 }
2886 static const char _data_FX_MODE_TRI_STATIC_PATTERN[] PROGMEM = "Solid Pattern Tri@,Size;1,2,3;;;pal=0";
2887
2888
2889 46 static uint16_t spots_base(uint16_t threshold)
2890 {
2891
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
46 if (SEGLEN <= 1) return mode_static();
2892
1/2
✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
46 if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1));
2893
2894 46 unsigned maxZones = SEGLEN >> 2;
2895 46 unsigned zones = 1 + ((SEGMENT.intensity * maxZones) >> 8);
2896 46 unsigned zoneLen = SEGLEN / zones;
2897 46 unsigned offset = (SEGLEN - zones * zoneLen) >> 1;
2898
2899
2/2
✓ Branch 0 taken 1902 times.
✓ Branch 1 taken 46 times.
1948 for (unsigned z = 0; z < zones; z++)
2900 {
2901 1902 unsigned pos = offset + z * zoneLen;
2902
2/2
✓ Branch 0 taken 14282 times.
✓ Branch 1 taken 1902 times.
16184 for (unsigned i = 0; i < zoneLen; i++)
2903 {
2904 14282 unsigned wave = triwave16((i * 0xFFFF) / zoneLen);
2905
2/2
✓ Branch 0 taken 7548 times.
✓ Branch 1 taken 6734 times.
14282 if (wave > threshold) {
2906 7548 unsigned index = 0 + pos + i;
2907 7548 unsigned s = (wave - threshold)*255 / (0xFFFF - threshold);
2908
2/4
✓ Branch 0 taken 7548 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7548 times.
7548 SEGMENT.setPixelColor(index, color_blend(SEGMENT.color_from_palette(index, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), uint8_t(255-s)));
2909 }
2910 }
2911 }
2912
2913 46 return FRAMETIME;
2914 }
2915
2916
2917 //Intensity slider sets number of "lights", speed sets LEDs per light
2918 23 uint16_t mode_spots()
2919 {
2920 23 return spots_base((255 - SEGMENT.speed) << 8);
2921 }
2922 static const char _data_FX_MODE_SPOTS[] PROGMEM = "Spots@Spread,Width,,,,,Overlay;!,!;!";
2923
2924
2925 //Intensity slider sets number of "lights", LEDs per light fade in and out
2926 23 uint16_t mode_spots_fade()
2927 {
2928 23 unsigned counter = strip().now * ((SEGMENT.speed >> 2) +8);
2929 23 unsigned t = triwave16(counter);
2930 23 unsigned tr = (t >> 1) + (t >> 2);
2931 23 return spots_base(tr);
2932 }
2933 static const char _data_FX_MODE_SPOTS_FADE[] PROGMEM = "Spots Fade@Spread,Width,,,,,Overlay;!,!;!";
2934
2935 //each needs 12 bytes
2936 typedef struct Ball {
2937 unsigned long lastBounceTime;
2938 float impactVelocity;
2939 float height;
2940 } ball;
2941
2942 /*
2943 * Bouncing Balls Effect
2944 */
2945 23 uint16_t mode_bouncing_balls(void) {
2946
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
2947 //allocate segment data
2948 23 const unsigned strips = SEGMENT.nrOfVStrips(); // adapt for 2D
2949 23 const size_t maxNumBalls = 16;
2950 23 unsigned dataSize = sizeof(ball) * maxNumBalls;
2951
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed
2952
2953 23 Ball* balls = reinterpret_cast<Ball*>(SEGENV.data);
2954
2955
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(2) ? BLACK : SEGCOLOR(1));
2956
2957 // virtualStrip idea by @ewowi (Ewoud Wijma)
2958 // requires virtual strip # to be embedded into upper 16 bits of index in setPixelColor()
2959 // the following functions will not work on virtual strips: fill(), fade_out(), fadeToBlack(), blur()
2960 struct virtualStrip {
2961 456 static void runStrip(size_t stripNr, Ball* balls) {
2962 // number of balls based on intensity setting to max of 7 (cycles colors)
2963 // non-chosen color is a random color
2964 456 unsigned numBalls = (SEGMENT.intensity * (maxNumBalls - 1)) / 255 + 1; // minimum 1 ball
2965 456 const float gravity = -9.81f; // standard value of gravity
2966 456 const bool hasCol2 = SEGCOLOR(2);
2967 456 const unsigned long time = strip().now;
2968
2969
2/2
✓ Branch 0 taken 104 times.
✓ Branch 1 taken 352 times.
456 if (SEGENV.call == 0) {
2970
2/2
✓ Branch 0 taken 1664 times.
✓ Branch 1 taken 104 times.
1768 for (size_t i = 0; i < maxNumBalls; i++) balls[i].lastBounceTime = time;
2971 }
2972
2973
2/2
✓ Branch 0 taken 3776 times.
✓ Branch 1 taken 456 times.
4232 for (size_t i = 0; i < numBalls; i++) {
2974 3776 float timeSinceLastBounce = (time - balls[i].lastBounceTime)/((255-SEGMENT.speed)/64 +1);
2975 3776 float timeSec = timeSinceLastBounce/1000.0f;
2976 3776 balls[i].height = (0.5f * gravity * timeSec + balls[i].impactVelocity) * timeSec; // avoid use pow(x, 2) - its extremely slow !
2977
2978
2/2
✓ Branch 0 taken 864 times.
✓ Branch 1 taken 2912 times.
3776 if (balls[i].height <= 0.0f) {
2979 864 balls[i].height = 0.0f;
2980 //damping for better effect using multiple balls
2981 864 float dampening = 0.9f - float(i)/float(numBalls * numBalls); // avoid use pow(x, 2) - its extremely slow !
2982 864 balls[i].impactVelocity = dampening * balls[i].impactVelocity;
2983 864 balls[i].lastBounceTime = time;
2984
2985
1/2
✓ Branch 0 taken 864 times.
✗ Branch 1 not taken.
864 if (balls[i].impactVelocity < 0.015f) {
2986 864 float impactVelocityStart = sqrtf(-2.0f * gravity) * hw_random8(5,11)/10.0f; // randomize impact velocity
2987 864 balls[i].impactVelocity = impactVelocityStart;
2988 }
2989
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2912 times.
2912 } else if (balls[i].height > 1.0f) {
2990 ✗ continue; // do not draw OOB ball
2991 }
2992
2993 3776 uint32_t color = SEGCOLOR(0);
2994
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3776 times.
3776 if (SEGMENT.palette) {
2995 ✗ color = SEGMENT.color_wheel(i*(256/MAX(numBalls, 8)));
2996
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3776 times.
3776 } else if (hasCol2) {
2997 ✗ color = SEGCOLOR(i % NUM_COLORS);
2998 }
2999
3000 3776 int pos = roundf(balls[i].height * (SEGLEN - 1));
3001 #ifdef WLED_USE_AA_PIXELS
3002 if (SEGLEN<32) SEGMENT.setPixelColor(indexToVStrip(pos, stripNr), color); // encode virtual strip into index
3003 else SEGMENT.setPixelColor(balls[i].height + (stripNr+1)*10.0f, color);
3004 #else
3005 3776 SEGMENT.setPixelColor(indexToVStrip(pos, stripNr), color); // encode virtual strip into index
3006 #endif
3007 }
3008 456 }
3009 };
3010
3011
2/2
✓ Branch 0 taken 456 times.
✓ Branch 1 taken 23 times.
479 for (unsigned stripNr=0; stripNr<strips; stripNr++)
3012 456 virtualStrip::runStrip(stripNr, &balls[stripNr * maxNumBalls]);
3013
3014 23 return FRAMETIME;
3015 }
3016 static const char _data_FX_MODE_BOUNCINGBALLS[] PROGMEM = "Bouncing Balls@Gravity,# of balls,,,,,Overlay;!,!,!;!;1;m12=1"; //bar
3017
3018 #ifdef WLED_PS_DONT_REPLACE_FX
3019 /*
3020 * bouncing balls on a track track Effect modified from Aircoookie's bouncing balls
3021 * Courtesy of pjhatch (https://github.com/pjhatch)
3022 * https://github.com/wled-dev/WLED/pull/1039
3023 */
3024 // modified for balltrack mode
3025 typedef struct RollingBall {
3026 unsigned long lastBounceUpdate;
3027 float mass; // could fix this to be = 1. if memory is an issue
3028 float velocity;
3029 float height;
3030 } rball_t;
3031
3032 23 static uint16_t rolling_balls(void) {
3033 //allocate segment data
3034 23 const unsigned maxNumBalls = 16; // 255/16 + 1
3035 23 unsigned dataSize = sizeof(rball_t) * maxNumBalls;
3036
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
3037
3038 23 rball_t *balls = reinterpret_cast<rball_t *>(SEGENV.data);
3039
3040 // number of balls based on intensity setting to max of 16 (cycles colors)
3041 // non-chosen color is a random color
3042 23 unsigned numBalls = SEGMENT.intensity/16 + 1;
3043 23 bool hasCol2 = SEGCOLOR(2);
3044
3045
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if (SEGENV.call == 0) {
3046
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 SEGMENT.fill(hasCol2 ? BLACK : SEGCOLOR(1)); // start clean
3047
2/2
✓ Branch 0 taken 80 times.
✓ Branch 1 taken 5 times.
85 for (unsigned i = 0; i < maxNumBalls; i++) {
3048 80 balls[i].lastBounceUpdate = strip().now;
3049 80 balls[i].velocity = 20.0f * float(hw_random16(1000, 10000))/10000.0f; // number from 1 to 10
3050
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 48 times.
80 if (hw_random8()<128) balls[i].velocity = -balls[i].velocity; // 50% chance of reverse direction
3051 80 balls[i].height = (float(hw_random16(0, 10000)) / 10000.0f); // from 0. to 1.
3052 80 balls[i].mass = (float(hw_random16(1000, 10000)) / 10000.0f); // from .1 to 1.
3053 }
3054 }
3055
3056 23 float cfac = float(scale8(8, 255-SEGMENT.speed) +1)*20000.0f; // this uses the Aircoookie conversion factor for scaling time using speed slider
3057
3058
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGMENT.check3) SEGMENT.fade_out(250); // 2-8 pixel trails (optional)
3059 else {
3060
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 23 times.
23 if (!SEGMENT.check2) SEGMENT.fill(hasCol2 ? BLACK : SEGCOLOR(1)); // don't fill with background color if user wants to see trails
3061 }
3062
3063
2/2
✓ Branch 0 taken 203 times.
✓ Branch 1 taken 23 times.
226 for (unsigned i = 0; i < numBalls; i++) {
3064 203 float timeSinceLastUpdate = float((strip().now - balls[i].lastBounceUpdate))/cfac;
3065 203 float thisHeight = balls[i].height + balls[i].velocity * timeSinceLastUpdate; // this method keeps higher resolution
3066 // test if intensity level was increased and some balls are way off the track then put them back
3067
2/4
✓ Branch 0 taken 203 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 203 times.
203 if (thisHeight < -0.5f || thisHeight > 1.5f) {
3068 ✗ thisHeight = balls[i].height = (float(hw_random16(0, 10000)) / 10000.0f); // from 0. to 1.
3069 ✗ balls[i].lastBounceUpdate = strip().now;
3070 }
3071 // check if reached ends of the strip
3072
4/8
✗ Branch 0 not taken.
✓ Branch 1 taken 203 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 202 times.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
203 if ((thisHeight <= 0.0f && balls[i].velocity < 0.0f) || (thisHeight >= 1.0f && balls[i].velocity > 0.0f)) {
3073 1 balls[i].velocity = -balls[i].velocity; // reverse velocity
3074 1 balls[i].lastBounceUpdate = strip().now;
3075 1 balls[i].height = thisHeight;
3076 }
3077 // check for collisions
3078
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 203 times.
203 if (SEGMENT.check1) {
3079 ✗ for (unsigned j = i+1; j < numBalls; j++) {
3080 ✗ if (balls[j].velocity != balls[i].velocity) {
3081 // tcollided + balls[j].lastBounceUpdate is acutal time of collision (this keeps precision with long to float conversions)
3082 ✗ float tcollided = (cfac*(balls[i].height - balls[j].height) +
3083 ✗ balls[i].velocity*float(balls[j].lastBounceUpdate - balls[i].lastBounceUpdate))/(balls[j].velocity - balls[i].velocity);
3084
3085 ✗ if ((tcollided > 2.0f) && (tcollided < float(strip().now - balls[j].lastBounceUpdate))) { // 2ms minimum to avoid duplicate bounces
3086 ✗ balls[i].height = balls[i].height + balls[i].velocity*(tcollided + float(balls[j].lastBounceUpdate - balls[i].lastBounceUpdate))/cfac;
3087 ✗ balls[j].height = balls[i].height;
3088 ✗ balls[i].lastBounceUpdate = (unsigned long)(tcollided + 0.5f) + balls[j].lastBounceUpdate;
3089 ✗ balls[j].lastBounceUpdate = balls[i].lastBounceUpdate;
3090 ✗ float vtmp = balls[i].velocity;
3091 ✗ balls[i].velocity = ((balls[i].mass - balls[j].mass)*vtmp + 2.0f*balls[j].mass*balls[j].velocity)/(balls[i].mass + balls[j].mass);
3092 ✗ balls[j].velocity = ((balls[j].mass - balls[i].mass)*balls[j].velocity + 2.0f*balls[i].mass*vtmp) /(balls[i].mass + balls[j].mass);
3093 ✗ thisHeight = balls[i].height + balls[i].velocity*(strip().now - balls[i].lastBounceUpdate)/cfac;
3094 }
3095 }
3096 }
3097 }
3098
3099 203 uint32_t color = SEGCOLOR(0);
3100
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 203 times.
203 if (SEGMENT.palette) {
3101 //color = SEGMENT.color_wheel(i*(256/MAX(numBalls, 8)));
3102 ✗ color = SEGMENT.color_from_palette(i*255/numBalls, false, PALETTE_SOLID_WRAP, 0);
3103
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 203 times.
203 } else if (hasCol2) {
3104 ✗ color = SEGCOLOR(i % NUM_COLORS);
3105 }
3106
3107
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 203 times.
203 if (thisHeight < 0.0f) thisHeight = 0.0f;
3108
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 202 times.
203 if (thisHeight > 1.0f) thisHeight = 1.0f;
3109 203 unsigned pos = round(thisHeight * (SEGLEN - 1));
3110 203 SEGMENT.setPixelColor(pos, color);
3111 203 balls[i].lastBounceUpdate = strip().now;
3112 203 balls[i].height = thisHeight;
3113 }
3114
3115 23 return FRAMETIME;
3116 }
3117 static const char _data_FX_MODE_ROLLINGBALLS[] PROGMEM = "Rolling Balls@!,# of balls,,,,Collide,Overlay,Trails;!,!,!;!;1;m12=1"; //bar
3118 #endif // WLED_PS_DONT_REPLACE_FX
3119
3120 /*
3121 * Shimmer (ported from upstream WLED)
3122 * A pulse of light moves across the segment. Pulses appear at randomized intervals.
3123 */
3124 33 static uint16_t mode_shimmer() {
3125
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
33 if (!SEGENV.allocateData(sizeof(uint32_t))) return mode_static();
3126 33 uint32_t* lastTime = reinterpret_cast<uint32_t*>(SEGENV.data);
3127
3128 33 uint32_t radius = (SEGMENT.custom1 * SEGLEN >> 7) + 1; // [1, 2*SEGLEN+1] pixels
3129 33 uint32_t traversalDistance = (SEGLEN + 2 * radius) << 8; // total subpixels to cross, 1 pixel = 256 subpixels
3130 33 uint32_t traversalTime = 200 + (255 - SEGMENT.speed) * 80; // [200, 20600] ms
3131 33 uint32_t speed = ((traversalDistance << 5) / traversalTime); // subpixels/512ms
3132 33 int32_t position = static_cast<int32_t>(SEGENV.step); // current position in subpixels
3133 33 uint16_t inputstate = (uint16_t(SEGMENT.intensity) << 8) | uint16_t(SEGMENT.custom1);
3134
3135
5/6
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 27 times.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 27 times.
33 if (SEGENV.call == 0 || inputstate != SEGENV.aux1) {
3136 6 position = -(int32_t)(radius << 8);
3137 6 SEGENV.aux0 = 0; // pause timer
3138 6 *lastTime = strip().now;
3139 6 SEGENV.aux1 = inputstate;
3140 }
3141
3142
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 4 times.
33 if (SEGMENT.speed) {
3143 29 uint32_t deltaTime = (strip().now - *lastTime) & 0x7F; // clamp to 127ms
3144 29 *lastTime = strip().now;
3145
3146
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
29 if (SEGENV.aux0 > 0) {
3147 ✗ SEGENV.aux0 = (SEGENV.aux0 > deltaTime) ? SEGENV.aux0 - deltaTime : 0;
3148 } else {
3149 29 int32_t step = 1 + ((speed * deltaTime) >> 5);
3150 29 position += step;
3151 29 int endposition = (SEGLEN + radius) << 8;
3152
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
29 if (position > endposition) {
3153 ✗ SEGENV.aux0 = SEGMENT.intensity * 236; // [0, 60180] ms pause
3154 ✗ if (SEGMENT.check3) SEGENV.aux0 = hw_random(SEGENV.aux0 + 1000);
3155 ✗ position = -(int32_t)(radius << 8);
3156 }
3157 29 SEGENV.step = (uint32_t)position;
3158 }
3159
3160
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
29 if (SEGMENT.check2)
3161 ✗ position = (SEGLEN << 8) - position; // invert position
3162 } else {
3163 4 position = (SEGLEN << 7); // static center
3164 }
3165
3166
2/2
✓ Branch 0 taken 12631 times.
✓ Branch 1 taken 33 times.
12664 for (int i = 0; i < (int)SEGLEN; i++) {
3167 12631 uint32_t dist = abs(position - (i << 8));
3168
2/2
✓ Branch 0 taken 3055 times.
✓ Branch 1 taken 9576 times.
12631 if (dist < (radius << 8)) {
3169 3055 uint32_t color = SEGMENT.color_from_palette(i * 255 / SEGLEN, false, false, 0);
3170 3055 uint8_t blend = dist / radius; // [0, 255]
3171
2/2
✓ Branch 0 taken 1537 times.
✓ Branch 1 taken 1518 times.
3055 if (SEGMENT.custom2) {
3172 uint8_t modVal;
3173
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1537 times.
1537 if (SEGMENT.check1) {
3174 ✗ modVal = (sin16_t((i * SEGMENT.custom2 << 6) + (strip().now * SEGMENT.custom3 << 5)) >> 8) + 128;
3175 } else {
3176 1537 modVal = perlin16((i * SEGMENT.custom2 << 7), strip().now * SEGMENT.custom3 << 5) >> 8;
3177 }
3178 1537 color = color_fade(color, modVal, true);
3179 }
3180 3055 SEGMENT.setPixelColor(i, color_blend(color, SEGCOLOR(1), blend));
3181 } else {
3182 9576 SEGMENT.setPixelColor(i, SEGCOLOR(1));
3183 }
3184 }
3185 33 return FRAMETIME;
3186 }
3187 static const char _data_FX_MODE_SHIMMER[] PROGMEM = "Shimmer@Speed,Interval,Size,Granular,Flow,Zebra,Reverse,Sporadic;Fx,Bg,Cx;!;1;pal=15,sx=220,ix=10,c2=0,c3=0";
3188
3189
3190 /////////////////////////
3191 // Copy Segment //
3192 /////////////////////////
3193 // Mirrors another segment into this one, with optional hue/saturation/value adjustment.
3194 // Ported from upstream WLED. Note: upstream reads either the source segment's own pixel
3195 // buffer or the global (last rendered frame) buffer depending on check2. This fork has no
3196 // per-segment buffers -- Segment::getPixelColor* already reads the shared strip buffer --
3197 // so both paths resolve to the same source here and check2 has no visible effect. The
3198 // option is kept so the mode data stays in step with upstream.
3199 ✗ uint16_t mode_copy_segment(void) {
3200 ✗ const unsigned sourceid = SEGMENT.custom3;
3201 ✗ if (sourceid >= strip().getSegmentsNum() || sourceid == strip().getCurrSegmentId()) { // invalid source
3202 ✗ SEGMENT.fadeToBlackBy(5); // fade out
3203 ✗ return FRAMETIME;
3204 }
3205 ✗ Segment& sourcesegment = strip().getSegment(sourceid);
3206 ✗ if (!sourcesegment.isActive()) return FRAMETIME;
3207
3208 uint32_t color;
3209 ✗ if (sourcesegment.is2D()) { // 2D source; copying 2D to 1D takes the first row (or column if 'Switch axis' is set)
3210 ✗ const unsigned vH = SEGMENT.vHeight();
3211 ✗ const unsigned vW = SEGMENT.vWidth();
3212 ✗ for (unsigned y = 0; y < vH; y++) {
3213 ✗ for (unsigned x = 0; x < vW; x++) {
3214 ✗ unsigned sx = x; // source coordinates
3215 ✗ unsigned sy = y;
3216 ✗ if (SEGMENT.check1) std::swap(sx, sy); // flip axis
3217 ✗ sourcesegment.setDrawDimensions(); // address the source segment
3218 ✗ color = sourcesegment.getPixelColorXY(sx, sy);
3219 ✗ adjust_color(color, SEGMENT.intensity, SEGMENT.custom1, SEGMENT.custom2); // hue shift, sat change, value change
3220 ✗ SEGMENT.setDrawDimensions(); // back to this segment
3221 ✗ SEGMENT.setPixelColorXY(x, y, color);
3222 }
3223 }
3224 } else { // 1D source, can be expanded into 2D
3225 ✗ const unsigned vL = SEGMENT.vLength();
3226 ✗ for (unsigned i = 0; i < vL; i++) {
3227 ✗ sourcesegment.setDrawDimensions();
3228 ✗ color = sourcesegment.getPixelColor(i);
3229 ✗ adjust_color(color, SEGMENT.intensity, SEGMENT.custom1, SEGMENT.custom2);
3230 ✗ SEGMENT.setDrawDimensions();
3231 ✗ SEGMENT.setPixelColor(i, color);
3232 }
3233 }
3234
3235 ✗ return FRAMETIME;
3236 }
3237 static const char _data_FX_MODE_COPY[] PROGMEM = "Copy Segment@,Color shift,Lighten,Brighten,ID,Axis(2D),FullStack(last frame);;;12;ix=0,c1=0,c2=0,c3=0";
3238
3239 /*
3240 * Color Clouds (ported from upstream WLED)
3241 * Soft, drifting cloud-like colors driven by 2D perlin noise.
3242 */
3243 34 static uint16_t mode_ColorClouds() {
3244
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 28 times.
34 if (SEGENV.call == 0) {
3245 6 SEGENV.aux0 = hw_random16();
3246 6 SEGENV.aux1 = hw_random16();
3247 }
3248 34 const uint32_t volX0 = SEGENV.aux0;
3249 34 const uint32_t hueX0 = SEGENV.aux1;
3250 34 const uint8_t hueOffset0 = volX0 + hueX0;
3251
3252 34 const bool cozy = SEGMENT.check3;
3253
3254 34 const uint32_t volSpeed = 1 + SEGMENT.speed;
3255 34 const uint32_t hueSpeed = 1 + SEGMENT.intensity;
3256 34 const uint32_t volSqueeze = 8 + SEGMENT.custom1;
3257 34 const uint32_t hueSqueeze = SEGMENT.custom2;
3258
3259 34 const int32_t volCutoff = 12500 + SEGMENT.custom3 * 900;
3260 34 const int32_t volSaturate = 52000;
3261
3262 34 const uint32_t now = strip().now;
3263 34 const uint32_t volT = now * volSpeed / 8;
3264 34 const uint32_t hueT = now * hueSpeed / 8;
3265 34 const uint8_t hueOffset = beat88(64) >> 8;
3266
3267
2/2
✓ Branch 0 taken 13143 times.
✓ Branch 1 taken 34 times.
13177 for (int i = 0; i < (int)SEGLEN; i++) {
3268 13143 const uint32_t volX = i * volSqueeze * 64;
3269 13143 int32_t vol = perlin16(volX0 + volX, volT);
3270 13143 vol = map(vol, volCutoff, volSaturate, 0, 255);
3271
2/2
✓ Branch 0 taken 10331 times.
✓ Branch 1 taken 2812 times.
13143 vol = constrain(vol, 0, 255);
3272
3273 13143 const uint32_t hueX = i * hueSqueeze * 8;
3274 13143 uint8_t hue = perlin16(hueX0 + hueX, hueT) >> 7;
3275 13143 hue += hueOffset0;
3276 13143 hue += hueOffset;
3277
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13143 times.
13143 if (cozy) {
3278 ✗ hue = cos8_t(128 + hue / 2);
3279 }
3280
3281 uint32_t pixel;
3282
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13143 times.
13143 if (SEGMENT.palette) {
3283 ✗ pixel = SEGMENT.color_from_palette(hue, false, true, 0, vol);
3284 } else {
3285 // CHSV(hue, 255, vol) -> CRGB -> CRGBW (FPP doesn't have CHSV32 ctor for CRGBW)
3286 CRGB tmp;
3287
1/2
✓ Branch 0 taken 13143 times.
✗ Branch 1 not taken.
13143 hsv2rgb_rainbow(CHSV((uint8_t)hue, (uint8_t)255, (uint8_t)vol), tmp);
3288 26286 pixel = CRGBW(tmp);
3289 }
3290
3291
2/2
✓ Branch 0 taken 3349 times.
✓ Branch 1 taken 9794 times.
13143 if (int(R(pixel)) + G(pixel) + B(pixel) <= 2) {
3292 3349 pixel = 0;
3293 }
3294
3295 13143 SEGMENT.setPixelColor(i, pixel);
3296 }
3297 34 return FRAMETIME;
3298 }
3299 static const char _data_FX_MODE_COLORCLOUDS[] PROGMEM = "Color Clouds@!,!,Clouds,Colors,Distance,,,Cozy;;!;;sx=24,ix=32,c1=48,c2=64,c3=12,pal=0";
3300
3301 /*
3302 * PacMan (ported from upstream WLED)
3303 * The classic chase across the strip — PacMan + ghosts + power dots.
3304 */
3305 typedef struct PacManChars {
3306 signed pos;
3307 signed topPos; // LED position of farthest PacMan has moved
3308 uint32_t color;
3309 bool direction; // true = moving away from first LED
3310 bool blue; // used for ghosts only
3311 bool eaten; // used for power dots only
3312 } pacmancharacters_t;
3313
3314 33 static uint16_t mode_pacman(void) {
3315 33 constexpr unsigned ORANGEYELLOW = 0xFFCC00;
3316 33 constexpr unsigned PURPLEISH = 0xB000B0;
3317 33 constexpr unsigned ORANGEISH = 0xFF8800;
3318 33 constexpr unsigned WHITEISH = 0x999999;
3319 33 constexpr unsigned PACMAN = 0;
3320 33 constexpr uint32_t ghostColors[] = {RED, PURPLEISH, CYAN, ORANGEISH};
3321
3322 33 unsigned maxPowerDots = std::min<unsigned>(SEGLEN / 10U, 255U);
3323
2/4
✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 33 times.
✗ Branch 3 not taken.
33 unsigned numPowerDots = map(SEGMENT.intensity, 0, 255, 1, maxPowerDots);
3324
2/4
✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 33 times.
✗ Branch 3 not taken.
33 unsigned numGhosts = map(SEGMENT.custom3, 0, 31, 2, 8);
3325
2/4
✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 33 times.
✗ Branch 3 not taken.
33 bool smearMode = SEGMENT.check2;
3326
3327 33 uint16_t combined_value = uint16_t(((numPowerDots & 0xFF) << 8) | (numGhosts & 0xFF));
3328
6/10
✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 33 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 27 times.
✓ Branch 6 taken 6 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 6 times.
✗ Branch 9 not taken.
33 if (combined_value != SEGENV.aux0) SEGENV.call = 0;
3329
2/4
✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 33 times.
✗ Branch 3 not taken.
33 SEGENV.aux0 = combined_value;
3330
3331 33 unsigned dataSize = sizeof(pacmancharacters_t) * (numGhosts + maxPowerDots + 1);
3332
9/14
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 28 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 28 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 28 times.
✓ Branch 10 taken 5 times.
✓ Branch 11 taken 28 times.
✓ Branch 12 taken 5 times.
✗ Branch 13 not taken.
33 if (SEGLEN <= 16 + (2 * numGhosts) || !SEGENV.allocateData(dataSize)) return mode_static();
3333
2/4
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
28 pacmancharacters_t *character = reinterpret_cast<pacmancharacters_t *>(SEGENV.data);
3334
3335
4/6
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 23 times.
28 int maxBlinkPos = (SEGENV.call == 0) ? (int)SEGLEN - 1 : character[PACMAN].topPos;
3336
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 5 times.
28 if (maxBlinkPos < 20) maxBlinkPos = 20;
3337 28 int startBlinkingGhostsLED = (SEGLEN < 64)
3338 ✗ ? (int)SEGLEN / 3
3339
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 28 times.
✗ Branch 5 not taken.
28 : map(SEGMENT.custom1, 0, 255, 20, maxBlinkPos);
3340
3341
4/6
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 23 times.
28 if (SEGENV.call == 0) {
3342 5 character[PACMAN].color = YELLOW;
3343 5 character[PACMAN].pos = 0;
3344 5 character[PACMAN].topPos = 0;
3345 5 character[PACMAN].direction = true;
3346 5 character[PACMAN].blue = false;
3347
3348
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 5 times.
27 for (unsigned i = 1; i <= numGhosts; i++) {
3349 22 character[i].color = ghostColors[(i - 1) % 4];
3350 22 character[i].pos = -2 * (int)(i + 1);
3351 22 character[i].direction = true;
3352 22 character[i].blue = false;
3353 }
3354
3355
2/2
✓ Branch 0 taken 82 times.
✓ Branch 1 taken 5 times.
87 for (unsigned i = 0; i < numPowerDots; i++) {
3356 82 character[i + numGhosts + 1].color = ORANGEYELLOW;
3357 82 character[i + numGhosts + 1].eaten = false;
3358 }
3359 5 character[numGhosts + 1].pos = SEGLEN - 1;
3360 }
3361
3362
4/8
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 28 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 28 times.
✗ Branch 7 not taken.
28 if (strip().now > SEGENV.step) {
3363
3/6
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 28 times.
✗ Branch 5 not taken.
28 SEGENV.step = strip().now;
3364
2/4
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
28 SEGENV.aux1++;
3365 }
3366
3367
4/8
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 28 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 28 times.
✗ Branch 7 not taken.
28 if (!smearMode) SEGMENT.fill(BLACK);
3368
3369
3/6
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 28 times.
✗ Branch 5 not taken.
28 if (SEGMENT.check1) {
3370
3/6
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 28 times.
28 int step = SEGMENT.check3 ? 1 : 2;
3371
2/2
✓ Branch 0 taken 6254 times.
✓ Branch 1 taken 28 times.
6282 for (int i = (int)SEGLEN - 1; i > character[PACMAN].topPos; i -= step) {
3372
3/6
✓ Branch 0 taken 6254 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6254 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6254 times.
✗ Branch 5 not taken.
6254 SEGMENT.setPixelColor(i, WHITEISH);
3373 }
3374 }
3375
3376 28 uint32_t everyXLeds = (((uint32_t)SEGLEN - 10U) << 8) / numPowerDots;
3377
2/2
✓ Branch 0 taken 395 times.
✓ Branch 1 taken 28 times.
423 for (unsigned i = 1; i < numPowerDots; i++) {
3378 395 character[i + numGhosts + 1].pos = 10 + ((i * everyXLeds) >> 8);
3379 }
3380
3381
4/6
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 27 times.
28 if (SEGENV.aux1 % 10 == 0) {
3382
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 uint32_t dotColor = (character[numGhosts + 1].color == ORANGEYELLOW) ? BLACK : ORANGEYELLOW;
3383
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 1 times.
14 for (unsigned i = 0; i < numPowerDots; i++) {
3384 13 character[i + numGhosts + 1].color = dotColor;
3385 }
3386 }
3387
3388
4/12
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 28 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 28 times.
28 if (SEGENV.aux1 % 15 == 0 && character[1].blue && character[PACMAN].pos <= startBlinkingGhostsLED) {
3389 ✗ uint32_t ghostColor = (character[1].color == BLUE) ? WHITEISH : BLUE;
3390 ✗ for (unsigned i = 1; i <= numGhosts; i++) {
3391 ✗ character[i].color = ghostColor;
3392 }
3393 }
3394
3395
2/2
✓ Branch 0 taken 423 times.
✓ Branch 1 taken 28 times.
451 for (unsigned i = 0; i < numPowerDots; i++) {
3396
3/6
✓ Branch 0 taken 423 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 423 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 423 times.
✗ Branch 5 not taken.
423 if (!character[i + numGhosts + 1].eaten && (unsigned)character[i + numGhosts + 1].pos < SEGLEN) {
3397
3/6
✓ Branch 0 taken 423 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 423 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 423 times.
✗ Branch 5 not taken.
423 SEGMENT.setPixelColor(character[i + numGhosts + 1].pos, character[i + numGhosts + 1].color);
3398 }
3399 }
3400
3401
2/2
✓ Branch 0 taken 423 times.
✓ Branch 1 taken 28 times.
451 for (unsigned j = 0; j < numPowerDots; j++) {
3402 423 auto &dot = character[j + numGhosts + 1];
3403
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 423 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
423 if (character[PACMAN].pos == dot.pos && !dot.eaten) {
3404 ✗ for (unsigned i = 0; i <= numGhosts; i++) {
3405 ✗ character[i].direction = false;
3406 }
3407 ✗ for (unsigned i = 1; i <= numGhosts; i++) {
3408 ✗ character[i].color = BLUE;
3409 ✗ character[i].blue = true;
3410 }
3411 ✗ dot.eaten = true;
3412 ✗ break;
3413 }
3414 }
3415
3416
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
28 if (character[1].blue && character[PACMAN].pos <= 0) {
3417 ✗ for (unsigned i = 0; i <= numGhosts; i++) {
3418 ✗ character[i].direction = true;
3419 }
3420 ✗ for (unsigned i = 1; i <= numGhosts; i++) {
3421 ✗ character[i].color = ghostColors[(i - 1) % 4];
3422 ✗ character[i].blue = false;
3423 }
3424 ✗ if (character[numGhosts + 1].eaten) {
3425 ✗ for (unsigned i = 0; i < numPowerDots; i++) {
3426 ✗ character[i + numGhosts + 1].eaten = false;
3427 }
3428 ✗ character[PACMAN].topPos = 0;
3429 }
3430 }
3431
3432
4/8
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 28 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 28 times.
✗ Branch 7 not taken.
28 bool updatePositions = (SEGENV.aux1 % map(SEGMENT.speed, 0, 255, 15, 1) == 0);
3433
3434
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 20 times.
28 if (updatePositions) {
3435
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 character[PACMAN].pos += character[PACMAN].direction ? 1 : -1;
3436
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 8 times.
56 for (unsigned i = 1; i <= numGhosts; i++) {
3437
1/2
✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
48 character[i].pos += character[i].direction ? 1 : -1;
3438 }
3439 }
3440
3441
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 if ((unsigned)character[PACMAN].pos < SEGLEN) {
3442
3/6
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 28 times.
✗ Branch 5 not taken.
28 SEGMENT.setPixelColor(character[PACMAN].pos, character[PACMAN].color);
3443 }
3444
3445
2/2
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 28 times.
148 for (unsigned i = 1; i <= numGhosts; i++) {
3446
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 119 times.
120 if ((unsigned)character[i].pos < SEGLEN) {
3447
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 SEGMENT.setPixelColor(character[i].pos, character[i].color);
3448 }
3449 }
3450
3451
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 20 times.
28 if (character[PACMAN].topPos < character[PACMAN].pos) {
3452 8 character[PACMAN].topPos = character[PACMAN].pos;
3453 }
3454
3455
5/10
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 28 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 28 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 28 times.
✗ Branch 9 not taken.
28 SEGMENT.blur(SEGMENT.custom2 >> 1);
3456
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 return FRAMETIME;
3457 }
3458 static const char _data_FX_MODE_PACMAN[] PROGMEM = "PacMan@Speed,# of PowerDots,Blink distance,Blur,# of Ghosts,Dots,Smear,Compact;;!;1;m12=0,sx=192,ix=64,c1=64,c2=0,c3=12,o1=1,o2=0";
3459
3460 /*
3461 * Slow Transition (ported from upstream WLED)
3462 * Smoothly cross-fades the current palette / white / CCT to the selected target
3463 * over a user-specified time window (1-255 minutes). When the user changes the
3464 * palette mid-transition, blending re-anchors to the current state.
3465 */
3466 typedef struct SlowTransitionData {
3467 CRGBPalette16 startPalette;
3468 CRGBPalette16 currentPalette;
3469 CRGBPalette16 endPalette;
3470 uint8_t startWhite;
3471 uint8_t currentWhite;
3472 uint8_t endWhite;
3473 uint8_t startCCT;
3474 uint8_t currentCCT;
3475 uint8_t endCCT;
3476 } slow_transition_data;
3477
3478 34 static uint16_t mode_slow_transition(void) {
3479
2/4
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 34 times.
✗ Branch 3 not taken.
34 uint32_t* startTime = &SEGMENT.step;
3480
2/4
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 34 times.
✗ Branch 3 not taken.
34 uint16_t* stepsDone = &SEGMENT.aux0;
3481
2/4
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 34 times.
✗ Branch 3 not taken.
34 uint16_t* startSpeed = &SEGMENT.aux1;
3482
3483 34 size_t dataSize = sizeof(slow_transition_data);
3484
4/10
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 34 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 34 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 34 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
34 if (!SEGMENT.allocateData(dataSize)) return mode_static();
3485
2/4
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 34 times.
✗ Branch 3 not taken.
34 slow_transition_data* data = reinterpret_cast<slow_transition_data*>(SEGMENT.data);
3486 34 bool changed = (data->endPalette != SEGPALETTE
3487 ✗ || *startSpeed != SEGMENT.speed
3488 ✗ || data->endWhite != W(SEGCOLOR(0))
3489
1/8
✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
34 || data->currentCCT != SEGMENT.cct);
3490
3491
2/10
✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 34 times.
✗ Branch 9 not taken.
34 if (changed || SEGMENT.call == 0) {
3492
4/6
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 34 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 28 times.
34 if (SEGMENT.call == 0) {
3493 6 data->startPalette = SEGPALETTE;
3494 6 data->currentPalette = SEGPALETTE;
3495 6 data->endPalette = SEGPALETTE;
3496 6 data->startWhite = data->currentWhite = data->endWhite = W(SEGCOLOR(0));
3497
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 data->startCCT = data->currentCCT = data->endCCT = SEGMENT.cct;
3498 6 *stepsDone = 0xFFFF;
3499 } else {
3500 28 data->startPalette = data->currentPalette;
3501 28 data->endPalette = SEGPALETTE;
3502 28 data->startWhite = data->currentWhite;
3503 28 data->endWhite = W(SEGCOLOR(0));
3504 28 data->startCCT = data->currentCCT;
3505
2/4
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
28 data->endCCT = SEGMENT.cct;
3506 28 *stepsDone = 0;
3507 }
3508
2/4
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 34 times.
✗ Branch 3 not taken.
34 *startSpeed = SEGMENT.speed;
3509
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
34 *startTime = millis();
3510 }
3511
3512
3/6
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 34 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 34 times.
34 uint32_t totalSteps = SEGMENT.check2 ? 16 * 255 : 255;
3513
6/10
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 34 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 30 times.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 4 times.
✗ Branch 9 not taken.
34 uint32_t duration = (SEGMENT.speed == 0) ? 10000 : (uint32_t)SEGMENT.speed * 60000;
3514
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
34 uint32_t elapsed = millis() - *startTime;
3515 34 uint32_t expectedSteps = (uint64_t)elapsed * totalSteps / duration;
3516 34 expectedSteps = std::min<uint32_t>(expectedSteps, totalSteps);
3517
3518
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 28 times.
34 if (*stepsDone > expectedSteps)
3519 6 *stepsDone = expectedSteps;
3520
3521
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
34 if (*stepsDone < expectedSteps) {
3522 ✗ *stepsDone = expectedSteps;
3523 uint8_t blendAmount;
3524 ✗ if (SEGMENT.check2) {
3525 ✗ uint8_t i = *stepsDone % 16;
3526 ✗ blendAmount = *stepsDone / 16;
3527 ✗ data->currentPalette[i] = CRGB(color_blend((uint32_t)CRGBW(data->startPalette[i]),
3528 ✗ (uint32_t)CRGBW(data->endPalette[i]),
3529 ✗ blendAmount));
3530 } else {
3531 ✗ blendAmount = (uint8_t)*stepsDone;
3532 ✗ for (uint8_t i = 0; i < 16; i++) {
3533 ✗ data->currentPalette[i] = CRGB(color_blend((uint32_t)CRGBW(data->startPalette[i]),
3534 ✗ (uint32_t)CRGBW(data->endPalette[i]),
3535 ✗ blendAmount));
3536 }
3537 }
3538 ✗ data->currentWhite = (data->startWhite * (255 - blendAmount) + data->endWhite * blendAmount) / 255;
3539 ✗ data->currentCCT = (data->startCCT * (255 - blendAmount) + data->endCCT * blendAmount) / 255;
3540 ✗ if (*stepsDone >= totalSteps) {
3541 ✗ data->currentPalette = data->endPalette;
3542 ✗ data->currentWhite = data->endWhite;
3543 ✗ data->currentCCT = data->endCCT;
3544 }
3545 }
3546
3547
2/2
✓ Branch 0 taken 13143 times.
✓ Branch 1 taken 34 times.
13177 for (unsigned i = 0; i < SEGLEN; i++) {
3548 13143 uint8_t paletteIndex = (i * 255) / SEGLEN;
3549
1/2
✓ Branch 0 taken 13143 times.
✗ Branch 1 not taken.
13143 CRGBW palcol = ColorFromPalette(data->currentPalette, paletteIndex, 255, LINEARBLEND_NOWRAP);
3550 13143 palcol.w = data->currentWhite;
3551
3/6
✓ Branch 0 taken 13143 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 13143 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13143 times.
✗ Branch 5 not taken.
13143 SEGMENT.setPixelColor(i, palcol.color32);
3552 }
3553
2/4
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 34 times.
✗ Branch 3 not taken.
34 SEGMENT.cct = data->currentCCT;
3554
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
34 return FRAMETIME;
3555 }
3556 static const char _data_FX_MODE_SLOW_TRANSITION[] PROGMEM = "Slow Transition@Time (min),,,,,,Sweep;!;!;1;pal=2,sx=0,ix=0";
3557
3558 /*
3559 * Sinelon stolen from FASTLED examples
3560 */
3561 69 static uint16_t sinelon_base(bool dual, bool rainbow=false) {
3562
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 69 times.
69 if (SEGLEN <= 1) return mode_static();
3563 69 SEGMENT.fade_out(SEGMENT.intensity);
3564 69 unsigned pos = beatsin16_t(SEGMENT.speed/10,0,SEGLEN-1);
3565
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 54 times.
69 if (SEGENV.call == 0) SEGENV.aux0 = pos;
3566 69 uint32_t color1 = SEGMENT.color_from_palette(pos, true, false, 0);
3567 69 uint32_t color2 = SEGCOLOR(2);
3568
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 46 times.
69 if (rainbow) {
3569 23 color1 = SEGMENT.color_wheel((pos & 0x07) * 32);
3570 }
3571 69 SEGMENT.setPixelColor(pos, color1);
3572
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 46 times.
69 if (dual) {
3573
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 if (!color2) color2 = SEGMENT.color_from_palette(pos, true, false, 0);
3574
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (rainbow) color2 = color1; //rainbow
3575 23 SEGMENT.setPixelColor(SEGLEN-1-pos, color2);
3576 }
3577
2/2
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 29 times.
69 if (SEGENV.aux0 != pos) {
3578
1/2
✓ Branch 0 taken 40 times.
✗ Branch 1 not taken.
40 if (SEGENV.aux0 < pos) {
3579
2/2
✓ Branch 0 taken 450 times.
✓ Branch 1 taken 40 times.
490 for (unsigned i = SEGENV.aux0; i < pos ; i++) {
3580 450 SEGMENT.setPixelColor(i, color1);
3581
2/2
✓ Branch 0 taken 163 times.
✓ Branch 1 taken 287 times.
450 if (dual) SEGMENT.setPixelColor(SEGLEN-1-i, color2);
3582 }
3583 } else {
3584 ✗ for (unsigned i = SEGENV.aux0; i > pos ; i--) {
3585 ✗ SEGMENT.setPixelColor(i, color1);
3586 ✗ if (dual) SEGMENT.setPixelColor(SEGLEN-1-i, color2);
3587 }
3588 }
3589 40 SEGENV.aux0 = pos;
3590 }
3591
3592 69 return FRAMETIME;
3593 }
3594
3595
3596 23 uint16_t mode_sinelon(void) {
3597 23 return sinelon_base(false);
3598 }
3599 static const char _data_FX_MODE_SINELON[] PROGMEM = "Sinelon@!,Trail;!,!,!;!";
3600
3601
3602 23 uint16_t mode_sinelon_dual(void) {
3603 23 return sinelon_base(true);
3604 }
3605 static const char _data_FX_MODE_SINELON_DUAL[] PROGMEM = "Sinelon Dual@!,Trail;!,!,!;!";
3606
3607
3608 23 uint16_t mode_sinelon_rainbow(void) {
3609 23 return sinelon_base(false, true);
3610 }
3611 static const char _data_FX_MODE_SINELON_RAINBOW[] PROGMEM = "Sinelon Rainbow@!,Trail;,,!;!";
3612
3613
3614 // utility function that will add random glitter to SEGMENT
3615 46 void glitter_base(uint8_t intensity, uint32_t col = ULTRAWHITE) {
3616
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 19 times.
46 if (intensity > hw_random8()) SEGMENT.setPixelColor(hw_random16(SEGLEN), col);
3617 46 }
3618
3619 //Glitter with palette background, inspired by https://gist.github.com/kriegsman/062e10f7f07ba8518af6
3620 23 uint16_t mode_glitter()
3621 {
3622
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 if (!SEGMENT.check2) { // use "* Color 1" palette for solid background (replacing "Solid glitter")
3623 23 unsigned counter = 0;
3624
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 4 times.
23 if (SEGMENT.speed != 0) {
3625 19 counter = (strip().now * ((SEGMENT.speed >> 3) +1)) & 0xFFFF;
3626 19 counter = counter >> 8;
3627 }
3628
3629
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 19 times.
23 bool noWrap = (strip().paletteBlend == 2 || (strip().paletteBlend == 0 && SEGMENT.speed == 0));
3630
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
3631 7511 unsigned colorIndex = (i * 255 / SEGLEN) - counter;
3632
2/2
✓ Branch 0 taken 2048 times.
✓ Branch 1 taken 5463 times.
7511 if (noWrap) colorIndex = map(colorIndex, 0, 255, 0, 240); //cut off blend at palette "end"
3633 7511 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(colorIndex, false, true, 255));
3634 }
3635 }
3636
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 glitter_base(SEGMENT.intensity, SEGCOLOR(2) ? SEGCOLOR(2) : ULTRAWHITE);
3637 23 return FRAMETIME;
3638 }
3639 static const char _data_FX_MODE_GLITTER[] PROGMEM = "Glitter@!,!,,,,,Overlay;,,Glitter color;!;;pal=11,m12=0"; //pixels
3640
3641
3642 //Solid colour background with glitter (can be replaced by Glitter)
3643 23 uint16_t mode_solid_glitter()
3644 {
3645 23 SEGMENT.fill(SEGCOLOR(0));
3646
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 glitter_base(SEGMENT.intensity, SEGCOLOR(2) ? SEGCOLOR(2) : ULTRAWHITE);
3647 23 return FRAMETIME;
3648 }
3649 static const char _data_FX_MODE_SOLID_GLITTER[] PROGMEM = "Solid Glitter@,!;Bg,,Glitter color;;;m12=0";
3650
3651 //each needs 20 bytes
3652 //Spark type is used for popcorn, 1D fireworks, and drip
3653 typedef struct Spark {
3654 float pos, posX;
3655 float vel, velX;
3656 uint16_t col;
3657 uint8_t colIndex;
3658 } spark;
3659
3660 #define maxNumPopcorn 21 // max 21 on 16 segment ESP8266
3661 /*
3662 * POPCORN
3663 * modified from https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Popcorn.h
3664 */
3665 23 uint16_t mode_popcorn(void) {
3666
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
3667 //allocate segment data
3668 23 unsigned strips = SEGMENT.nrOfVStrips();
3669 23 unsigned usablePopcorns = maxNumPopcorn;
3670
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
23 if (usablePopcorns * strips * sizeof(spark) > FAIR_DATA_PER_SEG) usablePopcorns = FAIR_DATA_PER_SEG / (strips * sizeof(spark)) + 1; // at least 1 popcorn per vstrip
3671 23 unsigned dataSize = sizeof(spark) * usablePopcorns; // on a matrix 64x64 this could consume a little less than 27kB when Bar expansion is used
3672
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed
3673
3674 23 Spark* popcorn = reinterpret_cast<Spark*>(SEGENV.data);
3675
3676 23 bool hasCol2 = SEGCOLOR(2);
3677
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 23 times.
23 if (!SEGMENT.check2) SEGMENT.fill(hasCol2 ? BLACK : SEGCOLOR(1));
3678
3679 struct virtualStrip {
3680 456 static void runStrip(uint16_t stripNr, Spark* popcorn, unsigned usablePopcorns) {
3681 456 float gravity = -0.0001f - (SEGMENT.speed/200000.0f); // m/s/s
3682 456 gravity *= SEGLEN;
3683
3684 456 unsigned numPopcorn = SEGMENT.intensity * usablePopcorns / 255;
3685
2/2
✓ Branch 0 taken 128 times.
✓ Branch 1 taken 328 times.
456 if (numPopcorn == 0) numPopcorn = 1;
3686
3687
2/2
✓ Branch 0 taken 897 times.
✓ Branch 1 taken 456 times.
1353 for (unsigned i = 0; i < numPopcorn; i++) {
3688
2/2
✓ Branch 0 taken 415 times.
✓ Branch 1 taken 482 times.
897 if (popcorn[i].pos >= 0.0f) { // if kernel is active, update its position
3689 415 popcorn[i].pos += popcorn[i].vel;
3690 415 popcorn[i].vel += gravity;
3691 } else { // if kernel is inactive, randomly pop it
3692
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 479 times.
482 if (hw_random8() < 2) { // POP!!!
3693 3 popcorn[i].pos = 0.01f;
3694
3695 3 unsigned peakHeight = 128 + hw_random8(128); //0-255
3696 3 peakHeight = (peakHeight * (SEGLEN -1)) >> 8;
3697 3 popcorn[i].vel = sqrtf(-2.0f * gravity * peakHeight);
3698
3699
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (SEGMENT.palette)
3700 {
3701 ✗ popcorn[i].colIndex = hw_random8();
3702 } else {
3703 3 byte col = hw_random8(0, NUM_COLORS);
3704
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 if (!SEGCOLOR(2) || !SEGCOLOR(col)) col = 0;
3705 3 popcorn[i].colIndex = col;
3706 }
3707 }
3708 }
3709
2/2
✓ Branch 0 taken 213 times.
✓ Branch 1 taken 684 times.
897 if (popcorn[i].pos >= 0.0f) { // draw now active popcorn (either active before or just popped)
3710 213 uint32_t col = SEGMENT.color_wheel(popcorn[i].colIndex);
3711
3/6
✓ Branch 0 taken 213 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 213 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 213 times.
✗ Branch 5 not taken.
213 if (!SEGMENT.palette && popcorn[i].colIndex < NUM_COLORS) col = SEGCOLOR(popcorn[i].colIndex);
3712 213 unsigned ledIndex = popcorn[i].pos;
3713
1/2
✓ Branch 0 taken 213 times.
✗ Branch 1 not taken.
213 if (ledIndex < SEGLEN) SEGMENT.setPixelColor(indexToVStrip(ledIndex, stripNr), col);
3714 }
3715 }
3716 456 }
3717 };
3718
3719
2/2
✓ Branch 0 taken 456 times.
✓ Branch 1 taken 23 times.
479 for (unsigned stripNr=0; stripNr<strips; stripNr++)
3720 456 virtualStrip::runStrip(stripNr, &popcorn[stripNr * usablePopcorns], usablePopcorns);
3721
3722 23 return FRAMETIME;
3723 }
3724 static const char _data_FX_MODE_POPCORN[] PROGMEM = "Popcorn@!,!,,,,,Overlay;!,!,!;!;;m12=1"; //bar
3725
3726 //values close to 100 produce 5Hz flicker, which looks very candle-y
3727 //Inspired by https://github.com/avanhanegem/ArduinoCandleEffectNeoPixel
3728 //and https://cpldcpu.wordpress.com/2016/01/05/reverse-engineering-a-real-candle/
3729
3730 46 uint16_t candle(bool multi)
3731 {
3732
5/6
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 23 times.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✓ Branch 5 taken 23 times.
46 if (multi && SEGLEN > 1) {
3733 //allocate segment data
3734 23 unsigned dataSize = max(1, (int)SEGLEN -1) *3; //max. 1365 pixels (ESP8266)
3735
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (!SEGENV.allocateData(dataSize)) return candle(false); //allocation failed
3736 }
3737
3738 //max. flicker range controlled by intensity
3739 46 unsigned valrange = SEGMENT.intensity;
3740 46 unsigned rndval = valrange >> 1; //max 127
3741
3742 //step (how much to move closer to target per frame) coarsely set by speed
3743 46 unsigned speedFactor = 4;
3744
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 38 times.
46 if (SEGMENT.speed > 252) { //epilepsy
3745 8 speedFactor = 1;
3746
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 } else if (SEGMENT.speed > 99) { //regular candle (mode called every ~25 ms, so 4 frames to have a new target every 100ms)
3747 ✗ speedFactor = 2;
3748
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 8 times.
38 } else if (SEGMENT.speed > 49) { //slower fade
3749 30 speedFactor = 3;
3750 } //else 4 (slowest)
3751
3752
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 23 times.
46 unsigned numCandles = (multi) ? SEGLEN : 1;
3753
3754
2/2
✓ Branch 0 taken 7534 times.
✓ Branch 1 taken 46 times.
7580 for (unsigned i = 0; i < numCandles; i++)
3755 {
3756 7534 unsigned d = 0; //data location
3757
3758 7534 unsigned s = SEGENV.aux0, s_target = SEGENV.aux1, fadeStep = SEGENV.step;
3759
2/2
✓ Branch 0 taken 7488 times.
✓ Branch 1 taken 46 times.
7534 if (i > 0) {
3760 7488 d = (i-1) *3;
3761 7488 s = SEGENV.data[d]; s_target = SEGENV.data[d+1]; fadeStep = SEGENV.data[d+2];
3762 }
3763
2/2
✓ Branch 0 taken 1712 times.
✓ Branch 1 taken 5822 times.
7534 if (fadeStep == 0) { //init vals
3764 1712 s = 128; s_target = 130 + hw_random8(4); fadeStep = 1;
3765 }
3766
3767 7534 bool newTarget = false;
3768
2/2
✓ Branch 0 taken 6977 times.
✓ Branch 1 taken 557 times.
7534 if (s_target > s) { //fade up
3769
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6977 times.
6977 s = qadd8(s, fadeStep);
3770
2/2
✓ Branch 0 taken 1492 times.
✓ Branch 1 taken 5485 times.
6977 if (s >= s_target) newTarget = true;
3771 } else {
3772
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 557 times.
557 s = qsub8(s, fadeStep);
3773
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 497 times.
557 if (s <= s_target) newTarget = true;
3774 }
3775
3776
2/2
✓ Branch 0 taken 1552 times.
✓ Branch 1 taken 5982 times.
7534 if (newTarget) {
3777 1552 s_target = hw_random8(rndval) + hw_random8(rndval); //between 0 and rndval*2 -2 = 252
3778
2/2
✓ Branch 0 taken 157 times.
✓ Branch 1 taken 1395 times.
1552 if (s_target < (rndval >> 1)) s_target = (rndval >> 1) + hw_random8(rndval);
3779 1552 unsigned offset = (255 - valrange);
3780 1552 s_target += offset;
3781
3782
2/2
✓ Branch 0 taken 1068 times.
✓ Branch 1 taken 484 times.
1552 unsigned dif = (s_target > s) ? s_target - s : s - s_target;
3783
3784 1552 fadeStep = dif >> speedFactor;
3785
2/2
✓ Branch 0 taken 105 times.
✓ Branch 1 taken 1447 times.
1552 if (fadeStep == 0) fadeStep = 1;
3786 }
3787
3788
2/2
✓ Branch 0 taken 7488 times.
✓ Branch 1 taken 46 times.
7534 if (i > 0) {
3789
2/4
✓ Branch 0 taken 7488 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7488 times.
7488 SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), uint8_t(s)));
3790
3791 7488 SEGENV.data[d] = s; SEGENV.data[d+1] = s_target; SEGENV.data[d+2] = fadeStep;
3792 } else {
3793
2/2
✓ Branch 0 taken 15022 times.
✓ Branch 1 taken 46 times.
15068 for (unsigned j = 0; j < SEGLEN; j++) {
3794
2/4
✓ Branch 0 taken 15022 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 15022 times.
15022 SEGMENT.setPixelColor(j, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(j, true, PALETTE_SOLID_WRAP, 0), uint8_t(s)));
3795 }
3796
3797 46 SEGENV.aux0 = s; SEGENV.aux1 = s_target; SEGENV.step = fadeStep;
3798 }
3799 }
3800
3801 46 return FRAMETIME_FIXED;
3802 }
3803
3804
3805 23 uint16_t mode_candle()
3806 {
3807 23 return candle(false);
3808 }
3809 static const char _data_FX_MODE_CANDLE[] PROGMEM = "Candle@!,!;!,!;!;01;sx=96,ix=224,pal=0";
3810
3811
3812 23 uint16_t mode_candle_multi()
3813 {
3814 23 return candle(true);
3815 }
3816 static const char _data_FX_MODE_CANDLE_MULTI[] PROGMEM = "Candle Multi@!,!;!,!;!;;sx=96,ix=224,pal=0";
3817
3818 #ifdef WLED_PS_DONT_REPLACE_FX
3819 /*
3820 / Fireworks in starburst effect
3821 / based on the video: https://www.reddit.com/r/arduino/comments/c3sd46/i_made_this_fireworks_effect_for_my_led_strips/
3822 / Speed sets frequency of new starbursts, intensity is the intensity of the burst
3823 */
3824 #ifdef ESP8266
3825 #define STARBURST_MAX_FRAG 8 //52 bytes / star
3826 #else
3827 #define STARBURST_MAX_FRAG 10 //60 bytes / star
3828 #endif
3829 //each needs 20+STARBURST_MAX_FRAG*4 bytes
3830 typedef struct particle {
3831 CRGB color;
3832 uint32_t birth =0;
3833 uint32_t last =0;
3834 float vel =0;
3835 uint16_t pos =-1;
3836 float fragment[STARBURST_MAX_FRAG];
3837 } star;
3838
3839 23 uint16_t mode_starburst(void) {
3840
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
3841 23 unsigned maxData = FAIR_DATA_PER_SEG; //ESP8266: 256 ESP32: 640
3842 23 unsigned segs = strip().getActiveSegmentsNum();
3843
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 if (segs <= (strip().getMaxSegments() /2)) maxData *= 2; //ESP8266: 512 if <= 8 segs ESP32: 1280 if <= 16 segs
3844
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 if (segs <= (strip().getMaxSegments() /4)) maxData *= 2; //ESP8266: 1024 if <= 4 segs ESP32: 2560 if <= 8 segs
3845 23 unsigned maxStars = maxData / sizeof(star); //ESP8266: max. 4/9/19 stars/seg, ESP32: max. 10/21/42 stars/seg
3846
3847 23 unsigned numStars = 1 + (SEGLEN >> 3);
3848
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (numStars > maxStars) numStars = maxStars;
3849 23 unsigned dataSize = sizeof(star) * numStars;
3850
3851
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
3852
3853 23 uint32_t it = strip().now;
3854
3855 23 star* stars = reinterpret_cast<star*>(SEGENV.data);
3856
3857 23 float maxSpeed = 375.0f; // Max velocity
3858 23 float particleIgnition = 250.0f; // How long to "flash"
3859 23 float particleFadeTime = 1500.0f; // Fade out time
3860
3861
2/2
✓ Branch 0 taken 955 times.
✓ Branch 1 taken 23 times.
978 for (unsigned j = 0; j < numStars; j++)
3862 {
3863 // speed to adjust chance of a burst, max is nearly always.
3864
5/6
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 926 times.
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 29 times.
✓ Branch 5 taken 926 times.
955 if (hw_random8((144-(SEGMENT.speed >> 1))) == 0 && stars[j].birth == 0)
3865 {
3866 // Pick a random color and location.
3867 29 unsigned startPos = hw_random16(SEGLEN-1);
3868 29 float multiplier = (float)(hw_random8())/255.0f * 1.0f;
3869
3870
3/6
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 29 times.
✗ Branch 5 not taken.
29 stars[j].color = CRGB(SEGMENT.color_wheel(hw_random8()));
3871 29 stars[j].pos = startPos;
3872 29 stars[j].vel = maxSpeed * (float)(hw_random8())/255.0f * multiplier;
3873 29 stars[j].birth = it;
3874 29 stars[j].last = it;
3875 // more fragments means larger burst effect
3876 29 int num = hw_random8(3,6 + (SEGMENT.intensity >> 5));
3877
3878
2/2
✓ Branch 0 taken 290 times.
✓ Branch 1 taken 29 times.
319 for (int i=0; i < STARBURST_MAX_FRAG; i++) {
3879
2/2
✓ Branch 0 taken 213 times.
✓ Branch 1 taken 77 times.
290 if (i < num) stars[j].fragment[i] = startPos;
3880 77 else stars[j].fragment[i] = -1;
3881 }
3882 }
3883 }
3884
3885
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1));
3886
3887
2/2
✓ Branch 0 taken 955 times.
✓ Branch 1 taken 23 times.
978 for (unsigned j=0; j<numStars; j++)
3888 {
3889
2/2
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 882 times.
955 if (stars[j].birth != 0) {
3890 73 float dt = (it-stars[j].last)/1000.0;
3891
3892
2/2
✓ Branch 0 taken 730 times.
✓ Branch 1 taken 73 times.
803 for (int i=0; i < STARBURST_MAX_FRAG; i++) {
3893 730 int var = i >> 1;
3894
3895
2/2
✓ Branch 0 taken 548 times.
✓ Branch 1 taken 182 times.
730 if (stars[j].fragment[i] > 0) {
3896 //all fragments travel right, will be mirrored on other side
3897 548 stars[j].fragment[i] += stars[j].vel * dt * (float)var/3.0;
3898 }
3899 }
3900 73 stars[j].last = it;
3901 73 stars[j].vel -= 3*stars[j].vel*dt;
3902 }
3903
3904 955 CRGB c = stars[j].color;
3905
3906 // If the star is brand new, it flashes white briefly.
3907 // Otherwise it just fades over time.
3908 955 float fade = 0.0f;
3909 955 float age = it-stars[j].birth;
3910
3911
2/2
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 882 times.
955 if (age < particleIgnition) {
3912
1/2
✓ Branch 0 taken 73 times.
✗ Branch 1 not taken.
73 c = CRGB(color_blend(WHITE, RGBW32(c.r,c.g,c.b,0), uint8_t(254.5f*((age / particleIgnition)))));
3913 } else {
3914 // Figure out how much to fade and shrink the star based on
3915 // its age relative to its lifetime
3916
1/2
✓ Branch 0 taken 882 times.
✗ Branch 1 not taken.
882 if (age > particleIgnition + particleFadeTime) {
3917 882 fade = 1.0f; // Black hole, all faded out
3918 882 stars[j].birth = 0;
3919 882 c = CRGB(SEGCOLOR(1));
3920 } else {
3921 ✗ age -= particleIgnition;
3922 ✗ fade = (age / particleFadeTime); // Fading star
3923 ✗ c = CRGB(color_blend(RGBW32(c.r,c.g,c.b,0), SEGCOLOR(1), uint8_t(254.5f*fade)));
3924 }
3925 }
3926
3927 955 float particleSize = (1.0f - fade) * 2.0f;
3928
3929
2/2
✓ Branch 0 taken 19100 times.
✓ Branch 1 taken 955 times.
20055 for (size_t index=0; index < STARBURST_MAX_FRAG*2; index++) {
3930 19100 bool mirrored = index & 0x1;
3931 19100 unsigned i = index >> 1;
3932
2/2
✓ Branch 0 taken 1096 times.
✓ Branch 1 taken 18004 times.
19100 if (stars[j].fragment[i] > 0) {
3933 1096 float loc = stars[j].fragment[i];
3934
2/2
✓ Branch 0 taken 548 times.
✓ Branch 1 taken 548 times.
1096 if (mirrored) loc -= (loc-stars[j].pos)*2;
3935 1096 unsigned start = loc - particleSize;
3936 1096 unsigned end = loc + particleSize;
3937 if (start < 0) start = 0;
3938
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1096 times.
1096 if (start == end) end++;
3939
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 1066 times.
1096 if (end > SEGLEN) end = SEGLEN;
3940
2/2
✓ Branch 0 taken 4284 times.
✓ Branch 1 taken 1096 times.
5380 for (unsigned p = start; p < end; p++) {
3941
3/6
✓ Branch 0 taken 4284 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4284 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4284 times.
✗ Branch 5 not taken.
4284 SEGMENT.setPixelColor(p, c);
3942 }
3943 }
3944 }
3945 }
3946 23 return FRAMETIME;
3947 }
3948 #undef STARBURST_MAX_FRAG
3949 static const char _data_FX_MODE_STARBURST[] PROGMEM = "Fireworks Starburst@Chance,Fragments,,,,,Overlay;,!;!;;pal=11,m12=0";
3950 #endif // WLED_PS_DONT_REPLACE_FX
3951
3952 #ifdef WLED_PS_DONT_REPLACE_FX
3953 /*
3954 * Exploding fireworks effect
3955 * adapted from: http://www.anirama.com/1000leds/1d-fireworks/
3956 * adapted for 2D WLED by blazoncek (Blaz Kristan (AKA blazoncek))
3957 */
3958 23 uint16_t mode_exploding_fireworks(void)
3959 {
3960
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
23 if (SEGLEN <= 1) return mode_static();
3961
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✓ Branch 5 taken 5 times.
23 const int cols = SEGMENT.is2D() ? SEG_W : 1;
3962
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✓ Branch 5 taken 5 times.
23 const int rows = SEGMENT.is2D() ? SEG_H : SEGLEN;
3963
3964 //allocate segment data
3965
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 unsigned maxData = FAIR_DATA_PER_SEG; //ESP8266: 256 ESP32: 640
3966
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 unsigned segs = strip().getActiveSegmentsNum();
3967
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 if (segs <= (strip().getMaxSegments() /2)) maxData *= 2; //ESP8266: 512 if <= 8 segs ESP32: 1280 if <= 16 segs
3968
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 if (segs <= (strip().getMaxSegments() /4)) maxData *= 2; //ESP8266: 1024 if <= 4 segs ESP32: 2560 if <= 8 segs
3969 23 int maxSparks = maxData / sizeof(spark); //ESP8266: max. 21/42/85 sparks/seg, ESP32: max. 53/106/213 sparks/seg
3970
3971 23 unsigned numSparks = min(5 + ((rows*cols) >> 1), maxSparks);
3972 23 unsigned dataSize = sizeof(spark) * numSparks;
3973
4/10
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 23 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
23 if (!SEGENV.allocateData(dataSize + sizeof(float))) return mode_static(); //allocation failed
3974
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 float *dying_gravity = reinterpret_cast<float*>(SEGENV.data + dataSize);
3975
3976
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (dataSize != SEGENV.aux1) { //reset to flare if sparks were reallocated (it may be good idea to reset segment if bounds change)
3977 5 *dying_gravity = 0.0f;
3978
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 SEGENV.aux0 = 0;
3979
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 SEGENV.aux1 = dataSize;
3980 }
3981
3982
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 SEGMENT.fade_out(252);
3983
3984
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 Spark* sparks = reinterpret_cast<Spark*>(SEGENV.data);
3985 23 Spark* flare = sparks; //first spark is flare data
3986
3987
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 float gravity = -0.0004f - (SEGMENT.speed/800000.0f); // m/s/s
3988 23 gravity *= rows;
3989
3990
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 if (SEGENV.aux0 < 2) { //FLARE
3991
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGENV.aux0 == 0) { //init flare
3992 5 flare->pos = 0;
3993
7/12
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 1 times.
5 flare->posX = SEGMENT.is2D() ? hw_random16(2,cols-3) : (SEGMENT.intensity > hw_random8()); // will enable random firing side on 1D
3994 5 unsigned peakHeight = 75 + hw_random8(180); //0-255
3995 5 peakHeight = (peakHeight * (rows -1)) >> 8;
3996 5 flare->vel = sqrtf(-2.0f * gravity * peakHeight);
3997
4/6
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 1 times.
5 flare->velX = SEGMENT.is2D() ? (hw_random8(9)-4)/64.0f : 0; // no X velocity on 1D
3998 5 flare->col = 255; //brightness
3999
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 SEGENV.aux0 = 1;
4000 }
4001
4002 // launch
4003
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 if (flare->vel > 12 * gravity) {
4004 // flare
4005
7/12
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 18 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 18 times.
✗ Branch 11 not taken.
23 if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(unsigned(flare->posX), rows - uint16_t(flare->pos) - 1, flare->col, flare->col, flare->col);
4006
4/8
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 5 times.
✗ Branch 7 not taken.
5 else SEGMENT.setPixelColor((flare->posX > 0.0f) ? rows - int(flare->pos) - 1 : int(flare->pos), flare->col, flare->col, flare->col);
4007 23 flare->pos += flare->vel;
4008
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 23 times.
23 flare->pos = constrain(flare->pos, 0, rows-1);
4009
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✓ Branch 5 taken 5 times.
23 if (SEGMENT.is2D()) {
4010 18 flare->posX += flare->velX;
4011
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
18 flare->posX = constrain(flare->posX, 0, cols-1);
4012 }
4013 23 flare->vel += gravity;
4014 23 flare->col -= 2;
4015 } else {
4016 ✗ SEGENV.aux0 = 2; // ready to explode
4017 }
4018 ✗ } else if (SEGENV.aux0 < 4) {
4019 /*
4020 * Explode!
4021 *
4022 * Explosion happens where the flare ended.
4023 * Size is proportional to the height.
4024 */
4025 ✗ unsigned nSparks = flare->pos + hw_random8(4);
4026 ✗ nSparks = std::max(nSparks, 4U); // This is not a standard constrain; numSparks is not guaranteed to be at least 4
4027 ✗ nSparks = std::min(nSparks, numSparks);
4028
4029 // initialize sparks
4030 ✗ if (SEGENV.aux0 == 2) {
4031 ✗ for (unsigned i = 1; i < nSparks; i++) {
4032 ✗ sparks[i].pos = flare->pos;
4033 ✗ sparks[i].posX = flare->posX;
4034 ✗ sparks[i].vel = (float(hw_random16(20001)) / 10000.0f) - 0.9f; // from -0.9 to 1.1
4035 ✗ sparks[i].vel *= rows<32 ? 0.5f : 1; // reduce velocity for smaller strips
4036 ✗ sparks[i].velX = SEGMENT.is2D() ? (float(hw_random16(20001)) / 10000.0f) - 1.0f : 0; // from -1 to 1
4037 ✗ sparks[i].col = 345;//abs(sparks[i].vel * 750.0); // set colors before scaling velocity to keep them bright
4038 //sparks[i].col = constrain(sparks[i].col, 0, 345);
4039 ✗ sparks[i].colIndex = hw_random8();
4040 ✗ sparks[i].vel *= flare->pos/rows; // proportional to height
4041 ✗ sparks[i].velX *= SEGMENT.is2D() ? flare->posX/cols : 0; // proportional to width
4042 ✗ sparks[i].vel *= -gravity *50;
4043 }
4044 //sparks[1].col = 345; // this will be our known spark
4045 ✗ *dying_gravity = gravity/2;
4046 ✗ SEGENV.aux0 = 3;
4047 }
4048
4049 ✗ if (sparks[1].col > 4) {//&& sparks[1].pos > 0) { // as long as our known spark is lit, work with all the sparks
4050 ✗ for (unsigned i = 1; i < nSparks; i++) {
4051 ✗ sparks[i].pos += sparks[i].vel;
4052 ✗ sparks[i].posX += sparks[i].velX;
4053 ✗ sparks[i].vel += *dying_gravity;
4054 ✗ sparks[i].velX += SEGMENT.is2D() ? *dying_gravity : 0;
4055 ✗ if (sparks[i].col > 3) sparks[i].col -= 4;
4056
4057 ✗ if (sparks[i].pos > 0 && sparks[i].pos < rows) {
4058 ✗ if (SEGMENT.is2D() && !(sparks[i].posX >= 0 && sparks[i].posX < cols)) continue;
4059 ✗ unsigned prog = sparks[i].col;
4060 ✗ uint32_t spColor = (SEGMENT.palette) ? SEGMENT.color_wheel(sparks[i].colIndex) : SEGCOLOR(0);
4061 ✗ CRGBW c = BLACK; //HeatColor(sparks[i].col);
4062 ✗ if (prog > 300) { //fade from white to spark color
4063 ✗ c = color_blend(spColor, WHITE, uint8_t((prog - 300)*5));
4064 ✗ } else if (prog > 45) { //fade from spark color to black
4065 ✗ c = color_blend(BLACK, spColor, uint8_t(prog - 45));
4066 ✗ unsigned cooling = (300 - prog) >> 5;
4067 ✗ c.g = qsub8(c.g, cooling);
4068 ✗ c.b = qsub8(c.b, cooling * 2);
4069 }
4070 ✗ if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(int(sparks[i].posX), rows - int(sparks[i].pos) - 1, c);
4071 ✗ else SEGMENT.setPixelColor(int(sparks[i].posX) ? rows - int(sparks[i].pos) - 1 : int(sparks[i].pos), c);
4072 }
4073 }
4074 ✗ if (SEGMENT.check3) SEGMENT.blur(16);
4075 ✗ *dying_gravity *= .8f; // as sparks burn out they fall slower
4076 } else {
4077 ✗ SEGENV.aux0 = 6 + hw_random8(10); //wait for this many frames
4078 }
4079 } else {
4080 ✗ SEGENV.aux0--;
4081 ✗ if (SEGENV.aux0 < 4) {
4082 ✗ SEGENV.aux0 = 0; //back to flare
4083 }
4084 }
4085
4086
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 return FRAMETIME;
4087 }
4088 #undef MAX_SPARKS
4089 static const char _data_FX_MODE_EXPLODING_FIREWORKS[] PROGMEM = "Fireworks 1D@Gravity,Firing side;!,!;!;12;pal=11,ix=128";
4090 #endif // WLED_PS_DONT_REPLACE_FX
4091
4092 /*
4093 * Drip Effect
4094 * ported of: https://www.youtube.com/watch?v=sru2fXh4r7k
4095 */
4096 23 uint16_t mode_drip(void)
4097 {
4098
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
4099 //allocate segment data
4100 23 unsigned strips = SEGMENT.nrOfVStrips();
4101 23 const int maxNumDrops = 4;
4102 23 unsigned dataSize = sizeof(spark) * maxNumDrops;
4103
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed
4104 23 Spark* drops = reinterpret_cast<Spark*>(SEGENV.data);
4105
4106
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1));
4107
4108 struct virtualStrip {
4109 456 static void runStrip(uint16_t stripNr, Spark* drops) {
4110
4111 456 unsigned numDrops = 1 + (SEGMENT.intensity >> 6); // 255>>6 = 3
4112
4113 456 float gravity = -0.0005f - (SEGMENT.speed/50000.0f);
4114 456 gravity *= max(1, (int)SEGLEN-1);
4115 456 int sourcedrop = 12;
4116
4117
2/2
✓ Branch 0 taken 1240 times.
✓ Branch 1 taken 456 times.
1696 for (unsigned j=0;j<numDrops;j++) {
4118
2/2
✓ Branch 0 taken 280 times.
✓ Branch 1 taken 960 times.
1240 if (drops[j].colIndex == 0) { //init
4119 280 drops[j].pos = SEGLEN-1; // start at end
4120 280 drops[j].vel = 0; // speed
4121 280 drops[j].col = sourcedrop; // brightness
4122 280 drops[j].colIndex = 1; // drop state (0 init, 1 forming, 2 falling, 5 bouncing)
4123 }
4124
4125 1240 SEGMENT.setPixelColor(indexToVStrip(SEGLEN-1, stripNr), color_blend(BLACK,SEGCOLOR(0), uint8_t(sourcedrop)));// water source
4126
2/2
✓ Branch 0 taken 1226 times.
✓ Branch 1 taken 14 times.
1240 if (drops[j].colIndex==1) {
4127
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1226 times.
1226 if (drops[j].col>255) drops[j].col=255;
4128 1226 SEGMENT.setPixelColor(indexToVStrip(uint16_t(drops[j].pos), stripNr), color_blend(BLACK,SEGCOLOR(0),uint8_t(drops[j].col)));
4129
4130 1226 drops[j].col += map(SEGMENT.speed, 0, 255, 1, 6); // swelling
4131
4132
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1218 times.
1226 if (hw_random8() < drops[j].col/10) { // random drop
4133 8 drops[j].colIndex=2; //fall
4134 8 drops[j].col=255;
4135 }
4136 }
4137
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 1218 times.
1240 if (drops[j].colIndex > 1) { // falling
4138
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22 if (drops[j].pos > 0) { // fall until end of segment
4139 22 drops[j].pos += drops[j].vel;
4140
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (drops[j].pos < 0) drops[j].pos = 0;
4141 22 drops[j].vel += gravity; // gravity is negative
4142
4143
2/2
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 22 times.
110 for (int i=1;i<7-drops[j].colIndex;i++) { // some minor math so we don't expand bouncing droplets
4144
2/2
✓ Branch 0 taken 74 times.
✓ Branch 1 taken 14 times.
88 unsigned pos = constrain(unsigned(drops[j].pos) +i, 0, SEGLEN-1); //this is BAD, returns a pos >= SEGLEN occasionally
4145 88 SEGMENT.setPixelColor(indexToVStrip(pos, stripNr), color_blend(BLACK,SEGCOLOR(0),uint8_t(drops[j].col/i))); //spread pixel with fade while falling
4146 }
4147
4148
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (drops[j].colIndex > 2) { // during bounce, some water is on the floor
4149 ✗ SEGMENT.setPixelColor(indexToVStrip(0, stripNr), color_blend(SEGCOLOR(0),BLACK,uint8_t(drops[j].col)));
4150 }
4151 } else { // we hit bottom
4152 ✗ if (drops[j].colIndex > 2) { // already hit once, so back to forming
4153 ✗ drops[j].colIndex = 0;
4154 ✗ drops[j].col = sourcedrop;
4155
4156 } else {
4157
4158 ✗ if (drops[j].colIndex==2) { // init bounce
4159 ✗ drops[j].vel = -drops[j].vel/4;// reverse velocity with damping
4160 ✗ drops[j].pos += drops[j].vel;
4161 }
4162 ✗ drops[j].col = sourcedrop*2;
4163 ✗ drops[j].colIndex = 5; // bouncing
4164 }
4165 }
4166 }
4167 }
4168 456 }
4169 };
4170
4171
2/2
✓ Branch 0 taken 456 times.
✓ Branch 1 taken 23 times.
479 for (unsigned stripNr=0; stripNr<strips; stripNr++)
4172 456 virtualStrip::runStrip(stripNr, &drops[stripNr*maxNumDrops]);
4173
4174 23 return FRAMETIME;
4175 }
4176 static const char _data_FX_MODE_DRIP[] PROGMEM = "Drip@Gravity,# of drips,,,,,Overlay;!,!;!;;m12=1"; //bar
4177
4178 /*
4179 * Tetris or Stacking (falling bricks) Effect
4180 * by Blaz Kristan (AKA blazoncek) (https://github.com/blazoncek, https://blaz.at/home)
4181 */
4182 //20 bytes
4183 typedef struct Tetris {
4184 float pos;
4185 float speed;
4186 uint8_t col; // color index
4187 uint16_t brick; // brick size in pixels
4188 uint16_t stack; // stack size in pixels
4189 uint32_t step; // 2D-fication of SEGENV.step (state)
4190 } tetris;
4191
4192 23 uint16_t mode_tetrix(void) {
4193
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
4194 23 unsigned strips = SEGMENT.nrOfVStrips(); // allow running on virtual strips (columns in 2D segment)
4195 23 unsigned dataSize = sizeof(tetris);
4196
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed
4197 23 Tetris* drops = reinterpret_cast<Tetris*>(SEGENV.data);
4198
4199 //if (SEGENV.call == 0) SEGMENT.fill(SEGCOLOR(1)); // will fill entire segment (1D or 2D), then use drop->step = 0 below
4200
4201 // virtualStrip idea by @ewowi (Ewoud Wijma)
4202 // requires virtual strip # to be embedded into upper 16 bits of index in setPixelcolor()
4203 // the following functions will not work on virtual strips: fill(), fade_out(), fadeToBlack(), blur()
4204 struct virtualStrip {
4205 456 static void runStrip(size_t stripNr, Tetris *drop) {
4206 // initialize dropping on first call or segment full
4207
2/2
✓ Branch 0 taken 104 times.
✓ Branch 1 taken 352 times.
456 if (SEGENV.call == 0) {
4208 104 drop->stack = 0; // reset brick stack size
4209 104 drop->step = strip().now + 2000; // start by fading out strip
4210
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 104 times.
104 if (SEGMENT.check1) drop->col = 0;// use only one color from palette
4211 }
4212
4213
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 456 times.
456 if (drop->step == 0) { // init brick
4214 // speed calculation: a single brick should reach bottom of strip in X seconds
4215 // if the speed is set to 1 this should take 5s and at 255 it should take 0.25s
4216 // as this is dependant on SEGLEN it should be taken into account and the fact that effect runs every FRAMETIME s
4217 ✗ int speed = SEGMENT.speed ? SEGMENT.speed : hw_random8(1,255);
4218 ✗ speed = map(speed, 1, 255, 5000, 250); // time taken for full (SEGLEN) drop
4219 ✗ drop->speed = float(SEGLEN * FRAMETIME) / float(speed); // set speed
4220 ✗ drop->pos = SEGLEN; // start at end of segment (no need to subtract 1)
4221 ✗ if (!SEGMENT.check1) drop->col = hw_random8(0,15)<<4; // limit color choices so there is enough HUE gap
4222 ✗ drop->step = 1; // drop state (0 init, 1 forming, 2 falling)
4223 ✗ drop->brick = (SEGMENT.intensity ? (SEGMENT.intensity>>5)+1 : hw_random8(1,5)) * (1+(SEGLEN>>6)); // size of brick
4224 }
4225
4226
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 456 times.
456 if (drop->step == 1) { // forming
4227 ✗ if (hw_random8()>>6) { // random drop
4228 ✗ drop->step = 2; // fall
4229 }
4230 }
4231
4232
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 456 times.
456 if (drop->step == 2) { // falling
4233 ✗ if (drop->pos > drop->stack) { // fall until top of stack
4234 ✗ drop->pos -= drop->speed; // may add gravity as: speed += gravity
4235 ✗ if (int(drop->pos) < int(drop->stack)) drop->pos = drop->stack;
4236 ✗ for (unsigned i = unsigned(drop->pos); i < SEGLEN; i++) {
4237 ✗ uint32_t col = i < unsigned(drop->pos)+drop->brick ? SEGMENT.color_from_palette(drop->col, false, false, 0) : SEGCOLOR(1);
4238 ✗ SEGMENT.setPixelColor(indexToVStrip(i, stripNr), col);
4239 }
4240 } else { // we hit bottom
4241 ✗ drop->step = 0; // proceed with next brick, go back to init
4242 ✗ drop->stack += drop->brick; // increase the stack size
4243 ✗ if (drop->stack >= SEGLEN) drop->step = strip().now + 2000; // fade out stack
4244 }
4245 }
4246
4247
1/2
✓ Branch 0 taken 456 times.
✗ Branch 1 not taken.
456 if (drop->step > 2) { // fade strip
4248 456 drop->brick = 0; // reset brick size (no more growing)
4249
1/2
✓ Branch 0 taken 456 times.
✗ Branch 1 not taken.
456 if (drop->step > strip().now) {
4250 // allow fading of virtual strip
4251
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 456 times.
7967 for (unsigned i = 0; i < SEGLEN; i++) SEGMENT.blendPixelColor(indexToVStrip(i, stripNr), SEGCOLOR(1), 25); // 10% blend
4252 } else {
4253 ✗ drop->stack = 0; // reset brick stack size
4254 ✗ drop->step = 0; // proceed with next brick
4255 ✗ if (SEGMENT.check1) drop->col += 8; // gradually increase palette index
4256 }
4257 }
4258 456 }
4259 };
4260
4261
2/2
✓ Branch 0 taken 456 times.
✓ Branch 1 taken 23 times.
479 for (unsigned stripNr=0; stripNr<strips; stripNr++)
4262 456 virtualStrip::runStrip(stripNr, &drops[stripNr]);
4263
4264 23 return FRAMETIME;
4265 }
4266 static const char _data_FX_MODE_TETRIX[] PROGMEM = "Tetrix@!,Width,,,,One color;!,!;!;;sx=0,ix=0,pal=11,m12=1";
4267
4268
4269 /*
4270 / Plasma Effect
4271 / adapted from https://github.com/atuline/FastLED-Demos/blob/master/plasma/plasma.ino
4272 */
4273 23 uint16_t mode_plasma(void) {
4274 // initialize phases on start
4275
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if (SEGENV.call == 0) {
4276 5 SEGENV.aux0 = hw_random8(0,2); // add a bit of randomness
4277 }
4278 23 unsigned thisPhase = beatsin8_t(6+SEGENV.aux0,-64,64);
4279 23 unsigned thatPhase = beatsin8_t(7+SEGENV.aux0,-64,64);
4280
4281
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) { // For each of the LED's in the strand, set color & brightness based on a wave as follows:
4282 7511 unsigned colorIndex = cubicwave8((i*(2+ 3*(SEGMENT.speed >> 5))+thisPhase) & 0xFF)/2 // factor=23 // Create a wave and add a phase change and add another wave with its own phase change.
4283 7511 + cos8_t((i*(1+ 2*(SEGMENT.speed >> 5))+thatPhase) & 0xFF)/2; // factor=15 // Hey, you can even change the frequencies if you wish.
4284
2/2
✓ Branch 0 taken 932 times.
✓ Branch 1 taken 6579 times.
7511 unsigned thisBright = qsub8(colorIndex, beatsin8_t(7,0, (128 - (SEGMENT.intensity>>1))));
4285
2/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7511 times.
7511 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0, thisBright));
4286 }
4287
4288 23 return FRAMETIME;
4289 }
4290 static const char _data_FX_MODE_PLASMA[] PROGMEM = "Plasma@Phase,!;!;!";
4291
4292
4293 /*
4294 * Percentage display
4295 * Intensity values from 0-100 turn on the leds.
4296 */
4297 861 uint16_t mode_percent(void) {
4298
4299 861 unsigned percent = SEGMENT.intensity;
4300 861 percent = constrain(percent, 0, 200);
4301 842 unsigned active_leds = (percent < 100) ? roundf(SEGLEN * percent / 100.0f)
4302
2/2
✓ Branch 0 taken 842 times.
✓ Branch 1 taken 19 times.
1703 : roundf(SEGLEN * (200 - percent) / 100.0f);
4303
4304 861 unsigned size = (1 + ((SEGMENT.speed * SEGLEN) >> 11));
4305
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 861 times.
861 if (SEGMENT.speed == 255) size = 255;
4306
4307
2/2
✓ Branch 0 taken 842 times.
✓ Branch 1 taken 19 times.
861 if (percent <= 100) {
4308
2/2
✓ Branch 0 taken 22160 times.
✓ Branch 1 taken 842 times.
23002 for (unsigned i = 0; i < SEGLEN; i++) {
4309
2/2
✓ Branch 0 taken 5778 times.
✓ Branch 1 taken 16382 times.
22160 if (i < SEGENV.aux1) {
4310
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5778 times.
5778 if (SEGMENT.check1)
4311 ✗ SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(map(percent,0,100,0,255), false, false, 0));
4312 else
4313
2/4
✓ Branch 0 taken 5778 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5778 times.
5778 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
4314 }
4315 else {
4316 16382 SEGMENT.setPixelColor(i, SEGCOLOR(1));
4317 }
4318 }
4319 } else {
4320
2/2
✓ Branch 0 taken 5463 times.
✓ Branch 1 taken 19 times.
5482 for (unsigned i = 0; i < SEGLEN; i++) {
4321
2/2
✓ Branch 0 taken 5013 times.
✓ Branch 1 taken 450 times.
5463 if (i < (SEGLEN - SEGENV.aux1)) {
4322 5013 SEGMENT.setPixelColor(i, SEGCOLOR(1));
4323 }
4324 else {
4325
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 450 times.
450 if (SEGMENT.check1)
4326 ✗ SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(map(percent,100,200,255,0), false, false, 0));
4327 else
4328
2/4
✓ Branch 0 taken 450 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 450 times.
450 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
4329 }
4330 }
4331 }
4332
4333
2/2
✓ Branch 0 taken 116 times.
✓ Branch 1 taken 745 times.
861 if(active_leds > SEGENV.aux1) { // smooth transition to the target value
4334 116 SEGENV.aux1 += size;
4335
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 116 times.
116 if (SEGENV.aux1 > active_leds) SEGENV.aux1 = active_leds;
4336
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 745 times.
745 } else if (active_leds < SEGENV.aux1) {
4337 ✗ if (SEGENV.aux1 > size) SEGENV.aux1 -= size; else SEGENV.aux1 = 0;
4338 ✗ if (SEGENV.aux1 < active_leds) SEGENV.aux1 = active_leds;
4339 }
4340
4341 861 return FRAMETIME;
4342 }
4343 static const char _data_FX_MODE_PERCENT[] PROGMEM = "Percent@,% of fill,,,,One color;!,!;!";
4344
4345
4346 /*
4347 * Modulates the brightness similar to a heartbeat
4348 * (unimplemented?) tries to draw an ECG approximation on a 2D matrix
4349 */
4350 23 uint16_t mode_heartbeat(void) {
4351 23 unsigned bpm = 40 + (SEGMENT.speed >> 3);
4352 23 uint32_t msPerBeat = (60000L / bpm);
4353 23 uint32_t secondBeat = (msPerBeat / 3);
4354 23 uint32_t bri_lower = SEGENV.aux1;
4355 23 unsigned long beatTimer = strip().now - SEGENV.step;
4356
4357 23 bri_lower = bri_lower * 2042 / (2048 + SEGMENT.intensity);
4358 23 SEGENV.aux1 = bri_lower;
4359
4360
5/6
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if ((beatTimer > secondBeat) && !SEGENV.aux0) { // time for the second beat?
4361 5 SEGENV.aux1 = UINT16_MAX; //3/4 bri
4362 5 SEGENV.aux0 = 1;
4363 }
4364
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if (beatTimer > msPerBeat) { // time to reset the beat timer?
4365 5 SEGENV.aux1 = UINT16_MAX; //full bri
4366 5 SEGENV.aux0 = 0;
4367 5 SEGENV.step = strip().now;
4368 }
4369
4370
2/2
✓ Branch 0 taken 973 times.
✓ Branch 1 taken 23 times.
996 for (unsigned i = 0; i < SEGLEN; i++) {
4371
2/4
✓ Branch 0 taken 973 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 973 times.
973 SEGMENT.setPixelColor(i, color_blend(SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), uint8_t(255 - (SEGENV.aux1 >> 8))));
4372 }
4373
4374 23 return FRAMETIME;
4375 }
4376 static const char _data_FX_MODE_HEARTBEAT[] PROGMEM = "Heartbeat@!,!;!,!;!;01;m12=1";
4377
4378
4379 // "Pacifica"
4380 // Gentle, blue-green ocean waves.
4381 // December 2019, Mark Kriegsman and Mary Corey March.
4382 // For Dan.
4383 //
4384 //
4385 // In this animation, there are four "layers" of waves of light.
4386 //
4387 // Each layer moves independently, and each is scaled separately.
4388 //
4389 // All four wave layers are added together on top of each other, and then
4390 // another filter is applied that adds "whitecaps" of brightness where the
4391 // waves line up with each other more. Finally, another pass is taken
4392 // over the led array to 'deepen' (dim) the blues and greens.
4393 //
4394 // The speed and scale and motion each layer varies slowly within independent
4395 // hand-chosen ranges, which is why the code has a lot of low-speed 'beatsin8' functions
4396 // with a lot of oddly specific numeric ranges.
4397 //
4398 // These three custom blue-green color palettes were inspired by the colors found in
4399 // the waters off the southern coast of California, https://goo.gl/maps/QQgd97jjHesHZVxQ7
4400 //
4401 // Modified for WLED, based on https://github.com/FastLED/FastLED/blob/master/examples/Pacifica/Pacifica.ino
4402 //
4403 // Add one layer of waves into the led array
4404 30044 static CRGB pacifica_one_layer(uint16_t i, const CRGBPalette16& p, uint16_t cistart, uint16_t wavescale, uint8_t bri, uint16_t ioff)
4405 {
4406 30044 unsigned ci = cistart;
4407 30044 unsigned waveangle = ioff;
4408 30044 unsigned wavescale_half = (wavescale >> 1) + 20;
4409
4410 30044 waveangle += ((120 + SEGMENT.intensity) * i); //original 250 * i
4411 30044 unsigned s16 = sin16_t(waveangle) + 32768;
4412 30044 unsigned cs = scale16(s16, wavescale_half) + wavescale_half;
4413 30044 ci += (cs * i);
4414 30044 unsigned sindex16 = sin16_t(ci) + 32768;
4415 30044 unsigned sindex8 = scale16(sindex16, 240);
4416
1/2
✓ Branch 0 taken 30044 times.
✗ Branch 1 not taken.
30044 return CRGB(ColorFromPalette(p, sindex8, bri, LINEARBLEND));
4417 }
4418
4419 23 uint16_t mode_pacifica()
4420 {
4421
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 uint32_t nowOld = strip().now;
4422
4423 CRGBPalette16 pacifica_palette_1 =
4424 { 0x000507, 0x000409, 0x00030B, 0x00030D, 0x000210, 0x000212, 0x000114, 0x000117,
4425 23 0x000019, 0x00001C, 0x000026, 0x000031, 0x00003B, 0x000046, 0x14554B, 0x28AA50 };
4426 CRGBPalette16 pacifica_palette_2 =
4427 { 0x000507, 0x000409, 0x00030B, 0x00030D, 0x000210, 0x000212, 0x000114, 0x000117,
4428 23 0x000019, 0x00001C, 0x000026, 0x000031, 0x00003B, 0x000046, 0x0C5F52, 0x19BE5F };
4429 CRGBPalette16 pacifica_palette_3 =
4430 { 0x000208, 0x00030E, 0x000514, 0x00061A, 0x000820, 0x000927, 0x000B2D, 0x000C33,
4431 23 0x000E39, 0x001040, 0x001450, 0x001860, 0x001C70, 0x002080, 0x1040BF, 0x2060FF };
4432
4433
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 if (SEGMENT.palette) {
4434 23 pacifica_palette_1 = SEGPALETTE;
4435 23 pacifica_palette_2 = SEGPALETTE;
4436 23 pacifica_palette_3 = SEGPALETTE;
4437 }
4438
4439 // Increment the four "color index start" counters, one for each wave layer.
4440 // Each is incremented at a different speed, and the speeds vary over time.
4441
8/16
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 23 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 23 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 23 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 23 times.
✗ Branch 15 not taken.
23 unsigned sCIStart1 = SEGENV.aux0, sCIStart2 = SEGENV.aux1, sCIStart3 = SEGENV.step & 0xFFFF, sCIStart4 = (SEGENV.step >> 16);
4442
4/8
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
23 uint32_t deltams = (FRAMETIME >> 2) + ((FRAMETIME * SEGMENT.speed) >> 7);
4443
4/8
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
23 uint64_t deltat = (strip().now >> 2) + ((strip().now * SEGMENT.speed) >> 7);
4444
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 strip().now = deltat;
4445
4446
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 unsigned speedfactor1 = beatsin16_t(3, 179, 269);
4447
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 unsigned speedfactor2 = beatsin16_t(4, 179, 269);
4448 23 uint32_t deltams1 = (deltams * speedfactor1) / 256;
4449 23 uint32_t deltams2 = (deltams * speedfactor2) / 256;
4450 23 uint32_t deltams21 = (deltams1 + deltams2) / 2;
4451
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 sCIStart1 += (deltams1 * beatsin88_t(1011,10,13));
4452
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 sCIStart2 -= (deltams21 * beatsin88_t(777,8,11));
4453
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 sCIStart3 -= (deltams1 * beatsin88_t(501,5,7));
4454
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 sCIStart4 -= (deltams2 * beatsin88_t(257,4,6));
4455
4/8
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
23 SEGENV.aux0 = sCIStart1; SEGENV.aux1 = sCIStart2;
4456
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 SEGENV.step = (sCIStart4 << 16) | (sCIStart3 & 0xFFFF);
4457
4458 // Clear out the LED array to a dim background blue-green
4459 //SEGMENT.fill(132618);
4460
4461
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 unsigned basethreshold = beatsin8_t( 9, 55, 65);
4462
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 unsigned wave = beat8( 7 );
4463
4464
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
4465 7511 CRGB c = CRGB(2, 6, 10);
4466 // Render each of four layers, with different scales and speeds, that vary over time
4467
5/10
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7511 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7511 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 7511 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 7511 times.
✗ Branch 9 not taken.
7511 c += pacifica_one_layer(i, pacifica_palette_1, sCIStart1, beatsin16_t(3, 11 * 256, 14 * 256), beatsin8_t(10, 70, 130), 0-beat16(301));
4468
5/10
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7511 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7511 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 7511 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 7511 times.
✗ Branch 9 not taken.
7511 c += pacifica_one_layer(i, pacifica_palette_2, sCIStart2, beatsin16_t(4, 6 * 256, 9 * 256), beatsin8_t(17, 40, 80), beat16(401));
4469
4/8
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7511 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7511 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 7511 times.
✗ Branch 7 not taken.
7511 c += pacifica_one_layer(i, pacifica_palette_3, sCIStart3, 6 * 256 , beatsin8_t(9, 10,38) , 0-beat16(503));
4470
4/8
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7511 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7511 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 7511 times.
✗ Branch 7 not taken.
7511 c += pacifica_one_layer(i, pacifica_palette_3, sCIStart4, 5 * 256 , beatsin8_t(8, 10,28) , beat16(601));
4471
4472 // Add extra 'white' to areas where the four layers of light have lined up brightly
4473
1/2
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
7511 unsigned threshold = scale8( sin8_t( wave), 20) + basethreshold;
4474 7511 wave += 7;
4475 7511 unsigned l = c.getAverageLight();
4476
2/2
✓ Branch 0 taken 4214 times.
✓ Branch 1 taken 3297 times.
7511 if (l > threshold) {
4477 4214 unsigned overage = l - threshold;
4478
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4214 times.
4214 unsigned overage2 = qadd8(overage, overage);
4479
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4214 times.
✓ Branch 2 taken 4214 times.
✗ Branch 3 not taken.
8428 c += CRGB(overage, overage2, qadd8(overage2, overage2));
4480 }
4481
4482 //deepen the blues and greens
4483 7511 c.blue = scale8(c.blue, 145);
4484 7511 c.green = scale8(c.green, 200);
4485 7511 c |= CRGB( 2, 5, 7);
4486
4487
3/6
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7511 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7511 times.
✗ Branch 5 not taken.
7511 SEGMENT.setPixelColor(i, c);
4488 }
4489
4490
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 strip().now = nowOld;
4491
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
46 return FRAMETIME;
4492 }
4493 static const char _data_FX_MODE_PACIFICA[] PROGMEM = "Pacifica@!,Angle;;!;;pal=51";
4494
4495
4496 /*
4497 * Mode simulates a gradual sunrise
4498 */
4499 23 uint16_t mode_sunrise() {
4500
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
4501 //speed 0 - static sun
4502 //speed 1 - 60: sunrise time in minutes
4503 //speed 60 - 120 : sunset time in minutes - 60;
4504 //speed above: "breathing" rise and set
4505
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGENV.call == 0 || SEGMENT.speed != SEGENV.aux0) {
4506 5 SEGENV.step = millis(); //save starting time, millis() because strip().now can change from sync
4507 5 SEGENV.aux0 = SEGMENT.speed;
4508 }
4509
4510 23 SEGMENT.fill(BLACK);
4511 23 unsigned stage = 0xFFFF;
4512
4513 23 uint32_t s10SinceStart = (millis() - SEGENV.step) /100; //tenths of seconds
4514
4515
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 19 times.
23 if (SEGMENT.speed > 120) { //quick sunrise and sunset
4516 4 unsigned counter = (strip().now >> 1) * (((SEGMENT.speed -120) >> 1) +1);
4517 4 stage = triwave16(counter);
4518
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 4 times.
19 } else if (SEGMENT.speed) { //sunrise
4519 15 unsigned durMins = SEGMENT.speed;
4520
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (durMins > 60) durMins -= 60;
4521 15 uint32_t s10Target = durMins * 600;
4522
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (s10SinceStart > s10Target) s10SinceStart = s10Target;
4523 15 stage = map(s10SinceStart, 0, s10Target, 0, 0xFFFF);
4524
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (SEGMENT.speed > 60) stage = 0xFFFF - stage; //sunset
4525 }
4526
4527
2/2
✓ Branch 0 taken 3776 times.
✓ Branch 1 taken 23 times.
3799 for (unsigned i = 0; i <= SEGLEN/2; i++)
4528 {
4529 //default palette is Fire
4530 3776 unsigned wave = triwave16((i * stage) / SEGLEN);
4531 3776 wave = (wave >> 8) + ((wave * SEGMENT.intensity) >> 15);
4532 uint32_t c;
4533
2/2
✓ Branch 0 taken 698 times.
✓ Branch 1 taken 3078 times.
3776 if (wave > 240) { //clipped, full white sun
4534 698 c = SEGMENT.color_from_palette( 240, false, true, 255);
4535 } else { //transition
4536 3078 c = SEGMENT.color_from_palette(wave, false, true, 255);
4537 }
4538 3776 SEGMENT.setPixelColor(i, c);
4539 3776 SEGMENT.setPixelColor(SEGLEN - i - 1, c);
4540 }
4541
4542 23 return FRAMETIME;
4543 }
4544 static const char _data_FX_MODE_SUNRISE[] PROGMEM = "Sunrise@Time [min],Width;;!;;pal=35,sx=60";
4545
4546
4547 /*
4548 * Effects by Andrew Tuline
4549 */
4550 46 static uint16_t phased_base(uint8_t moder) { // We're making sine waves here. By Andrew Tuline.
4551
4552 46 unsigned allfreq = 16; // Base frequency.
4553 46 float *phase = reinterpret_cast<float*>(&SEGENV.step); // Phase change value gets calculated (float fits into unsigned long).
4554 46 unsigned cutOff = (255-SEGMENT.intensity); // You can change the number of pixels. AKA INTENSITY (was 192).
4555 46 unsigned modVal = 5;//SEGMENT.fft1/8+1; // You can change the modulus. AKA FFT1 (was 5).
4556
4557 46 unsigned index = strip().now/64; // Set color rotation speed
4558 46 *phase += SEGMENT.speed/32.0; // You can change the speed of the wave. AKA SPEED (was .4)
4559
4560
2/2
✓ Branch 0 taken 15022 times.
✓ Branch 1 taken 46 times.
15068 for (unsigned i = 0; i < SEGLEN; i++) {
4561
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 7511 times.
15022 if (moder == 1) modVal = (perlin8(i*10 + i*10) /16); // Let's randomize our mod length with some Perlin noise.
4562 15022 unsigned val = (i+1) * allfreq; // This sets the frequency of the waves. The +1 makes sure that led 0 is used.
4563
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 14983 times.
15022 if (modVal == 0) modVal = 1;
4564 15022 val += *phase * (i % modVal +1) /2; // This sets the varying phase change of the waves. By Andrew Tuline.
4565 15022 unsigned b = cubicwave8(val); // Now we make an 8 bit sinewave.
4566
2/2
✓ Branch 0 taken 7284 times.
✓ Branch 1 taken 7738 times.
15022 b = (b > cutOff) ? (b - cutOff) : 0; // A ternary operator to cutoff the light.
4567 15022 SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(index, false, false, 0), uint8_t(b)));
4568 15022 index += 256 / SEGLEN;
4569
2/2
✓ Branch 0 taken 13312 times.
✓ Branch 1 taken 1710 times.
15022 if (SEGLEN > 256) index ++; // Correction for segments longer than 256 LEDs
4570 }
4571
4572 46 return FRAMETIME;
4573 }
4574
4575
4576 23 uint16_t mode_phased(void) {
4577 23 return phased_base(0);
4578 }
4579 static const char _data_FX_MODE_PHASED[] PROGMEM = "Phased@!,!;!,!;!";
4580
4581
4582 23 uint16_t mode_phased_noise(void) {
4583 23 return phased_base(1);
4584 }
4585 static const char _data_FX_MODE_PHASEDNOISE[] PROGMEM = "Phased Noise@!,!;!,!;!";
4586
4587
4588 23 uint16_t mode_twinkleup(void) { // A very short twinkle routine with fade-in and dual controls. By Andrew Tuline.
4589 23 unsigned prevSeed = random16_get_seed(); // save seed so we can restore it at the end of the function
4590 23 random16_set_seed(535); // The randomizer needs to be re-set each time through the loop in order for the same 'random' numbers to be the same each time through.
4591
4592
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
4593 7511 unsigned ranstart = random8(); // The starting value (aka brightness) for each pixel. Must be consistent each time through the loop for this to work.
4594 7511 unsigned pixBri = sin8_t(ranstart + 16 * strip().now/(256-SEGMENT.speed));
4595
2/2
✓ Branch 0 taken 3739 times.
✓ Branch 1 taken 3772 times.
7511 if (random8() > SEGMENT.intensity) pixBri = 0;
4596
2/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7511 times.
7511 SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(random8()+strip().now/100, false, PALETTE_SOLID_WRAP, 0), pixBri));
4597 }
4598
4599 23 random16_set_seed(prevSeed); // restore original seed so other effects can use "random" PRNG
4600 23 return FRAMETIME;
4601 }
4602 static const char _data_FX_MODE_TWINKLEUP[] PROGMEM = "Twinkleup@!,Intensity;!,!;!;;m12=0";
4603
4604
4605 // Peaceful noise that's slow and with gradually changing palettes. Does not support WLED palettes or default colours or controls.
4606 23 uint16_t mode_noisepal(void) { // Slow noise palette by Andrew Tuline.
4607 23 unsigned scale = 15 + (SEGMENT.intensity >> 2); //default was 30
4608 //#define scale 30
4609
4610 23 unsigned dataSize = sizeof(CRGBPalette16) * 2; //allocate space for 2 Palettes (2 * 16 * 3 = 96 bytes)
4611
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
4612
4613 23 CRGBPalette16* palettes = reinterpret_cast<CRGBPalette16*>(SEGENV.data);
4614
4615 23 unsigned changePaletteMs = 4000 + SEGMENT.speed *10; //between 4 - 6.5sec
4616
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if (strip().now - SEGENV.step > changePaletteMs)
4617 {
4618 5 SEGENV.step = strip().now;
4619
4620 5 unsigned baseI = hw_random8();
4621
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 palettes[1] = CRGBPalette16(CHSV(baseI+hw_random8(64), 255, hw_random8(128,255)), CHSV(baseI+128, 255, hw_random8(128,255)), CHSV(baseI+hw_random8(92), 192, hw_random8(128,255)), CHSV(baseI+hw_random8(92), 255, hw_random8(128,255)));
4622 }
4623
4624 //EVERY_N_MILLIS(10) { //(don't have to time this, effect function is only called every 24ms)
4625 23 nblendPaletteTowardPalette(palettes[0], palettes[1], 48); // Blend towards the target palette over 48 iterations.
4626
4627
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGMENT.palette > 0) palettes[0] = SEGPALETTE;
4628
4629
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
4630 7511 unsigned index = perlin8(i*scale, SEGENV.aux0+i*scale); // Get a value from the noise function. I'm using both x and y axis.
4631 7511 SEGMENT.setPixelColor(i, ColorFromPalette(palettes[0], index, 255, LINEARBLEND)); // Use my own palette.
4632 }
4633
4634 23 SEGENV.aux0 += beatsin8_t(10,1,4); // Moving along the distance. Vary it a bit with a sine wave.
4635
4636 23 return FRAMETIME;
4637 }
4638 static const char _data_FX_MODE_NOISEPAL[] PROGMEM = "Noise Pal@!,Scale;;!";
4639
4640
4641 // Sine waves that have controllable phase change speed, frequency and cutoff. By Andrew Tuline.
4642 // SEGMENT.speed ->Speed, SEGMENT.intensity -> Frequency (SEGMENT.fft1 -> Color change, SEGMENT.fft2 -> PWM cutoff)
4643 //
4644 23 uint16_t mode_sinewave(void) { // Adjustable sinewave. By Andrew Tuline
4645 //#define qsuba(x, b) ((x>b)?x-b:0) // Analog Unsigned subtraction macro. if result <0, then => 0
4646
4647 23 unsigned colorIndex = strip().now /32;//(256 - SEGMENT.fft1); // Amount of colour change.
4648
4649 23 SEGENV.step += SEGMENT.speed/16; // Speed of animation.
4650 23 unsigned freq = SEGMENT.intensity/4;//SEGMENT.fft2/8; // Frequency of the signal.
4651
4652
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) { // For each of the LED's in the strand, set a brightness based on a wave as follows:
4653 7511 uint8_t pixBri = cubicwave8((i*freq)+SEGENV.step);//qsuba(cubicwave8((i*freq)+SEGENV.step), (255-SEGMENT.intensity)); // qsub sets a minimum value called thiscutoff. If < thiscutoff, then bright = 0. Otherwise, bright = 128 (as defined in qsub)..
4654 //setPixCol(i, i*colorIndex/255, pixBri);
4655
2/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7511 times.
7511 SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i*colorIndex/255, false, PALETTE_SOLID_WRAP, 0), pixBri));
4656 }
4657
4658 23 return FRAMETIME;
4659 }
4660 static const char _data_FX_MODE_SINEWAVE[] PROGMEM = "Sine@!,Scale;;!";
4661
4662
4663 /*
4664 * Best of both worlds from Palette and Spot effects. By Aircoookie
4665 */
4666 23 uint16_t mode_flow(void)
4667 {
4668 23 unsigned counter = 0;
4669
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 4 times.
23 if (SEGMENT.speed != 0)
4670 {
4671 19 counter = strip().now * ((SEGMENT.speed >> 2) +1);
4672 19 counter = counter >> 8;
4673 }
4674
4675 23 unsigned maxZones = SEGLEN / 6; //only looks good if each zone has at least 6 LEDs
4676 23 unsigned zones = (SEGMENT.intensity * maxZones) >> 8;
4677
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 14 times.
23 if (zones & 0x01) zones++; //zones must be even
4678
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 14 times.
23 if (zones < 2) zones = 2;
4679 23 unsigned zoneLen = SEGLEN / zones;
4680 23 unsigned offset = (SEGLEN - zones * zoneLen) >> 1;
4681
4682 23 SEGMENT.fill(SEGMENT.color_from_palette(-counter, false, true, 255));
4683
4684
2/2
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 23 times.
119 for (unsigned z = 0; z < zones; z++)
4685 {
4686 96 unsigned pos = offset + z * zoneLen;
4687
2/2
✓ Branch 0 taken 938 times.
✓ Branch 1 taken 96 times.
1034 for (unsigned i = 0; i < zoneLen; i++)
4688 {
4689 938 unsigned colorIndex = (i * 255 / zoneLen) - counter;
4690
2/2
✓ Branch 0 taken 469 times.
✓ Branch 1 taken 469 times.
938 unsigned led = (z & 0x01) ? i : (zoneLen -1) -i;
4691
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 938 times.
938 if (SEGMENT.reverse) led = (zoneLen -1) -led;
4692 938 SEGMENT.setPixelColor(pos + led, SEGMENT.color_from_palette(colorIndex, false, true, 255));
4693 }
4694 }
4695
4696 23 return FRAMETIME;
4697 }
4698 static const char _data_FX_MODE_FLOW[] PROGMEM = "Flow@!,Zones;;!;;m12=1"; //vertical
4699
4700
4701 /*
4702 * Dots waving around in a sine/pendulum motion.
4703 * Little pixel birds flying in a circle. By Aircoookie
4704 */
4705 23 uint16_t mode_chunchun(void)
4706 {
4707
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
4708 23 SEGMENT.fade_out(254); // add a bit of trail
4709 23 unsigned counter = strip().now * (6 + (SEGMENT.speed >> 4));
4710 23 unsigned numBirds = 2 + (SEGLEN >> 3); // 2 + 1/8 of a segment
4711 23 unsigned span = (SEGMENT.intensity << 8) / numBirds;
4712
4713
2/2
✓ Branch 0 taken 978 times.
✓ Branch 1 taken 23 times.
1001 for (unsigned i = 0; i < numBirds; i++)
4714 {
4715 978 counter -= span;
4716 978 unsigned megumin = sin16_t(counter) + 0x8000;
4717 978 unsigned bird = uint32_t(megumin * SEGLEN) >> 16;
4718
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 978 times.
978 bird = constrain(bird, 0U, SEGLEN-1U);
4719 978 SEGMENT.setPixelColor(bird, SEGMENT.color_from_palette((i * 255)/ numBirds, false, false, 0)); // no palette wrapping
4720 }
4721 23 return FRAMETIME;
4722 }
4723 static const char _data_FX_MODE_CHUNCHUN[] PROGMEM = "Chunchun@!,Gap size;!,!;!";
4724
4725 #define SPOT_TYPE_SOLID 0
4726 #define SPOT_TYPE_GRADIENT 1
4727 #define SPOT_TYPE_2X_GRADIENT 2
4728 #define SPOT_TYPE_2X_DOT 3
4729 #define SPOT_TYPE_3X_DOT 4
4730 #define SPOT_TYPE_4X_DOT 5
4731 #define SPOT_TYPES_COUNT 6
4732 #ifdef ESP8266
4733 #define SPOT_MAX_COUNT 17 //Number of simultaneous waves
4734 #else
4735 #define SPOT_MAX_COUNT 49 //Number of simultaneous waves
4736 #endif
4737
4738 #ifdef WLED_PS_DONT_REPLACE_FX
4739 //13 bytes
4740 typedef struct Spotlight {
4741 float speed;
4742 uint8_t colorIdx;
4743 int16_t position;
4744 unsigned long lastUpdateTime;
4745 uint8_t width;
4746 uint8_t type;
4747 } spotlight;
4748
4749 /*
4750 * Spotlights moving back and forth that cast dancing shadows.
4751 * Shine this through tree branches/leaves or other close-up objects that cast
4752 * interesting shadows onto a ceiling or tarp.
4753 *
4754 * By Steve Pomeroy @xxv
4755 */
4756 23 uint16_t mode_dancing_shadows(void)
4757 {
4758
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
4759 23 unsigned numSpotlights = map(SEGMENT.intensity, 0, 255, 2, SPOT_MAX_COUNT); // 49 on 32 segment ESP32, 17 on 16 segment ESP8266
4760 23 bool initialize = SEGENV.aux0 != numSpotlights;
4761 23 SEGENV.aux0 = numSpotlights;
4762
4763 23 unsigned dataSize = sizeof(spotlight) * numSpotlights;
4764
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
4765 23 Spotlight* spotlights = reinterpret_cast<Spotlight*>(SEGENV.data);
4766
4767 23 SEGMENT.fill(BLACK);
4768
4769 23 unsigned long time = strip().now;
4770 23 bool respawn = false;
4771
4772
2/2
✓ Branch 0 taken 579 times.
✓ Branch 1 taken 23 times.
602 for (size_t i = 0; i < numSpotlights; i++) {
4773
2/2
✓ Branch 0 taken 453 times.
✓ Branch 1 taken 126 times.
579 if (!initialize) {
4774 // advance the position of the spotlight
4775 906 int delta = (float)(time - spotlights[i].lastUpdateTime) *
4776 453 (spotlights[i].speed * ((1.0 + SEGMENT.speed)/100.0));
4777
4778
2/2
✓ Branch 0 taken 447 times.
✓ Branch 1 taken 6 times.
453 if (abs(delta) >= 1) {
4779 447 spotlights[i].position += delta;
4780 447 spotlights[i].lastUpdateTime = time;
4781 }
4782
4783
2/2
✓ Branch 0 taken 213 times.
✓ Branch 1 taken 7 times.
673 respawn = (spotlights[i].speed > 0.0 && spotlights[i].position > (int)(SEGLEN + 2))
4784
6/6
✓ Branch 0 taken 220 times.
✓ Branch 1 taken 233 times.
✓ Branch 2 taken 233 times.
✓ Branch 3 taken 213 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 230 times.
673 || (spotlights[i].speed < 0.0 && spotlights[i].position < -(spotlights[i].width + 2));
4785 }
4786
4787
4/4
✓ Branch 0 taken 453 times.
✓ Branch 1 taken 126 times.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 443 times.
579 if (initialize || respawn) {
4788 136 spotlights[i].colorIdx = hw_random8();
4789 136 spotlights[i].width = hw_random8(1, 10);
4790
4791 136 spotlights[i].speed = 1.0/hw_random8(4, 50);
4792
4793
2/2
✓ Branch 0 taken 126 times.
✓ Branch 1 taken 10 times.
136 if (initialize) {
4794 126 spotlights[i].position = hw_random16(SEGLEN);
4795
2/2
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 64 times.
126 spotlights[i].speed *= hw_random8(2) ? 1.0 : -1.0;
4796 } else {
4797
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 6 times.
10 if (hw_random8(2)) {
4798 4 spotlights[i].position = SEGLEN + spotlights[i].width;
4799 4 spotlights[i].speed *= -1.0;
4800 }else {
4801 6 spotlights[i].position = -spotlights[i].width;
4802 }
4803 }
4804
4805 136 spotlights[i].lastUpdateTime = time;
4806 136 spotlights[i].type = hw_random8(SPOT_TYPES_COUNT);
4807 }
4808
4809 579 uint32_t color = SEGMENT.color_from_palette(spotlights[i].colorIdx, false, false, 255);
4810 579 int start = spotlights[i].position;
4811
4812
2/2
✓ Branch 0 taken 65 times.
✓ Branch 1 taken 514 times.
579 if (spotlights[i].width <= 1) {
4813
5/6
✓ Branch 0 taken 65 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 61 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 61 times.
✓ Branch 5 taken 4 times.
65 if (start >= 0 && start < (int)SEGLEN) {
4814 61 SEGMENT.blendPixelColor(start, color, 128);
4815 }
4816 } else {
4817
6/7
✓ Branch 0 taken 77 times.
✓ Branch 1 taken 131 times.
✓ Branch 2 taken 87 times.
✓ Branch 3 taken 89 times.
✓ Branch 4 taken 39 times.
✓ Branch 5 taken 91 times.
✗ Branch 6 not taken.
514 switch (spotlights[i].type) {
4818 77 case SPOT_TYPE_SOLID:
4819
2/2
✓ Branch 0 taken 418 times.
✓ Branch 1 taken 77 times.
495 for (size_t j = 0; j < spotlights[i].width; j++) {
4820
2/2
✓ Branch 0 taken 398 times.
✓ Branch 1 taken 20 times.
418 if ((start + j) >= 0 && (start + j) < SEGLEN) {
4821 398 SEGMENT.blendPixelColor(start + j, color, 128);
4822 }
4823 }
4824 77 break;
4825
4826 131 case SPOT_TYPE_GRADIENT:
4827
2/2
✓ Branch 0 taken 647 times.
✓ Branch 1 taken 131 times.
778 for (size_t j = 0; j < spotlights[i].width; j++) {
4828
2/2
✓ Branch 0 taken 614 times.
✓ Branch 1 taken 33 times.
647 if ((start + j) >= 0 && (start + j) < SEGLEN) {
4829 614 SEGMENT.blendPixelColor(start + j, color, cubicwave8(map(j, 0, spotlights[i].width - 1, 0, 255)));
4830 }
4831 }
4832 131 break;
4833
4834 87 case SPOT_TYPE_2X_GRADIENT:
4835
2/2
✓ Branch 0 taken 401 times.
✓ Branch 1 taken 87 times.
488 for (size_t j = 0; j < spotlights[i].width; j++) {
4836
2/2
✓ Branch 0 taken 388 times.
✓ Branch 1 taken 13 times.
401 if ((start + j) >= 0 && (start + j) < SEGLEN) {
4837 388 SEGMENT.blendPixelColor(start + j, color, cubicwave8(2 * map(j, 0, spotlights[i].width - 1, 0, 255)));
4838 }
4839 }
4840 87 break;
4841
4842 89 case SPOT_TYPE_2X_DOT:
4843
2/2
✓ Branch 0 taken 280 times.
✓ Branch 1 taken 89 times.
369 for (size_t j = 0; j < spotlights[i].width; j += 2) {
4844
2/2
✓ Branch 0 taken 245 times.
✓ Branch 1 taken 35 times.
280 if ((start + j) >= 0 && (start + j) < SEGLEN) {
4845 245 SEGMENT.blendPixelColor(start + j, color, 128);
4846 }
4847 }
4848 89 break;
4849
4850 39 case SPOT_TYPE_3X_DOT:
4851
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 39 times.
105 for (size_t j = 0; j < spotlights[i].width; j += 3) {
4852
2/2
✓ Branch 0 taken 65 times.
✓ Branch 1 taken 1 times.
66 if ((start + j) >= 0 && (start + j) < SEGLEN) {
4853 65 SEGMENT.blendPixelColor(start + j, color, 128);
4854 }
4855 }
4856 39 break;
4857
4858 91 case SPOT_TYPE_4X_DOT:
4859
2/2
✓ Branch 0 taken 166 times.
✓ Branch 1 taken 91 times.
257 for (size_t j = 0; j < spotlights[i].width; j += 4) {
4860
2/2
✓ Branch 0 taken 153 times.
✓ Branch 1 taken 13 times.
166 if ((start + j) >= 0 && (start + j) < SEGLEN) {
4861 153 SEGMENT.blendPixelColor(start + j, color, 128);
4862 }
4863 }
4864 91 break;
4865 }
4866 }
4867 }
4868
4869 23 return FRAMETIME;
4870 }
4871 static const char _data_FX_MODE_DANCING_SHADOWS[] PROGMEM = "Dancing Shadows@!,# of shadows;!;!";
4872 #endif // WLED_PS_DONT_REPLACE_FX
4873
4874 /*
4875 Imitates a washing machine, rotating same waves forward, then pause, then backward.
4876 By Stefan Seegel
4877 */
4878 23 uint16_t mode_washing_machine(void) {
4879 23 int speed = tristate_square8(strip().now >> 7, 90, 15);
4880
4881 23 SEGENV.step += (speed * 2048) / (512 - SEGMENT.speed);
4882
4883
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
4884 7511 uint8_t col = sin8_t(((SEGMENT.intensity / 25 + 1) * 255 * i / SEGLEN) + (SEGENV.step >> 7));
4885
2/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7511 times.
7511 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(col, false, PALETTE_SOLID_WRAP, 3));
4886 }
4887
4888 23 return FRAMETIME;
4889 }
4890 static const char _data_FX_MODE_WASHING_MACHINE[] PROGMEM = "Washing Machine@!,!;;!";
4891
4892
4893 /*
4894 Image effect
4895 Draws a .gif image from filesystem on the matrix/strip
4896 */
4897 ✗ uint16_t mode_image(void) {
4898 #ifndef WLED_ENABLE_GIF
4899 ✗ return mode_static();
4900 #else
4901 renderImageToSegment(SEGMENT);
4902 return FRAMETIME;
4903 #endif
4904 // if (status != 0 && status != 254 && status != 255) {
4905 // Serial.print("GIF renderer return: ");
4906 // Serial.println(status);
4907 // }
4908 }
4909 static const char _data_FX_MODE_IMAGE[] PROGMEM = "Image@!,;;;12;sx=128";
4910
4911 /*
4912 Blends random colors across palette
4913 Modified, originally by Mark Kriegsman https://gist.github.com/kriegsman/1f7ccbbfa492a73c015e
4914 */
4915 23 uint16_t mode_blends(void) {
4916
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 13 times.
23 unsigned pixelLen = SEGLEN > UINT8_MAX ? UINT8_MAX : SEGLEN;
4917 23 unsigned dataSize = sizeof(uint32_t) * (pixelLen + 1); // max segment length of 56 pixels on 16 segment ESP8266
4918
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
4919 23 uint32_t* pixels = reinterpret_cast<uint32_t*>(SEGENV.data);
4920 23 uint8_t blendSpeed = map(SEGMENT.intensity, 0, UINT8_MAX, 10, 128);
4921 23 unsigned shift = (strip().now * ((SEGMENT.speed >> 3) +1)) >> 8;
4922
4923
2/2
✓ Branch 0 taken 4170 times.
✓ Branch 1 taken 23 times.
4193 for (unsigned i = 0; i < pixelLen; i++) {
4924
2/4
✓ Branch 0 taken 4170 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4170 times.
4170 pixels[i] = color_blend(pixels[i], SEGMENT.color_from_palette(shift + quadwave8((i + 1) * 16), false, PALETTE_SOLID_WRAP, 255), blendSpeed);
4925 4170 shift += 3;
4926 }
4927
4928 23 unsigned offset = 0;
4929
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
4930 7511 SEGMENT.setPixelColor(i, pixels[offset++]);
4931
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 7475 times.
7511 if (offset >= pixelLen) offset = 0;
4932 }
4933
4934 23 return FRAMETIME;
4935 }
4936 static const char _data_FX_MODE_BLENDS[] PROGMEM = "Blends@Shift speed,Blend speed;;!";
4937
4938
4939 /*
4940 TV Simulator
4941 Modified and adapted to WLED by Def3nder, based on "Fake TV Light for Engineers" by Phillip Burgess https://learn.adafruit.com/fake-tv-light-for-engineers/arduino-sketch
4942 */
4943 //43 bytes
4944 typedef struct TvSim {
4945 uint32_t totalTime = 0;
4946 uint32_t fadeTime = 0;
4947 uint32_t startTime = 0;
4948 uint32_t elapsed = 0;
4949 uint32_t pixelNum = 0;
4950 uint16_t sliderValues = 0;
4951 uint32_t sceeneStart = 0;
4952 uint32_t sceeneDuration = 0;
4953 uint16_t sceeneColorHue = 0;
4954 uint8_t sceeneColorSat = 0;
4955 uint8_t sceeneColorBri = 0;
4956 uint8_t actualColorR = 0;
4957 uint8_t actualColorG = 0;
4958 uint8_t actualColorB = 0;
4959 uint16_t pr = 0; // Prev R, G, B
4960 uint16_t pg = 0;
4961 uint16_t pb = 0;
4962 } tvSim;
4963
4964 23 uint16_t mode_tv_simulator(void) {
4965 int nr, ng, nb, r, g, b, i, hue;
4966 uint8_t sat, bri, j;
4967
4968
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (!SEGENV.allocateData(sizeof(tvSim))) return mode_static(); //allocation failed
4969 23 TvSim* tvSimulator = reinterpret_cast<TvSim*>(SEGENV.data);
4970
4971 23 uint8_t colorSpeed = map(SEGMENT.speed, 0, UINT8_MAX, 1, 20);
4972 23 uint8_t colorIntensity = map(SEGMENT.intensity, 0, UINT8_MAX, 10, 30);
4973
4974 23 i = SEGMENT.speed << 8 | SEGMENT.intensity;
4975
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 19 times.
23 if (i != tvSimulator->sliderValues) {
4976 4 tvSimulator->sliderValues = i;
4977 4 SEGENV.aux1 = 0;
4978 }
4979
4980 // create a new sceene
4981
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (((strip().now - tvSimulator->sceeneStart) >= tvSimulator->sceeneDuration) || SEGENV.aux1 == 0) {
4982 5 tvSimulator->sceeneStart = strip().now; // remember the start of the new sceene
4983 5 tvSimulator->sceeneDuration = hw_random16(60* 250* colorSpeed, 60* 750 * colorSpeed); // duration of a "movie sceene" which has similar colors (5 to 15 minutes with max speed slider)
4984 5 tvSimulator->sceeneColorHue = hw_random16( 0, 768); // random start color-tone for the sceene
4985 5 tvSimulator->sceeneColorSat = hw_random8 ( 100, 130 + colorIntensity); // random start color-saturation for the sceene
4986 5 tvSimulator->sceeneColorBri = hw_random8 ( 200, 240); // random start color-brightness for the sceene
4987 5 SEGENV.aux1 = 1;
4988 5 SEGENV.aux0 = 0;
4989 }
4990
4991 // slightly change the color-tone in this sceene
4992
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if (SEGENV.aux0 == 0) {
4993 // hue change in both directions
4994 5 j = hw_random8(4 * colorIntensity);
4995
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
7 hue = (hw_random8() < 128) ? ((j < tvSimulator->sceeneColorHue) ? tvSimulator->sceeneColorHue - j : 767 - tvSimulator->sceeneColorHue - j) : // negative
4996
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 ((j + tvSimulator->sceeneColorHue) < 767 ? tvSimulator->sceeneColorHue + j : tvSimulator->sceeneColorHue + j - 767) ; // positive
4997
4998 // saturation
4999 5 j = hw_random8(2 * colorIntensity);
5000 5 sat = (tvSimulator->sceeneColorSat - j) < 0 ? 0 : tvSimulator->sceeneColorSat - j;
5001
5002 // brightness
5003 5 j = hw_random8(100);
5004 5 bri = (tvSimulator->sceeneColorBri - j) < 0 ? 0 : tvSimulator->sceeneColorBri - j;
5005
5006 // calculate R,G,B from HSV
5007 // Source: https://blog.adafruit.com/2012/03/14/constant-brightness-hsb-to-rgb-algorithm/
5008 { // just to create a local scope for the variables
5009 5 uint8_t temp[5], n = (hue >> 8) % 3;
5010 5 uint8_t x = ((((hue & 255) * sat) >> 8) * bri) >> 8;
5011 5 uint8_t s = ( (256 - sat) * bri) >> 8;
5012 5 temp[0] = temp[3] = s;
5013 5 temp[1] = temp[4] = x + s;
5014 5 temp[2] = bri - x;
5015 5 tvSimulator->actualColorR = temp[n + 2];
5016 5 tvSimulator->actualColorG = temp[n + 1];
5017 5 tvSimulator->actualColorB = temp[n ];
5018 }
5019 }
5020 // Apply gamma correction, further expand to 16/16/16
5021 23 nr = (uint8_t)gamma8(tvSimulator->actualColorR) * 257; // New R/G/B
5022 23 ng = (uint8_t)gamma8(tvSimulator->actualColorG) * 257;
5023 23 nb = (uint8_t)gamma8(tvSimulator->actualColorB) * 257;
5024
5025
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if (SEGENV.aux0 == 0) { // initialize next iteration
5026 5 SEGENV.aux0 = 1;
5027
5028 // randomize total duration and fade duration for the actual color
5029 5 tvSimulator->totalTime = hw_random16(250, 2500); // Semi-random pixel-to-pixel time
5030 5 tvSimulator->fadeTime = hw_random16(0, tvSimulator->totalTime); // Pixel-to-pixel transition time
5031
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 if (hw_random8(10) < 3) tvSimulator->fadeTime = 0; // Force scene cut 30% of time
5032
5033 5 tvSimulator->startTime = strip().now;
5034 } // end of initialization
5035
5036 // how much time is elapsed ?
5037 23 tvSimulator->elapsed = strip().now - tvSimulator->startTime;
5038
5039 // fade from prev color to next color
5040
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 8 times.
23 if (tvSimulator->elapsed < tvSimulator->fadeTime) {
5041 15 r = map(tvSimulator->elapsed, 0, tvSimulator->fadeTime, tvSimulator->pr, nr);
5042 15 g = map(tvSimulator->elapsed, 0, tvSimulator->fadeTime, tvSimulator->pg, ng);
5043 15 b = map(tvSimulator->elapsed, 0, tvSimulator->fadeTime, tvSimulator->pb, nb);
5044 } else { // Avoid divide-by-zero in map()
5045 8 r = nr;
5046 8 g = ng;
5047 8 b = nb;
5048 }
5049
5050 // set strip color
5051
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (i = 0; i < (int)SEGLEN; i++) {
5052 7511 SEGMENT.setPixelColor(i, r >> 8, g >> 8, b >> 8); // Quantize to 8-bit
5053 }
5054
5055 // if total duration has passed, remember last color and restart the loop
5056
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if ( tvSimulator->elapsed >= tvSimulator->totalTime) {
5057 ✗ tvSimulator->pr = nr; // Prev RGB = new RGB
5058 ✗ tvSimulator->pg = ng;
5059 ✗ tvSimulator->pb = nb;
5060 ✗ SEGENV.aux0 = 0;
5061 }
5062
5063 23 return FRAMETIME;
5064 }
5065 static const char _data_FX_MODE_TV_SIMULATOR[] PROGMEM = "TV Simulator@!,!;;!;01";
5066
5067
5068 /*
5069 Aurora effect
5070 */
5071
5072 //CONFIG
5073 #ifdef ESP8266
5074 #define W_MAX_COUNT 9 //Number of simultaneous waves
5075 #else
5076 #define W_MAX_COUNT 20 //Number of simultaneous waves
5077 #endif
5078 #define W_MAX_SPEED 6 //Higher number, higher speed
5079 #define W_WIDTH_FACTOR 6 //Higher number, smaller waves
5080
5081 //24 bytes
5082 class AuroraWave {
5083 private:
5084 uint16_t ttl;
5085 CRGB basecolor;
5086 float basealpha;
5087 uint16_t age;
5088 uint16_t width;
5089 float center;
5090 bool goingleft;
5091 float speed_factor;
5092 bool alive = true;
5093
5094 public:
5095 60 void init(uint32_t segment_length, CRGB color) {
5096 60 ttl = hw_random16(500, 1501);
5097 60 basecolor = color;
5098 60 basealpha = hw_random8(60, 101) / (float)100;
5099 60 age = 0;
5100 60 width = hw_random16(segment_length / 20, segment_length / W_WIDTH_FACTOR); //half of width to make math easier
5101
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 if (!width) width = 1;
5102 60 center = hw_random8(101) / (float)100 * segment_length;
5103 60 goingleft = hw_random8(0, 2) == 0;
5104 60 speed_factor = (hw_random8(10, 31) / (float)100 * W_MAX_SPEED / 255);
5105 60 alive = true;
5106 60 }
5107
5108 83621 CRGB getColorForLED(int ledIndex) {
5109
4/4
✓ Branch 0 taken 53203 times.
✓ Branch 1 taken 30418 times.
✓ Branch 2 taken 36524 times.
✓ Branch 3 taken 16679 times.
83621 if(ledIndex < center - width || ledIndex > center + width) return 0; //Position out of range of this wave
5110
5111 CRGB rgb;
5112
5113 //Offset of this led from center of wave
5114 //The further away from the center, the dimmer the LED
5115 16679 float offset = ledIndex - center;
5116
2/2
✓ Branch 0 taken 8859 times.
✓ Branch 1 taken 7820 times.
16679 if (offset < 0) offset = -offset;
5117 16679 float offsetFactor = offset / width;
5118
5119 //The age of the wave determines it brightness.
5120 //At half its maximum age it will be the brightest.
5121 16679 float ageFactor = 0.1;
5122
1/2
✓ Branch 0 taken 16679 times.
✗ Branch 1 not taken.
16679 if((float)age / ttl < 0.5) {
5123 16679 ageFactor = (float)age / (ttl / 2);
5124 } else {
5125 ✗ ageFactor = (float)(ttl - age) / ((float)ttl * 0.5);
5126 }
5127
5128 //Calculate color based on above factors and basealpha value
5129 16679 float factor = (1 - offsetFactor) * ageFactor * basealpha;
5130 16679 rgb.r = basecolor.r * factor;
5131 16679 rgb.g = basecolor.g * factor;
5132 16679 rgb.b = basecolor.b * factor;
5133
5134 16679 return rgb;
5135 };
5136
5137 //Change position and age of wave
5138 //Determine if its sill "alive"
5139 273 void update(uint32_t segment_length, uint32_t speed) {
5140
2/2
✓ Branch 0 taken 157 times.
✓ Branch 1 taken 116 times.
273 if(goingleft) {
5141 157 center -= speed_factor * speed;
5142 } else {
5143 116 center += speed_factor * speed;
5144 }
5145
5146 273 age++;
5147
5148
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 273 times.
273 if(age > ttl) {
5149 ✗ alive = false;
5150 } else {
5151
2/2
✓ Branch 0 taken 157 times.
✓ Branch 1 taken 116 times.
273 if(goingleft) {
5152
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 157 times.
157 if(center + width < 0) {
5153 ✗ alive = false;
5154 }
5155 } else {
5156
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 116 times.
116 if(center - width > segment_length) {
5157 ✗ alive = false;
5158 }
5159 }
5160 }
5161 273 };
5162
5163 273 bool stillAlive() {
5164 273 return alive;
5165 };
5166 };
5167
5168 27 uint16_t mode_aurora(void) {
5169 //aux1 = Wavecount
5170 //aux2 = Intensity in last loop
5171
5172 AuroraWave* waves;
5173
5174 //TODO: I am not sure this is a correct way of handling memory allocation since if it fails on 1st run
5175 // it will display static effect but on second run it may crash ESP since data will be nullptr
5176
5177
6/6
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 21 times.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 21 times.
27 if(SEGENV.aux0 != SEGMENT.intensity || SEGENV.call == 0) {
5178 //Intensity slider changed or first call
5179 6 SEGENV.aux1 = map(SEGMENT.intensity, 0, 255, 2, W_MAX_COUNT);
5180 6 SEGENV.aux0 = SEGMENT.intensity;
5181
5182
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if(!SEGENV.allocateData(sizeof(AuroraWave) * SEGENV.aux1)) { // 26 on 32 segment ESP32, 9 on 16 segment ESP8266
5183 ✗ return mode_static(); //allocation failed
5184 }
5185
5186 6 waves = reinterpret_cast<AuroraWave*>(SEGENV.data);
5187
5188
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 6 times.
66 for (int i = 0; i < SEGENV.aux1; i++) {
5189
3/6
✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 60 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 60 times.
✗ Branch 5 not taken.
60 waves[i].init(SEGLEN, CRGB(SEGMENT.color_from_palette(hw_random8(), false, false, hw_random8(0, 3))));
5190 }
5191 } else {
5192 21 waves = reinterpret_cast<AuroraWave*>(SEGENV.data);
5193 }
5194
5195
2/2
✓ Branch 0 taken 273 times.
✓ Branch 1 taken 27 times.
300 for (int i = 0; i < SEGENV.aux1; i++) {
5196 //Update values of wave
5197 273 waves[i].update(SEGLEN, SEGMENT.speed);
5198
5199
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 273 times.
273 if(!(waves[i].stillAlive())) {
5200 //If a wave dies, reinitialize it starts over.
5201 ✗ waves[i].init(SEGLEN, CRGB(SEGMENT.color_from_palette(hw_random8(), false, false, hw_random8(0, 3))));
5202 }
5203 }
5204
5205 27 uint8_t backlight = 1; //dimmer backlight if less active colors
5206
1/2
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
27 if (SEGCOLOR(0)) backlight++;
5207
1/2
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
27 if (SEGCOLOR(1)) backlight++;
5208
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 23 times.
27 if (SEGCOLOR(2)) backlight++;
5209 //Loop through LEDs to determine color
5210
2/2
✓ Branch 0 taken 7711 times.
✓ Branch 1 taken 27 times.
7738 for (unsigned i = 0; i < SEGLEN; i++) {
5211 7711 CRGB mixedRgb = CRGB(backlight, backlight, backlight);
5212
5213 //For each LED we must check each wave if it is "active" at this position.
5214 //If there are multiple waves active on a LED we multiply their values.
5215
4/6
✓ Branch 0 taken 91332 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 91332 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 83621 times.
✓ Branch 5 taken 7711 times.
91332 for (int j = 0; j < SEGENV.aux1; j++) {
5216 83621 CRGB rgb = waves[j].getColorForLED(i);
5217
5218
2/2
✓ Branch 0 taken 1479 times.
✓ Branch 1 taken 82142 times.
167242 if(rgb != CRGB(0)) {
5219
1/2
✓ Branch 0 taken 1479 times.
✗ Branch 1 not taken.
1479 mixedRgb += rgb;
5220 }
5221 }
5222
5223
3/6
✓ Branch 0 taken 7711 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7711 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7711 times.
✗ Branch 5 not taken.
30844 SEGMENT.setPixelColor(i, mixedRgb[0], mixedRgb[1], mixedRgb[2]);
5224 }
5225
5226 27 return FRAMETIME;
5227 }
5228 static const char _data_FX_MODE_AURORA[] PROGMEM = "Aurora@!,!;1,2,3;!;;sx=24,pal=50";
5229
5230 // WLED-SR effects
5231
5232 /////////////////////////
5233 // Perlin Move //
5234 /////////////////////////
5235 // 16 bit perlinmove. Use Perlin Noise instead of sinewaves for movement. By Andrew Tuline.
5236 // Controls are speed, # of pixels, faderate.
5237 23 uint16_t mode_perlinmove(void) {
5238
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
5239 23 SEGMENT.fade_out(255-SEGMENT.custom1);
5240
2/2
✓ Branch 0 taken 203 times.
✓ Branch 1 taken 23 times.
226 for (int i = 0; i < SEGMENT.intensity/16 + 1; i++) {
5241 203 unsigned locn = perlin16(strip().now*128/(260-SEGMENT.speed)+i*15000, strip().now*128/(260-SEGMENT.speed)); // Get a new pixel location from moving noise.
5242 203 unsigned pixloc = map(locn, 50*256, 192*256, 0, SEGLEN-1); // Map that to the length of the strand, and ensure we don't go over.
5243
2/4
✓ Branch 0 taken 203 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 203 times.
203 SEGMENT.setPixelColor(pixloc, SEGMENT.color_from_palette(pixloc%255, false, PALETTE_SOLID_WRAP, 0));
5244 }
5245
5246 23 return FRAMETIME;
5247 } // mode_perlinmove()
5248 static const char _data_FX_MODE_PERLINMOVE[] PROGMEM = "Perlin Move@!,# of pixels,Fade rate;!,!;!";
5249
5250
5251 /////////////////////////
5252 // Waveins //
5253 /////////////////////////
5254 // Uses beatsin8() + phase shifting. By: Andrew Tuline
5255 23 uint16_t mode_wavesins(void) {
5256
5257
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
5258 7511 uint8_t bri = sin8_t(strip().now/4 + i * SEGMENT.intensity);
5259 7511 uint8_t index = beatsin8_t(SEGMENT.speed, SEGMENT.custom1, SEGMENT.custom1+SEGMENT.custom2, 0, i * (SEGMENT.custom3<<3)); // custom3 is reduced resolution slider
5260 //SEGMENT.setPixelColor(i, ColorFromPalette(SEGPALETTE, index, bri, LINEARBLEND));
5261
2/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7511 times.
7511 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0, bri));
5262 }
5263
5264 23 return FRAMETIME;
5265 } // mode_waveins()
5266 static const char _data_FX_MODE_WAVESINS[] PROGMEM = "Wavesins@!,Brightness variation,Starting color,Range of colors,Color variation;!;!";
5267
5268
5269 //////////////////////////////
5270 // Flow Stripe //
5271 //////////////////////////////
5272 // By: ldirko https://editor.soulmatelights.com/gallery/392-flow-led-stripe , modifed by: Andrew Tuline
5273 23 uint16_t mode_FlowStripe(void) {
5274
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
5275 23 const int hl = SEGLEN * 10 / 13;
5276 23 uint8_t hue = strip().now / (SEGMENT.speed+1);
5277 23 uint32_t t = strip().now / (SEGMENT.intensity/8+1);
5278
5279
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) {
5280 7511 int c = (abs((int)i - hl) / hl) * 127;
5281 7511 c = sin8_t(c);
5282 7511 c = sin8_t(c / 2 + t);
5283 7511 byte b = sin8_t(c + t/8);
5284
1/2
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
15022 SEGMENT.setPixelColor(i, CHSV(b + hue, 255, 255));
5285 }
5286
5287 23 return FRAMETIME;
5288 } // mode_FlowStripe()
5289 static const char _data_FX_MODE_FLOWSTRIPE[] PROGMEM = "Flow Stripe@Hue speed,Effect speed;;";
5290
5291
5292 #ifndef WLED_DISABLE_2D
5293 ///////////////////////////////////////////////////////////////////////////////
5294 //*************************** 2D routines ***********************************
5295
5296
5297 // Black hole
5298 34 uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulmatelights.com/gallery/1012 , Modified by: Andrew Tuline
5299
5/6
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 29 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 29 times.
34 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
5300
5301 29 const int cols = SEG_W;
5302 29 const int rows = SEG_H;
5303 int x, y;
5304
5305 29 SEGMENT.fadeToBlackBy(16 + (SEGMENT.speed>>3)); // create fading trails
5306 29 unsigned long t = strip().now/128; // timebase
5307 // outer stars
5308
2/2
✓ Branch 0 taken 232 times.
✓ Branch 1 taken 29 times.
261 for (size_t i = 0; i < 8; i++) {
5309 232 x = beatsin8_t(SEGMENT.custom1>>3, 0, cols - 1, 0, ((i % 2) ? 128 : 0) + t * i);
5310
2/2
✓ Branch 0 taken 116 times.
✓ Branch 1 taken 116 times.
232 y = beatsin8_t(SEGMENT.intensity>>3, 0, rows - 1, 0, ((i % 2) ? 192 : 64) + t * i);
5311
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 232 times.
✓ Branch 2 taken 232 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 232 times.
232 SEGMENT.addPixelColorXY(x, y, SEGMENT.color_from_palette(i*32, false, PALETTE_SOLID_WRAP, SEGMENT.check1?0:255));
5312 }
5313 // inner stars
5314
2/2
✓ Branch 0 taken 116 times.
✓ Branch 1 taken 29 times.
145 for (size_t i = 0; i < 4; i++) {
5315 116 x = beatsin8_t(SEGMENT.custom2>>3, cols/4, cols - 1 - cols/4, 0, ((i % 2) ? 128 : 0) + t * i);
5316
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 58 times.
116 y = beatsin8_t(SEGMENT.custom3 , rows/4, rows - 1 - rows/4, 0, ((i % 2) ? 192 : 64) + t * i);
5317
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 116 times.
✓ Branch 2 taken 116 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 116 times.
116 SEGMENT.addPixelColorXY(x, y, SEGMENT.color_from_palette(255-i*64, false, PALETTE_SOLID_WRAP, SEGMENT.check1?0:255));
5318 }
5319 // central white dot
5320 29 SEGMENT.setPixelColorXY(cols/2, rows/2, WHITE);
5321 // blur everything a bit
5322
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
29 if (SEGMENT.check3) SEGMENT.blur(16, cols*rows < 100);
5323
5324 29 return FRAMETIME;
5325 } // mode_2DBlackHole()
5326 static const char _data_FX_MODE_2DBLACKHOLE[] PROGMEM = "Black Hole@Fade rate,Outer Y freq.,Outer X freq.,Inner X freq.,Inner Y freq.,Solid,,Blur;!;!;2;pal=11";
5327
5328
5329 ////////////////////////////
5330 // 2D Colored Bursts //
5331 ////////////////////////////
5332 23 uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.soulmatelights.com/gallery/819-colored-bursts , modified by: Andrew Tuline
5333
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
5334
5335 18 const int cols = SEG_W;
5336 18 const int rows = SEG_H;
5337
5338
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 14 times.
18 if (SEGENV.call == 0) {
5339 4 SEGENV.aux0 = 0; // start with red hue
5340 }
5341
5342 18 bool dot = SEGMENT.check3;
5343 18 bool grad = SEGMENT.check1;
5344
5345 18 byte numLines = SEGMENT.intensity/16 + 1;
5346
5347 18 SEGENV.aux0++; // hue
5348 18 SEGMENT.fadeToBlackBy(40 - SEGMENT.check2 * 8);
5349
2/2
✓ Branch 0 taken 158 times.
✓ Branch 1 taken 18 times.
176 for (size_t i = 0; i < numLines; i++) {
5350 158 byte x1 = beatsin8_t(2 + SEGMENT.speed/16, 0, (cols - 1));
5351 158 byte x2 = beatsin8_t(1 + SEGMENT.speed/16, 0, (rows - 1));
5352 158 byte y1 = beatsin8_t(5 + SEGMENT.speed/16, 0, (cols - 1), 0, i * 24);
5353 158 byte y2 = beatsin8_t(3 + SEGMENT.speed/16, 0, (rows - 1), 0, i * 48 + 64);
5354 158 uint32_t color = ColorFromPalette(SEGPALETTE, i * 255 / numLines + (SEGENV.aux0&0xFF), 255, LINEARBLEND);
5355
5356
2/2
✓ Branch 0 taken 107 times.
✓ Branch 1 taken 51 times.
158 byte xsteps = abs8(x1 - y1) + 1;
5357
2/2
✓ Branch 0 taken 74 times.
✓ Branch 1 taken 84 times.
158 byte ysteps = abs8(x2 - y2) + 1;
5358
2/2
✓ Branch 0 taken 115 times.
✓ Branch 1 taken 43 times.
158 byte steps = xsteps >= ysteps ? xsteps : ysteps;
5359 //Draw gradient line
5360
2/2
✓ Branch 0 taken 1828 times.
✓ Branch 1 taken 158 times.
1986 for (size_t j = 1; j <= steps; j++) {
5361 1828 uint8_t rate = j * 255 / steps;
5362 1828 byte dx = lerp8by8(x1, y1, rate);
5363 1828 byte dy = lerp8by8(x2, y2, rate);
5364 //SEGMENT.setPixelColorXY(dx, dy, grad ? color.nscale8_video(255-rate) : color); // use addPixelColorXY for different look
5365 1828 SEGMENT.addPixelColorXY(dx, dy, color); // use setPixelColorXY for different look
5366
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1828 times.
1828 if (grad) SEGMENT.fadePixelColorXY(dx, dy, rate);
5367 }
5368
5369
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 158 times.
158 if (dot) { //add white point at the ends of line
5370 ✗ SEGMENT.setPixelColorXY(x1, x2, WHITE);
5371 ✗ SEGMENT.setPixelColorXY(y1, y2, DARKSLATEGRAY);
5372 }
5373 }
5374 18 SEGMENT.blur(SEGMENT.custom3>>1, SEGMENT.check2);
5375
5376 18 return FRAMETIME;
5377 } // mode_2DColoredBursts()
5378 static const char _data_FX_MODE_2DCOLOREDBURSTS[] PROGMEM = "Colored Bursts@Speed,# of lines,,,Blur,Gradient,Smear,Dots;;!;2;c3=16";
5379
5380
5381 /////////////////////
5382 // 2D DNA //
5383 /////////////////////
5384 23 uint16_t mode_2Ddna(void) { // dna originally by by ldirko at https://pastebin.com/pCkkkzcs. Updated by Preyy. WLED conversion by Andrew Tuline.
5385
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
5386
5387 18 const int cols = SEG_W;
5388 18 const int rows = SEG_H;
5389
5390 18 SEGMENT.fadeToBlackBy(64);
5391
2/2
✓ Branch 0 taken 451 times.
✓ Branch 1 taken 18 times.
469 for (int i = 0; i < cols; i++) {
5392 451 SEGMENT.setPixelColorXY(i, beatsin8_t(SEGMENT.speed/8, 0, rows-1, 0, i*4 ), ColorFromPalette(SEGPALETTE, i*5+strip().now/17, beatsin8_t(5, 55, 255, 0, i*10), LINEARBLEND));
5393 451 SEGMENT.setPixelColorXY(i, beatsin8_t(SEGMENT.speed/8, 0, rows-1, 0, i*4+128), ColorFromPalette(SEGPALETTE, i*5+128+strip().now/17, beatsin8_t(5, 55, 255, 0, i*10+128), LINEARBLEND));
5394 }
5395 18 SEGMENT.blur(SEGMENT.intensity / (8 - (SEGMENT.check1 * 2)), SEGMENT.check1);
5396
5397 18 return FRAMETIME;
5398 } // mode_2Ddna()
5399 static const char _data_FX_MODE_2DDNA[] PROGMEM = "DNA@Scroll speed,Blur,,,,Smear;;!;2;ix=0";
5400
5401 /////////////////////////
5402 // 2D DNA Spiral //
5403 /////////////////////////
5404 23 uint16_t mode_2DDNASpiral() { // By: ldirko https://editor.soulmatelights.com/gallery/512-dna-spiral-variation , modified by: Andrew Tuline
5405
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
5406
5407 18 const int cols = SEG_W;
5408 18 const int rows = SEG_H;
5409
5410
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 14 times.
18 if (SEGENV.call == 0) {
5411 4 SEGMENT.fill(BLACK);
5412 }
5413
5414 18 unsigned speeds = SEGMENT.speed/2 + 7;
5415 18 unsigned freq = SEGMENT.intensity/8;
5416
5417 18 uint32_t ms = strip().now / 20;
5418 18 SEGMENT.fadeToBlackBy(135);
5419
5420
2/2
✓ Branch 0 taken 223 times.
✓ Branch 1 taken 18 times.
241 for (int i = 0; i < rows; i++) {
5421 223 int x = beatsin8_t(speeds, 0, cols - 1, 0, i * freq) + beatsin8_t(speeds - 7, 0, cols - 1, 0, i * freq + 128);
5422 223 int x1 = beatsin8_t(speeds, 0, cols - 1, 0, 128 + i * freq) + beatsin8_t(speeds - 7, 0, cols - 1, 0, 128 + 64 + i * freq);
5423 223 unsigned hue = (i * 128 / rows) + ms;
5424 // skip every 4th row every now and then (fade it more)
5425
2/2
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 55 times.
223 if ((i + ms / 8) & 3) {
5426 // draw a gradient line between x and x1
5427 168 x = x / 2; x1 = x1 / 2;
5428
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 104 times.
168 unsigned steps = abs8(x - x1) + 1;
5429 168 bool positive = (x1 >= x); // direction of drawing
5430
2/2
✓ Branch 0 taken 2832 times.
✓ Branch 1 taken 168 times.
3000 for (size_t k = 1; k <= steps; k++) {
5431 2832 unsigned rate = k * 255 / steps;
5432 //unsigned dx = lerp8by8(x, x1, rate);
5433
2/2
✓ Branch 0 taken 866 times.
✓ Branch 1 taken 1966 times.
2832 unsigned dx = positive? (x + k-1) : (x - k+1); // behaves the same as "lerp8by8" but does not create holes
5434 //SEGMENT.setPixelColorXY(dx, i, ColorFromPalette(SEGPALETTE, hue, 255, LINEARBLEND).nscale8_video(rate));
5435 2832 SEGMENT.addPixelColorXY(dx, i, ColorFromPalette(SEGPALETTE, hue, 255, LINEARBLEND)); // use setPixelColorXY for different look
5436 2832 SEGMENT.fadePixelColorXY(dx, i, rate);
5437 }
5438 168 SEGMENT.setPixelColorXY(x, i, DARKSLATEGRAY);
5439 168 SEGMENT.setPixelColorXY(x1, i, WHITE);
5440 }
5441 }
5442 18 SEGMENT.blur(((uint16_t)SEGMENT.custom1 * 3) / (6 + SEGMENT.check1), SEGMENT.check1);
5443
5444 18 return FRAMETIME;
5445 } // mode_2DDNASpiral()
5446 static const char _data_FX_MODE_2DDNASPIRAL[] PROGMEM = "DNA Spiral@Scroll speed,Y frequency,Blur,,,Smear;;!;2;c1=0";
5447
5448
5449 /////////////////////////
5450 // 2D Drift //
5451 /////////////////////////
5452 23 uint16_t mode_2DDrift() { // By: Stepko https://editor.soulmatelights.com/gallery/884-drift , Modified by: Andrew Tuline
5453
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
5454
5455 18 const int cols = SEG_W;
5456 18 const int rows = SEG_H;
5457
5458 18 const int colsCenter = (cols>>1) + (cols%2);
5459 18 const int rowsCenter = (rows>>1) + (rows%2);
5460
5461 18 SEGMENT.fadeToBlackBy(128);
5462
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 const float maxDim = MAX(cols, rows)/2;
5463 18 unsigned long t = strip().now / (32 - (SEGMENT.speed>>3));
5464 18 unsigned long t_20 = t/20; // softhack007: pre-calculating this gives about 10% speedup
5465
2/2
✓ Branch 0 taken 820 times.
✓ Branch 1 taken 18 times.
838 for (float i = 1.0f; i < maxDim; i += 0.25f) {
5466 820 float angle = radians(t * (maxDim - i));
5467 820 int mySin = sin_t(angle) * i;
5468 820 int myCos = cos_t(angle) * i;
5469 820 SEGMENT.setPixelColorXY(colsCenter + mySin, rowsCenter + myCos, ColorFromPalette(SEGPALETTE, (i * 20) + t_20, 255, LINEARBLEND));
5470
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 820 times.
820 if (SEGMENT.check1) SEGMENT.setPixelColorXY(colsCenter + myCos, rowsCenter + mySin, ColorFromPalette(SEGPALETTE, (i * 20) + t_20, 255, LINEARBLEND));
5471 }
5472 18 SEGMENT.blur(SEGMENT.intensity>>(3 - SEGMENT.check2), SEGMENT.check2);
5473
5474 18 return FRAMETIME;
5475 } // mode_2DDrift()
5476 static const char _data_FX_MODE_2DDRIFT[] PROGMEM = "Drift@Rotation speed,Blur,,,,Twin,Smear;;!;2;ix=0";
5477
5478
5479 //////////////////////////
5480 // 2D Firenoise //
5481 //////////////////////////
5482 23 uint16_t mode_2Dfirenoise(void) { // firenoise2d. By Andrew Tuline. Yet another short routine.
5483
9/14
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 18 times.
✓ Branch 10 taken 5 times.
✓ Branch 11 taken 18 times.
✓ Branch 12 taken 5 times.
✗ Branch 13 not taken.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
5484
5485 18 const int cols = SEG_W;
5486 18 const int rows = SEG_H;
5487
5488
4/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 14 times.
18 if (SEGENV.call == 0) {
5489
3/6
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 SEGMENT.fill(BLACK);
5490 }
5491
5492
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 unsigned xscale = SEGMENT.intensity*4;
5493
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 unsigned yscale = SEGMENT.speed*8;
5494 18 unsigned indexx = 0;
5495
5496
6/12
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 18 times.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 18 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 18 times.
✗ Branch 11 not taken.
18 CRGBPalette16 pal = SEGMENT.check1 ? SEGPALETTE : SEGMENT.loadPalette(pal, 35);
5497
2/2
✓ Branch 0 taken 451 times.
✓ Branch 1 taken 18 times.
469 for (int j=0; j < cols; j++) {
5498
2/2
✓ Branch 0 taken 6761 times.
✓ Branch 1 taken 451 times.
7212 for (int i=0; i < rows; i++) {
5499
2/4
✓ Branch 0 taken 6761 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6761 times.
✗ Branch 3 not taken.
6761 indexx = perlin8(j*yscale*rows/255, i*xscale+strip().now/4); // We're moving along our Perlin map.
5500
4/8
✓ Branch 0 taken 6761 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6761 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6761 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6761 times.
✗ Branch 7 not taken.
6761 SEGMENT.setPixelColorXY(j, i, ColorFromPalette(pal, min(i*indexx/11, 225U), i*255/rows, LINEARBLEND)); // With that value, look up the 8 bit colour palette value and assign it to the current LED.
5501 } // for i
5502 } // for j
5503
5504
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 return FRAMETIME;
5505 } // mode_2Dfirenoise()
5506 static const char _data_FX_MODE_2DFIRENOISE[] PROGMEM = "Firenoise@X scale,Y scale,,,,Palette;;!;2;pal=66";
5507
5508
5509 //////////////////////////////
5510 // 2D Frizzles //
5511 //////////////////////////////
5512 23 uint16_t mode_2DFrizzles(void) { // By: Stepko https://editor.soulmatelights.com/gallery/640-color-frizzles , Modified by: Andrew Tuline
5513
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
5514
5515 18 const int cols = SEG_W;
5516 18 const int rows = SEG_H;
5517
5518 18 SEGMENT.fadeToBlackBy(16 + SEGMENT.check1 * 10);
5519
2/2
✓ Branch 0 taken 144 times.
✓ Branch 1 taken 18 times.
162 for (size_t i = 8; i > 0; i--) {
5520 432 SEGMENT.addPixelColorXY(beatsin8_t(SEGMENT.speed/8 + i, 0, cols - 1),
5521 144 beatsin8_t(SEGMENT.intensity/8 - i, 0, rows - 1),
5522 144 ColorFromPalette(SEGPALETTE, beatsin8_t(12, 0, 255), 255, LINEARBLEND));
5523 }
5524 18 SEGMENT.blur(SEGMENT.custom1 >> (3 + SEGMENT.check1), SEGMENT.check1);
5525 18 return FRAMETIME;
5526 } // mode_2DFrizzles()
5527 static const char _data_FX_MODE_2DFRIZZLES[] PROGMEM = "Frizzles@X frequency,Y frequency,Blur,,,Smear;;!;2";
5528
5529
5530 ///////////////////////////////////////////
5531 // 2D Cellular Automata Game of life //
5532 ///////////////////////////////////////////
5533 typedef struct ColorCount {
5534 CRGB color;
5535 int8_t count;
5536 } colorCount;
5537
5538 23 uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https://natureofcode.com/book/chapter-7-cellular-automata/ and https://github.com/DougHaber/nlife-color
5539
9/14
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 18 times.
✓ Branch 10 taken 5 times.
✓ Branch 11 taken 18 times.
✓ Branch 12 taken 5 times.
✗ Branch 13 not taken.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
5540
5541 18 const int cols = SEG_W;
5542 18 const int rows = SEG_H;
5543 22247 const auto XY = [&](int x, int y) { return (x%cols) + (y%rows) * cols; };
5544
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 const unsigned dataSize = sizeof(CRGB) * SEGMENT.length(); // using width*height prevents reallocation if mirroring is enabled
5545 18 const int crcBufferLen = 2; //(SEGMENT.width() + SEGMENT.height())*71/100; // roughly sqrt(2)/2 for better repetition detection (Ewowi)
5546
5547
4/10
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 18 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
18 if (!SEGENV.allocateData(dataSize + sizeof(uint16_t)*crcBufferLen)) return mode_static(); //allocation failed
5548
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 CRGB *prevLeds = reinterpret_cast<CRGB*>(SEGENV.data);
5549
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 uint16_t *crcBuffer = reinterpret_cast<uint16_t*>(SEGENV.data + dataSize);
5550
5551 18 CRGB backgroundColor = SEGCOLOR(1);
5552
5553
10/16
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 14 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 14 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 14 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 14 times.
✓ Branch 14 taken 4 times.
✓ Branch 15 taken 14 times.
18 if (SEGENV.call == 0 || strip().now - SEGMENT.step > 3000) {
5554
3/6
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 SEGENV.step = strip().now;
5555
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 SEGENV.aux0 = 0;
5556
5557 //give the leds random state and colors (based on intensity, colors from palette or all posible colors are chosen)
5558
4/4
✓ Branch 0 taken 1557 times.
✓ Branch 1 taken 103 times.
✓ Branch 2 taken 103 times.
✓ Branch 3 taken 4 times.
1664 for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) {
5559 1557 unsigned state = hw_random8()%2;
5560
2/2
✓ Branch 0 taken 719 times.
✓ Branch 1 taken 838 times.
1557 if (state == 0)
5561
3/6
✓ Branch 0 taken 719 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 719 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 719 times.
✗ Branch 5 not taken.
719 SEGMENT.setPixelColorXY(x,y, backgroundColor);
5562 else
5563
10/20
✓ Branch 0 taken 838 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 838 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 838 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 838 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 838 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 838 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 838 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 838 times.
✓ Branch 16 taken 838 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 838 times.
✗ Branch 19 not taken.
838 SEGMENT.setPixelColorXY(x,y, SEGMENT.color_from_palette(hw_random8(), false, PALETTE_SOLID_WRAP, 255));
5564 }
5565
5566
4/4
✓ Branch 0 taken 1557 times.
✓ Branch 1 taken 51 times.
✓ Branch 2 taken 51 times.
✓ Branch 3 taken 4 times.
1612 for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) prevLeds[XY(x,y)] = CRGB::Black;
5567 4 memset(crcBuffer, 0, sizeof(uint16_t)*crcBufferLen);
5568
7/12
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 14 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 14 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 13 times.
✓ Branch 11 taken 1 times.
14 } else if (strip().now - SEGENV.step < FRAMETIME_FIXED * (uint32_t)map(SEGMENT.speed,0,255,64,4)) {
5569 // update only when appropriate time passes (in 42 FPS slots)
5570
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 return FRAMETIME;
5571 }
5572
5573 //copy previous leds (save previous generation)
5574 //NOTE: using lossy getPixelColor() is a benefit as endlessly repeating patterns will eventually fade out causing a reset
5575
7/10
✓ Branch 0 taken 2069 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2069 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2069 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2069 times.
✓ Branch 7 taken 135 times.
✓ Branch 8 taken 135 times.
✓ Branch 9 taken 5 times.
2209 for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) prevLeds[XY(x,y)] = SEGMENT.getPixelColorXY(x,y);
5576
5577 //calculate new leds
5578
4/4
✓ Branch 0 taken 2069 times.
✓ Branch 1 taken 135 times.
✓ Branch 2 taken 135 times.
✓ Branch 3 taken 5 times.
2209 for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) {
5579
5580
2/2
✓ Branch 0 taken 18621 times.
✓ Branch 1 taken 2069 times.
20690 colorCount colorsCount[9]; // count the different colors in the 3*3 matrix
5581
2/2
✓ Branch 0 taken 18621 times.
✓ Branch 1 taken 2069 times.
20690 for (int i=0; i<9; i++) colorsCount[i] = {backgroundColor, 0}; // init colorsCount
5582
5583 // iterate through neighbors and count them and their different colors
5584 2069 int neighbors = 0;
5585
4/4
✓ Branch 0 taken 18621 times.
✓ Branch 1 taken 6207 times.
✓ Branch 2 taken 6207 times.
✓ Branch 3 taken 2069 times.
26897 for (int i = -1; i <= 1; i++) for (int j = -1; j <= 1; j++) { // iterate through 3*3 matrix
5586
4/4
✓ Branch 0 taken 6207 times.
✓ Branch 1 taken 12414 times.
✓ Branch 2 taken 2069 times.
✓ Branch 3 taken 4138 times.
18621 if (i==0 && j==0) continue; // ignore itself
5587 // wrap around segment
5588 16552 int xx = x+i, yy = y+j;
5589
4/4
✓ Branch 0 taken 201 times.
✓ Branch 1 taken 16351 times.
✓ Branch 2 taken 201 times.
✓ Branch 3 taken 16150 times.
16552 if (x+i < 0) xx = cols-1; else if (x+i >= cols) xx = 0;
5590
4/4
✓ Branch 0 taken 405 times.
✓ Branch 1 taken 16147 times.
✓ Branch 2 taken 405 times.
✓ Branch 3 taken 15742 times.
16552 if (y+j < 0) yy = rows-1; else if (y+j >= rows) yy = 0;
5591
5592 16552 unsigned xy = XY(xx, yy); // previous cell xy to check
5593 // count different neighbours and colors
5594
4/4
✓ Branch 0 taken 11048 times.
✓ Branch 1 taken 5504 times.
✓ Branch 2 taken 12712 times.
✓ Branch 3 taken 3840 times.
33104 if (prevLeds[xy] != backgroundColor) {
5595 12712 neighbors++;
5596 12712 bool colorFound = false;
5597 int k;
5598
3/4
✓ Branch 0 taken 31368 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18656 times.
✓ Branch 3 taken 12712 times.
31368 for (k=0; k<9 && colorsCount[k].count != 0; k++)
5599
4/4
✓ Branch 0 taken 8982 times.
✓ Branch 1 taken 9674 times.
✓ Branch 2 taken 6328 times.
✓ Branch 3 taken 12328 times.
37312 if (colorsCount[k].color == prevLeds[xy]) {
5600 6328 colorsCount[k].count++;
5601 6328 colorFound = true;
5602 }
5603
2/2
✓ Branch 0 taken 6384 times.
✓ Branch 1 taken 6328 times.
12712 if (!colorFound) colorsCount[k] = {prevLeds[xy], 1}; //add new color found in the array
5604 }
5605 } // i,j
5606
5607 // Rules of Life
5608 2069 uint32_t col = uint32_t(prevLeds[XY(x,y)]) & 0x00FFFFFF; // uint32_t operator returns RGBA, we want RGBW -> cut off "alpha" byte
5609 2069 uint32_t bgc = RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0);
5610
7/10
✓ Branch 0 taken 1589 times.
✓ Branch 1 taken 480 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 1578 times.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 11 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 11 times.
✗ Branch 9 not taken.
2069 if ((col != bgc) && (neighbors < 2)) SEGMENT.setPixelColorXY(x,y, bgc); // Loneliness
5611
7/10
✓ Branch 0 taken 1578 times.
✓ Branch 1 taken 480 times.
✓ Branch 2 taken 1410 times.
✓ Branch 3 taken 168 times.
✓ Branch 4 taken 1410 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1410 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1410 times.
✗ Branch 9 not taken.
2058 else if ((col != bgc) && (neighbors > 3)) SEGMENT.setPixelColorXY(x,y, bgc); // Overpopulation
5612
4/4
✓ Branch 0 taken 480 times.
✓ Branch 1 taken 168 times.
✓ Branch 2 taken 65 times.
✓ Branch 3 taken 415 times.
648 else if ((col == bgc) && (neighbors == 3)) { // Reproduction
5613 // find dominant color and assign it to a cell
5614 65 colorCount dominantColorCount = {backgroundColor, 0};
5615
3/4
✓ Branch 0 taken 259 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 194 times.
✓ Branch 3 taken 65 times.
259 for (int i=0; i<9 && colorsCount[i].count != 0; i++)
5616
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 128 times.
194 if (colorsCount[i].count > dominantColorCount.count) dominantColorCount = colorsCount[i];
5617 // assign the dominant color w/ a bit of randomness to avoid "gliders"
5618
6/12
✓ Branch 0 taken 65 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 65 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 65 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 65 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 65 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 65 times.
✗ Branch 11 not taken.
65 if (dominantColorCount.count > 0 && hw_random8(128)) SEGMENT.setPixelColorXY(x,y, dominantColorCount.color);
5619
8/8
✓ Branch 0 taken 415 times.
✓ Branch 1 taken 168 times.
✓ Branch 2 taken 30 times.
✓ Branch 3 taken 385 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 29 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 582 times.
648 } else if ((col == bgc) && (neighbors == 2) && !hw_random8(128)) { // Mutation
5620
10/20
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 1 times.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 1 times.
✗ Branch 19 not taken.
1 SEGMENT.setPixelColorXY(x,y, SEGMENT.color_from_palette(hw_random8(), false, PALETTE_SOLID_WRAP, 255));
5621 }
5622 // else do nothing!
5623 } //x,y
5624
5625 // calculate CRC16 of leds
5626
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 uint16_t crc = crc16((const unsigned char*)prevLeds, dataSize);
5627 // check if we had same CRC and reset if needed
5628 5 bool repetition = false;
5629
3/4
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
15 for (int i=0; i<crcBufferLen && !repetition; i++) repetition = (crc == crcBuffer[i]); // (Ewowi)
5630 // same CRC would mean image did not change or was repeating itself
5631
4/8
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 5 times.
✗ Branch 7 not taken.
5 if (!repetition) SEGENV.step = strip().now; //if no repetition avoid reset
5632 // remember CRCs across frames
5633
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 crcBuffer[SEGENV.aux0] = crc;
5634
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 ++SEGENV.aux0 %= crcBufferLen;
5635
5636
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 return FRAMETIME;
5637 } // mode_2Dgameoflife()
5638 static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!;!,!;!;2";
5639
5640
5641 /////////////////////////
5642 // 2D Hiphotic //
5643 /////////////////////////
5644 23 uint16_t mode_2DHiphotic() { // By: ldirko https://editor.soulmatelights.com/gallery/810 , Modified by: Andrew Tuline
5645
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
5646
5647 18 const int cols = SEG_W;
5648 18 const int rows = SEG_H;
5649 18 const uint32_t a = strip().now / ((SEGMENT.custom3>>1)+1);
5650
5651
2/2
✓ Branch 0 taken 451 times.
✓ Branch 1 taken 18 times.
469 for (int x = 0; x < cols; x++) {
5652
2/2
✓ Branch 0 taken 6761 times.
✓ Branch 1 taken 451 times.
7212 for (int y = 0; y < rows; y++) {
5653
2/4
✓ Branch 0 taken 6761 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6761 times.
6761 SEGMENT.setPixelColorXY(x, y, SEGMENT.color_from_palette(sin8_t(cos8_t(x * SEGMENT.speed/16 + a / 3) + sin8_t(y * SEGMENT.intensity/16 + a / 4) + a), false, PALETTE_SOLID_WRAP, 0));
5654 }
5655 }
5656
5657 18 return FRAMETIME;
5658 } // mode_2DHiphotic()
5659 static const char _data_FX_MODE_2DHIPHOTIC[] PROGMEM = "Hiphotic@X scale,Y scale,,,Speed;!;!;2";
5660
5661
5662 /////////////////////////
5663 // 2D Julia //
5664 /////////////////////////
5665 // Sliders are:
5666 // intensity = Maximum number of iterations per pixel.
5667 // Custom1 = Location of X centerpoint
5668 // Custom2 = Location of Y centerpoint
5669 // Custom3 = Size of the area (small value = smaller area)
5670 typedef struct Julia {
5671 float xcen;
5672 float ycen;
5673 float xymag;
5674 } julia;
5675
5676 23 uint16_t mode_2DJulia(void) { // An animated Julia set by Andrew Tuline.
5677
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
5678
5679 18 const int cols = SEG_W;
5680 18 const int rows = SEG_H;
5681
5682
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if (!SEGENV.allocateData(sizeof(julia))) return mode_static();
5683 18 Julia* julias = reinterpret_cast<Julia*>(SEGENV.data);
5684
5685 float reAl;
5686 float imAg;
5687
5688
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 14 times.
18 if (SEGENV.call == 0) { // Reset the center if we've just re-started this animation.
5689 4 julias->xcen = 0.;
5690 4 julias->ycen = 0.;
5691 4 julias->xymag = 1.0;
5692
5693 4 SEGMENT.custom1 = 128; // Make sure the location widgets are centered to start.
5694 4 SEGMENT.custom2 = 128;
5695 4 SEGMENT.custom3 = 16;
5696 4 SEGMENT.intensity = 24;
5697 }
5698
5699 18 julias->xcen = julias->xcen + (float)(SEGMENT.custom1 - 128)/100000.f;
5700 18 julias->ycen = julias->ycen + (float)(SEGMENT.custom2 - 128)/100000.f;
5701 18 julias->xymag = julias->xymag + (float)((SEGMENT.custom3 - 16)<<3)/100000.f; // reduced resolution slider
5702
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if (julias->xymag < 0.01f) julias->xymag = 0.01f;
5703
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if (julias->xymag > 1.0f) julias->xymag = 1.0f;
5704
5705 18 float xmin = julias->xcen - julias->xymag;
5706 18 float xmax = julias->xcen + julias->xymag;
5707 18 float ymin = julias->ycen - julias->xymag;
5708 18 float ymax = julias->ycen + julias->xymag;
5709
5710 // Whole set should be within -1.2,1.2 to -.8 to 1.
5711
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
18 xmin = constrain(xmin, -1.2f, 1.2f);
5712
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
18 xmax = constrain(xmax, -1.2f, 1.2f);
5713
1/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
18 ymin = constrain(ymin, -0.8f, 1.0f);
5714
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
18 ymax = constrain(ymax, -0.8f, 1.0f);
5715
5716 float dx; // Delta x is mapped to the matrix size.
5717 float dy; // Delta y is mapped to the matrix size.
5718
5719 18 int maxIterations = 15; // How many iterations per pixel before we give up. Make it 8 bits to match our range of colours.
5720 18 float maxCalc = 16.0; // How big is each calculation allowed to be before we give up.
5721
5722 18 maxIterations = SEGMENT.intensity/2;
5723
5724
5725 // Resize section on the fly for some animaton.
5726 18 reAl = -0.94299f; // PixelBlaze example
5727 18 imAg = 0.3162f;
5728
5729 18 reAl += (float)sin16_t(strip().now * 34) / 655340.f;
5730 18 imAg += (float)sin16_t(strip().now * 26) / 655340.f;
5731
5732 18 dx = (xmax - xmin) / (cols); // Scale the delta x and y values to our matrix size.
5733 18 dy = (ymax - ymin) / (rows);
5734
5735 // Start y
5736 18 float y = ymin;
5737
2/2
✓ Branch 0 taken 223 times.
✓ Branch 1 taken 18 times.
241 for (int j = 0; j < rows; j++) {
5738
5739 // Start x
5740 223 float x = xmin;
5741
2/2
✓ Branch 0 taken 6761 times.
✓ Branch 1 taken 223 times.
6984 for (int i = 0; i < cols; i++) {
5742
5743 // Now we test, as we iterate z = z^2 + c does z tend towards infinity?
5744 6761 float a = x;
5745 6761 float b = y;
5746 6761 int iter = 0;
5747
5748
2/2
✓ Branch 0 taken 52261 times.
✓ Branch 1 taken 1781 times.
54042 while (iter < maxIterations) { // Here we determine whether or not we're out of bounds.
5749 52261 float aa = a * a;
5750 52261 float bb = b * b;
5751 52261 float len = aa + bb;
5752
2/2
✓ Branch 0 taken 4980 times.
✓ Branch 1 taken 47281 times.
52261 if (len > maxCalc) { // |z| = sqrt(a^2+b^2) OR z^2 = a^2+b^2 to save on having to perform a square root.
5753 4980 break; // Bail
5754 }
5755
5756 // This operation corresponds to z -> z^2+c where z=a+ib c=(x,y). Remember to use 'foil'.
5757 47281 b = 2*a*b + imAg;
5758 47281 a = aa - bb + reAl;
5759 47281 iter++;
5760 } // while
5761
5762 // We color each pixel based on how long it takes to get to infinity, or black if it never gets there.
5763
2/2
✓ Branch 0 taken 1781 times.
✓ Branch 1 taken 4980 times.
6761 if (iter == maxIterations) {
5764 1781 SEGMENT.setPixelColorXY(i, j, 0);
5765 } else {
5766
2/4
✓ Branch 0 taken 4980 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4980 times.
4980 SEGMENT.setPixelColorXY(i, j, SEGMENT.color_from_palette(iter*255/maxIterations, false, PALETTE_SOLID_WRAP, 0));
5767 }
5768 6761 x += dx;
5769 }
5770 223 y += dy;
5771 }
5772
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if(SEGMENT.check1)
5773 ✗ SEGMENT.blur(100, true);
5774
5775 18 return FRAMETIME;
5776 } // mode_2DJulia()
5777 static const char _data_FX_MODE_2DJULIA[] PROGMEM = "Julia@,Max iterations per pixel,X center,Y center,Area size, Blur;!;!;2;ix=24,c1=128,c2=128,c3=16";
5778
5779
5780 //////////////////////////////
5781 // 2D Lissajous //
5782 //////////////////////////////
5783 23 uint16_t mode_2DLissajous(void) { // By: Andrew Tuline
5784
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
5785
5786 18 const int cols = SEG_W;
5787 18 const int rows = SEG_H;
5788
5789 18 SEGMENT.fadeToBlackBy(SEGMENT.intensity);
5790 18 uint_fast16_t phase = (strip().now * (1 + SEGENV.custom3)) /32; // allow user to control rotation speed
5791
5792 //for (int i=0; i < 4*(cols+rows); i ++) {
5793
2/2
✓ Branch 0 taken 4608 times.
✓ Branch 1 taken 18 times.
4626 for (int i=0; i < 256; i ++) {
5794 //float xlocn = float(sin8_t(now/4+i*(SEGMENT.speed>>5))) / 255.0f;
5795 //float ylocn = float(cos8_t(now/4+i*2)) / 255.0f;
5796 4608 uint_fast8_t xlocn = sin8_t(phase/2 + (i*SEGMENT.speed)/32);
5797 4608 uint_fast8_t ylocn = cos8_t(phase/2 + i*2);
5798
1/2
✓ Branch 0 taken 4608 times.
✗ Branch 1 not taken.
4608 xlocn = (cols < 2) ? 1 : (map(2*xlocn, 0,511, 0,2*(cols-1)) +1) /2; // softhack007: "(2* ..... +1) /2" for proper rounding
5799
1/2
✓ Branch 0 taken 4608 times.
✗ Branch 1 not taken.
4608 ylocn = (rows < 2) ? 1 : (map(2*ylocn, 0,511, 0,2*(rows-1)) +1) /2; // "rows > 1" is needed to avoid div/0 in map()
5800
2/4
✓ Branch 0 taken 4608 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4608 times.
4608 SEGMENT.setPixelColorXY((uint8_t)xlocn, (uint8_t)ylocn, SEGMENT.color_from_palette(strip().now/100+i, false, PALETTE_SOLID_WRAP, 0));
5801 }
5802 18 SEGMENT.blur(SEGMENT.custom1 >> (1 + SEGMENT.check1 * 3), SEGMENT.check1);
5803
5804 18 return FRAMETIME;
5805 } // mode_2DLissajous()
5806 static const char _data_FX_MODE_2DLISSAJOUS[] PROGMEM = "Lissajous@X frequency,Fade rate,Blur,,Speed,Smear;!;!;2;c1=0";
5807
5808
5809 ///////////////////////
5810 // 2D Matrix //
5811 ///////////////////////
5812 23 uint16_t mode_2Dmatrix(void) { // Matrix2D. By Jeremy Williams. Adapted by Andrew Tuline & improved by merkisoft and ewowi, and softhack007.
5813
9/14
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 18 times.
✓ Branch 10 taken 5 times.
✓ Branch 11 taken 18 times.
✓ Branch 12 taken 5 times.
✗ Branch 13 not taken.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
5814
5815 18 const int cols = SEG_W;
5816 18 const int rows = SEG_H;
5817 10362 const auto XY = [&](int x, int y) { return (x%cols) + (y%rows) * cols; };
5818
5819
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 unsigned dataSize = (SEGMENT.length()+7) >> 3; //1 bit per LED for trails
5820
4/10
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 18 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
18 if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
5821
5822
4/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 14 times.
18 if (SEGENV.call == 0) {
5823
3/6
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 SEGMENT.fill(BLACK);
5824
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 SEGENV.step = 0;
5825 }
5826
5827
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 uint8_t fade = map(SEGMENT.custom1, 0, 255, 50, 250); // equals trail size
5828
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 uint8_t speed = (256-SEGMENT.speed) >> map(min(rows, 150), 0, 150, 0, 3); // slower speeds for small displays
5829
5830 uint32_t spawnColor;
5831 uint32_t trailColor;
5832
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 18 times.
18 if (SEGMENT.check1) {
5833 ✗ spawnColor = SEGCOLOR(0);
5834 ✗ trailColor = SEGCOLOR(1);
5835 } else {
5836 18 spawnColor = RGBW32(175,255,175,0);
5837 18 trailColor = RGBW32(27,130,39,0);
5838 }
5839
5840 18 bool emptyScreen = true;
5841
5/8
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 12 times.
✓ Branch 7 taken 6 times.
18 if (strip().now - SEGENV.step >= speed) {
5842
3/6
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
12 SEGENV.step = strip().now;
5843 // move pixels one row down. Falling codes keep color and add trail pixels; all others pixels are faded
5844 // TODO: it would be better to paint trails idividually instead of relying on fadeToBlackBy()
5845
3/6
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
12 SEGMENT.fadeToBlackBy(fade);
5846
2/2
✓ Branch 0 taken 166 times.
✓ Branch 1 taken 12 times.
178 for (int row = rows-1; row >= 0; row--) {
5847
2/2
✓ Branch 0 taken 5162 times.
✓ Branch 1 taken 166 times.
5328 for (int col = 0; col < cols; col++) {
5848 5162 unsigned index = XY(col, row) >> 3;
5849 5162 unsigned bitNum = XY(col, row) & 0x07;
5850
4/6
✓ Branch 0 taken 5162 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5162 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
✓ Branch 5 taken 5151 times.
5162 if (bitRead(SEGENV.data[index], bitNum)) {
5851
3/6
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
11 SEGMENT.setPixelColorXY(col, row, trailColor); // create trail
5852
2/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
11 bitClear(SEGENV.data[index], bitNum);
5853
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 if (row < rows-1) {
5854
3/6
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
11 SEGMENT.setPixelColorXY(col, row+1, spawnColor);
5855 11 index = XY(col, row+1) >> 3;
5856 11 bitNum = XY(col, row+1) & 0x07;
5857
2/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
11 bitSet(SEGENV.data[index], bitNum);
5858 11 emptyScreen = false;
5859 }
5860 }
5861 }
5862 }
5863
5864 // spawn new falling code
5865
8/10
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 6 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 4 times.
✓ Branch 8 taken 8 times.
✓ Branch 9 taken 4 times.
12 if (hw_random8() <= SEGMENT.intensity || emptyScreen) {
5866 8 uint8_t spawnX = hw_random8(cols);
5867
3/6
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 SEGMENT.setPixelColorXY(spawnX, 0, spawnColor);
5868 // update hint for next run
5869 8 unsigned index = XY(spawnX, 0) >> 3;
5870 8 unsigned bitNum = XY(spawnX, 0) & 0x07;
5871
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 bitSet(SEGENV.data[index], bitNum);
5872 }
5873 }
5874
5875
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 return FRAMETIME;
5876 } // mode_2Dmatrix()
5877 static const char _data_FX_MODE_2DMATRIX[] PROGMEM = "Matrix@!,Spawning rate,Trail,,,Custom color;Spawn,Trail;;2";
5878
5879
5880 /////////////////////////
5881 // 2D Metaballs //
5882 /////////////////////////
5883 23 uint16_t mode_2Dmetaballs(void) { // Metaballs by Stefan Petrick. Cannot have one of the dimensions be 2 or less. Adapted by Andrew Tuline.
5884
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
5885
5886 18 const int cols = SEG_W;
5887 18 const int rows = SEG_H;
5888
5889 18 float speed = 0.25f * (1+(SEGMENT.speed>>6));
5890
5891 // get some 2 random moving points
5892 18 int x2 = map(perlin8(strip().now * speed, 25355, 685), 0, 255, 0, cols-1);
5893 18 int y2 = map(perlin8(strip().now * speed, 355, 11685), 0, 255, 0, rows-1);
5894
5895 18 int x3 = map(perlin8(strip().now * speed, 55355, 6685), 0, 255, 0, cols-1);
5896 18 int y3 = map(perlin8(strip().now * speed, 25355, 22685), 0, 255, 0, rows-1);
5897
5898 // and one Lissajou function
5899 18 int x1 = beatsin8_t(23 * speed, 0, cols-1);
5900 18 int y1 = beatsin8_t(28 * speed, 0, rows-1);
5901
5902
2/2
✓ Branch 0 taken 223 times.
✓ Branch 1 taken 18 times.
241 for (int y = 0; y < rows; y++) {
5903
2/2
✓ Branch 0 taken 6761 times.
✓ Branch 1 taken 223 times.
6984 for (int x = 0; x < cols; x++) {
5904 // calculate distances of the 3 points from actual pixel
5905 // and add them together with weightening
5906 6761 unsigned dx = abs(x - x1);
5907 6761 unsigned dy = abs(y - y1);
5908 6761 unsigned dist = 2 * sqrt32_bw((dx * dx) + (dy * dy));
5909
5910 6761 dx = abs(x - x2);
5911 6761 dy = abs(y - y2);
5912 6761 dist += sqrt32_bw((dx * dx) + (dy * dy));
5913
5914 6761 dx = abs(x - x3);
5915 6761 dy = abs(y - y3);
5916 6761 dist += sqrt32_bw((dx * dx) + (dy * dy));
5917
5918 // inverse result
5919
1/2
✓ Branch 0 taken 6761 times.
✗ Branch 1 not taken.
6761 int color = dist ? 1000 / dist : 255;
5920
5921 // map color between thresholds
5922
3/4
✓ Branch 0 taken 6761 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6656 times.
✓ Branch 3 taken 105 times.
6761 if (color > 0 and color < 60) {
5923
2/4
✓ Branch 0 taken 6656 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6656 times.
6656 SEGMENT.setPixelColorXY(x, y, SEGMENT.color_from_palette(map(color * 9, 9, 531, 0, 255), false, PALETTE_SOLID_WRAP, 0));
5924 } else {
5925
2/4
✓ Branch 0 taken 105 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 105 times.
105 SEGMENT.setPixelColorXY(x, y, SEGMENT.color_from_palette(0, false, PALETTE_SOLID_WRAP, 0));
5926 }
5927 // show the 3 points, too
5928 6761 SEGMENT.setPixelColorXY(x1, y1, WHITE);
5929 6761 SEGMENT.setPixelColorXY(x2, y2, WHITE);
5930 6761 SEGMENT.setPixelColorXY(x3, y3, WHITE);
5931 }
5932 }
5933
5934 18 return FRAMETIME;
5935 } // mode_2Dmetaballs()
5936 static const char _data_FX_MODE_2DMETABALLS[] PROGMEM = "Metaballs@!;;!;2";
5937
5938
5939 //////////////////////
5940 // 2D Noise //
5941 //////////////////////
5942 23 uint16_t mode_2Dnoise(void) { // By Andrew Tuline
5943
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
5944
5945 18 const int cols = SEG_W;
5946 18 const int rows = SEG_H;
5947
5948 18 const unsigned scale = SEGMENT.intensity+2;
5949
5950
2/2
✓ Branch 0 taken 223 times.
✓ Branch 1 taken 18 times.
241 for (int y = 0; y < rows; y++) {
5951
2/2
✓ Branch 0 taken 6761 times.
✓ Branch 1 taken 223 times.
6984 for (int x = 0; x < cols; x++) {
5952 6761 uint8_t pixelHue8 = perlin8(x * scale, y * scale, strip().now / (16 - SEGMENT.speed/16));
5953 6761 SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, pixelHue8));
5954 }
5955 }
5956
5957 18 return FRAMETIME;
5958 } // mode_2Dnoise()
5959 static const char _data_FX_MODE_2DNOISE[] PROGMEM = "Noise2D@!,Scale;;!;2";
5960
5961
5962 //////////////////////////////
5963 // 2D Plasma Ball //
5964 //////////////////////////////
5965 23 uint16_t mode_2DPlasmaball(void) { // By: Stepko https://editor.soulmatelights.com/gallery/659-plasm-ball , Modified by: Andrew Tuline
5966
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
5967
5968 18 const int cols = SEG_W;
5969 18 const int rows = SEG_H;
5970
5971 18 SEGMENT.fadeToBlackBy(SEGMENT.custom1>>2);
5972 18 uint_fast32_t t = (strip().now * 8) / (256 - SEGMENT.speed); // optimized to avoid float
5973
2/2
✓ Branch 0 taken 451 times.
✓ Branch 1 taken 18 times.
469 for (int i = 0; i < cols; i++) {
5974 451 unsigned thisVal = perlin8(i * 30, t, t);
5975 451 unsigned thisMax = map(thisVal, 0, 255, 0, cols-1);
5976
2/2
✓ Branch 0 taken 6761 times.
✓ Branch 1 taken 451 times.
7212 for (int j = 0; j < rows; j++) {
5977 6761 unsigned thisVal_ = perlin8(t, j * 30, t);
5978 6761 unsigned thisMax_ = map(thisVal_, 0, 255, 0, rows-1);
5979 6761 int x = (i + thisMax_ - cols / 2);
5980 6761 int y = (j + thisMax - cols / 2);
5981 6761 int cx = (i + thisMax_);
5982 6761 int cy = (j + thisMax);
5983
5984
4/4
✓ Branch 0 taken 3336 times.
✓ Branch 1 taken 3425 times.
✓ Branch 2 taken 2667 times.
✓ Branch 3 taken 669 times.
13522 SEGMENT.addPixelColorXY(i, j, ((x - y > -2) && (x - y < 2)) ||
5985
6/6
✓ Branch 0 taken 5899 times.
✓ Branch 1 taken 193 times.
✓ Branch 2 taken 5667 times.
✓ Branch 3 taken 232 times.
✓ Branch 4 taken 5667 times.
✓ Branch 5 taken 193 times.
6092 ((cols - 1 - x - y) > -2 && (cols - 1 - x - y < 2)) ||
5986 5667 (cols - cx == 0) ||
5987
4/4
✓ Branch 0 taken 5466 times.
✓ Branch 1 taken 201 times.
✓ Branch 2 taken 5264 times.
✓ Branch 3 taken 202 times.
5667 (cols - 1 - cx == 0) ||
5988 5264 ((rows - cy == 0) ||
5989
2/2
✓ Branch 0 taken 139 times.
✓ Branch 1 taken 5125 times.
6761 (rows - 1 - cy == 0)) ? ColorFromPalette(SEGPALETTE, beat8(5), thisVal, LINEARBLEND) : CRGB::Black);
5990 }
5991 }
5992 18 SEGMENT.blur(SEGMENT.custom2>>5);
5993
5994 18 return FRAMETIME;
5995 } // mode_2DPlasmaball()
5996 static const char _data_FX_MODE_2DPLASMABALL[] PROGMEM = "Plasma Ball@Speed,,Fade,Blur;;!;2";
5997
5998
5999 ////////////////////////////////
6000 // 2D Polar Lights //
6001 ////////////////////////////////
6002
6003 23 uint16_t mode_2DPolarLights(void) { // By: Kostyantyn Matviyevskyy https://editor.soulmatelights.com/gallery/762-polar-lights , Modified by: Andrew Tuline & @dedehai (palette support)
6004
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
6005
6006 18 const int cols = SEG_W;
6007 18 const int rows = SEG_H;
6008
6009
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 14 times.
18 if (SEGENV.call == 0) {
6010 4 SEGMENT.fill(BLACK);
6011 4 SEGENV.step = 0;
6012 }
6013
6014 18 float adjustHeight = (float)map(rows, 8, 32, 28, 12); // maybe use mapf() ???
6015 18 unsigned adjScale = map(cols, 8, 64, 310, 63);
6016 18 unsigned _scale = map(SEGMENT.intensity, 0, 255, 30, adjScale);
6017 18 int _speed = map(SEGMENT.speed, 0, 255, 128, 16);
6018
6019
2/2
✓ Branch 0 taken 451 times.
✓ Branch 1 taken 18 times.
469 for (int x = 0; x < cols; x++) {
6020
2/2
✓ Branch 0 taken 6761 times.
✓ Branch 1 taken 451 times.
7212 for (int y = 0; y < rows; y++) {
6021 6761 SEGENV.step++;
6022
2/2
✓ Branch 0 taken 2347 times.
✓ Branch 1 taken 4414 times.
6761 uint8_t palindex = qsub8(perlin8((SEGENV.step%2) + x * _scale, y * 16 + SEGENV.step % 16, SEGENV.step / _speed), fabsf((float)rows / 2.0f - (float)y) * adjustHeight);
6023 6761 uint8_t palbrightness = palindex;
6024
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6761 times.
6761 if(SEGMENT.check1) palindex = 255 - palindex; //flip palette
6025 6761 SEGMENT.setPixelColorXY(x, y, SEGMENT.color_from_palette(palindex, false, false, 255, palbrightness));
6026 }
6027 }
6028
6029 18 return FRAMETIME;
6030 } // mode_2DPolarLights()
6031 static const char _data_FX_MODE_2DPOLARLIGHTS[] PROGMEM = "Polar Lights@!,Scale,,,,Flip Palette;;!;2;pal=71";
6032
6033
6034 /////////////////////////
6035 // 2D Pulser //
6036 /////////////////////////
6037 23 uint16_t mode_2DPulser(void) { // By: ldirko https://editor.soulmatelights.com/gallery/878-pulse-test , modifed by: Andrew Tuline
6038
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
6039
6040 18 const int cols = SEG_W;
6041 18 const int rows = SEG_H;
6042
6043 18 SEGMENT.fadeToBlackBy(8 - (SEGMENT.intensity>>5));
6044 18 uint32_t a = strip().now / (18 - SEGMENT.speed / 16);
6045 18 int x = (a / 14) % cols;
6046 18 int y = map((sin8_t(a * 5) + sin8_t(a * 4) + sin8_t(a * 2)), 0, 765, rows-1, 0);
6047 18 SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, map(y, 0, rows-1, 0, 255), 255, LINEARBLEND));
6048
6049 18 SEGMENT.blur(SEGMENT.intensity>>4);
6050
6051 18 return FRAMETIME;
6052 } // mode_2DPulser()
6053 static const char _data_FX_MODE_2DPULSER[] PROGMEM = "Pulser@!,Blur;;!;2";
6054
6055
6056 /////////////////////////
6057 // 2D Sindots //
6058 /////////////////////////
6059 23 uint16_t mode_2DSindots(void) { // By: ldirko https://editor.soulmatelights.com/gallery/597-sin-dots , modified by: Andrew Tuline
6060
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
6061
6062 18 const int cols = SEG_W;
6063 18 const int rows = SEG_H;
6064
6065
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 14 times.
18 if (SEGENV.call == 0) {
6066 4 SEGMENT.fill(BLACK);
6067 }
6068
6069 18 SEGMENT.fadeToBlackBy((SEGMENT.custom1>>3) + (SEGMENT.check1 * 24));
6070
6071 18 byte t1 = strip().now / (257 - SEGMENT.speed); // 20;
6072 18 byte t2 = sin8_t(t1) / 4 * 2;
6073
2/2
✓ Branch 0 taken 234 times.
✓ Branch 1 taken 18 times.
252 for (int i = 0; i < 13; i++) {
6074 234 int x = sin8_t(t1 + i * SEGMENT.intensity/8)*(cols-1)/255; // max index now 255x15/255=15!
6075 234 int y = sin8_t(t2 + i * SEGMENT.intensity/8)*(rows-1)/255; // max index now 255x15/255=15!
6076 234 SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, i * 255 / 13, 255, LINEARBLEND));
6077 }
6078 18 SEGMENT.blur(SEGMENT.custom2 >> (3 + SEGMENT.check1), SEGMENT.check1);
6079
6080 18 return FRAMETIME;
6081 } // mode_2DSindots()
6082 static const char _data_FX_MODE_2DSINDOTS[] PROGMEM = "Sindots@!,Dot distance,Fade rate,Blur,,Smear;;!;2;";
6083
6084
6085 //////////////////////////////
6086 // 2D Squared Swirl //
6087 //////////////////////////////
6088 // custom3 affects the blur amount.
6089 23 uint16_t mode_2Dsquaredswirl(void) { // By: Mark Kriegsman. https://gist.github.com/kriegsman/368b316c55221134b160
6090 // Modifed by: Andrew Tuline
6091
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
6092
6093 18 const int cols = SEG_W;
6094 18 const int rows = SEG_H;
6095
6096 18 const uint8_t kBorderWidth = 2;
6097
6098 18 SEGMENT.fadeToBlackBy(1 + SEGMENT.intensity / 5);
6099 18 SEGMENT.blur(SEGMENT.custom3>>1);
6100
6101 // Use two out-of-sync sine waves
6102 18 int i = beatsin8_t(19, kBorderWidth, cols-kBorderWidth);
6103 18 int j = beatsin8_t(22, kBorderWidth, cols-kBorderWidth);
6104 18 int k = beatsin8_t(17, kBorderWidth, cols-kBorderWidth);
6105 18 int m = beatsin8_t(18, kBorderWidth, rows-kBorderWidth);
6106 18 int n = beatsin8_t(15, kBorderWidth, rows-kBorderWidth);
6107 18 int p = beatsin8_t(20, kBorderWidth, rows-kBorderWidth);
6108
6109 18 SEGMENT.addPixelColorXY(i, m, ColorFromPalette(SEGPALETTE, strip().now/29, 255, LINEARBLEND));
6110 18 SEGMENT.addPixelColorXY(j, n, ColorFromPalette(SEGPALETTE, strip().now/41, 255, LINEARBLEND));
6111 18 SEGMENT.addPixelColorXY(k, p, ColorFromPalette(SEGPALETTE, strip().now/73, 255, LINEARBLEND));
6112
6113 18 return FRAMETIME;
6114 } // mode_2Dsquaredswirl()
6115 static const char _data_FX_MODE_2DSQUAREDSWIRL[] PROGMEM = "Squared Swirl@,Fade,,,Blur;;!;2";
6116
6117
6118 //////////////////////////////
6119 // 2D Sun Radiation //
6120 //////////////////////////////
6121 23 uint16_t mode_2DSunradiation(void) { // By: ldirko https://editor.soulmatelights.com/gallery/599-sun-radiation , modified by: Andrew Tuline
6122
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
6123
6124 18 const int cols = SEG_W;
6125 18 const int rows = SEG_H;
6126
6127
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if (!SEGENV.allocateData(sizeof(byte)*(cols+2)*(rows+2))) return mode_static(); //allocation failed
6128 18 byte *bump = reinterpret_cast<byte*>(SEGENV.data);
6129
6130
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 14 times.
18 if (SEGENV.call == 0) {
6131 4 SEGMENT.fill(BLACK);
6132 }
6133
6134 18 unsigned long t = strip().now / 4;
6135 18 unsigned index = 0;
6136 18 uint8_t someVal = SEGMENT.speed/4; // Was 25.
6137
2/2
✓ Branch 0 taken 259 times.
✓ Branch 1 taken 18 times.
277 for (int j = 0; j < (rows + 2); j++) {
6138
2/2
✓ Branch 0 taken 8181 times.
✓ Branch 1 taken 259 times.
8440 for (int i = 0; i < (cols + 2); i++) {
6139 //byte col = (inoise8_raw(i * someVal, j * someVal, t)) / 2;
6140 8181 byte col = ((int16_t)perlin8(i * someVal, j * someVal, t) - 0x7F) / 3;
6141 8181 bump[index++] = col;
6142 }
6143 }
6144
6145 18 int yindex = cols + 3;
6146 18 int vly = -(rows / 2 + 1);
6147
2/2
✓ Branch 0 taken 223 times.
✓ Branch 1 taken 18 times.
241 for (int y = 0; y < rows; y++) {
6148 223 ++vly;
6149 223 int vlx = -(cols / 2 + 1);
6150
2/2
✓ Branch 0 taken 6761 times.
✓ Branch 1 taken 223 times.
6984 for (int x = 0; x < cols; x++) {
6151 6761 ++vlx;
6152 6761 int nx = bump[x + yindex + 1] - bump[x + yindex - 1];
6153 6761 int ny = bump[x + yindex + (cols + 2)] - bump[x + yindex - (cols + 2)];
6154
2/2
✓ Branch 0 taken 3426 times.
✓ Branch 1 taken 3335 times.
6761 unsigned difx = abs8(vlx * 7 - nx);
6155
2/2
✓ Branch 0 taken 3533 times.
✓ Branch 1 taken 3228 times.
6761 unsigned dify = abs8(vly * 7 - ny);
6156 6761 int temp = difx * difx + dify * dify;
6157 6761 int col = 255 - temp / 8; //8 its a size of effect
6158
2/2
✓ Branch 0 taken 4970 times.
✓ Branch 1 taken 1791 times.
6761 if (col < 0) col = 0;
6159 6761 SEGMENT.setPixelColorXY(x, y, HeatColor(col / (3.0f-(float)(SEGMENT.intensity)/128.f)));
6160 }
6161 223 yindex += (cols + 2);
6162 }
6163
6164 18 return FRAMETIME;
6165 } // mode_2DSunradiation()
6166 static const char _data_FX_MODE_2DSUNRADIATION[] PROGMEM = "Sun Radiation@Variance,Brightness;;;2";
6167
6168
6169 /////////////////////////
6170 // 2D Tartan //
6171 /////////////////////////
6172 23 uint16_t mode_2Dtartan(void) { // By: Elliott Kember https://editor.soulmatelights.com/gallery/3-tartan , Modified by: Andrew Tuline
6173
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
6174
6175 18 const int cols = SEG_W;
6176 18 const int rows = SEG_H;
6177
6178
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 14 times.
18 if (SEGENV.call == 0) {
6179 4 SEGMENT.fill(BLACK);
6180 }
6181
6182 uint8_t hue, bri;
6183 size_t intensity;
6184 18 int offsetX = beatsin16_t(3, -360, 360);
6185 18 int offsetY = beatsin16_t(2, -360, 360);
6186 18 int sharpness = SEGMENT.custom3 / 8; // 0-3
6187
6188
2/2
✓ Branch 0 taken 451 times.
✓ Branch 1 taken 18 times.
469 for (int x = 0; x < cols; x++) {
6189
2/2
✓ Branch 0 taken 6761 times.
✓ Branch 1 taken 451 times.
7212 for (int y = 0; y < rows; y++) {
6190 6761 hue = x * beatsin16_t(10, 1, 10) + offsetY;
6191 6761 intensity = bri = sin8_t(x * SEGMENT.speed/2 + offsetX);
6192
2/2
✓ Branch 0 taken 11474 times.
✓ Branch 1 taken 6761 times.
18235 for (int i=0; i<sharpness; i++) intensity *= bri;
6193 6761 intensity >>= 8*sharpness;
6194 6761 SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, hue, intensity, LINEARBLEND));
6195 6761 hue = y * 3 + offsetX;
6196 6761 intensity = bri = sin8_t(y * SEGMENT.intensity/2 + offsetY);
6197
2/2
✓ Branch 0 taken 11474 times.
✓ Branch 1 taken 6761 times.
18235 for (int i=0; i<sharpness; i++) intensity *= bri;
6198 6761 intensity >>= 8*sharpness;
6199 6761 SEGMENT.addPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, hue, intensity, LINEARBLEND));
6200 }
6201 }
6202
6203 18 return FRAMETIME;
6204 } // mode_2DTartan()
6205 static const char _data_FX_MODE_2DTARTAN[] PROGMEM = "Tartan@X scale,Y scale,,,Sharpness;;!;2";
6206
6207
6208 /////////////////////////
6209 // 2D spaceships //
6210 /////////////////////////
6211 23 uint16_t mode_2Dspaceships(void) { //// Space ships by stepko (c)05.02.21 [https://editor.soulmatelights.com/gallery/639-space-ships], adapted by Blaz Kristan (AKA blazoncek)
6212
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
6213
6214 18 const int cols = SEG_W;
6215 18 const int rows = SEG_H;
6216
6217 18 uint32_t tb = strip().now >> 12; // every ~4s
6218
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 13 times.
18 if (tb > SEGENV.step) {
6219 5 int dir = ++SEGENV.aux0;
6220 5 dir += (int)hw_random8(3)-1;
6221
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (dir > 7) SEGENV.aux0 = 0;
6222
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 else if (dir < 0) SEGENV.aux0 = 7;
6223 5 else SEGENV.aux0 = dir;
6224 5 SEGENV.step = tb + hw_random8(4);
6225 }
6226
6227 18 SEGMENT.fadeToBlackBy(map(SEGMENT.speed, 0, 255, 248, 16));
6228 18 SEGMENT.move(SEGENV.aux0, 1);
6229
6230
2/2
✓ Branch 0 taken 144 times.
✓ Branch 1 taken 18 times.
162 for (size_t i = 0; i < 8; i++) {
6231 144 int x = beatsin8_t(12 + i, 2, cols - 3);
6232 144 int y = beatsin8_t(15 + i, 2, rows - 3);
6233 144 uint32_t color = ColorFromPalette(SEGPALETTE, beatsin8_t(12 + i, 0, 255), 255);
6234 144 SEGMENT.addPixelColorXY(x, y, color);
6235
3/4
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 104 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 40 times.
144 if (cols > 24 || rows > 24) {
6236 104 SEGMENT.addPixelColorXY(x+1, y, color);
6237 104 SEGMENT.addPixelColorXY(x-1, y, color);
6238 104 SEGMENT.addPixelColorXY(x, y+1, color);
6239 104 SEGMENT.addPixelColorXY(x, y-1, color);
6240 }
6241 }
6242 18 SEGMENT.blur(SEGMENT.intensity >> 3, SEGMENT.check1);
6243
6244 18 return FRAMETIME;
6245 }
6246 static const char _data_FX_MODE_2DSPACESHIPS[] PROGMEM = "Spaceships@!,Blur,,,,Smear;;!;2";
6247
6248
6249 /////////////////////////
6250 // 2D Crazy Bees //
6251 /////////////////////////
6252 //// Crazy bees by stepko (c)12.02.21 [https://editor.soulmatelights.com/gallery/651-crazy-bees], adapted by Blaz Kristan (AKA blazoncek), improved by @dedehai
6253 #define MAX_BEES 5
6254 23 uint16_t mode_2Dcrazybees(void) {
6255
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
6256
6257 18 const int cols = SEG_W;
6258 18 const int rows = SEG_H;
6259
6260
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 byte n = MIN(MAX_BEES, (rows * cols) / 256 + 1);
6261
6262 typedef struct Bee {
6263 uint8_t posX, posY, aimX, aimY, hue;
6264 int8_t deltaX, deltaY, signX, signY, error;
6265 14 void aimed(uint16_t w, uint16_t h) {
6266 //random16_set_seed(millis());
6267 14 aimX = random8(0, w);
6268 14 aimY = random8(0, h);
6269 14 hue = random8();
6270 14 deltaX = abs(aimX - posX);
6271 14 deltaY = abs(aimY - posY);
6272
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 9 times.
14 signX = posX < aimX ? 1 : -1;
6273
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
14 signY = posY < aimY ? 1 : -1;
6274 14 error = deltaX - deltaY;
6275 14 };
6276 } bee_t;
6277
6278
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if (!SEGENV.allocateData(sizeof(bee_t)*MAX_BEES)) return mode_static(); //allocation failed
6279 18 bee_t *bee = reinterpret_cast<bee_t*>(SEGENV.data);
6280
6281
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 14 times.
18 if (SEGENV.call == 0) {
6282 4 random16_set_seed(strip().now);
6283
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 4 times.
14 for (size_t i = 0; i < n; i++) {
6284 10 bee[i].posX = random8(0, cols);
6285 10 bee[i].posY = random8(0, rows);
6286 10 bee[i].aimed(cols, rows);
6287 }
6288 }
6289
6290
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 3 times.
18 if (strip().now > SEGENV.step) {
6291 15 SEGENV.step = strip().now + (FRAMETIME * 16 / ((SEGMENT.speed>>4)+1));
6292 15 SEGMENT.fadeToBlackBy(32 + ((SEGMENT.check1*SEGMENT.intensity) / 25));
6293 15 SEGMENT.blur(SEGMENT.intensity / (2 + SEGMENT.check1 * 9), SEGMENT.check1);
6294
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 15 times.
50 for (size_t i = 0; i < n; i++) {
6295 35 uint32_t flowerCcolor = SEGMENT.color_from_palette(bee[i].hue, false, true, 255);
6296 35 SEGMENT.addPixelColorXY(bee[i].aimX + 1, bee[i].aimY, flowerCcolor);
6297 35 SEGMENT.addPixelColorXY(bee[i].aimX, bee[i].aimY + 1, flowerCcolor);
6298 35 SEGMENT.addPixelColorXY(bee[i].aimX - 1, bee[i].aimY, flowerCcolor);
6299 35 SEGMENT.addPixelColorXY(bee[i].aimX, bee[i].aimY - 1, flowerCcolor);
6300
4/4
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 4 times.
35 if (bee[i].posX != bee[i].aimX || bee[i].posY != bee[i].aimY) {
6301
1/2
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
62 SEGMENT.setPixelColorXY(bee[i].posX, bee[i].posY, CRGB(CHSV(bee[i].hue, 60, 255)));
6302 31 int error2 = bee[i].error * 2;
6303
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 4 times.
31 if (error2 > -bee[i].deltaY) {
6304 27 bee[i].error -= bee[i].deltaY;
6305 27 bee[i].posX += bee[i].signX;
6306 }
6307
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 16 times.
31 if (error2 < bee[i].deltaX) {
6308 15 bee[i].error += bee[i].deltaX;
6309 15 bee[i].posY += bee[i].signY;
6310 }
6311 31 } else {
6312 4 bee[i].aimed(cols, rows);
6313 }
6314 }
6315 }
6316 18 return FRAMETIME;
6317 }
6318 static const char _data_FX_MODE_2DCRAZYBEES[] PROGMEM = "Crazy Bees@!,Blur,,,,Smear;;!;2;pal=11,ix=0";
6319 #undef MAX_BEES
6320
6321 #ifdef WLED_PS_DONT_REPLACE_FX
6322 /////////////////////////
6323 // 2D Ghost Rider //
6324 /////////////////////////
6325 //// Ghost Rider by stepko (c)2021 [https://editor.soulmatelights.com/gallery/716-ghost-rider], adapted by Blaz Kristan (AKA blazoncek)
6326 #define LIGHTERS_AM 64 // max lighters (adequate for 32x32 matrix)
6327 23 uint16_t mode_2Dghostrider(void) {
6328
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
6329
6330 18 const int cols = SEG_W;
6331 18 const int rows = SEG_H;
6332
6333 typedef struct Lighter {
6334 int16_t gPosX;
6335 int16_t gPosY;
6336 uint16_t gAngle;
6337 int8_t angleSpeed;
6338 uint16_t lightersPosX[LIGHTERS_AM];
6339 uint16_t lightersPosY[LIGHTERS_AM];
6340 uint16_t Angle[LIGHTERS_AM];
6341 uint16_t time[LIGHTERS_AM];
6342 bool reg[LIGHTERS_AM];
6343 int8_t Vspeed;
6344 } lighter_t;
6345
6346
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if (!SEGENV.allocateData(sizeof(lighter_t))) return mode_static(); //allocation failed
6347 18 lighter_t *lighter = reinterpret_cast<lighter_t*>(SEGENV.data);
6348
6349 18 const size_t maxLighters = min(cols + rows, LIGHTERS_AM);
6350
6351
5/6
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 14 times.
18 if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) {
6352 4 SEGENV.aux0 = cols;
6353 4 SEGENV.aux1 = rows;
6354 4 lighter->angleSpeed = hw_random8(0,20) - 10;
6355 4 lighter->gAngle = hw_random16();
6356 4 lighter->Vspeed = 5;
6357 4 lighter->gPosX = (cols/2) * 10;
6358 4 lighter->gPosY = (rows/2) * 10;
6359
2/2
✓ Branch 0 taken 154 times.
✓ Branch 1 taken 4 times.
158 for (size_t i = 0; i < maxLighters; i++) {
6360 154 lighter->lightersPosX[i] = lighter->gPosX;
6361 154 lighter->lightersPosY[i] = lighter->gPosY + i;
6362 154 lighter->time[i] = i * 2;
6363 154 lighter->reg[i] = false;
6364 }
6365 }
6366
6367
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 3 times.
18 if (strip().now > SEGENV.step) {
6368
3/6
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
15 SEGENV.step = strip().now + 1024 / (cols+rows);
6369
6370
5/10
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 15 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 15 times.
✗ Branch 9 not taken.
15 SEGMENT.fadeToBlackBy((SEGMENT.speed>>2)+64);
6371
6372 15 CRGB color = CRGB::White;
6373
3/6
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
15 SEGMENT.wu_pixel(lighter->gPosX * 256 / 10, lighter->gPosY * 256 / 10, color);
6374
6375
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 lighter->gPosX += lighter->Vspeed * sin_t(radians(lighter->gAngle));
6376
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 lighter->gPosY += lighter->Vspeed * cos_t(radians(lighter->gAngle));
6377 15 lighter->gAngle += lighter->angleSpeed;
6378
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (lighter->gPosX < 0) lighter->gPosX = (cols - 1) * 10;
6379
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (lighter->gPosX > (cols - 1) * 10) lighter->gPosX = 0;
6380
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (lighter->gPosY < 0) lighter->gPosY = (rows - 1) * 10;
6381
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (lighter->gPosY > (rows - 1) * 10) lighter->gPosY = 0;
6382
2/2
✓ Branch 0 taken 644 times.
✓ Branch 1 taken 15 times.
659 for (size_t i = 0; i < maxLighters; i++) {
6383 644 lighter->time[i] += hw_random8(5, 20);
6384
1/2
✓ Branch 0 taken 644 times.
✗ Branch 1 not taken.
644 if (lighter->time[i] >= 255 ||
6385
1/2
✓ Branch 0 taken 644 times.
✗ Branch 1 not taken.
644 (lighter->lightersPosX[i] <= 0) ||
6386
1/2
✓ Branch 0 taken 644 times.
✗ Branch 1 not taken.
644 (lighter->lightersPosX[i] >= (cols - 1) * 10) ||
6387
1/2
✓ Branch 0 taken 644 times.
✗ Branch 1 not taken.
644 (lighter->lightersPosY[i] <= 0) ||
6388
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 644 times.
644 (lighter->lightersPosY[i] >= (rows - 1) * 10)) {
6389 ✗ lighter->reg[i] = true;
6390 }
6391
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 644 times.
644 if (lighter->reg[i]) {
6392 ✗ lighter->lightersPosY[i] = lighter->gPosY;
6393 ✗ lighter->lightersPosX[i] = lighter->gPosX;
6394 ✗ lighter->Angle[i] = lighter->gAngle + ((int)hw_random8(20) - 10);
6395 ✗ lighter->time[i] = 0;
6396 ✗ lighter->reg[i] = false;
6397 } else {
6398
1/2
✓ Branch 0 taken 644 times.
✗ Branch 1 not taken.
644 lighter->lightersPosX[i] += -7 * sin_t(radians(lighter->Angle[i]));
6399
1/2
✓ Branch 0 taken 644 times.
✗ Branch 1 not taken.
644 lighter->lightersPosY[i] += -7 * cos_t(radians(lighter->Angle[i]));
6400 }
6401
4/8
✓ Branch 0 taken 644 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 644 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 644 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 644 times.
✗ Branch 7 not taken.
644 SEGMENT.wu_pixel(lighter->lightersPosX[i] * 256 / 10, lighter->lightersPosY[i] * 256 / 10, ColorFromPalette(SEGPALETTE, (256 - lighter->time[i])));
6402 }
6403
5/10
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 15 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 15 times.
✗ Branch 9 not taken.
15 SEGMENT.blur(SEGMENT.intensity>>3);
6404 }
6405
6406 18 return FRAMETIME;
6407 }
6408 static const char _data_FX_MODE_2DGHOSTRIDER[] PROGMEM = "Ghost Rider@Fade rate,Blur;;!;2";
6409 #undef LIGHTERS_AM
6410
6411 ////////////////////////////
6412 // 2D Floating Blobs //
6413 ////////////////////////////
6414 //// Floating Blobs by stepko (c)2021 [https://editor.soulmatelights.com/gallery/573-blobs], adapted by Blaz Kristan (AKA blazoncek)
6415 #define MAX_BLOBS 8
6416 23 uint16_t mode_2Dfloatingblobs(void) {
6417
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
6418
6419 18 const int cols = SEG_W;
6420 18 const int rows = SEG_H;
6421
6422 typedef struct Blob {
6423 float x[MAX_BLOBS], y[MAX_BLOBS];
6424 float sX[MAX_BLOBS], sY[MAX_BLOBS]; // speed
6425 float r[MAX_BLOBS];
6426 bool grow[MAX_BLOBS];
6427 byte color[MAX_BLOBS];
6428 } blob_t;
6429
6430 18 size_t Amount = (SEGMENT.intensity>>5) + 1; // NOTE: be sure to update MAX_BLOBS if you change this
6431
6432
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if (!SEGENV.allocateData(sizeof(blob_t))) return mode_static(); //allocation failed
6433 18 blob_t *blob = reinterpret_cast<blob_t*>(SEGENV.data);
6434
6435
5/6
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 14 times.
18 if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) {
6436 4 SEGENV.aux0 = cols; // re-initialise if virtual size changes
6437 4 SEGENV.aux1 = rows;
6438 //SEGMENT.fill(BLACK);
6439
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 4 times.
36 for (size_t i = 0; i < MAX_BLOBS; i++) {
6440
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 8 times.
32 blob->r[i] = hw_random8(1, cols>8 ? (cols/4) : 2);
6441 32 blob->sX[i] = (float) hw_random8(3, cols) / (float)(256 - SEGMENT.speed); // speed x
6442 32 blob->sY[i] = (float) hw_random8(3, rows) / (float)(256 - SEGMENT.speed); // speed y
6443 32 blob->x[i] = hw_random8(0, cols-1);
6444 32 blob->y[i] = hw_random8(0, rows-1);
6445 32 blob->color[i] = hw_random8();
6446 32 blob->grow[i] = (blob->r[i] < 1.f);
6447
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 if (blob->sX[i] == 0) blob->sX[i] = 1;
6448
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 if (blob->sY[i] == 0) blob->sY[i] = 1;
6449 }
6450 }
6451
6452 18 SEGMENT.fadeToBlackBy((SEGMENT.custom2>>3)+1);
6453
6454 // Bounce balls around
6455
2/2
✓ Branch 0 taken 86 times.
✓ Branch 1 taken 18 times.
104 for (size_t i = 0; i < Amount; i++) {
6456
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 67 times.
105 if (SEGENV.step < strip().now) blob->color[i] = add8(blob->color[i], 4); // slowly change color
6457 // change radius if needed
6458
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 54 times.
86 if (blob->grow[i]) {
6459 // enlarge radius until it is >= 4
6460
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 3 times.
32 blob->r[i] += (fabsf(blob->sX[i]) > fabsf(blob->sY[i]) ? fabsf(blob->sX[i]) : fabsf(blob->sY[i])) * 0.05f;
6461
4/4
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 28 times.
32 if (blob->r[i] >= MIN(cols/4.f,2.f)) {
6462 4 blob->grow[i] = false;
6463 }
6464 } else {
6465 // reduce radius until it is < 1
6466
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 6 times.
54 blob->r[i] -= (fabsf(blob->sX[i]) > fabsf(blob->sY[i]) ? fabsf(blob->sX[i]) : fabsf(blob->sY[i])) * 0.05f;
6467
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 43 times.
54 if (blob->r[i] < 1.f) {
6468 11 blob->grow[i] = true;
6469 }
6470 }
6471 86 uint32_t c = SEGMENT.color_from_palette(blob->color[i], false, false, 0);
6472
2/2
✓ Branch 0 taken 67 times.
✓ Branch 1 taken 19 times.
86 if (blob->r[i] > 1.f) SEGMENT.fillCircle(roundf(blob->x[i]), roundf(blob->y[i]), roundf(blob->r[i]), c);
6473 19 else SEGMENT.setPixelColorXY((int)roundf(blob->x[i]), (int)roundf(blob->y[i]), c);
6474 // move x
6475
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 48 times.
86 if (blob->x[i] + blob->r[i] >= cols - 1) blob->x[i] += (blob->sX[i] * ((cols - 1 - blob->x[i]) / blob->r[i] + 0.005f));
6476
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 38 times.
48 else if (blob->x[i] - blob->r[i] <= 0) blob->x[i] += (blob->sX[i] * (blob->x[i] / blob->r[i] + 0.005f));
6477 38 else blob->x[i] += blob->sX[i];
6478 // move y
6479
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 42 times.
86 if (blob->y[i] + blob->r[i] >= rows - 1) blob->y[i] += (blob->sY[i] * ((rows - 1 - blob->y[i]) / blob->r[i] + 0.005f));
6480
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 22 times.
42 else if (blob->y[i] - blob->r[i] <= 0) blob->y[i] += (blob->sY[i] * (blob->y[i] / blob->r[i] + 0.005f));
6481 22 else blob->y[i] += blob->sY[i];
6482 // bounce x
6483
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 84 times.
86 if (blob->x[i] < 0.01f) {
6484 2 blob->sX[i] = (float)hw_random8(3, cols) / (256 - SEGMENT.speed);
6485 2 blob->x[i] = 0.01f;
6486
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 76 times.
84 } else if (blob->x[i] > (float)cols - 1.01f) {
6487 8 blob->sX[i] = (float)hw_random8(3, cols) / (256 - SEGMENT.speed);
6488 8 blob->sX[i] = -blob->sX[i];
6489 8 blob->x[i] = (float)cols - 1.01f;
6490 }
6491 // bounce y
6492
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 82 times.
86 if (blob->y[i] < 0.01f) {
6493 4 blob->sY[i] = (float)hw_random8(3, rows) / (256 - SEGMENT.speed);
6494 4 blob->y[i] = 0.01f;
6495
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 75 times.
82 } else if (blob->y[i] > (float)rows - 1.01f) {
6496 7 blob->sY[i] = (float)hw_random8(3, rows) / (256 - SEGMENT.speed);
6497 7 blob->sY[i] = -blob->sY[i];
6498 7 blob->y[i] = (float)rows - 1.01f;
6499 }
6500 }
6501 18 SEGMENT.blur(SEGMENT.custom1>>2);
6502
6503
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 14 times.
18 if (SEGENV.step < strip().now) SEGENV.step = strip().now + 2000; // change colors every 2 seconds
6504
6505 18 return FRAMETIME;
6506 }
6507 static const char _data_FX_MODE_2DBLOBS[] PROGMEM = "Blobs@!,# blobs,Blur,Trail;!;!;2;c1=8";
6508 #undef MAX_BLOBS
6509 #endif // WLED_PS_DONT_REPLACE_FX
6510
6511 ////////////////////////////
6512 // 2D Scrolling text //
6513 ////////////////////////////
6514 147 uint16_t mode_2Dscrollingtext(void) {
6515
9/14
✓ Branch 0 taken 147 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 142 times.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 142 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 142 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 142 times.
✓ Branch 10 taken 5 times.
✓ Branch 11 taken 142 times.
✓ Branch 12 taken 5 times.
✗ Branch 13 not taken.
147 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
6516 // FPP: the global localTime is only set when the library loads, so #TIME
6517 // and the rest showed fppd's start time. Read the clock each frame.
6518 142 const time_t localTime = time(nullptr);
6519
6520 142 const int cols = SEG_W;
6521 142 const int rows = SEG_H;
6522
6523 unsigned letterWidth, rotLW;
6524 unsigned letterHeight, rotLH;
6525
5/9
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 142 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 134 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 4 times.
142 switch (map(SEGMENT.custom2, 0, 255, 1, 5)) {
6526 4 default:
6527 4 case 1: letterWidth = 4; letterHeight = 6; break;
6528 ✗ case 2: letterWidth = 5; letterHeight = 8; break;
6529 134 case 3: letterWidth = 6; letterHeight = 8; break;
6530 ✗ case 4: letterWidth = 7; letterHeight = 9; break;
6531 4 case 5: letterWidth = 5; letterHeight = 12; break;
6532 }
6533 // letters are rotated
6534
3/6
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 142 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 142 times.
142 if (((SEGMENT.custom3+1)>>3) % 2) {
6535 ✗ rotLH = letterWidth;
6536 ✗ rotLW = letterHeight;
6537 } else {
6538 142 rotLW = letterWidth;
6539 142 rotLH = letterHeight;
6540 }
6541
6542 // FPP: the segment name is the effect's Text argument, which is not held to
6543 // WLED_MAX_SEGNAME_LEN, so the buffer is sized from it rather than fixed.
6544
6/10
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 142 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 124 times.
✓ Branch 5 taken 18 times.
✓ Branch 6 taken 124 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 124 times.
✗ Branch 9 not taken.
142 const size_t nameLen = SEGMENT.name ? strlen(SEGMENT.name) : 0;
6545
1/2
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
284 std::vector<char> textBuf(std::max(nameLen, (size_t)WLED_MAX_SEGNAME_LEN) + 1, '\0');
6546 142 char *text = textBuf.data();
6547
15/26
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 142 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 124 times.
✓ Branch 5 taken 18 times.
✓ Branch 6 taken 16554 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 16554 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 16554 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 16554 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 16554 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 16554 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 16554 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 16554 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 16554 times.
✗ Branch 23 not taken.
✓ Branch 24 taken 16554 times.
✓ Branch 25 taken 124 times.
16696 if (SEGMENT.name) for (size_t i=0,j=0; i<nameLen; i++) if (SEGMENT.name[i]>31 && SEGMENT.name[i]<128) text[j++] = SEGMENT.name[i];
6548 142 const bool zero = strchr(text, '0') != nullptr;
6549
6550 char sec[5];
6551
1/2
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
142 int AmPmHour = hour(localTime);
6552 142 bool isitAM = true;
6553 if (useAMPM) {
6554 if (AmPmHour > 11) { AmPmHour -= 12; isitAM = false; }
6555 if (AmPmHour == 0) { AmPmHour = 12; }
6556 sprintf_P(sec, PSTR(" %2s"), (isitAM ? "AM" : "PM"));
6557 } else {
6558
1/2
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
142 sprintf_P(sec, PSTR(":%02d"), second(localTime));
6559 }
6560
6561
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 124 times.
142 if (!strlen(text)) { // fallback if empty segment name: display date and time
6562
5/10
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 18 times.
✗ Branch 9 not taken.
18 sprintf_P(text, PSTR("%s %d, %d %d:%02d%s"), monthShortStr(month(localTime)), day(localTime), year(localTime), AmPmHour, minute(localTime), sec);
6563 } else {
6564
4/4
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 62 times.
✓ Branch 2 taken 4030 times.
✓ Branch 3 taken 62 times.
4154 if (text[0] == '#') for (auto &c : textBuf) c = std::toupper(c);
6565
1/10
✗ Branch 0 not taken.
✓ Branch 1 taken 124 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
124 if (!strncmp_P(text,PSTR("#DATE"),5)) sprintf_P(text, zero?PSTR("%02d.%02d.%04d"):PSTR("%d.%d.%d"), day(localTime), month(localTime), year(localTime));
6566
1/8
✗ Branch 0 not taken.
✓ Branch 1 taken 124 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
124 else if (!strncmp_P(text,PSTR("#DDMM"),5)) sprintf_P(text, zero?PSTR("%02d.%02d") :PSTR("%d.%d"), day(localTime), month(localTime));
6567
1/8
✗ Branch 0 not taken.
✓ Branch 1 taken 124 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
124 else if (!strncmp_P(text,PSTR("#MMDD"),5)) sprintf_P(text, zero?PSTR("%02d/%02d") :PSTR("%d/%d"), month(localTime), day(localTime));
6568
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 124 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
124 else if (!strncmp_P(text,PSTR("#TIME"),5)) sprintf_P(text, zero?PSTR("%02d:%02d%s") :PSTR("%2d:%02d%s"), AmPmHour, minute(localTime), sec);
6569
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 124 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
124 else if (!strncmp_P(text,PSTR("#HHMM"),5)) sprintf_P(text, zero?PSTR("%02d:%02d") :PSTR("%d:%02d"), AmPmHour, minute(localTime));
6570
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 124 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
124 else if (!strncmp_P(text,PSTR("#HH"),3)) sprintf (text, zero? ("%02d") : ("%d"), AmPmHour);
6571
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 124 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
124 else if (!strncmp_P(text,PSTR("#MM"),3)) sprintf (text, zero? ("%02d") : ("%d"), minute(localTime));
6572
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 124 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
124 else if (!strncmp_P(text,PSTR("#SS"),3)) sprintf (text, ("%02d") , second(localTime));
6573
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 124 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
124 else if (!strncmp_P(text,PSTR("#DD"),3)) sprintf (text, zero? ("%02d") : ("%d"), day(localTime));
6574
4/6
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 62 times.
✓ Branch 2 taken 62 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 62 times.
✗ Branch 5 not taken.
124 else if (!strncmp_P(text,PSTR("#DAY"),4)) sprintf (text, ("%s") , dayShortStr(weekday(localTime)));
6575
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
62 else if (!strncmp_P(text,PSTR("#DDDD"),5)) sprintf (text, ("%s") , dayStr(weekday(localTime)));
6576
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
62 else if (!strncmp_P(text,PSTR("#MO"),3)) sprintf (text, zero? ("%02d") : ("%d"), month(localTime));
6577
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
62 else if (!strncmp_P(text,PSTR("#MON"),4)) sprintf (text, ("%s") , monthShortStr(month(localTime)));
6578
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
62 else if (!strncmp_P(text,PSTR("#MMMM"),5)) sprintf (text, ("%s") , monthStr(month(localTime)));
6579
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
62 else if (!strncmp_P(text,PSTR("#YY"),3)) sprintf (text, ("%02d") , year(localTime)%100);
6580
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
62 else if (!strncmp_P(text,PSTR("#YYYY"),5)) sprintf_P(text, zero?PSTR("%04d") : ("%d"), year(localTime));
6581 }
6582
6583 142 const int numberOfLetters = strlen(text);
6584 142 int width = (numberOfLetters * rotLW);
6585
2/4
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 142 times.
✗ Branch 3 not taken.
142 int yoffset = map(SEGMENT.intensity, 0, 255, -rows/2, rows/2) + (rows-rotLH)/2;
6586
2/2
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 80 times.
142 if (width <= cols) {
6587 // scroll vertically (e.g. ^^ Way out ^^) if it fits
6588
2/4
✓ Branch 0 taken 62 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 62 times.
✗ Branch 3 not taken.
62 int speed = map(SEGMENT.speed, 0, 255, 5000, 1000);
6589
1/2
✓ Branch 0 taken 62 times.
✗ Branch 1 not taken.
62 int frac = strip().now % speed + 1;
6590
3/6
✓ Branch 0 taken 62 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 62 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 62 times.
62 if (SEGMENT.intensity == 255) {
6591 ✗ yoffset = (2 * frac * rows)/speed - rows;
6592
3/6
✓ Branch 0 taken 62 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 62 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 62 times.
62 } else if (SEGMENT.intensity == 0) {
6593 ✗ yoffset = rows - (2 * frac * rows)/speed;
6594 }
6595 }
6596
6597
5/8
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 142 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 142 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 39 times.
✓ Branch 7 taken 103 times.
142 if (SEGENV.step < strip().now) {
6598 // calculate start offset
6599
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 16 times.
39 if (width > cols) {
6600
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 23 times.
23 if (SEGMENT.check3) {
6601 ✗ if (SEGENV.aux0 == 0) SEGENV.aux0 = width + cols - 1;
6602 ✗ else --SEGENV.aux0;
6603
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 } else ++SEGENV.aux0 %= width + cols;
6604
2/4
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
16 } else SEGENV.aux0 = (cols + width)/2;
6605
2/4
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
39 ++SEGENV.aux1 &= 0xFF; // color shift
6606
5/10
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 39 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 39 times.
✗ Branch 9 not taken.
39 SEGENV.step = strip().now + map(SEGMENT.speed, 0, 255, 250, 50); // shift letters every ~250ms to ~50ms
6607 }
6608
6609
8/16
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 142 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 142 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 142 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 142 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 142 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 142 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 142 times.
✗ Branch 15 not taken.
142 if (!SEGMENT.check2) SEGMENT.fade_out(255 - (SEGMENT.custom1>>4)); // trail
6610 142 bool usePaletteGradient = false;
6611
9/18
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 142 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 142 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 142 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 142 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 142 times.
✓ Branch 12 taken 142 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 142 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 142 times.
✗ Branch 17 not taken.
142 uint32_t col1 = SEGMENT.color_from_palette(SEGENV.aux1, false, PALETTE_SOLID_WRAP, 0);
6612 142 uint32_t col2 = BLACK;
6613
3/6
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 142 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 142 times.
142 if (SEGMENT.check1) { // use gradient
6614 ✗ if(SEGMENT.palette == 0) { // use colors for gradient
6615 ✗ col1 = SEGCOLOR(0);
6616 ✗ col2 = SEGCOLOR(2);
6617 }
6618 ✗ else usePaletteGradient = true;
6619 }
6620
6621
2/2
✓ Branch 0 taken 15426 times.
✓ Branch 1 taken 142 times.
15568 for (int i = 0; i < numberOfLetters; i++) {
6622
2/4
✓ Branch 0 taken 15426 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15426 times.
✗ Branch 3 not taken.
15426 int xoffset = int(cols) - int(SEGENV.aux0) + rotLW*i;
6623 if (xoffset + rotLW < 0) continue; // don't draw characters off-screen
6624
5/10
✓ Branch 0 taken 15426 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15426 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 15426 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 15426 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 15426 times.
✗ Branch 9 not taken.
15426 SEGMENT.drawCharacter(text[i], xoffset, yoffset, letterWidth, letterHeight, col1, col2, map(SEGMENT.custom3, 0, 31, -2, 2), usePaletteGradient);
6625 }
6626
6627
1/2
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
142 return FRAMETIME;
6628 }
6629 static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size,Rotate,Gradient,Overlay,Reverse;!,!,Gradient;!;2;ix=128,c1=0,rev=0,mi=0,rY=0,mY=0";
6630
6631
6632 ////////////////////////////
6633 // 2D Drift Rose //
6634 ////////////////////////////
6635 //// Drift Rose by stepko (c)2021 [https://editor.soulmatelights.com/gallery/1369-drift-rose-pattern], adapted by Blaz Kristan (AKA blazoncek) improved by @dedehai
6636 23 uint16_t mode_2Ddriftrose(void) {
6637
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
6638
6639 18 const int cols = SEG_W;
6640 18 const int rows = SEG_H;
6641
6642 18 const float CX = (cols-cols%2)/2.f - .5f;
6643 18 const float CY = (rows-rows%2)/2.f - .5f;
6644 18 const float L = min(cols, rows) / 2.f;
6645
6646 18 SEGMENT.fadeToBlackBy(32+(SEGMENT.speed>>3));
6647
2/2
✓ Branch 0 taken 648 times.
✓ Branch 1 taken 18 times.
666 for (size_t i = 1; i < 37; i++) {
6648 648 float angle = radians(i * 10);
6649 648 uint32_t x = (CX + (sin_t(angle) * (beatsin8_t(i, 0, L*2)-L))) * 255.f;
6650 648 uint32_t y = (CY + (cos_t(angle) * (beatsin8_t(i, 0, L*2)-L))) * 255.f;
6651
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 648 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
648 if(SEGMENT.palette == 0) SEGMENT.wu_pixel(x, y, CHSV(i * 10, 255, 255));
6652
2/4
✓ Branch 0 taken 648 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 648 times.
✗ Branch 3 not taken.
648 else SEGMENT.wu_pixel(x, y, ColorFromPalette(SEGPALETTE, i * 10));
6653 }
6654 18 SEGMENT.blur(SEGMENT.intensity >> 4, SEGMENT.check1);
6655
6656 18 return FRAMETIME;
6657 }
6658 static const char _data_FX_MODE_2DDRIFTROSE[] PROGMEM = "Drift Rose@Fade,Blur,,,,Smear;;!;2;pal=11";
6659
6660 /////////////////////////////
6661 // 2D PLASMA ROTOZOOMER //
6662 /////////////////////////////
6663 // Plasma Rotozoomer by ldirko (c)2020 [https://editor.soulmatelights.com/gallery/457-plasma-rotozoomer], adapted for WLED by Blaz Kristan (AKA blazoncek)
6664 23 uint16_t mode_2Dplasmarotozoom() {
6665
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
6666
6667 18 const int cols = SEG_W;
6668 18 const int rows = SEG_H;
6669
6670 18 unsigned dataSize = SEGMENT.length() + sizeof(float);
6671
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
6672 18 float *a = reinterpret_cast<float*>(SEGENV.data);
6673 18 byte *plasma = reinterpret_cast<byte*>(SEGENV.data+sizeof(float));
6674
6675 18 unsigned ms = strip().now/15;
6676
6677 // plasma
6678
2/2
✓ Branch 0 taken 223 times.
✓ Branch 1 taken 18 times.
241 for (int j = 0; j < rows; j++) {
6679 223 int index = j*cols;
6680
2/2
✓ Branch 0 taken 6761 times.
✓ Branch 1 taken 223 times.
6984 for (int i = 0; i < cols; i++) {
6681
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6761 times.
6761 if (SEGMENT.check1) plasma[index+i] = (i * 4 ^ j * 4) + ms / 6;
6682 6761 else plasma[index+i] = inoise8(i * 40, j * 40, ms);
6683 }
6684 }
6685
6686 // rotozoom
6687 18 float f = (sin_t(*a/2)+((128-SEGMENT.intensity)/128.0f)+1.1f)/1.5f; // scale factor
6688 18 float kosinus = cos_t(*a) * f;
6689 18 float sinus = sin_t(*a) * f;
6690
2/2
✓ Branch 0 taken 451 times.
✓ Branch 1 taken 18 times.
469 for (int i = 0; i < cols; i++) {
6691 451 float u1 = i * kosinus;
6692 451 float v1 = i * sinus;
6693
2/2
✓ Branch 0 taken 6761 times.
✓ Branch 1 taken 451 times.
7212 for (int j = 0; j < rows; j++) {
6694
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6761 times.
6761 byte u = abs8(u1 - j * sinus) % cols;
6695
2/2
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 6698 times.
6761 byte v = abs8(v1 + j * kosinus) % rows;
6696
2/4
✓ Branch 0 taken 6761 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6761 times.
6761 SEGMENT.setPixelColorXY(i, j, SEGMENT.color_from_palette(plasma[v*cols+u], false, PALETTE_SOLID_WRAP, 255));
6697 }
6698 }
6699 18 *a -= 0.03f + float(SEGENV.speed-128)*0.0002f; // rotation speed
6700
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if(*a < -6283.18530718f) *a += 6283.18530718f; // 1000*2*PI, protect sin/cos from very large input float values (will give wrong results)
6701
6702 18 return FRAMETIME;
6703 }
6704 static const char _data_FX_MODE_2DPLASMAROTOZOOM[] PROGMEM = "Rotozoomer@!,Scale,,,,Alt;;!;2;pal=54";
6705
6706 #endif // WLED_DISABLE_2D
6707
6708
6709 ///////////////////////////////////////////////////////////////////////////////
6710 /******************** audio enhanced routines ************************/
6711 ///////////////////////////////////////////////////////////////////////////////
6712
6713
6714 /////////////////////////////////
6715 // * Ripple Peak //
6716 /////////////////////////////////
6717 23 uint16_t mode_ripplepeak(void) { // * Ripple peak. By Andrew Tuline.
6718 // This currently has no controls.
6719 #define MAXSTEPS 16 // Case statement wouldn't allow a variable.
6720
6721 23 unsigned maxRipples = 16;
6722 23 unsigned dataSize = sizeof(Ripple) * maxRipples;
6723
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
6724 23 Ripple* ripples = reinterpret_cast<Ripple*>(SEGENV.data);
6725
6726 23 um_data_t *um_data = getAudioData();
6727 23 uint8_t samplePeak = *(uint8_t*)um_data->u_data[3];
6728 #ifdef ESP32
6729 float FFT_MajorPeak = *(float*) um_data->u_data[4];
6730 #endif
6731 23 uint8_t *maxVol = (uint8_t*)um_data->u_data[6];
6732 23 uint8_t *binNum = (uint8_t*)um_data->u_data[7];
6733
6734 // printUmData();
6735
6736
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if (SEGENV.call == 0) {
6737 5 SEGMENT.custom1 = *binNum;
6738 5 SEGMENT.custom2 = *maxVol * 2;
6739 }
6740
6741 23 *binNum = SEGMENT.custom1; // Select a bin.
6742 23 *maxVol = SEGMENT.custom2 / 2; // Our volume comparator.
6743
6744 23 SEGMENT.fade_out(240); // Lower frame rate means less effective fading than FastLED
6745 23 SEGMENT.fade_out(240);
6746
6747
2/2
✓ Branch 0 taken 180 times.
✓ Branch 1 taken 23 times.
203 for (int i = 0; i < SEGMENT.intensity/16; i++) { // Limit the number of ripples.
6748
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 149 times.
180 if (samplePeak) ripples[i].state = 255;
6749
6750
3/5
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
✓ Branch 2 taken 47 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 102 times.
180 switch (ripples[i].state) {
6751 ✗ case 254: // Inactive mode
6752 ✗ break;
6753
6754 31 case 255: // Initialize ripple variables.
6755 31 ripples[i].pos = hw_random16(SEGLEN);
6756 #ifdef ESP32
6757 if (FFT_MajorPeak > 1) // log10(0) is "forbidden" (throws exception)
6758 ripples[i].color = (int)(log10f(FFT_MajorPeak)*128);
6759 else ripples[i].color = 0;
6760 #else
6761 31 ripples[i].color = hw_random8();
6762 #endif
6763 31 ripples[i].state = 0;
6764 31 break;
6765
6766 47 case 0:
6767
2/4
✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 47 times.
47 SEGMENT.setPixelColor(ripples[i].pos, SEGMENT.color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0));
6768 47 ripples[i].state++;
6769 47 break;
6770
6771 ✗ case MAXSTEPS: // At the end of the ripples. 254 is an inactive mode.
6772 ✗ ripples[i].state = 254;
6773 ✗ break;
6774
6775 102 default: // Middle of the ripples.
6776
2/4
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 102 times.
102 SEGMENT.setPixelColor((ripples[i].pos + ripples[i].state + SEGLEN) % SEGLEN, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), uint8_t(2*255/ripples[i].state)));
6777
2/4
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 102 times.
102 SEGMENT.setPixelColor((ripples[i].pos - ripples[i].state + SEGLEN) % SEGLEN, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), uint8_t(2*255/ripples[i].state)));
6778 102 ripples[i].state++; // Next step.
6779 102 break;
6780 } // switch step
6781 } // for i
6782
6783 23 return FRAMETIME;
6784 } // mode_ripplepeak()
6785 static const char _data_FX_MODE_RIPPLEPEAK[] PROGMEM = "Ripple Peak@Fade rate,Max # of ripples,Select bin,Volume (min);!,!;!;1v;c2=0,m12=0,si=0"; // Pixel, Beatsin
6786
6787
6788 #ifndef WLED_DISABLE_2D
6789 /////////////////////////
6790 // * 2D Swirl //
6791 /////////////////////////
6792 // By: Mark Kriegsman https://gist.github.com/kriegsman/5adca44e14ad025e6d3b , modified by Andrew Tuline
6793 23 uint16_t mode_2DSwirl(void) {
6794
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
6795
6796 18 const int cols = SEG_W;
6797 18 const int rows = SEG_H;
6798
6799
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 14 times.
18 if (SEGENV.call == 0) {
6800 4 SEGMENT.fill(BLACK);
6801 }
6802
6803 18 const uint8_t borderWidth = 2;
6804
6805 18 SEGMENT.blur(SEGMENT.custom1);
6806
6807 18 int i = beatsin8_t( 27*SEGMENT.speed/255, borderWidth, cols - borderWidth);
6808 18 int j = beatsin8_t( 41*SEGMENT.speed/255, borderWidth, rows - borderWidth);
6809 18 int ni = (cols - 1) - i;
6810 18 int nj = (cols - 1) - j;
6811
6812 18 um_data_t *um_data = getAudioData();
6813 18 float volumeSmth = *(float*) um_data->u_data[0]; //ewowi: use instead of sampleAvg???
6814 18 int volumeRaw = *(int16_t*) um_data->u_data[1];
6815
6816 18 SEGMENT.addPixelColorXY( i, j, ColorFromPalette(SEGPALETTE, (strip().now / 11 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 11, 200, 255);
6817 18 SEGMENT.addPixelColorXY( j, i, ColorFromPalette(SEGPALETTE, (strip().now / 13 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 13, 200, 255);
6818 18 SEGMENT.addPixelColorXY(ni,nj, ColorFromPalette(SEGPALETTE, (strip().now / 17 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 17, 200, 255);
6819 18 SEGMENT.addPixelColorXY(nj,ni, ColorFromPalette(SEGPALETTE, (strip().now / 29 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 29, 200, 255);
6820 18 SEGMENT.addPixelColorXY( i,nj, ColorFromPalette(SEGPALETTE, (strip().now / 37 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 37, 200, 255);
6821 18 SEGMENT.addPixelColorXY(ni, j, ColorFromPalette(SEGPALETTE, (strip().now / 41 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 41, 200, 255);
6822
6823 18 return FRAMETIME;
6824 } // mode_2DSwirl()
6825 static const char _data_FX_MODE_2DSWIRL[] PROGMEM = "Swirl@!,Sensitivity,Blur;,Bg Swirl;!;2v;ix=64,si=0"; // Beatsin // TODO: color 1 unused?
6826
6827
6828 /////////////////////////
6829 // * 2D Waverly //
6830 /////////////////////////
6831 // By: Stepko, https://editor.soulmatelights.com/gallery/652-wave , modified by Andrew Tuline
6832 75 uint16_t mode_2DWaverly(void) {
6833
5/6
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 58 times.
✓ Branch 4 taken 17 times.
✓ Branch 5 taken 58 times.
75 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
6834
6835 58 const int cols = SEG_W;
6836 58 const int rows = SEG_H;
6837
6838 58 um_data_t *um_data = getAudioData();
6839 58 float volumeSmth = *(float*) um_data->u_data[0];
6840
6841 58 SEGMENT.fadeToBlackBy(SEGMENT.speed);
6842
6843 58 long t = strip().now / 2;
6844
2/2
✓ Branch 0 taken 1231 times.
✓ Branch 1 taken 58 times.
1289 for (int i = 0; i < cols; i++) {
6845 1231 unsigned thisVal = (1 + SEGMENT.intensity/64) * perlin8(i * 45 , t , t)/2;
6846 // use audio if available
6847
1/2
✓ Branch 0 taken 1231 times.
✗ Branch 1 not taken.
1231 if (um_data) {
6848 1231 thisVal /= 32; // reduce intensity of perlin8()
6849 1231 thisVal *= volumeSmth;
6850 }
6851 1231 int thisMax = map(thisVal, 0, 512, 0, rows);
6852
6853
2/2
✓ Branch 0 taken 24456 times.
✓ Branch 1 taken 1231 times.
25687 for (int j = 0; j < thisMax; j++) {
6854 24456 SEGMENT.addPixelColorXY(i, j, ColorFromPalette(SEGPALETTE, map(j, 0, thisMax, 250, 0), 255, LINEARBLEND));
6855 24456 SEGMENT.addPixelColorXY((cols - 1) - i, (rows - 1) - j, ColorFromPalette(SEGPALETTE, map(j, 0, thisMax, 250, 0), 255, LINEARBLEND));
6856 }
6857 }
6858
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
58 if (SEGMENT.check3) SEGMENT.blur(16, cols*rows < 100);
6859
6860 58 return FRAMETIME;
6861 } // mode_2DWaverly()
6862 static const char _data_FX_MODE_2DWAVERLY[] PROGMEM = "Waverly@Amplification,Sensitivity,,,,,Blur;;!;2v;ix=64,si=0"; // Beatsin
6863
6864 #endif // WLED_DISABLE_2D
6865
6866 // Gravity struct requited for GRAV* effects
6867 typedef struct Gravity {
6868 int topLED;
6869 int gravityCounter;
6870 } gravity;
6871
6872 ///////////////////////
6873 // * GRAVCENTER //
6874 ///////////////////////
6875 // Gravcenter effects By Andrew Tuline.
6876 // Gravcenter base function for Gravcenter (0), Gravcentric (1), Gravimeter (2), Gravfreq (3) (merged by @dedehai)
6877
6878 92 uint16_t mode_gravcenter_base(unsigned mode) {
6879
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 92 times.
92 if (SEGLEN == 1) return mode_static();
6880
6881 92 const unsigned dataSize = sizeof(gravity);
6882
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 92 times.
92 if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
6883 92 Gravity* gravcen = reinterpret_cast<Gravity*>(SEGENV.data);
6884
6885 92 um_data_t *um_data = getAudioData();
6886 92 float volumeSmth = *(float*) um_data->u_data[0];
6887
6888
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 69 times.
92 if(mode == 1) SEGMENT.fade_out(253); // //Gravcentric
6889
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 46 times.
69 else if(mode == 2) SEGMENT.fade_out(249); // Gravimeter
6890
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 23 times.
46 else if(mode == 3) SEGMENT.fade_out(250); // Gravfreq
6891 23 else SEGMENT.fade_out(251); // Gravcenter
6892
6893 float mySampleAvg;
6894 int tempsamp;
6895 92 float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0f;
6896
6897
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 69 times.
92 if(mode == 2) { //Gravimeter
6898 23 segmentSampleAvg *= 0.25; // divide by 4, to compensate for later "sensitivity" upscaling
6899 23 mySampleAvg = mapf(segmentSampleAvg*2.0, 0, 64, 0, (SEGLEN-1)); // map to pixels availeable in current segment
6900
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 19 times.
23 tempsamp = constrain(mySampleAvg,0,SEGLEN-1); // Keep the sample from overflowing.
6901 }
6902 else { // Gravcenter or Gravcentric or Gravfreq
6903 69 segmentSampleAvg *= 0.125f; // divide by 8, to compensate for later "sensitivity" upscaling
6904 69 mySampleAvg = mapf(segmentSampleAvg*2.0, 0.0f, 32.0f, 0.0f, (float)SEGLEN/2.0f); // map to pixels availeable in current segment
6905
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 69 times.
✓ Branch 2 taken 32 times.
✓ Branch 3 taken 37 times.
69 tempsamp = constrain(mySampleAvg, 0, SEGLEN/2); // Keep the sample from overflowing.
6906 }
6907
6908 92 uint8_t gravity = 8 - SEGMENT.speed/32;
6909 92 int offset = 1;
6910
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 69 times.
92 if(mode == 2) offset = 0; // Gravimeter
6911
2/2
✓ Branch 0 taken 69 times.
✓ Branch 1 taken 23 times.
92 if (tempsamp >= gravcen->topLED + offset) gravcen->topLED = tempsamp-offset;
6912
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 14 times.
23 else if (gravcen->gravityCounter % gravity == 0) gravcen->topLED--;
6913
6914
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 69 times.
92 if(mode == 1) { //Gravcentric
6915
2/2
✓ Branch 0 taken 520 times.
✓ Branch 1 taken 23 times.
543 for (int i=0; i<tempsamp; i++) {
6916 520 uint8_t index = segmentSampleAvg*24+strip().now/200;
6917
2/4
✓ Branch 0 taken 520 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 520 times.
520 SEGMENT.setPixelColor(i+SEGLEN/2, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0));
6918
2/4
✓ Branch 0 taken 520 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 520 times.
520 SEGMENT.setPixelColor(SEGLEN/2-1-i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0));
6919 }
6920
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 4 times.
23 if (gravcen->topLED >= 0) {
6921 19 SEGMENT.setPixelColor(gravcen->topLED+SEGLEN/2, CRGB::Gray);
6922 19 SEGMENT.setPixelColor(SEGLEN/2-1-gravcen->topLED, CRGB::Gray);
6923 }
6924 }
6925
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 46 times.
69 else if(mode == 2) { //Gravimeter
6926
2/2
✓ Branch 0 taken 892 times.
✓ Branch 1 taken 23 times.
915 for (int i=0; i<tempsamp; i++) {
6927 892 uint8_t index = perlin8(i*segmentSampleAvg+strip().now, 5000+i*segmentSampleAvg);
6928
2/4
✓ Branch 0 taken 892 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 892 times.
892 SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0), uint8_t(segmentSampleAvg*8)));
6929 }
6930
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 4 times.
23 if (gravcen->topLED > 0) {
6931
2/4
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 19 times.
19 SEGMENT.setPixelColor(gravcen->topLED, SEGMENT.color_from_palette(strip().now, false, PALETTE_SOLID_WRAP, 0));
6932 }
6933 }
6934
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 23 times.
46 else if(mode == 3) { //Gravfreq
6935
2/2
✓ Branch 0 taken 2512 times.
✓ Branch 1 taken 23 times.
2535 for (int i=0; i<tempsamp; i++) {
6936 2512 float FFT_MajorPeak = *(float*)um_data->u_data[4]; // used in mode 3: Gravfreq
6937
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2512 times.
2512 if (FFT_MajorPeak < 1) FFT_MajorPeak = 1;
6938 2512 uint8_t index = (log10f(FFT_MajorPeak) - (MAX_FREQ_LOG10 - 1.78f)) * 255;
6939
2/4
✓ Branch 0 taken 2512 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2512 times.
2512 SEGMENT.setPixelColor(i+SEGLEN/2, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0));
6940
2/4
✓ Branch 0 taken 2512 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2512 times.
2512 SEGMENT.setPixelColor(SEGLEN/2-i-1, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0));
6941 }
6942
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 4 times.
23 if (gravcen->topLED >= 0) {
6943 19 SEGMENT.setPixelColor(gravcen->topLED+SEGLEN/2, CRGB::Gray);
6944 19 SEGMENT.setPixelColor(SEGLEN/2-1-gravcen->topLED, CRGB::Gray);
6945 }
6946 }
6947 else { //Gravcenter
6948
2/2
✓ Branch 0 taken 536 times.
✓ Branch 1 taken 23 times.
559 for (int i=0; i<tempsamp; i++) {
6949 536 uint8_t index = perlin8(i*segmentSampleAvg+strip().now, 5000+i*segmentSampleAvg);
6950
2/4
✓ Branch 0 taken 536 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 536 times.
536 SEGMENT.setPixelColor(i+SEGLEN/2, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0), uint8_t(segmentSampleAvg*8)));
6951
2/4
✓ Branch 0 taken 536 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 536 times.
536 SEGMENT.setPixelColor(SEGLEN/2-i-1, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0), uint8_t(segmentSampleAvg*8)));
6952 }
6953
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 4 times.
23 if (gravcen->topLED >= 0) {
6954
2/4
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 19 times.
19 SEGMENT.setPixelColor(gravcen->topLED+SEGLEN/2, SEGMENT.color_from_palette(strip().now, false, PALETTE_SOLID_WRAP, 0));
6955
2/4
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 19 times.
19 SEGMENT.setPixelColor(SEGLEN/2-1-gravcen->topLED, SEGMENT.color_from_palette(strip().now, false, PALETTE_SOLID_WRAP, 0));
6956 }
6957 }
6958 92 gravcen->gravityCounter = (gravcen->gravityCounter + 1) % gravity;
6959
6960 92 return FRAMETIME;
6961 }
6962
6963 23 uint16_t mode_gravcenter(void) { // Gravcenter. By Andrew Tuline.
6964 23 return mode_gravcenter_base(0);
6965 }
6966 static const char _data_FX_MODE_GRAVCENTER[] PROGMEM = "Gravcenter@Rate of fall,Sensitivity;!,!;!;1v;ix=128,m12=2,si=0"; // Circle, Beatsin
6967
6968 ///////////////////////
6969 // * GRAVCENTRIC //
6970 ///////////////////////
6971 23 uint16_t mode_gravcentric(void) { // Gravcentric. By Andrew Tuline.
6972 23 return mode_gravcenter_base(1);
6973 }
6974 static const char _data_FX_MODE_GRAVCENTRIC[] PROGMEM = "Gravcentric@Rate of fall,Sensitivity;!,!;!;1v;ix=128,m12=3,si=0"; // Corner, Beatsin
6975
6976
6977 ///////////////////////
6978 // * GRAVIMETER //
6979 ///////////////////////
6980 23 uint16_t mode_gravimeter(void) { // Gravmeter. By Andrew Tuline.
6981 23 return mode_gravcenter_base(2);
6982 }
6983 static const char _data_FX_MODE_GRAVIMETER[] PROGMEM = "Gravimeter@Rate of fall,Sensitivity;!,!;!;1v;ix=128,m12=2,si=0"; // Circle, Beatsin
6984
6985
6986 ///////////////////////
6987 // ** Gravfreq //
6988 ///////////////////////
6989 23 uint16_t mode_gravfreq(void) { // Gravfreq. By Andrew Tuline.
6990 23 return mode_gravcenter_base(3);
6991 }
6992 static const char _data_FX_MODE_GRAVFREQ[] PROGMEM = "Gravfreq@Rate of fall,Sensitivity;!,!;!;1f;ix=128,m12=0,si=0"; // Pixels, Beatsin
6993
6994
6995 //////////////////////
6996 // * JUGGLES //
6997 //////////////////////
6998 23 uint16_t mode_juggles(void) { // Juggles. By Andrew Tuline.
6999 23 um_data_t *um_data = getAudioData();
7000 23 float volumeSmth = *(float*) um_data->u_data[0];
7001
7002 23 SEGMENT.fade_out(224); // 6.25%
7003 23 uint8_t my_sampleAgc = fmax(fmin(volumeSmth, 255.0), 0);
7004
7005
2/2
✓ Branch 0 taken 111 times.
✓ Branch 1 taken 23 times.
134 for (size_t i=0; i<SEGMENT.intensity/32+1U; i++) {
7006 // if SEGLEN equals 1, we will always set color to the first and only pixel, but the effect is still good looking
7007
2/4
✓ Branch 0 taken 111 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 111 times.
111 SEGMENT.setPixelColor(beatsin16_t(SEGMENT.speed/4+i*2,0,SEGLEN-1), color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(strip().now/4+i*2, false, PALETTE_SOLID_WRAP, 0), my_sampleAgc));
7008 }
7009
7010 23 return FRAMETIME;
7011 } // mode_juggles()
7012 static const char _data_FX_MODE_JUGGLES[] PROGMEM = "Juggles@!,# of balls;!,!;!;01v;m12=0,si=0"; // Pixels, Beatsin
7013
7014
7015 //////////////////////
7016 // * MATRIPIX //
7017 //////////////////////
7018 23 uint16_t mode_matripix(void) { // Matripix. By Andrew Tuline.
7019 // effect can work on single pixels, we just lose the shifting effect
7020 23 unsigned dataSize = sizeof(uint32_t) * SEGLEN;
7021
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
7022 23 uint32_t* pixels = reinterpret_cast<uint32_t*>(SEGENV.data);
7023
7024 23 um_data_t *um_data = getAudioData();
7025 23 int volumeRaw = *(int16_t*)um_data->u_data[1];
7026
7027
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if (SEGENV.call == 0) {
7028
2/2
✓ Branch 0 taken 262 times.
✓ Branch 1 taken 5 times.
267 for (unsigned i = 0; i < SEGLEN; i++) pixels[i] = BLACK; // may not be needed as resetIfRequired() clears buffer
7029 }
7030
7031 23 uint8_t secondHand = micros()/(256-SEGMENT.speed)/500 % 16;
7032
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 6 times.
23 if(SEGENV.aux0 != secondHand) {
7033 17 SEGENV.aux0 = secondHand;
7034
7035 17 int pixBri = volumeRaw * SEGMENT.intensity / 64;
7036 17 unsigned k = SEGLEN-1;
7037 // loop will not execute if SEGLEN equals 1
7038
2/2
✓ Branch 0 taken 926 times.
✓ Branch 1 taken 17 times.
943 for (unsigned i = 0; i < k; i++) {
7039 926 pixels[i] = pixels[i+1]; // shift left
7040 926 SEGMENT.setPixelColor(i, pixels[i]);
7041 }
7042
2/4
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 17 times.
17 pixels[k] = color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(strip().now, false, PALETTE_SOLID_WRAP, 0), pixBri);
7043 17 SEGMENT.setPixelColor(k, pixels[k]);
7044 }
7045
7046 23 return FRAMETIME;
7047 } // mode_matripix()
7048 static const char _data_FX_MODE_MATRIPIX[] PROGMEM = "Matripix@!,Brightness;!,!;!;1v;ix=64,m12=2,si=1"; //,rev=1,mi=1,rY=1,mY=1 Circle, WeWillRockYou, reverseX
7049
7050
7051 //////////////////////
7052 // * MIDNOISE //
7053 //////////////////////
7054 23 uint16_t mode_midnoise(void) { // Midnoise. By Andrew Tuline.
7055
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
7056 // Changing xdist to SEGENV.aux0 and ydist to SEGENV.aux1.
7057
7058 23 um_data_t *um_data = getAudioData();
7059 23 float volumeSmth = *(float*) um_data->u_data[0];
7060
7061 23 SEGMENT.fade_out(SEGMENT.speed);
7062 23 SEGMENT.fade_out(SEGMENT.speed);
7063
7064 23 float tmpSound2 = volumeSmth * (float)SEGMENT.intensity / 256.0; // Too sensitive.
7065 23 tmpSound2 *= (float)SEGMENT.intensity / 128.0; // Reduce sensitivity/length.
7066
7067 23 unsigned maxLen = mapf(tmpSound2, 0, 127, 0, SEGLEN/2);
7068
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 19 times.
23 if (maxLen >SEGLEN/2) maxLen = SEGLEN/2;
7069
7070
2/2
✓ Branch 0 taken 468 times.
✓ Branch 1 taken 23 times.
491 for (unsigned i=(SEGLEN/2-maxLen); i<(SEGLEN/2+maxLen); i++) {
7071 468 uint8_t index = perlin8(i*volumeSmth+SEGENV.aux0, SEGENV.aux1+i*volumeSmth); // Get a value from the noise function. I'm using both x and y axis.
7072
2/4
✓ Branch 0 taken 468 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 468 times.
468 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0));
7073 }
7074
7075 23 SEGENV.aux0=SEGENV.aux0+beatsin8_t(5,0,10);
7076 23 SEGENV.aux1=SEGENV.aux1+beatsin8_t(4,0,10);
7077
7078 23 return FRAMETIME;
7079 } // mode_midnoise()
7080 static const char _data_FX_MODE_MIDNOISE[] PROGMEM = "Midnoise@Fade rate,Max. length;!,!;!;1v;ix=128,m12=1,si=0"; // Bar, Beatsin
7081
7082
7083 //////////////////////
7084 // * NOISEFIRE //
7085 //////////////////////
7086 // I am the god of hellfire. . . Volume (only) reactive fire routine. Oh, look how short this is.
7087 23 uint16_t mode_noisefire(void) { // Noisefire. By Andrew Tuline.
7088 ✗ CRGBPalette16 myPal = CRGBPalette16(CHSV(0,255,2), CHSV(0,255,4), CHSV(0,255,8), CHSV(0, 255, 8), // Fire palette definition. Lower value = darker.
7089 ✗ CHSV(0, 255, 16), CRGB::Red, CRGB::Red, CRGB::Red,
7090 CRGB::DarkOrange, CRGB::DarkOrange, CRGB::Orange, CRGB::Orange,
7091 46 CRGB::Yellow, CRGB::Orange, CRGB::Yellow, CRGB::Yellow);
7092
7093
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 um_data_t *um_data = getAudioData();
7094 23 float volumeSmth = *(float*) um_data->u_data[0];
7095
7096
7/12
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
✓ Branch 6 taken 5 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 5 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 5 times.
✗ Branch 11 not taken.
23 if (SEGENV.call == 0) SEGMENT.fill(BLACK);
7097
7098
2/2
✓ Branch 0 taken 1240 times.
✓ Branch 1 taken 23 times.
1263 for (unsigned i = 0; i < SEGLEN; i++) {
7099
6/12
✓ Branch 0 taken 1240 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1240 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1240 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1240 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1240 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1240 times.
✗ Branch 11 not taken.
1240 unsigned index = perlin8(i*SEGMENT.speed/64,strip().now*SEGMENT.speed/64*SEGLEN/255); // X location is constant, but we move along the Y at the rate of millis(). By Andrew Tuline.
7100
2/4
✓ Branch 0 taken 1240 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1240 times.
✗ Branch 3 not taken.
1240 index = (255 - i*256/SEGLEN) * index/(256-SEGMENT.intensity); // Now we need to scale index so that it gets blacker as we get close to one of the ends.
7101 // This is a simple y=mx+b equation that's been scaled. index/128 is another scaling.
7102
7103
4/8
✓ Branch 0 taken 1240 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1240 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1240 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1240 times.
✗ Branch 7 not taken.
1240 SEGMENT.setPixelColor(i, ColorFromPalette(myPal, index, volumeSmth*2, LINEARBLEND)); // Use my own palette.
7104 }
7105
7106
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
46 return FRAMETIME;
7107 } // mode_noisefire()
7108 static const char _data_FX_MODE_NOISEFIRE[] PROGMEM = "Noisefire@!,!;;;01v;m12=2,si=0"; // Circle, Beatsin
7109
7110
7111 ///////////////////////
7112 // * Noisemeter //
7113 ///////////////////////
7114 23 uint16_t mode_noisemeter(void) { // Noisemeter. By Andrew Tuline.
7115
7116 23 um_data_t *um_data = getAudioData();
7117 23 float volumeSmth = *(float*) um_data->u_data[0];
7118 23 int volumeRaw = *(int16_t*)um_data->u_data[1];
7119
7120 //uint8_t fadeRate = map(SEGMENT.speed,0,255,224,255);
7121 23 uint8_t fadeRate = map(SEGMENT.speed,0,255,200,254);
7122 23 SEGMENT.fade_out(fadeRate);
7123
7124 23 float tmpSound2 = volumeRaw * 2.0 * (float)SEGMENT.intensity / 255.0;
7125 23 unsigned maxLen = mapf(tmpSound2, 0, 255, 0, SEGLEN); // map to pixels availeable in current segment // Still a bit too sensitive.
7126 if (maxLen < 0) maxLen = 0;
7127
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (maxLen > SEGLEN) maxLen = SEGLEN;
7128
7129
2/2
✓ Branch 0 taken 237 times.
✓ Branch 1 taken 23 times.
260 for (unsigned i=0; i<maxLen; i++) { // The louder the sound, the wider the soundbar. By Andrew Tuline.
7130 237 uint8_t index = perlin8(i*volumeSmth+SEGENV.aux0, SEGENV.aux1+i*volumeSmth); // Get a value from the noise function. I'm using both x and y axis.
7131
2/4
✓ Branch 0 taken 237 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 237 times.
237 SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0));
7132 }
7133
7134 23 SEGENV.aux0+=beatsin8_t(5,0,10);
7135 23 SEGENV.aux1+=beatsin8_t(4,0,10);
7136
7137 23 return FRAMETIME;
7138 } // mode_noisemeter()
7139 static const char _data_FX_MODE_NOISEMETER[] PROGMEM = "Noisemeter@Fade rate,Width;!,!;!;1v;ix=128,m12=2,si=0"; // Circle, Beatsin
7140
7141
7142 //////////////////////
7143 // * PIXELWAVE //
7144 //////////////////////
7145 23 uint16_t mode_pixelwave(void) { // Pixelwave. By Andrew Tuline.
7146
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
7147 // even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment
7148
7149
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if (SEGENV.call == 0) {
7150 5 SEGMENT.fill(BLACK);
7151 }
7152
7153 23 um_data_t *um_data = getAudioData();
7154 23 int volumeRaw = *(int16_t*)um_data->u_data[1];
7155
7156 23 uint8_t secondHand = micros()/(256-SEGMENT.speed)/500+1 % 16;
7157
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 4 times.
23 if (SEGENV.aux0 != secondHand) {
7158 19 SEGENV.aux0 = secondHand;
7159
7160 19 uint8_t pixBri = volumeRaw * SEGMENT.intensity / 64;
7161
7162
2/4
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 19 times.
19 SEGMENT.setPixelColor(SEGLEN/2, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(strip().now, false, PALETTE_SOLID_WRAP, 0), pixBri));
7163
2/2
✓ Branch 0 taken 495 times.
✓ Branch 1 taken 19 times.
514 for (unsigned i = SEGLEN - 1; i > SEGLEN/2; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left
7164
2/2
✓ Branch 0 taken 499 times.
✓ Branch 1 taken 19 times.
518 for (unsigned i = 0; i < SEGLEN/2; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right
7165 }
7166
7167 23 return FRAMETIME;
7168 } // mode_pixelwave()
7169 static const char _data_FX_MODE_PIXELWAVE[] PROGMEM = "Pixelwave@!,Sensitivity;!,!;!;1v;ix=64,m12=2,si=0"; // Circle, Beatsin
7170
7171
7172 //////////////////////
7173 // * PLASMOID //
7174 //////////////////////
7175 typedef struct Plasphase {
7176 int16_t thisphase;
7177 int16_t thatphase;
7178 } plasphase;
7179
7180 23 uint16_t mode_plasmoid(void) { // Plasmoid. By Andrew Tuline.
7181 // even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment
7182
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (!SEGENV.allocateData(sizeof(plasphase))) return mode_static(); //allocation failed
7183 23 Plasphase* plasmoip = reinterpret_cast<Plasphase*>(SEGENV.data);
7184
7185 23 um_data_t *um_data = getAudioData();
7186 23 float volumeSmth = *(float*) um_data->u_data[0];
7187
7188 23 SEGMENT.fadeToBlackBy(32);
7189
7190 23 plasmoip->thisphase += beatsin8_t(6,-4,4); // You can change direction and speed individually.
7191 23 plasmoip->thatphase += beatsin8_t(7,-4,4); // Two phase values to make a complex pattern. By Andrew Tuline.
7192
7193
2/2
✓ Branch 0 taken 7511 times.
✓ Branch 1 taken 23 times.
7534 for (unsigned i = 0; i < SEGLEN; i++) { // For each of the LED's in the strand, set a brightness based on a wave as follows.
7194 // updated, similar to "plasma" effect - softhack007
7195 7511 uint8_t thisbright = cubicwave8(((i*(1 + (3*SEGMENT.speed/32)))+plasmoip->thisphase) & 0xFF)/2;
7196 7511 thisbright += cos8_t(((i*(97 +(5*SEGMENT.speed/32)))+plasmoip->thatphase) & 0xFF)/2; // Let's munge the brightness a bit and animate it all with the phases.
7197
7198 7511 uint8_t colorIndex=thisbright;
7199
2/2
✓ Branch 0 taken 6928 times.
✓ Branch 1 taken 583 times.
7511 if (volumeSmth * SEGMENT.intensity / 64 < thisbright) {thisbright = 0;}
7200
7201
2/4
✓ Branch 0 taken 7511 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7511 times.
7511 SEGMENT.addPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0), thisbright));
7202 }
7203
7204 23 return FRAMETIME;
7205 } // mode_plasmoid()
7206 static const char _data_FX_MODE_PLASMOID[] PROGMEM = "Plasmoid@Phase,# of pixels;!,!;!;01v;sx=128,ix=128,m12=0,si=0"; // Pixels, Beatsin
7207
7208
7209 //////////////////////
7210 // * PUDDLES //
7211 //////////////////////
7212 // Puddles/Puddlepeak By Andrew Tuline. Merged by @dedehai
7213 46 uint16_t mode_puddles_base(bool peakdetect) {
7214
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
46 if (SEGLEN <= 1) return mode_static();
7215 46 unsigned size = 0;
7216 46 uint8_t fadeVal = map(SEGMENT.speed, 0, 255, 224, 254);
7217 46 unsigned pos = hw_random16(SEGLEN); // Set a random starting position.
7218 46 SEGMENT.fade_out(fadeVal);
7219
7220 46 um_data_t *um_data = getAudioData();
7221 46 int volumeRaw = *(int16_t*)um_data->u_data[1];
7222 46 uint8_t samplePeak = *(uint8_t*)um_data->u_data[3];
7223 46 uint8_t *maxVol = (uint8_t*)um_data->u_data[6];
7224 46 uint8_t *binNum = (uint8_t*)um_data->u_data[7];
7225 46 float volumeSmth = *(float*) um_data->u_data[0];
7226
7227
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 23 times.
46 if(peakdetect) { // puddles peak
7228 23 *binNum = SEGMENT.custom1; // Select a bin.
7229 23 *maxVol = SEGMENT.custom2 / 2; // Our volume comparator.
7230
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 22 times.
23 if (samplePeak == 1) {
7231 1 size = volumeSmth * SEGMENT.intensity /256 /4 + 1; // Determine size of the flash based on the volume.
7232
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (pos+size>= SEGLEN) size = SEGLEN - pos;
7233 }
7234 }
7235 else { // puddles
7236
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 if (volumeRaw > 1) {
7237 23 size = volumeRaw * SEGMENT.intensity /256 /8 + 1; // Determine size of the flash based on the volume.
7238
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 22 times.
23 if (pos+size >= SEGLEN) size = SEGLEN - pos;
7239 }
7240 }
7241
7242
2/2
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 46 times.
142 for (unsigned i=0; i<size; i++) { // Flash the LED's.
7243
2/4
✓ Branch 0 taken 96 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 96 times.
96 SEGMENT.setPixelColor(pos+i, SEGMENT.color_from_palette(strip().now, false, PALETTE_SOLID_WRAP, 0));
7244 }
7245
7246 46 return FRAMETIME;
7247 }
7248
7249 23 uint16_t mode_puddlepeak(void) { // Puddlepeak. By Andrew Tuline.
7250 23 return mode_puddles_base(true);
7251 }
7252 static const char _data_FX_MODE_PUDDLEPEAK[] PROGMEM = "Puddlepeak@Fade rate,Puddle size,Select bin,Volume (min);!,!;!;1v;c2=0,m12=0,si=0"; // Pixels, Beatsin
7253
7254 23 uint16_t mode_puddles(void) { // Puddles. By Andrew Tuline.
7255 23 return mode_puddles_base(false);
7256 }
7257 static const char _data_FX_MODE_PUDDLES[] PROGMEM = "Puddles@Fade rate,Puddle size;!,!;!;1v;m12=0,si=0"; // Pixels, Beatsin
7258
7259
7260 //////////////////////
7261 // * PIXELS //
7262 //////////////////////
7263 23 uint16_t mode_pixels(void) { // Pixels. By Andrew Tuline.
7264
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
23 if (SEGLEN <= 1) return mode_static();
7265
7266
4/10
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 23 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
23 if (!SEGENV.allocateData(32*sizeof(uint8_t))) return mode_static(); //allocation failed
7267
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 uint8_t *myVals = reinterpret_cast<uint8_t*>(SEGENV.data); // Used to store a pile of samples because WLED frame rate and WLED sample rate are not synchronized. Frame rate is too low.
7268
7269 um_data_t *um_data;
7270
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 if (!UsermodManager::getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
7271
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 um_data = simulateSound(SEGMENT.soundSim);
7272 }
7273 23 float volumeSmth = *(float*) um_data->u_data[0];
7274
7275
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 myVals[strip().now%32] = volumeSmth; // filling values semi randomly
7276
7277
5/10
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 23 times.
✗ Branch 9 not taken.
23 SEGMENT.fade_out(64+(SEGMENT.speed>>1));
7278
7279
4/6
✓ Branch 0 taken 387 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 387 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 364 times.
✓ Branch 5 taken 23 times.
387 for (int i=0; i <SEGMENT.intensity/8; i++) {
7280 364 unsigned segLoc = hw_random16(SEGLEN); // 16 bit for larger strands of LED's.
7281
11/22
✓ Branch 0 taken 364 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 364 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 364 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 364 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 364 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 364 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 364 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 364 times.
✓ Branch 16 taken 364 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 364 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 364 times.
✗ Branch 21 not taken.
364 SEGMENT.setPixelColor(segLoc, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(myVals[i%32]+i*4, false, PALETTE_SOLID_WRAP, 0), uint8_t(volumeSmth)));
7282 }
7283
7284
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 return FRAMETIME;
7285 } // mode_pixels()
7286 static const char _data_FX_MODE_PIXELS[] PROGMEM = "Pixels@Fade rate,# of pixels;!,!;!;1v;m12=0,si=0"; // Pixels, Beatsin
7287
7288 //////////////////////
7289 // ** Blurz //
7290 //////////////////////
7291 23 uint16_t mode_blurz(void) { // Blurz. By Andrew Tuline.
7292
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
7293 // even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment
7294
7295 23 um_data_t *um_data = getAudioData();
7296 23 uint8_t *fftResult = (uint8_t*)um_data->u_data[2];
7297
7298
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if (SEGENV.call == 0) {
7299 5 SEGMENT.fill(BLACK);
7300 5 SEGENV.aux0 = 0;
7301 }
7302
7303 23 int fadeoutDelay = (256 - SEGMENT.speed) / 32;
7304
6/6
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 12 times.
✓ Branch 4 taken 11 times.
✓ Branch 5 taken 12 times.
23 if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fade_out(SEGMENT.speed);
7305
7306 23 SEGENV.step += FRAMETIME;
7307
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 10 times.
23 if (SEGENV.step > SPEED_FORMULA_L) {
7308 13 unsigned segLoc = hw_random16(SEGLEN);
7309
2/4
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 13 times.
13 SEGMENT.setPixelColor(segLoc, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(2*fftResult[SEGENV.aux0%16]*240/max(1, (int)SEGLEN-1), false, PALETTE_SOLID_WRAP, 0), uint8_t(2*fftResult[SEGENV.aux0%16])));
7310 13 ++(SEGENV.aux0) %= 16; // make sure it doesn't cross 16
7311
7312 13 SEGENV.step = 1;
7313 13 SEGMENT.blur(SEGMENT.intensity); // note: blur > 210 results in a alternating pattern, this could be fixed by mapping but some may like it (very old bug)
7314 }
7315
7316 23 return FRAMETIME;
7317 } // mode_blurz()
7318 static const char _data_FX_MODE_BLURZ[] PROGMEM = "Blurz@Fade rate,Blur;!,Color mix;!;1f;m12=0,si=0"; // Pixels, Beatsin
7319
7320
7321 /////////////////////////
7322 // ** DJLight //
7323 /////////////////////////
7324 23 uint16_t mode_DJLight(void) { // Written by ??? Adapted by Will Tatam.
7325 // No need to prevent from executing on single led strips, only mid will be set (mid = 0)
7326 23 const int mid = SEGLEN / 2;
7327
7328 23 um_data_t *um_data = getAudioData();
7329 23 uint8_t *fftResult = (uint8_t*)um_data->u_data[2];
7330
7331
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if (SEGENV.call == 0) {
7332 5 SEGMENT.fill(BLACK);
7333 }
7334
7335 23 uint8_t secondHand = micros()/(256-SEGMENT.speed)/500+1 % 64;
7336
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 4 times.
23 if (SEGENV.aux0 != secondHand) { // Triggered millis timing.
7337
2/4
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
19 SEGENV.aux0 = secondHand;
7338
7339 19 CRGB color = CRGB(fftResult[15]/2, fftResult[5]/2, fftResult[0]/2); // 16-> 15 as 16 is out of bounds
7340
4/8
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 19 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 19 times.
✗ Branch 7 not taken.
19 SEGMENT.setPixelColor(mid, color.fadeToBlackBy(map(fftResult[4], 0, 255, 255, 4))); // TODO - Update
7341
7342 // if SEGLEN equals 1 these loops won't execute
7343
8/14
✓ Branch 0 taken 481 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 481 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 481 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 481 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 481 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 481 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 481 times.
✓ Branch 13 taken 19 times.
500 for (int i = SEGLEN - 1; i > mid; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); // move to the left
7344
8/14
✓ Branch 0 taken 485 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 485 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 485 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 485 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 485 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 485 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 485 times.
✓ Branch 13 taken 19 times.
504 for (int i = 0; i < mid; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right
7345 }
7346
7347 23 return FRAMETIME;
7348 } // mode_DJLight()
7349 static const char _data_FX_MODE_DJLIGHT[] PROGMEM = "DJ Light@Speed;;;01f;m12=2,si=0"; // Circle, Beatsin
7350
7351
7352 ////////////////////
7353 // ** Freqmap //
7354 ////////////////////
7355 23 uint16_t mode_freqmap(void) { // Map FFT_MajorPeak to SEGLEN. Would be better if a higher framerate.
7356
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN <= 1) return mode_static();
7357 // Start frequency = 60 Hz and log10(60) = 1.78
7358 // End frequency = MAX_FREQUENCY in Hz and lo10(MAX_FREQUENCY) = MAX_FREQ_LOG10
7359
7360 23 um_data_t *um_data = getAudioData();
7361 23 float FFT_MajorPeak = *(float*)um_data->u_data[4];
7362 23 float my_magnitude = *(float*)um_data->u_data[5] / 4.0f;
7363
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception)
7364
7365
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if (SEGENV.call == 0) SEGMENT.fill(BLACK);
7366 23 int fadeoutDelay = (256 - SEGMENT.speed) / 32;
7367
6/6
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 12 times.
✓ Branch 4 taken 11 times.
✓ Branch 5 taken 12 times.
23 if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fade_out(SEGMENT.speed);
7368
7369 23 int locn = (log10f((float)FFT_MajorPeak) - 1.78f) * (float)SEGLEN/(MAX_FREQ_LOG10 - 1.78f); // log10 frequency range is from 1.78 to 3.71. Let's scale to SEGLEN.
7370
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 22 times.
23 if (locn < 1) locn = 0; // avoid underflow
7371
7372
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (locn >= (int)SEGLEN) locn = SEGLEN-1;
7373 23 unsigned pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(MAX_FREQ_LOG10 - 1.78f); // Scale log10 of frequency values to the 255 colour index.
7374
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 22 times.
23 if (FFT_MajorPeak < 61.0f) pixCol = 0; // handle underflow
7375
7376 23 uint8_t bright = (uint8_t)my_magnitude;
7377
7378
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 23 times.
23 SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(SEGMENT.intensity+pixCol, false, PALETTE_SOLID_WRAP, 0), bright));
7379
7380 23 return FRAMETIME;
7381 } // mode_freqmap()
7382 static const char _data_FX_MODE_FREQMAP[] PROGMEM = "Freqmap@Fade rate,Starting color;!,!;!;1f;m12=0,si=0"; // Pixels, Beatsin
7383
7384
7385 ///////////////////////
7386 // ** Freqmatrix //
7387 ///////////////////////
7388 143 uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Pleschung.
7389 // No need to prevent from executing on single led strips, we simply change pixel 0 each time and avoid the shift
7390 143 um_data_t *um_data = getAudioData();
7391 143 float FFT_MajorPeak = *(float*)um_data->u_data[4];
7392 143 float volumeSmth = *(float*)um_data->u_data[0];
7393
7394
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 134 times.
143 if (SEGENV.call == 0) {
7395 9 SEGMENT.fill(BLACK);
7396 }
7397
7398 143 uint8_t secondHand = micros()/(256-SEGMENT.speed)/500 % 16;
7399
2/2
✓ Branch 0 taken 138 times.
✓ Branch 1 taken 5 times.
143 if(SEGENV.aux0 != secondHand) {
7400
2/4
✓ Branch 0 taken 138 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 138 times.
✗ Branch 3 not taken.
138 SEGENV.aux0 = secondHand;
7401
7402
2/4
✓ Branch 0 taken 138 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 138 times.
✗ Branch 3 not taken.
138 uint8_t sensitivity = map(SEGMENT.custom3, 0, 31, 1, 10); // reduced resolution slider
7403
2/4
✓ Branch 0 taken 138 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 138 times.
✗ Branch 3 not taken.
138 int pixVal = (volumeSmth * SEGMENT.intensity * sensitivity) / 256.0f;
7404
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 129 times.
138 if (pixVal > 255) pixVal = 255;
7405
7406 138 float intensity = map(pixVal, 0, 255, 0, 100) / 100.0f; // make a brightness from the last avg
7407
7408 138 CRGB color = CRGB::Black;
7409
7410
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 138 times.
138 if (FFT_MajorPeak > MAX_FREQUENCY) FFT_MajorPeak = 1;
7411 // MajorPeak holds the freq. value which is most abundant in the last sample.
7412 // With our sampling rate of 10240Hz we have a usable freq range from roughly 80Hz to 10240/2 Hz
7413 // we will treat everything with less than 65Hz as 0
7414
7415
2/2
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 98 times.
138 if (FFT_MajorPeak < 80) {
7416 color = CRGB::Black;
7417 } else {
7418
2/4
✓ Branch 0 taken 98 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
98 int upperLimit = 80 + 42 * SEGMENT.custom2;
7419
2/4
✓ Branch 0 taken 98 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
98 int lowerLimit = 80 + 3 * SEGMENT.custom1;
7420
2/2
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 2 times.
98 uint8_t i = lowerLimit!=upperLimit ? map(FFT_MajorPeak, lowerLimit, upperLimit, 0, 255) : FFT_MajorPeak; // may under/overflow - so we enforce uint8_t
7421 98 unsigned b = 255 * intensity;
7422
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 98 times.
98 if (b > 255) b = 255;
7423 196 color = CHSV(i, 240, (uint8_t)b); // implicit conversion to RGB supplied by FastLED
7424 }
7425
7426 // shift the pixels one pixel up
7427
3/6
✓ Branch 0 taken 138 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 138 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 138 times.
✗ Branch 5 not taken.
138 SEGMENT.setPixelColor(0, color);
7428 // if SEGLEN equals 1 this loop won't execute
7429
8/14
✓ Branch 0 taken 2010 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2010 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2010 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2010 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2010 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2010 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 2010 times.
✓ Branch 13 taken 138 times.
2148 for (int i = SEGLEN - 1; i > 0; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left
7430 }
7431
7432 143 return FRAMETIME;
7433 } // mode_freqmatrix()
7434 static const char _data_FX_MODE_FREQMATRIX[] PROGMEM = "Freqmatrix@Speed,Sound effect,Low bin,High bin,Sensitivity;;;01f;m12=3,si=0"; // Corner, Beatsin
7435
7436
7437 //////////////////////
7438 // ** Freqpixels //
7439 //////////////////////
7440 // Start frequency = 60 Hz and log10(60) = 1.78
7441 // End frequency = 5120 Hz and lo10(5120) = 3.71
7442 // SEGMENT.speed select faderate
7443 // SEGMENT.intensity select colour index
7444 23 uint16_t mode_freqpixels(void) { // Freqpixel. By Andrew Tuline.
7445 23 um_data_t *um_data = getAudioData();
7446 23 float FFT_MajorPeak = *(float*)um_data->u_data[4];
7447 23 float my_magnitude = *(float*)um_data->u_data[5] / 16.0f;
7448
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (FFT_MajorPeak < 1) FFT_MajorPeak = 1.0f; // log10(0) is "forbidden" (throws exception)
7449
7450 // this code translates to speed * (2 - speed/255) which is a) speed*2 or b) speed (when speed is 255)
7451 // and since fade_out() can only take 0-255 it will behave incorrectly when speed > 127
7452 //uint16_t fadeRate = 2*SEGMENT.speed - SEGMENT.speed*SEGMENT.speed/255; // Get to 255 as quick as you can.
7453 23 unsigned fadeRate = SEGMENT.speed*SEGMENT.speed; // Get to 255 as quick as you can.
7454 23 fadeRate = map(fadeRate, 0, 65535, 1, 255);
7455
7456 23 int fadeoutDelay = (256 - SEGMENT.speed) / 64;
7457
6/6
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 9 times.
✓ Branch 4 taken 14 times.
✓ Branch 5 taken 9 times.
23 if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fade_out(fadeRate);
7458
7459 23 uint8_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(MAX_FREQ_LOG10 - 1.78f); // Scale log10 of frequency values to the 255 colour index.
7460
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (FFT_MajorPeak < 61.0f) pixCol = 0; // handle underflow
7461
2/2
✓ Branch 0 taken 111 times.
✓ Branch 1 taken 23 times.
134 for (int i=0; i < SEGMENT.intensity/32+1; i++) {
7462 111 unsigned locn = hw_random16(0,SEGLEN);
7463
2/4
✓ Branch 0 taken 111 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 111 times.
111 SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(SEGMENT.intensity+pixCol, false, PALETTE_SOLID_WRAP, 0), (uint8_t)my_magnitude));
7464 }
7465
7466 23 return FRAMETIME;
7467 } // mode_freqpixels()
7468 static const char _data_FX_MODE_FREQPIXELS[] PROGMEM = "Freqpixels@Fade rate,Starting color and # of pixels;!,!,;!;1f;m12=0,si=0"; // Pixels, Beatsin
7469
7470
7471 //////////////////////
7472 // ** Freqwave //
7473 //////////////////////
7474 // Assign a color to the central (starting pixels) based on the predominant frequencies and the volume. The color is being determined by mapping the MajorPeak from the FFT
7475 // and then mapping this to the HSV color circle. Currently we are sampling at 10240 Hz, so the highest frequency we can look at is 5120Hz.
7476 //
7477 // SEGMENT.custom1: the lower cut off point for the FFT. (many, most time the lowest values have very little information since they are FFT conversion artifacts. Suggested value is close to but above 0
7478 // SEGMENT.custom2: The high cut off point. This depends on your sound profile. Most music looks good when this slider is between 50% and 100%.
7479 // SEGMENT.custom3: "preamp" for the audio signal for audio10.
7480 //
7481 // I suggest that for this effect you turn the brightness to 95%-100% but again it depends on your soundprofile you find yourself in.
7482 // Instead of using colorpalettes, This effect works on the HSV color circle with red being the lowest frequency
7483 //
7484 // As a compromise between speed and accuracy we are currently sampling with 10240Hz, from which we can then determine with a 512bin FFT our max frequency is 5120Hz.
7485 // Depending on the music stream you have you might find it useful to change the frequency mapping.
7486 23 uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschung.
7487 // As before, this effect can also work on single pixels, we just lose the shifting effect
7488 23 um_data_t *um_data = getAudioData();
7489 23 float FFT_MajorPeak = *(float*)um_data->u_data[4];
7490 23 float volumeSmth = *(float*)um_data->u_data[0];
7491
7492
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if (SEGENV.call == 0) {
7493 5 SEGMENT.fill(BLACK);
7494 }
7495
7496 23 uint8_t secondHand = micros()/(256-SEGMENT.speed)/500 % 16;
7497
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
23 if(SEGENV.aux0 != secondHand) {
7498
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 SEGENV.aux0 = secondHand;
7499
7500
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 float sensitivity = mapf(SEGMENT.custom3, 1, 31, 1, 10); // reduced resolution slider
7501
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 float pixVal = min(255.0f, volumeSmth * (float)SEGMENT.intensity / 256.0f * sensitivity);
7502
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 float intensity = mapf(pixVal, 0.0f, 255.0f, 0.0f, 100.0f) / 100.0f; // make a brightness from the last avg
7503
7504 18 CRGB color = 0;
7505
7506
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if (FFT_MajorPeak > MAX_FREQUENCY) FFT_MajorPeak = 1.0f;
7507 // MajorPeak holds the freq. value which is most abundant in the last sample.
7508 // With our sampling rate of 10240Hz we have a usable freq range from roughly 80Hz to 10240/2 Hz
7509 // we will treat everything with less than 65Hz as 0
7510
7511
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 17 times.
18 if (FFT_MajorPeak < 80) {
7512 color = CRGB::Black;
7513 } else {
7514
2/4
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
17 int upperLimit = 80 + 42 * SEGMENT.custom2;
7515
2/4
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
17 int lowerLimit = 80 + 3 * SEGMENT.custom1;
7516
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 1 times.
17 uint8_t i = lowerLimit!=upperLimit ? map(FFT_MajorPeak, lowerLimit, upperLimit, 0, 255) : FFT_MajorPeak; // may under/overflow - so we enforce uint8_t
7517 17 unsigned b = min(255.0f, 255.0f * intensity);
7518 34 color = CHSV(i, 240, (uint8_t)b); // implicit conversion to RGB supplied by FastLED
7519 }
7520
7521
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 SEGMENT.setPixelColor(SEGLEN/2, color);
7522
7523 // shift the pixels one pixel outwards
7524 // if SEGLEN equals 1 these loops won't execute
7525
8/14
✓ Branch 0 taken 478 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 478 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 478 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 478 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 478 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 478 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 478 times.
✓ Branch 13 taken 18 times.
496 for (unsigned i = SEGLEN - 1; i > SEGLEN/2; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left
7526
8/14
✓ Branch 0 taken 482 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 482 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 482 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 482 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 482 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 482 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 482 times.
✓ Branch 13 taken 18 times.
500 for (unsigned i = 0; i < SEGLEN/2; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right
7527 }
7528
7529 23 return FRAMETIME;
7530 } // mode_freqwave()
7531 static const char _data_FX_MODE_FREQWAVE[] PROGMEM = "Freqwave@Speed,Sound effect,Low bin,High bin,Pre-amp;;;01f;m12=2,si=0"; // Circle, Beatsin
7532
7533
7534 //////////////////////
7535 // ** Noisemove //
7536 //////////////////////
7537 23 uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuline
7538 23 um_data_t *um_data = getAudioData();
7539 23 uint8_t *fftResult = (uint8_t*)um_data->u_data[2];
7540
7541 23 int fadeoutDelay = (256 - SEGMENT.speed) / 96;
7542
6/6
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 19 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 21 times.
✓ Branch 5 taken 2 times.
23 if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fadeToBlackBy(4+ SEGMENT.speed/4);
7543
7544 23 uint8_t numBins = map(SEGMENT.intensity,0,255,0,16); // Map slider to fftResult bins.
7545
2/2
✓ Branch 0 taken 184 times.
✓ Branch 1 taken 23 times.
207 for (int i=0; i<numBins; i++) { // How many active bins are we using.
7546 184 unsigned locn = perlin16(strip().now*SEGMENT.speed+i*50000, strip().now*SEGMENT.speed); // Get a new pixel location from moving noise.
7547 // if SEGLEN equals 1 locn will be always 0, hence we set the first pixel only
7548 184 locn = map(locn, 7500, 58000, 0, SEGLEN-1); // Map that to the length of the strand, and ensure we don't go over.
7549
2/4
✓ Branch 0 taken 184 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 184 times.
184 SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i*64, false, PALETTE_SOLID_WRAP, 0), uint8_t(fftResult[i % 16]*4)));
7550 }
7551
7552 23 return FRAMETIME;
7553 } // mode_noisemove()
7554 static const char _data_FX_MODE_NOISEMOVE[] PROGMEM = "Noisemove@Move speed,Fade rate;!,!;!;01f;m12=0,si=0"; // Pixels, Beatsin
7555
7556
7557 //////////////////////
7558 // ** Rocktaves //
7559 //////////////////////
7560 83 uint16_t mode_rocktaves(void) { // Rocktaves. Same note from each octave is same colour. By: Andrew Tuline
7561 83 um_data_t *um_data = getAudioData();
7562 83 float FFT_MajorPeak = *(float*) um_data->u_data[4];
7563 83 float my_magnitude = *(float*) um_data->u_data[5] / 16.0f;
7564
7565 83 SEGMENT.fadeToBlackBy(16); // Just in case something doesn't get faded.
7566
7567 83 float frTemp = FFT_MajorPeak;
7568 83 uint8_t octCount = 0; // Octave counter.
7569 83 uint8_t volTemp = 0;
7570
7571 83 volTemp = 32.0f + my_magnitude * 1.5f; // brightness = volume (overflows are handled in next lines)
7572
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 79 times.
83 if (my_magnitude < 48) volTemp = 0; // We need to squelch out the background noise.
7573
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 83 times.
83 if (my_magnitude > 144) volTemp = 255; // everything above this is full brightness
7574
7575
2/2
✓ Branch 0 taken 238 times.
✓ Branch 1 taken 83 times.
321 while ( frTemp > 249 ) {
7576 238 octCount++; // This should go up to 5.
7577 238 frTemp = frTemp/2;
7578 }
7579
7580 83 frTemp -= 132.0f; // This should give us a base musical note of C3
7581 83 frTemp = fabsf(frTemp * 2.1f); // Fudge factors to compress octave range starting at 0 and going to 255;
7582
7583 83 unsigned i = map(beatsin8_t(8+octCount*4, 0, 255, 0, octCount*8), 0, 255, 0, SEGLEN-1);
7584
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 83 times.
83 i = constrain(i, 0U, SEGLEN-1U);
7585
2/4
✓ Branch 0 taken 83 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 83 times.
83 SEGMENT.addPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette((uint8_t)frTemp, false, PALETTE_SOLID_WRAP, 0), volTemp));
7586
7587 83 return FRAMETIME;
7588 } // mode_rocktaves()
7589 static const char _data_FX_MODE_ROCKTAVES[] PROGMEM = "Rocktaves@;!,!;!;01f;m12=1,si=0"; // Bar, Beatsin
7590
7591
7592 ///////////////////////
7593 // ** Waterfall //
7594 ///////////////////////
7595 // Combines peak detection with FFT_MajorPeak and FFT_Magnitude.
7596 23 uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tuline
7597 // effect can work on single pixels, we just lose the shifting effect
7598 23 unsigned dataSize = sizeof(uint32_t) * SEGLEN;
7599
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
7600 23 uint32_t* pixels = reinterpret_cast<uint32_t*>(SEGENV.data);
7601
7602 23 um_data_t *um_data = getAudioData();
7603 23 uint8_t samplePeak = *(uint8_t*)um_data->u_data[3];
7604 23 float FFT_MajorPeak = *(float*) um_data->u_data[4];
7605 23 uint8_t *maxVol = (uint8_t*)um_data->u_data[6];
7606 23 uint8_t *binNum = (uint8_t*)um_data->u_data[7];
7607 23 float my_magnitude = *(float*) um_data->u_data[5] / 8.0f;
7608
7609
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception)
7610
7611
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if (SEGENV.call == 0) {
7612
2/2
✓ Branch 0 taken 262 times.
✓ Branch 1 taken 5 times.
267 for (unsigned i = 0; i < SEGLEN; i++) pixels[i] = BLACK; // may not be needed as resetIfRequired() clears buffer
7613 5 SEGENV.aux0 = 255;
7614 5 SEGMENT.custom1 = *binNum;
7615 5 SEGMENT.custom2 = *maxVol * 2;
7616 }
7617
7618 23 *binNum = SEGMENT.custom1; // Select a bin.
7619 23 *maxVol = SEGMENT.custom2 / 2; // Our volume comparator.
7620
7621 23 uint8_t secondHand = micros() / (256-SEGMENT.speed)/500 + 1 % 16;
7622
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
23 if (SEGENV.aux0 != secondHand) { // Triggered millis timing.
7623 18 SEGENV.aux0 = secondHand;
7624
7625 //uint8_t pixCol = (log10f((float)FFT_MajorPeak) - 2.26f) * 177; // 10Khz sampling - log10 frequency range is from 2.26 (182hz) to 3.7 (5012hz). Let's scale accordingly.
7626 18 uint8_t pixCol = (log10f(FFT_MajorPeak) - 2.26f) * 150; // 22Khz sampling - log10 frequency range is from 2.26 (182hz) to 3.967 (9260hz). Let's scale accordingly.
7627
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if (FFT_MajorPeak < 182.0f) pixCol = 0; // handle underflow
7628
7629 18 unsigned k = SEGLEN-1;
7630
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 17 times.
18 if (samplePeak) {
7631 1 pixels[k] = (uint32_t)CRGB(CHSV(92,92,92));
7632 } else {
7633
2/4
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 17 times.
17 pixels[k] = color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(pixCol+SEGMENT.intensity, false, PALETTE_SOLID_WRAP, 0), (uint8_t)my_magnitude);
7634 }
7635 18 SEGMENT.setPixelColor(k, pixels[k]);
7636 // loop will not execute if SEGLEN equals 1
7637
2/2
✓ Branch 0 taken 960 times.
✓ Branch 1 taken 18 times.
978 for (unsigned i = 0; i < k; i++) {
7638 960 pixels[i] = pixels[i+1]; // shift left
7639 960 SEGMENT.setPixelColor(i, pixels[i]);
7640 }
7641 }
7642
7643 23 return FRAMETIME;
7644 } // mode_waterfall()
7645 static const char _data_FX_MODE_WATERFALL[] PROGMEM = "Waterfall@!,Adjust color,Select bin,Volume (min);!,!;!;01f;c2=0,m12=2,si=0"; // Circles, Beatsin
7646
7647
7648 #ifndef WLED_DISABLE_2D
7649 /////////////////////////
7650 // ** 2D GEQ //
7651 /////////////////////////
7652 497 uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma.
7653
5/6
✓ Branch 0 taken 492 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 492 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 492 times.
497 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
7654
7655 492 const int NUM_BANDS = map(SEGMENT.custom1, 0, 255, 1, 16);
7656 492 const int CENTER_BIN = map(SEGMENT.custom3, 0, 31, 0, 15);
7657 492 const int cols = SEG_W;
7658 492 const int rows = SEG_H;
7659
7660
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 492 times.
492 if (!SEGENV.allocateData(cols*sizeof(uint16_t))) return mode_static(); //allocation failed
7661 492 uint16_t *previousBarHeight = reinterpret_cast<uint16_t*>(SEGENV.data); //array of previous bar heights per frequency band
7662
7663 492 um_data_t *um_data = getAudioData();
7664 492 uint8_t *fftResult = (uint8_t*)um_data->u_data[2];
7665
7666
4/4
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 482 times.
✓ Branch 2 taken 199 times.
✓ Branch 3 taken 10 times.
691 if (SEGENV.call == 0) for (int i=0; i<cols; i++) previousBarHeight[i] = 0;
7667
7668 492 bool rippleTime = false;
7669
2/2
✓ Branch 0 taken 483 times.
✓ Branch 1 taken 9 times.
492 if (strip().now - SEGENV.step >= (256U - SEGMENT.intensity)) {
7670 483 SEGENV.step = strip().now;
7671 483 rippleTime = true;
7672 }
7673
7674 492 int fadeoutDelay = (256 - SEGMENT.speed) / 64;
7675
6/6
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 478 times.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 7 times.
✓ Branch 4 taken 485 times.
✓ Branch 5 taken 7 times.
492 if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fadeToBlackBy(SEGMENT.speed);
7676
7677
2/2
✓ Branch 0 taken 8035 times.
✓ Branch 1 taken 492 times.
8527 for (int x=0; x < cols; x++) {
7678 8035 int band = map(x, 0, cols, 0, NUM_BANDS);
7679
2/2
✓ Branch 0 taken 128 times.
✓ Branch 1 taken 7907 times.
8035 if (NUM_BANDS < 16) {
7680
1/2
✓ Branch 0 taken 128 times.
✗ Branch 1 not taken.
128 int startBin = constrain(CENTER_BIN - NUM_BANDS/2, 0, 15 - NUM_BANDS + 1);
7681
1/2
✓ Branch 0 taken 128 times.
✗ Branch 1 not taken.
128 if (NUM_BANDS <= 1)
7682 128 band = CENTER_BIN; // map() does not work for single band
7683 else
7684 ✗ band = map(band, 0, NUM_BANDS - 1, startBin, startBin + NUM_BANDS - 1);
7685 }
7686 8035 band = constrain(band, 0, 15);
7687 8035 unsigned colorIndex = band * 17;
7688 8035 int barHeight = map(fftResult[band], 0, 255, 0, rows); // do not subtract -1 from rows here
7689
2/2
✓ Branch 0 taken 5890 times.
✓ Branch 1 taken 2145 times.
8035 if (barHeight > previousBarHeight[x]) previousBarHeight[x] = barHeight; //drive the peak up
7690
7691 8035 uint32_t ledColor = BLACK;
7692
2/2
✓ Branch 0 taken 18834 times.
✓ Branch 1 taken 8035 times.
26869 for (int y=0; y < barHeight; y++) {
7693
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18834 times.
18834 if (SEGMENT.check1) //color_vertical / color bars toggle
7694 ✗ colorIndex = map(y, 0, rows-1, 0, 255);
7695
7696
2/4
✓ Branch 0 taken 18834 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 18834 times.
18834 ledColor = SEGMENT.color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0);
7697 18834 SEGMENT.setPixelColorXY(x, rows-1 - y, ledColor);
7698 }
7699
2/2
✓ Branch 0 taken 6273 times.
✓ Branch 1 taken 1762 times.
8035 if (previousBarHeight[x] > 0)
7700
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6273 times.
6273 SEGMENT.setPixelColorXY(x, rows - previousBarHeight[x], (SEGCOLOR(2) != BLACK) ? SEGCOLOR(2) : ledColor);
7701
7702
4/4
✓ Branch 0 taken 7822 times.
✓ Branch 1 taken 213 times.
✓ Branch 2 taken 6086 times.
✓ Branch 3 taken 1736 times.
8035 if (rippleTime && previousBarHeight[x]>0) previousBarHeight[x]--; //delay/ripple effect
7703 }
7704
7705 492 return FRAMETIME;
7706 } // mode_2DGEQ()
7707 static const char _data_FX_MODE_2DGEQ[] PROGMEM = "GEQ@Fade speed,Ripple decay,# of bands,,Bin,Color bars;!,,Peaks;!;2f;c1=255,c2=64,pal=11,si=0,c3=0"; // Beatsin
7708
7709
7710 /////////////////////////
7711 // ** 2D Funky plank //
7712 /////////////////////////
7713 23 uint16_t mode_2DFunkyPlank(void) { // Written by ??? Adapted by Will Tatam.
7714
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
7715
7716 18 const int cols = SEG_W;
7717 18 const int rows = SEG_H;
7718
7719 18 int NUMB_BANDS = map(SEGMENT.custom1, 0, 255, 1, 16);
7720 18 int barWidth = (cols / NUMB_BANDS);
7721 18 int bandInc = 1;
7722
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 13 times.
18 if (barWidth == 0) {
7723 // Matrix narrower than fft bands
7724 5 barWidth = 1;
7725 5 bandInc = (NUMB_BANDS / cols);
7726 }
7727
7728 18 um_data_t *um_data = getAudioData();
7729 18 uint8_t *fftResult = (uint8_t*)um_data->u_data[2];
7730
7731
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 14 times.
18 if (SEGENV.call == 0) {
7732 4 SEGMENT.fill(BLACK);
7733 }
7734
7735 18 uint8_t secondHand = micros()/(256-SEGMENT.speed)/500+1 % 64;
7736
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 3 times.
18 if (SEGENV.aux0 != secondHand) { // Triggered millis timing.
7737 15 SEGENV.aux0 = secondHand;
7738
7739 // display values of
7740 15 int b = 0;
7741
2/2
✓ Branch 0 taken 131 times.
✓ Branch 1 taken 15 times.
146 for (int band = 0; band < NUMB_BANDS; band += bandInc, b++) {
7742 131 int hue = fftResult[band % 16];
7743 131 int v = map(fftResult[band % 16], 0, 255, 10, 255);
7744
2/2
✓ Branch 0 taken 384 times.
✓ Branch 1 taken 131 times.
515 for (int w = 0; w < barWidth; w++) {
7745 384 int xpos = (barWidth * b) + w;
7746
1/2
✓ Branch 0 taken 384 times.
✗ Branch 1 not taken.
768 SEGMENT.setPixelColorXY(xpos, 0, CHSV(hue, 255, v));
7747 }
7748 }
7749
7750 // Update the display:
7751
2/2
✓ Branch 0 taken 173 times.
✓ Branch 1 taken 15 times.
188 for (int i = (rows - 1); i > 0; i--) {
7752
2/2
✓ Branch 0 taken 5336 times.
✓ Branch 1 taken 173 times.
5509 for (int j = (cols - 1); j >= 0; j--) {
7753 5336 SEGMENT.setPixelColorXY(j, i, SEGMENT.getPixelColorXY(j, i-1));
7754 }
7755 }
7756 }
7757
7758 18 return FRAMETIME;
7759 } // mode_2DFunkyPlank
7760 static const char _data_FX_MODE_2DFUNKYPLANK[] PROGMEM = "Funky Plank@Scroll speed,,# of bands;;;2f;si=0"; // Beatsin
7761
7762
7763 /////////////////////////
7764 // 2D Akemi //
7765 /////////////////////////
7766 static uint8_t akemi[] PROGMEM = {
7767 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
7768 0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,
7769 0,0,0,0,0,0,0,0,0,0,0,2,2,3,3,3,3,3,3,2,2,0,0,0,0,0,0,0,0,0,0,0,
7770 0,0,0,0,0,0,0,0,0,0,2,3,3,0,0,0,0,0,0,3,3,2,0,0,0,0,0,0,0,0,0,0,
7771 0,0,0,0,0,0,0,0,0,2,3,0,0,0,6,5,5,4,0,0,0,3,2,0,0,0,0,0,0,0,0,0,
7772 0,0,0,0,0,0,0,0,2,3,0,0,6,6,5,5,5,5,4,4,0,0,3,2,0,0,0,0,0,0,0,0,
7773 0,0,0,0,0,0,0,0,2,3,0,6,5,5,5,5,5,5,5,5,4,0,3,2,0,0,0,0,0,0,0,0,
7774 0,0,0,0,0,0,0,2,3,0,6,5,5,5,5,5,5,5,5,5,5,4,0,3,2,0,0,0,0,0,0,0,
7775 0,0,0,0,0,0,0,3,2,0,6,5,5,5,5,5,5,5,5,5,5,4,0,2,3,0,0,0,0,0,0,0,
7776 0,0,0,0,0,0,3,2,3,6,5,5,7,7,5,5,5,5,7,7,5,5,4,3,2,3,0,0,0,0,0,0,
7777 0,0,0,0,0,2,3,1,3,6,5,1,7,7,7,5,5,1,7,7,7,5,4,3,1,3,2,0,0,0,0,0,
7778 0,0,0,0,0,8,3,1,3,6,5,1,7,7,7,5,5,1,7,7,7,5,4,3,1,3,8,0,0,0,0,0,
7779 0,0,0,0,0,8,3,1,3,6,5,5,1,1,5,5,5,5,1,1,5,5,4,3,1,3,8,0,0,0,0,0,
7780 0,0,0,0,0,2,3,1,3,6,5,5,5,5,5,5,5,5,5,5,5,5,4,3,1,3,2,0,0,0,0,0,
7781 0,0,0,0,0,0,3,2,3,6,5,5,5,5,5,5,5,5,5,5,5,5,4,3,2,3,0,0,0,0,0,0,
7782 0,0,0,0,0,0,0,0,0,6,5,5,5,5,5,7,7,5,5,5,5,5,4,0,0,0,0,0,0,0,0,0,
7783 0,0,0,0,0,0,0,0,0,6,5,5,5,5,5,5,5,5,5,5,5,5,4,0,0,0,0,0,0,0,0,0,
7784 1,0,0,0,0,0,0,0,0,6,5,5,5,5,5,5,5,5,5,5,5,5,4,0,0,0,0,0,0,0,0,2,
7785 0,2,2,2,0,0,0,0,0,6,5,5,5,5,5,5,5,5,5,5,5,5,4,0,0,0,0,0,2,2,2,0,
7786 0,0,0,3,2,0,0,0,6,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0,0,2,2,0,0,0,
7787 0,0,0,3,2,0,0,0,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,0,0,0,2,3,0,0,0,
7788 0,0,0,0,3,2,0,0,0,0,3,3,0,3,3,0,0,3,3,0,3,3,0,0,0,0,2,2,0,0,0,0,
7789 0,0,0,0,3,2,0,0,0,0,3,2,0,3,2,0,0,3,2,0,3,2,0,0,0,0,2,3,0,0,0,0,
7790 0,0,0,0,0,3,2,0,0,3,2,0,0,3,2,0,0,3,2,0,0,3,2,0,0,2,3,0,0,0,0,0,
7791 0,0,0,0,0,3,2,2,2,2,0,0,0,3,2,0,0,3,2,0,0,0,3,2,2,2,3,0,0,0,0,0,
7792 0,0,0,0,0,0,3,3,3,0,0,0,0,3,2,0,0,3,2,0,0,0,0,3,3,3,0,0,0,0,0,0,
7793 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,
7794 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,
7795 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,
7796 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,
7797 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
7798 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
7799 };
7800
7801 23 uint16_t mode_2DAkemi(void) {
7802
9/14
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 18 times.
✓ Branch 10 taken 5 times.
✓ Branch 11 taken 18 times.
✓ Branch 12 taken 5 times.
✗ Branch 13 not taken.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
7803
7804 18 const int cols = SEG_W;
7805 18 const int rows = SEG_H;
7806
7807
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 unsigned counter = (strip().now * ((SEGMENT.speed >> 2) +2)) & 0xFFFF;
7808 18 counter = counter >> 8;
7809
7810 18 const float lightFactor = 0.15f;
7811 18 const float normalFactor = 0.4f;
7812
7813 um_data_t *um_data;
7814
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 if (!UsermodManager::getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
7815
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 um_data = simulateSound(SEGMENT.soundSim);
7816 }
7817 18 uint8_t *fftResult = (uint8_t*)um_data->u_data[2];
7818 18 float base = fftResult[0]/255.0f;
7819
7820 //draw and color Akemi
7821
4/4
✓ Branch 0 taken 6761 times.
✓ Branch 1 taken 223 times.
✓ Branch 2 taken 223 times.
✓ Branch 3 taken 18 times.
7002 for (int y=0; y < rows; y++) for (int x=0; x < cols; x++) {
7822 CRGB color;
7823 6761 CRGB soundColor = CRGB::Orange;
7824
3/6
✓ Branch 0 taken 6761 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6761 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6761 times.
✗ Branch 5 not taken.
6761 CRGB faceColor = CRGB(SEGMENT.color_wheel(counter));
7825
1/2
✓ Branch 0 taken 6761 times.
✗ Branch 1 not taken.
6761 CRGB armsAndLegsColor = CRGB(SEGCOLOR(1) > 0 ? SEGCOLOR(1) : 0xFFE0A0); //default warmish white 0xABA8FF; //0xFF52e5;//
7826 6761 uint8_t ak = pgm_read_byte_near(akemi + ((y * 32)/rows) * 32 + (x * 32)/cols); // akemi[(y * 32)/rows][(x * 32)/cols]
7827
9/9
✓ Branch 0 taken 561 times.
✓ Branch 1 taken 551 times.
✓ Branch 2 taken 130 times.
✓ Branch 3 taken 122 times.
✓ Branch 4 taken 1066 times.
✓ Branch 5 taken 122 times.
✓ Branch 6 taken 88 times.
✓ Branch 7 taken 26 times.
✓ Branch 8 taken 4095 times.
6761 switch (ak) {
7828 561 case 3: armsAndLegsColor.r *= lightFactor; armsAndLegsColor.g *= lightFactor; armsAndLegsColor.b *= lightFactor; color = armsAndLegsColor; break; //light arms and legs 0x9B9B9B
7829 551 case 2: armsAndLegsColor.r *= normalFactor; armsAndLegsColor.g *= normalFactor; armsAndLegsColor.b *= normalFactor; color = armsAndLegsColor; break; //normal arms and legs 0x888888
7830 130 case 1: color = armsAndLegsColor; break; //dark arms and legs 0x686868
7831 122 case 6: faceColor.r *= lightFactor; faceColor.g *= lightFactor; faceColor.b *= lightFactor; color=faceColor; break; //light face 0x31AAFF
7832 1066 case 5: faceColor.r *= normalFactor; faceColor.g *= normalFactor; faceColor.b *= normalFactor; color=faceColor; break; //normal face 0x0094FF
7833 122 case 4: color = faceColor; break; //dark face 0x007DC6
7834
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 88 times.
176 case 7: color = SEGCOLOR(2) > 0 ? SEGCOLOR(2) : 0xFFFFFF; break; //eyes and mouth default white
7835
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 12 times.
26 case 8: if (base > 0.4) {soundColor.r *= base; soundColor.g *= base; soundColor.b *= base; color=soundColor;} else color = armsAndLegsColor; break;
7836 8190 default: color = BLACK; break;
7837 }
7838
7839
7/12
✓ Branch 0 taken 6761 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6761 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2048 times.
✓ Branch 5 taken 4713 times.
✓ Branch 6 taken 2048 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 2048 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 6761 times.
6761 if (SEGMENT.intensity > 128 && fftResult && fftResult[0] > 128) { //dance if base is high
7840 ✗ SEGMENT.setPixelColorXY(x, 0, BLACK);
7841 ✗ SEGMENT.setPixelColorXY(x, y+1, color);
7842 } else
7843
3/6
✓ Branch 0 taken 6761 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6761 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6761 times.
✗ Branch 5 not taken.
6761 SEGMENT.setPixelColorXY(x, y, color);
7844 }
7845
7846 //add geq left and right
7847
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 if (um_data && fftResult) {
7848 18 int xMax = cols/8;
7849
2/2
✓ Branch 0 taken 52 times.
✓ Branch 1 taken 18 times.
70 for (int x=0; x < xMax; x++) {
7850 52 unsigned band = map(x, 0, max(xMax,4), 0, 15); // map 0..cols/8 to 16 GEQ bands
7851 52 band = constrain(band, 0, 15);
7852 52 int barHeight = map(fftResult[band], 0, 255, 0, 17*rows/32);
7853
7/14
✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 52 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 52 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 52 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 52 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 52 times.
✓ Branch 12 taken 52 times.
✗ Branch 13 not taken.
52 uint32_t color = SEGMENT.color_from_palette((band * 35), false, PALETTE_SOLID_WRAP, 0);
7854
7855
2/2
✓ Branch 0 taken 180 times.
✓ Branch 1 taken 52 times.
232 for (int y=0; y < barHeight; y++) {
7856
3/6
✓ Branch 0 taken 180 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 180 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 180 times.
✗ Branch 5 not taken.
180 SEGMENT.setPixelColorXY(x, rows/2-y, color);
7857
3/6
✓ Branch 0 taken 180 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 180 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 180 times.
✗ Branch 5 not taken.
180 SEGMENT.setPixelColorXY(cols-1-x, rows/2-y, color);
7858 }
7859 }
7860 }
7861
7862
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 return FRAMETIME;
7863 } // mode_2DAkemi
7864 static const char _data_FX_MODE_2DAKEMI[] PROGMEM = "Akemi@Color speed,Dance;Head palette,Arms & Legs,Eyes & Mouth;Face palette;2f;si=0"; //beatsin
7865
7866
7867 // Distortion waves - ldirko
7868 // https://editor.soulmatelights.com/gallery/1089-distorsion-waves
7869 // adapted for WLED by @blazoncek
7870 23 uint16_t mode_2Ddistortionwaves() {
7871
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
7872
7873 18 const int cols = SEG_W;
7874 18 const int rows = SEG_H;
7875
7876 18 uint8_t speed = SEGMENT.speed/32;
7877 18 uint8_t scale = SEGMENT.intensity/32;
7878
7879 18 uint8_t w = 2;
7880
7881 18 unsigned a = strip().now/32;
7882 18 unsigned a2 = a/2;
7883 18 unsigned a3 = a/3;
7884
7885 18 unsigned cx = beatsin8_t(10-speed,0,cols-1)*scale;
7886 18 unsigned cy = beatsin8_t(12-speed,0,rows-1)*scale;
7887 18 unsigned cx1 = beatsin8_t(13-speed,0,cols-1)*scale;
7888 18 unsigned cy1 = beatsin8_t(15-speed,0,rows-1)*scale;
7889 18 unsigned cx2 = beatsin8_t(17-speed,0,cols-1)*scale;
7890 18 unsigned cy2 = beatsin8_t(14-speed,0,rows-1)*scale;
7891
7892 18 unsigned xoffs = 0;
7893
2/2
✓ Branch 0 taken 451 times.
✓ Branch 1 taken 18 times.
469 for (int x = 0; x < cols; x++) {
7894 451 xoffs += scale;
7895 451 unsigned yoffs = 0;
7896
7897
2/2
✓ Branch 0 taken 6761 times.
✓ Branch 1 taken 451 times.
7212 for (int y = 0; y < rows; y++) {
7898 6761 yoffs += scale;
7899
7900 6761 byte rdistort = cos8_t((cos8_t(((x<<3)+a )&255)+cos8_t(((y<<3)-a2)&255)+a3 )&255)>>1;
7901 6761 byte gdistort = cos8_t((cos8_t(((x<<3)-a2)&255)+cos8_t(((y<<3)+a3)&255)+a+32 )&255)>>1;
7902 6761 byte bdistort = cos8_t((cos8_t(((x<<3)+a3)&255)+cos8_t(((y<<3)-a) &255)+a2+64)&255)>>1;
7903
7904 6761 byte valueR = rdistort+ w* (a- ( ((xoffs - cx) * (xoffs - cx) + (yoffs - cy) * (yoffs - cy))>>7 ));
7905 6761 byte valueG = gdistort+ w* (a2-( ((xoffs - cx1) * (xoffs - cx1) + (yoffs - cy1) * (yoffs - cy1))>>7 ));
7906 6761 byte valueB = bdistort+ w* (a3-( ((xoffs - cx2) * (xoffs - cx2) + (yoffs - cy2) * (yoffs - cy2))>>7 ));
7907
7908 6761 valueR = gamma8(cos8_t(valueR));
7909 6761 valueG = gamma8(cos8_t(valueG));
7910 6761 valueB = gamma8(cos8_t(valueB));
7911
7912 6761 SEGMENT.setPixelColorXY(x, y, RGBW32(valueR, valueG, valueB, 0));
7913 }
7914 }
7915
7916 18 return FRAMETIME;
7917 }
7918 static const char _data_FX_MODE_2DDISTORTIONWAVES[] PROGMEM = "Distortion Waves@!,Scale;;;2";
7919
7920
7921 //Soap
7922 //@Stepko
7923 //Idea from https://www.youtube.com/watch?v=DiHBgITrZck&ab_channel=StefanPetrick
7924 // adapted for WLED by @blazoncek, optimization by @dedehai
7925 36 static void soapPixels(bool isRow, uint8_t *noise3d, CRGB *pixels) {
7926 72 const int cols = SEG_W;
7927 36 const int rows = SEG_H;
7928 40566 const auto XY = [&](int x, int y) { return x + y * cols; };
7929 28392 const auto abs = [](int x) { return x<0 ? -x : x; };
7930
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 18 times.
36 const int tRC = isRow ? rows : cols; // transpose if isRow
7931
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 18 times.
36 const int tCR = isRow ? cols : rows; // transpose if isRow
7932
2/4
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
✗ Branch 3 not taken.
36 const int amplitude = max(1, (tCR - 8) >> 3) * (1 + (SEGMENT.custom1 >> 5));
7933 36 const int shift = 0; //(128 - SEGMENT.custom2)*2;
7934
7935
2/2
✓ Branch 0 taken 674 times.
✓ Branch 1 taken 36 times.
710 CRGB ledsbuff[tCR];
7936
7937
2/2
✓ Branch 0 taken 674 times.
✓ Branch 1 taken 36 times.
710 for (int i = 0; i < tRC; i++) {
7938
2/2
✓ Branch 0 taken 223 times.
✓ Branch 1 taken 451 times.
674 int amount = ((int)noise3d[isRow ? i*cols : i] - 128) * amplitude + shift; // use first row/column: XY(0,i)/XY(i,0)
7939 674 int delta = abs(amount) >> 8;
7940 674 int fraction = abs(amount) & 255;
7941
2/2
✓ Branch 0 taken 13522 times.
✓ Branch 1 taken 674 times.
14196 for (int j = 0; j < tCR; j++) {
7942 int zD, zF;
7943
2/2
✓ Branch 0 taken 9351 times.
✓ Branch 1 taken 4171 times.
13522 if (amount < 0) {
7944 9351 zD = j - delta;
7945 9351 zF = zD - 1;
7946 } else {
7947 4171 zD = j + delta;
7948 4171 zF = zD + 1;
7949 }
7950 13522 int yA = abs(zD)%tCR;
7951 13522 int yB = abs(zF)%tCR;
7952 13522 int xA = i;
7953 13522 int xB = i;
7954
2/2
✓ Branch 0 taken 6761 times.
✓ Branch 1 taken 6761 times.
13522 if (isRow) {
7955 6761 std::swap(xA,yA);
7956 6761 std::swap(xB,yB);
7957 }
7958 13522 const int indxA = XY(xA,yA);
7959 13522 const int indxB = XY(xB,yB);
7960 CRGB PixelA;
7961 CRGB PixelB;
7962
4/4
✓ Branch 0 taken 12074 times.
✓ Branch 1 taken 1448 times.
✓ Branch 2 taken 12054 times.
✓ Branch 3 taken 20 times.
13522 if ((zD >= 0) && (zD < tCR)) PixelA = pixels[indxA];
7963
1/2
✓ Branch 0 taken 1468 times.
✗ Branch 1 not taken.
1468 else PixelA = ColorFromPalette(SEGPALETTE, ~noise3d[indxA]*3);
7964
4/4
✓ Branch 0 taken 11646 times.
✓ Branch 1 taken 1876 times.
✓ Branch 2 taken 11429 times.
✓ Branch 3 taken 217 times.
13522 if ((zF >= 0) && (zF < tCR)) PixelB = pixels[indxB];
7965
1/2
✓ Branch 0 taken 2093 times.
✗ Branch 1 not taken.
2093 else PixelB = ColorFromPalette(SEGPALETTE, ~noise3d[indxB]*3);
7966
3/6
✓ Branch 0 taken 13522 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 13522 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 13522 times.
27044 ledsbuff[j] = (PixelA.nscale8(ease8InOutApprox(255 - fraction))) + (PixelB.nscale8(ease8InOutApprox(fraction)));
7967 }
7968
2/2
✓ Branch 0 taken 13522 times.
✓ Branch 1 taken 674 times.
14196 for (int j = 0; j < tCR; j++) {
7969 13522 CRGB c = ledsbuff[j];
7970
2/2
✓ Branch 0 taken 6761 times.
✓ Branch 1 taken 6761 times.
13522 if (isRow) std::swap(j,i);
7971
3/6
✓ Branch 0 taken 13522 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 13522 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13522 times.
✗ Branch 5 not taken.
13522 SEGMENT.setPixelColorXY(i, j, pixels[XY(i,j)] = c);
7972
2/2
✓ Branch 0 taken 6761 times.
✓ Branch 1 taken 6761 times.
13522 if (isRow) std::swap(j,i);
7973 }
7974 }
7975 72 }
7976
7977 23 uint16_t mode_2Dsoap() {
7978
9/14
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 18 times.
✓ Branch 10 taken 5 times.
✓ Branch 11 taken 18 times.
✓ Branch 12 taken 5 times.
✗ Branch 13 not taken.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
7979
7980 18 const int cols = SEG_W;
7981 18 const int rows = SEG_H;
7982 15079 const auto XY = [&](int x, int y) { return x + y * cols; };
7983
7984
4/8
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
18 const size_t segSize = SEGMENT.width() * SEGMENT.height(); // prevent reallocation if mirrored or grouped
7985 18 const size_t dataSize = segSize * (sizeof(uint8_t) + sizeof(CRGB)); // pixels and noise
7986
4/10
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 18 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
18 if (!SEGENV.allocateData(dataSize + sizeof(uint32_t)*3)) return mode_static(); //allocation failed
7987
7988
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 uint8_t *noise3d = reinterpret_cast<uint8_t*>(SEGENV.data);
7989
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 CRGB *pixels = reinterpret_cast<CRGB*>(SEGENV.data + segSize * sizeof(uint8_t));
7990
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 uint32_t *noisecoord = reinterpret_cast<uint32_t*>(SEGENV.data + dataSize); // x, y, z coordinates
7991 18 const uint32_t scale32_x = 160000U/cols;
7992 18 const uint32_t scale32_y = 160000U/rows;
7993
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 const uint32_t mov = MIN(cols,rows)*(SEGMENT.speed+2)/2;
7994
6/10
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 14 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 14 times.
✗ Branch 9 not taken.
18 const uint8_t smoothness = MIN(250,SEGMENT.intensity); // limit as >250 produces very little changes
7995
7996
6/8
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 14 times.
✓ Branch 6 taken 12 times.
✓ Branch 7 taken 4 times.
30 if (SEGENV.call == 0) for (int i = 0; i < 3; i++) noisecoord[i] = hw_random(); // init
7997
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 14 times.
56 else for (int i = 0; i < 3; i++) noisecoord[i] += mov;
7998
7999
2/2
✓ Branch 0 taken 451 times.
✓ Branch 1 taken 18 times.
469 for (int i = 0; i < cols; i++) {
8000 451 int32_t ioffset = scale32_x * (i - cols / 2);
8001
2/2
✓ Branch 0 taken 6761 times.
✓ Branch 1 taken 451 times.
7212 for (int j = 0; j < rows; j++) {
8002 6761 int32_t joffset = scale32_y * (j - rows / 2);
8003
1/2
✓ Branch 0 taken 6761 times.
✗ Branch 1 not taken.
6761 uint8_t data = perlin16(noisecoord[0] + ioffset, noisecoord[1] + joffset, noisecoord[2]) >> 8;
8004 20283 noise3d[XY(i,j)] = scale8(noise3d[XY(i,j)], smoothness) + scale8(data, 255 - smoothness);
8005 }
8006 }
8007 // init also if dimensions changed
8008
12/20
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 14 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 14 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 14 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 14 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 14 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✓ Branch 17 taken 14 times.
✓ Branch 18 taken 4 times.
✓ Branch 19 taken 14 times.
18 if (SEGENV.call == 0 || SEGMENT.aux0 != cols || SEGMENT.aux1 != rows) {
8009
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 SEGMENT.aux0 = cols;
8010
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 SEGMENT.aux1 = rows;
8011
2/2
✓ Branch 0 taken 103 times.
✓ Branch 1 taken 4 times.
107 for (int i = 0; i < cols; i++) {
8012
2/2
✓ Branch 0 taken 1557 times.
✓ Branch 1 taken 103 times.
1660 for (int j = 0; j < rows; j++) {
8013
4/8
✓ Branch 0 taken 1557 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1557 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1557 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1557 times.
✗ Branch 7 not taken.
1557 SEGMENT.setPixelColorXY(i, j, ColorFromPalette(SEGPALETTE,~noise3d[XY(i,j)]*3));
8014 }
8015 }
8016 }
8017
8018
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 soapPixels(true, noise3d, pixels); // rows
8019
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 soapPixels(false, noise3d, pixels); // cols
8020
8021
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 return FRAMETIME;
8022 }
8023 static const char _data_FX_MODE_2DSOAP[] PROGMEM = "Soap@!,Smoothness,Density;;!;2;pal=11";
8024
8025
8026 //Idea from https://www.youtube.com/watch?v=HsA-6KIbgto&ab_channel=GreatScott%21
8027 //Octopus (https://editor.soulmatelights.com/gallery/671-octopus)
8028 //Stepko and Sutaburosu
8029 // adapted for WLED by @blazoncek
8030 23 uint16_t mode_2Doctopus() {
8031
9/14
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 18 times.
✓ Branch 10 taken 5 times.
✓ Branch 11 taken 18 times.
✓ Branch 12 taken 5 times.
✗ Branch 13 not taken.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
8032
8033 18 const int cols = SEG_W;
8034 18 const int rows = SEG_H;
8035 16636 const auto XY = [&](int x, int y) { return (x%cols) + (y%rows) * cols; };
8036
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 const uint8_t mapp = 180 / MAX(cols,rows);
8037
8038 typedef struct {
8039 uint8_t angle;
8040 uint8_t radius;
8041 } map_t;
8042
8043
4/8
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
18 const size_t dataSize = SEGMENT.width() * SEGMENT.height() * sizeof(map_t); // prevent reallocation if mirrored or grouped
8044
4/10
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 18 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
18 if (!SEGENV.allocateData(dataSize + 2)) return mode_static(); //allocation failed
8045
8046
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 map_t *rMap = reinterpret_cast<map_t*>(SEGENV.data);
8047
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 uint8_t *offsX = reinterpret_cast<uint8_t*>(SEGENV.data + dataSize);
8048
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 uint8_t *offsY = reinterpret_cast<uint8_t*>(SEGENV.data + dataSize + 1);
8049
8050 // re-init if SEGMENT dimensions or offset changed
8051
18/32
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 14 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 14 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 14 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 14 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 14 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 14 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 14 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 14 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 14 times.
✗ Branch 23 not taken.
✓ Branch 24 taken 14 times.
✗ Branch 25 not taken.
✓ Branch 26 taken 14 times.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✓ Branch 29 taken 14 times.
✓ Branch 30 taken 4 times.
✓ Branch 31 taken 14 times.
18 if (SEGENV.call == 0 || SEGENV.aux0 != cols || SEGENV.aux1 != rows || SEGMENT.custom1 != *offsX || SEGMENT.custom2 != *offsY) {
8052
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 SEGENV.step = 0; // t
8053
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 SEGENV.aux0 = cols;
8054
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 SEGENV.aux1 = rows;
8055
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 *offsX = SEGMENT.custom1;
8056
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 *offsY = SEGMENT.custom2;
8057
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 const int C_X = (cols / 2) + ((SEGMENT.custom1 - 128)*cols)/255;
8058
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 const int C_Y = (rows / 2) + ((SEGMENT.custom2 - 128)*rows)/255;
8059
2/2
✓ Branch 0 taken 103 times.
✓ Branch 1 taken 4 times.
107 for (int x = 0; x < cols; x++) {
8060
2/2
✓ Branch 0 taken 1557 times.
✓ Branch 1 taken 103 times.
1660 for (int y = 0; y < rows; y++) {
8061 1557 int dx = (x - C_X);
8062 1557 int dy = (y - C_Y);
8063
1/2
✓ Branch 0 taken 1557 times.
✗ Branch 1 not taken.
1557 rMap[XY(x, y)].angle = int(40.7436f * atan2_t(dy, dx)); // avoid 128*atan2()/PI
8064 1557 rMap[XY(x, y)].radius = sqrtf(dx * dx + dy * dy) * mapp; //thanks Sutaburosu
8065 }
8066 }
8067 }
8068
8069
4/8
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
18 SEGENV.step += SEGMENT.speed / 32 + 1; // 1-4 range
8070
2/2
✓ Branch 0 taken 451 times.
✓ Branch 1 taken 18 times.
469 for (int x = 0; x < cols; x++) {
8071
2/2
✓ Branch 0 taken 6761 times.
✓ Branch 1 taken 451 times.
7212 for (int y = 0; y < rows; y++) {
8072 6761 byte angle = rMap[XY(x,y)].angle;
8073 6761 byte radius = rMap[XY(x,y)].radius;
8074 //CRGB c = CHSV(SEGENV.step / 2 - radius, 255, sin8_t(sin8_t((angle * 4 - radius) / 4 + SEGENV.step) + radius - SEGENV.step * 2 + angle * (SEGMENT.custom3/3+1)));
8075
8/16
✓ Branch 0 taken 6761 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6761 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6761 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6761 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 6761 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 6761 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 6761 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 6761 times.
✗ Branch 15 not taken.
6761 unsigned intensity = sin8_t(sin8_t((angle * 4 - radius) / 4 + SEGENV.step/2) + radius - SEGENV.step + angle * (SEGMENT.custom3/4+1));
8076 6761 intensity = map((intensity*intensity) & 0xFFFF, 0, 65535, 0, 255); // add a bit of non-linearity for cleaner display
8077
6/12
✓ Branch 0 taken 6761 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6761 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6761 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6761 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 6761 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 6761 times.
✗ Branch 11 not taken.
6761 SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, SEGENV.step / 2 - radius, intensity));
8078 }
8079 }
8080
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 return FRAMETIME;
8081 }
8082 static const char _data_FX_MODE_2DOCTOPUS[] PROGMEM = "Octopus@!,,Offset X,Offset Y,Legs,fasttan;;!;2;";
8083
8084
8085 //Waving Cell
8086 //@Stepko (https://editor.soulmatelights.com/gallery/1704-wavingcells)
8087 // adapted for WLED by @blazoncek, improvements by @dedehai
8088 23 uint16_t mode_2Dwavingcell() {
8089
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (!strip().isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
8090
8091 18 const int cols = SEG_W;
8092 18 const int rows = SEG_H;
8093
8094 18 uint32_t t = (strip().now*(SEGMENT.speed + 1))>>3;
8095 18 uint32_t aX = SEGMENT.custom1/16 + 9;
8096 18 uint32_t aY = SEGMENT.custom2/16 + 1;
8097 18 uint32_t aZ = SEGMENT.custom3 + 1;
8098
2/2
✓ Branch 0 taken 451 times.
✓ Branch 1 taken 18 times.
469 for (int x = 0; x < cols; x++) {
8099
2/2
✓ Branch 0 taken 6761 times.
✓ Branch 1 taken 451 times.
7212 for (int y = 0; y < rows; y++) {
8100 6761 uint32_t wave = sin8_t((x * aX) + sin8_t((((y<<8) + t) * aY)>>8)) + cos8_t(y * aZ); // bit shifts to increase temporal resolution
8101 6761 uint8_t colorIndex = wave + (t>>(8-(SEGMENT.check2*3)));
8102 6761 SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, colorIndex));
8103 }
8104 }
8105 18 SEGMENT.blur(SEGMENT.intensity);
8106 18 return FRAMETIME;
8107 }
8108 static const char _data_FX_MODE_2DWAVINGCELL[] PROGMEM = "Waving Cell@!,Blur,Amplitude 1,Amplitude 2,Amplitude 3,,Flow;;!;2;ix=0";
8109
8110 #ifndef WLED_DISABLE_PARTICLESYSTEM2D
8111
8112 /*
8113 Particle System Vortex
8114 Particles sprayed from center with a rotating spray
8115 Uses palette for particle color
8116 by DedeHai (Damian Schneider)
8117 */
8118 #define NUMBEROFSOURCES 8
8119 23 uint16_t mode_particlevortex(void) {
8120
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (SEGLEN == 1)
8121 ✗ return mode_static();
8122 23 ParticleSystem2D *PartSys = nullptr;
8123 uint32_t i, j;
8124
8125
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
8126
3/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4 times.
5 if (!initParticleSystem2D(PartSys, NUMBEROFSOURCES))
8127
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 return mode_static(); // allocation failed
8128 #ifdef ESP8266
8129 PartSys->setMotionBlur(180);
8130 #else
8131
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setMotionBlur(130);
8132 #endif
8133
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 4 times.
36 for (i = 0; i < min(PartSys->numSources, (uint32_t)NUMBEROFSOURCES); i++) {
8134 32 PartSys->sources[i].source.x = (PartSys->maxX + 1) >> 1; // center
8135 32 PartSys->sources[i].source.y = (PartSys->maxY + 1) >> 1; // center
8136 32 PartSys->sources[i].maxLife = 900;
8137 32 PartSys->sources[i].minLife = 800;
8138 }
8139
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setKillOutOfBounds(true);
8140 }
8141 else
8142
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem2D *>(SEGENV.data); // if not first call, just set the pointer to the PS
8143
8144
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 18 times.
22 if (PartSys == nullptr)
8145
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 return mode_static(); // something went wrong, no data!
8146
8147
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
8148
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 uint32_t spraycount = min(PartSys->numSources, (uint32_t)(1 + (SEGMENT.custom1 >> 5))); // number of sprays to display, 1-8
8149 #ifdef ESP8266
8150 for (i = 1; i < 4; i++) { // need static particles in the center to reduce blinking (would be black every other frame without this hack), just set them there fixed
8151 int partindex = (int)PartSys->usedParticles - (int)i;
8152 if (partindex >= 0) {
8153 PartSys->particles[partindex].x = (PartSys->maxX + 1) >> 1; // center
8154 PartSys->particles[partindex].y = (PartSys->maxY + 1) >> 1; // center
8155 PartSys->particles[partindex].sat = 230;
8156 PartSys->particles[partindex].ttl = 256; //keep alive
8157 }
8158 }
8159 #endif
8160
8161
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 18 times.
18 if (SEGMENT.check1)
8162 ✗ PartSys->setSmearBlur(90); // enable smear blur
8163 else
8164
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->setSmearBlur(0); // disable smear blur
8165
8166 // update colors of the sprays
8167
2/2
✓ Branch 0 taken 106 times.
✓ Branch 1 taken 18 times.
124 for (i = 0; i < spraycount; i++) {
8168 106 uint32_t coloroffset = 0xFF / spraycount;
8169 106 PartSys->sources[i].source.hue = coloroffset * i;
8170 }
8171
8172 // set rotation direction and speed
8173 // can use direction flag to determine current direction
8174
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 bool direction = SEGMENT.check2; //no automatic direction change, set it to flag
8175
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 int32_t currentspeed = (int32_t)SEGENV.step; // make a signed integer out of step
8176
8177
4/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 14 times.
18 if (SEGMENT.custom2 > 0) { // automatic direction change enabled
8178
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 uint32_t changeinterval = 1040 - ((uint32_t)SEGMENT.custom2 << 2);
8179
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 direction = SEGENV.aux1 & 0x01; //set direction according to flag
8180
8181
3/6
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
4 if (SEGMENT.check3) // random interval
8182 ✗ changeinterval = 20 + changeinterval + hw_random16(changeinterval);
8183
8184
4/6
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 3 times.
4 if (SEGMENT.call % changeinterval == 0) { //flip direction on next frame
8185
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 SEGENV.aux1 |= 0x02; // set the update flag (for random interval update)
8186
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (direction)
8187 ✗ SEGENV.aux1 &= ~0x01; // clear the direction flag
8188 else
8189
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 SEGENV.aux1 |= 0x01; // set the direction flag
8190 }
8191 }
8192
8193
4/6
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 int32_t targetspeed = (direction ? 1 : -1) * (SEGMENT.speed << 3);
8194 18 int32_t speeddiff = targetspeed - currentspeed;
8195 18 int32_t speedincrement = speeddiff / 50;
8196
8197
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 14 times.
18 if (speedincrement == 0) { //if speeddiff is not zero, make the increment at least 1 so it reaches target speed
8198
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (speeddiff < 0)
8199 ✗ speedincrement = -1;
8200
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 else if (speeddiff > 0)
8201 ✗ speedincrement = 1;
8202 }
8203
8204 18 currentspeed += speedincrement;
8205
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 SEGENV.aux0 += currentspeed;
8206
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 SEGENV.step = (uint32_t)currentspeed; //save it back
8207
8208 18 uint16_t angleoffset = 0xFFFF / spraycount; // angle offset for an even distribution
8209
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 uint32_t skip = PS_P_HALFRADIUS / (SEGMENT.intensity + 1) + 1; // intensity is emit speed, emit less on low speeds
8210
4/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 15 times.
✓ Branch 5 taken 3 times.
18 if (SEGMENT.call % skip == 0) {
8211 15 j = hw_random16(spraycount); // start with random spray so all get a chance to emit a particle if maximum number of particles alive is reached.
8212
2/2
✓ Branch 0 taken 103 times.
✓ Branch 1 taken 15 times.
118 for (i = 0; i < spraycount; i++) { // emit one particle per spray (if available)
8213
2/4
✓ Branch 0 taken 103 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 103 times.
✗ Branch 3 not taken.
103 PartSys->sources[j].var = (SEGMENT.custom3 >> 1); //update speed variation
8214 #ifdef ESP8266
8215 if (SEGMENT.call & 0x01) // every other frame, do not emit to save particles
8216 #endif
8217
5/10
✓ Branch 0 taken 103 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 103 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 103 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 103 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 103 times.
✗ Branch 9 not taken.
103 PartSys->angleEmit(PartSys->sources[j], SEGENV.aux0 + angleoffset * j, (SEGMENT.intensity >> 2)+1);
8218 103 j = (j + 1) % spraycount;
8219 }
8220 }
8221
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->update(); //update all particles and render to frame
8222
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 return FRAMETIME;
8223 }
8224 #undef NUMBEROFSOURCES
8225 static const char _data_FX_MODE_PARTICLEVORTEX[] PROGMEM = "PS Vortex@Rotation Speed,Particle Speed,Arms,Flip,Nozzle,Smear,Direction,Random Flip;;!;2;pal=27,c1=200,c2=0,c3=0";
8226
8227 /*
8228 Particle Fireworks
8229 Rockets shoot up and explode in a random color, sometimes in a defined pattern
8230 by DedeHai (Damian Schneider)
8231 */
8232 #define NUMBEROFSOURCES 8
8233
8234 23 uint16_t mode_particlefireworks(void) {
8235 23 ParticleSystem2D *PartSys = nullptr;
8236 uint32_t numRockets;
8237
8238
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
8239
3/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4 times.
5 if (!initParticleSystem2D(PartSys, NUMBEROFSOURCES))
8240
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 return mode_static(); // allocation failed
8241
8242
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setKillOutOfBounds(true); // out of bounds particles dont return (except on top, taken care of by gravity setting)
8243
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setWallHardness(120); // ground bounce is fixed
8244 4 numRockets = min(PartSys->numSources, (uint32_t)NUMBEROFSOURCES);
8245
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 4 times.
36 for (uint32_t j = 0; j < numRockets; j++) {
8246 32 PartSys->sources[j].source.ttl = 500 * j; // first rocket starts immediately, others follow soon
8247 32 PartSys->sources[j].source.vy = -1; // at negative speed, no particles are emitted and if rocket dies, it will be relaunched
8248 }
8249 }
8250 else
8251
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem2D *>(SEGENV.data); // if not first call, just set the pointer to the PS
8252
8253
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 18 times.
22 if (PartSys == nullptr)
8254
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 return mode_static(); // something went wrong, no data!
8255
8256
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
8257
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 numRockets = map(SEGMENT.speed, 0 , 255, 4, min(PartSys->numSources, (uint32_t)NUMBEROFSOURCES));
8258
8259
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setWrapX(SEGMENT.check1);
8260
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setBounceY(SEGMENT.check2);
8261
6/12
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 18 times.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 18 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 18 times.
✗ Branch 11 not taken.
18 PartSys->setGravity(map(SEGMENT.custom3, 0, 31, SEGMENT.check2 ? 1 : 0, 10)); // if bounded, set gravity to minimum of 1 or they will bounce at top
8262
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setMotionBlur(map(SEGMENT.custom2, 0, 255, 0, 245)); // anable motion blur
8263
8264 // update the rockets, set the speed state
8265
2/2
✓ Branch 0 taken 108 times.
✓ Branch 1 taken 18 times.
126 for (uint32_t j = 0; j < numRockets; j++) {
8266
1/2
✓ Branch 0 taken 108 times.
✗ Branch 1 not taken.
108 PartSys->applyGravity(PartSys->sources[j].source);
8267
1/2
✓ Branch 0 taken 108 times.
✗ Branch 1 not taken.
108 PartSys->particleMoveUpdate(PartSys->sources[j].source, PartSys->sources[j].sourceFlags);
8268
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 103 times.
108 if (PartSys->sources[j].source.ttl == 0) {
8269
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
5 if (PartSys->sources[j].source.vy > 0) { // rocket has died and is moving up. stop it so it will explode (is handled in the code below)
8270 1 PartSys->sources[j].source.vy = 0;
8271 }
8272
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 else if (PartSys->sources[j].source.vy < 0) { // rocket is exploded and time is up (ttl=0 and negative speed), relaunch it
8273 4 PartSys->sources[j].source.y = PS_P_RADIUS; // start from bottom
8274
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->sources[j].source.x = (PartSys->maxX >> 2) + hw_random(PartSys->maxX >> 1); // centered half
8275
4/8
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
4 PartSys->sources[j].source.vy = (SEGMENT.custom3) + hw_random16(SEGMENT.custom1 >> 3) + 5; // rocket speed TODO: need to adjust for segment height
8276 4 PartSys->sources[j].source.vx = hw_random16(7) - 3; // not perfectly straight up
8277 4 PartSys->sources[j].source.sat = 30; // low saturation -> exhaust is off-white
8278
4/8
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
4 PartSys->sources[j].source.ttl = hw_random16(SEGMENT.custom1) + (SEGMENT.custom1 >> 1); // set fuse time
8279 4 PartSys->sources[j].maxLife = 40; // exhaust particle life
8280 4 PartSys->sources[j].minLife = 10;
8281 4 PartSys->sources[j].vx = 0; // emitting speed
8282 4 PartSys->sources[j].vy = -5; // emitting speed
8283 4 PartSys->sources[j].var = 4; // speed variation around vx,vy (+/- var)
8284 }
8285 }
8286 }
8287 // check each rocket's state and emit particles according to its state: moving up = emit exhaust, at top = explode; falling down = standby time
8288 uint32_t emitparticles, frequency, baseangle, hueincrement; // number of particles to emit for each rocket's state
8289 // variables for circular explosions
8290 [[maybe_unused]] int32_t speed, currentspeed, speedvariation, percircle;
8291 18 int32_t counter = 0;
8292 [[maybe_unused]] uint16_t angle;
8293 [[maybe_unused]] unsigned angleincrement;
8294 18 bool circularexplosion = false;
8295
8296 // emit particles for each rocket
8297
2/2
✓ Branch 0 taken 108 times.
✓ Branch 1 taken 18 times.
126 for (uint32_t j = 0; j < numRockets; j++) {
8298 // determine rocket state by its speed:
8299
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 93 times.
108 if (PartSys->sources[j].source.vy > 0) { // moving up, emit exhaust
8300 15 emitparticles = 1;
8301 }
8302
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 1 times.
93 else if (PartSys->sources[j].source.vy < 0) { // falling down, standby time
8303 92 emitparticles = 0;
8304 }
8305 else { // speed is zero, explode!
8306 1 PartSys->sources[j].source.hue = hw_random16(); // random color
8307 1 PartSys->sources[j].source.sat = hw_random16(55) + 200;
8308 1 PartSys->sources[j].maxLife = 200;
8309 1 PartSys->sources[j].minLife = 100;
8310
4/8
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
1 PartSys->sources[j].source.ttl = hw_random16((2000 - ((uint32_t)SEGMENT.speed << 2))) + 550 - (SEGMENT.speed << 1); // standby time til next launch
8311
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 PartSys->sources[j].var = ((SEGMENT.intensity >> 4) + 5); // speed variation around vx,vy (+/- var)
8312 1 PartSys->sources[j].source.vy = -1; // set speed negative so it will emit no more particles after this explosion until relaunch
8313 #ifdef ESP8266
8314 emitparticles = hw_random16(SEGMENT.intensity >> 3) + (SEGMENT.intensity >> 3) + 5; // defines the size of the explosion
8315 #else
8316
4/8
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
1 emitparticles = hw_random16(SEGMENT.intensity >> 2) + (SEGMENT.intensity >> 2) + 5; // defines the size of the explosion
8317 #endif
8318
8319
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (random16() & 1) { // 50% chance for circular explosion
8320 ✗ circularexplosion = true;
8321 ✗ speed = 2 + hw_random16(3) + ((SEGMENT.intensity >> 6));
8322 ✗ currentspeed = speed;
8323 ✗ angleincrement = 2730 + hw_random16(5461); // minimum 15° + random(30°)
8324 ✗ angle = hw_random16(); // random start angle
8325 ✗ baseangle = angle; // save base angle for modulation
8326 ✗ percircle = 0xFFFF / angleincrement + 1; // number of particles to make complete circles
8327 ✗ hueincrement = hw_random16() & 127; // &127 is equivalent to %128
8328 ✗ int circles = 1 + hw_random16(3) + ((SEGMENT.intensity >> 6));
8329 ✗ frequency = hw_random16() & 127; // modulation frequency (= "waves per circle"), x.4 fixed point
8330 ✗ emitparticles = percircle * circles;
8331 ✗ PartSys->sources[j].var = angle & 1; // 0 or 1 variation, angle is random
8332 }
8333 }
8334 uint32_t i;
8335
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 108 times.
128 for (i = 0; i < emitparticles; i++) {
8336
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if (circularexplosion) {
8337 ✗ int32_t sineMod = 0xEFFF + sin16_t((uint16_t)(((angle * frequency) >> 4) + baseangle)); // shifted to positive values
8338 ✗ currentspeed = (speed/2 + ((sineMod * speed) >> 16)) >> 1; // sine modulation on speed based on emit angle
8339 ✗ PartSys->angleEmit(PartSys->sources[j], angle, currentspeed); // note: compiler warnings can be ignored, variables are set just above
8340 ✗ counter++;
8341 ✗ if (counter > percircle) { // full circle completed, increase speed
8342 ✗ counter = 0;
8343 ✗ speed += 3 + ((SEGMENT.intensity >> 6)); // increase speed to form a second wave
8344 ✗ PartSys->sources[j].source.hue += hueincrement; // new color for next circle
8345 ✗ PartSys->sources[j].source.sat = 100 + hw_random16(156);
8346 }
8347 ✗ angle += angleincrement; // set angle for next particle
8348 }
8349 else { // random explosion or exhaust
8350
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 PartSys->sprayEmit(PartSys->sources[j]);
8351
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 if ((j % 3) == 0) {
8352 20 PartSys->sources[j].source.hue = hw_random16(); // random color for each particle (this is also true for exhaust, but that is white anyways)
8353 }
8354 }
8355 }
8356
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 16 times.
108 if (i == 0) // no particles emitted, this rocket is falling
8357 92 PartSys->sources[j].source.y = 1000; // reset position so gravity wont pull it to the ground and bounce it (vy MUST stay negative until relaunch)
8358 108 circularexplosion = false; // reset for next rocket
8359 }
8360
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 18 times.
18 if (SEGMENT.check3) { // fast speed, move particles twice
8361 ✗ for (uint32_t i = 0; i < PartSys->usedParticles; i++) {
8362 ✗ PartSys->particleMoveUpdate(PartSys->particles[i], PartSys->particleFlags[i], nullptr, nullptr);
8363 }
8364 }
8365
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->update(); // update and render
8366
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 return FRAMETIME;
8367 }
8368 #undef NUMBEROFSOURCES
8369 static const char _data_FX_MODE_PARTICLEFIREWORKS[] PROGMEM = "PS Fireworks@Launches,Explosion Size,Fuse,Blur,Gravity,Cylinder,Ground,Fast;;!;2;pal=11,ix=50,c1=40,c2=0,c3=12";
8370
8371 /*
8372 Particle Volcano
8373 Particles are sprayed from below, spray moves back and forth if option is set
8374 Uses palette for particle color
8375 by DedeHai (Damian Schneider)
8376 */
8377 #define NUMBEROFSOURCES 1
8378 23 uint16_t mode_particlevolcano(void) {
8379 23 ParticleSystem2D *PartSys = nullptr;
8380 PSsettings2D volcanosettings;
8381 23 volcanosettings.asByte = 0b00000100; // PS settings for volcano movement: bounceX is enabled
8382 uint8_t numSprays; // note: so far only one tested but more is possible
8383 23 uint32_t i = 0;
8384
8385
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
8386
3/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4 times.
5 if (!initParticleSystem2D(PartSys, NUMBEROFSOURCES)) // init, no additional data needed
8387
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 return mode_static(); // allocation failed or not 2D
8388
8389
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setBounceY(true);
8390
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setGravity(); // enable with default gforce
8391
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setKillOutOfBounds(true); // out of bounds particles dont return (except on top, taken care of by gravity setting)
8392
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setMotionBlur(230); // anable motion blur
8393
8394 4 numSprays = min(PartSys->numSources, (uint32_t)NUMBEROFSOURCES); // number of sprays
8395
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 for (i = 0; i < numSprays; i++) {
8396 4 PartSys->sources[i].source.hue = hw_random16();
8397 4 PartSys->sources[i].source.x = PartSys->maxX / (numSprays + 1) * (i + 1); // distribute evenly
8398 4 PartSys->sources[i].maxLife = 300; // lifetime in frames
8399 4 PartSys->sources[i].minLife = 250;
8400 4 PartSys->sources[i].sourceFlags.collide = true; // seeded particles will collide (if enabled)
8401 4 PartSys->sources[i].sourceFlags.perpetual = true; // source never dies
8402 }
8403 }
8404 else
8405
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem2D *>(SEGENV.data); // if not first call, just set the pointer to the PS
8406
8407
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 18 times.
22 if (PartSys == nullptr)
8408
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 return mode_static(); // something went wrong, no data!
8409
8410 18 numSprays = min(PartSys->numSources, (uint32_t)NUMBEROFSOURCES); // number of volcanoes
8411
8412 // change source emitting color from time to time, emit one particle per spray
8413
6/10
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 9 times.
✓ Branch 9 taken 9 times.
18 if (SEGMENT.call % (11 - (SEGMENT.intensity / 25)) == 0) { // every nth frame, cycle color and emit particles (and update the sources)
8414
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 9 times.
18 for (i = 0; i < numSprays; i++) {
8415 9 PartSys->sources[i].source.y = PS_P_RADIUS + 5; // reset to just above the lower edge that is allowed for bouncing particles, if zero, particles already 'bounce' at start and loose speed.
8416 9 PartSys->sources[i].source.vy = 0; //reset speed (so no extra particlesettin is required to keep the source 'afloat')
8417 9 PartSys->sources[i].source.hue++; // = hw_random16(); //change hue of spray source (note: random does not look good)
8418
3/10
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 9 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 9 times.
✗ Branch 9 not taken.
9 PartSys->sources[i].source.vx = PartSys->sources[i].source.vx > 0 ? (SEGMENT.custom1 >> 2) : -(SEGMENT.custom1 >> 2); // set moving speed but keep the direction given by PS
8419
2/4
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
9 PartSys->sources[i].vy = SEGMENT.speed >> 2; // emitting speed (upwards)
8420 9 PartSys->sources[i].vx = 0;
8421
2/4
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
9 PartSys->sources[i].var = SEGMENT.custom3 >> 1; // emiting variation = nozzle size (custom 3 goes from 0-31)
8422
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 PartSys->sprayEmit(PartSys->sources[i]);
8423
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 PartSys->setWallHardness(255); // full hardness for source bounce
8424
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 PartSys->particleMoveUpdate(PartSys->sources[i].source, PartSys->sources[i].sourceFlags, &volcanosettings); //move the source
8425 }
8426 }
8427
8428 // Particle System settings
8429
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
8430
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setColorByAge(SEGMENT.check1);
8431
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setBounceX(SEGMENT.check2);
8432
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setWallHardness(SEGMENT.custom2);
8433
8434
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 18 times.
18 if (SEGMENT.check3) // collisions enabled
8435 ✗ PartSys->enableParticleCollisions(true, SEGMENT.custom2); // enable collisions and set particle collision hardness
8436 else
8437
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->enableParticleCollisions(false);
8438
8439
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->update(); // update and render
8440
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 return FRAMETIME;
8441 }
8442 #undef NUMBEROFSOURCES
8443 static const char _data_FX_MODE_PARTICLEVOLCANO[] PROGMEM = "PS Volcano@Speed,Intensity,Move,Bounce,Spread,AgeColor,Walls,Collide;;!;2;pal=35,sx=100,ix=190,c1=0,c2=160,c3=6,o1=1";
8444
8445 /*
8446 Particle Fire
8447 realistic fire effect using particles. heat based and using perlin-noise for wind
8448 by DedeHai (Damian Schneider)
8449 */
8450 23 uint16_t mode_particlefire(void) {
8451 23 ParticleSystem2D *PartSys = nullptr;
8452 uint32_t i; // index variable
8453 uint32_t numFlames; // number of flames: depends on fire width. for a fire width of 16 pixels, about 25-30 flames give good results
8454
8455
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
8456
6/10
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 5 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 4 times.
5 if (!initParticleSystem2D(PartSys, SEGMENT.virtualWidth(), 4)) //maximum number of source (PS may limit based on segment size); need 4 additional bytes for time keeping (uint32_t lastcall)
8457
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 return mode_static(); // allocation failed or not 2D
8458
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 SEGENV.aux0 = hw_random16(); // aux0 is wind position (index) in the perlin noise
8459 }
8460 else
8461
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem2D *>(SEGENV.data); // if not first call, just set the pointer to the PS
8462
8463
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 18 times.
22 if (PartSys == nullptr)
8464
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 return mode_static(); // something went wrong, no data!
8465
8466
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
8467
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setWrapX(SEGMENT.check2);
8468
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setMotionBlur(SEGMENT.check1 * 170); // anable/disable motion blur
8469
8470
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 uint32_t firespeed = max((uint8_t)100, SEGMENT.speed); //limit speed to 100 minimum, reduce frame rate to make it slower (slower speeds than 100 do not look nice)
8471
4/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 14 times.
18 if (SEGMENT.speed < 100) { //slow, limit FPS
8472 4 uint32_t *lastcall = reinterpret_cast<uint32_t *>(PartSys->PSdataEnd);
8473
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 uint32_t period = strip().now - *lastcall;
8474
3/6
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
4 if (period < (uint32_t)map(SEGMENT.speed, 0, 99, 50, 10)) { // limit to 90FPS - 20FPS
8475 ✗ SEGMENT.call--; //skipping a frame, decrement the counter (on call0, this is never executed as lastcall is 0, so its fine to not check if >0)
8476 //still need to render the frame or flickering will occur in transitions
8477 ✗ PartSys->updateFire(SEGMENT.intensity, true); // render the fire without updating particles (render only)
8478 ✗ return FRAMETIME; //do not update this frame
8479 }
8480
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 *lastcall = strip().now;
8481 }
8482
8483
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 uint32_t spread = (PartSys->maxX >> 5) * (SEGMENT.custom3 + 1); //fire around segment center (in subpixel points)
8484 18 numFlames = min((uint32_t)PartSys->numSources, (4 + ((spread / PS_P_RADIUS) << 1))); // number of flames used depends on spread with, good value is (fire width in pixel) * 2
8485 18 uint32_t percycle = (numFlames * 2) / 3; // maximum number of particles emitted per cycle (TODO: for ESP826 maybe use flames/2)
8486
8487 // update the flame sprays:
8488
2/2
✓ Branch 0 taken 344 times.
✓ Branch 1 taken 18 times.
362 for (i = 0; i < numFlames; i++) {
8489
7/10
✓ Branch 0 taken 344 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 344 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 152 times.
✓ Branch 5 taken 192 times.
✓ Branch 6 taken 152 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 152 times.
✓ Branch 9 taken 192 times.
344 if (SEGMENT.call & 1 && PartSys->sources[i].source.ttl > 0) { // every second frame
8490 152 PartSys->sources[i].source.ttl--;
8491 } else { // flame source is dead: initialize new flame: set properties of source
8492
1/2
✓ Branch 0 taken 192 times.
✗ Branch 1 not taken.
192 PartSys->sources[i].source.x = (PartSys->maxX >> 1) - (spread >> 1) + hw_random(spread); // change flame position: distribute randomly on chosen width
8493 192 PartSys->sources[i].source.y = -(PS_P_RADIUS << 2); // set the source below the frame
8494
4/8
✓ Branch 0 taken 192 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 192 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 192 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 192 times.
✗ Branch 7 not taken.
192 PartSys->sources[i].source.ttl = 20 + hw_random16((SEGMENT.custom1 * SEGMENT.custom1) >> 8) / (1 + (firespeed >> 5)); //'hotness' of fire, faster flames reduce the effect or flame height will scale too much with speed
8495
3/6
✓ Branch 0 taken 192 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 192 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 192 times.
✗ Branch 5 not taken.
192 PartSys->sources[i].maxLife = hw_random16(SEGMENT.virtualHeight() >> 1) + 16; // defines flame height together with the vy speed, vy speed*maxlife/PS_P_RADIUS is the average flame height
8496 192 PartSys->sources[i].minLife = PartSys->sources[i].maxLife >> 1;
8497 192 PartSys->sources[i].vx = hw_random16(4) - 2; // emitting speed (sideways)
8498
5/10
✓ Branch 0 taken 192 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 192 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 192 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 192 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 192 times.
✗ Branch 9 not taken.
192 PartSys->sources[i].vy = (SEGMENT.virtualHeight() >> 1) + (firespeed >> 4) + (SEGMENT.custom1 >> 4); // emitting speed (upwards)
8499 192 PartSys->sources[i].var = 2 + hw_random16(2 + (firespeed >> 4)); // speed variation around vx,vy (+/- var)
8500 }
8501 }
8502
8503
4/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 10 times.
18 if (SEGMENT.call % 3 == 0) { // update noise position and add wind
8504
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 SEGENV.aux0++; // position in the perlin noise matrix for wind generation
8505
4/6
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 4 times.
8 if (SEGMENT.call % 10 == 0)
8506
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 SEGENV.aux1++; // move in noise y direction so noise does not repeat as often
8507 // add wind force to all particles
8508
7/14
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 8 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 8 times.
✗ Branch 13 not taken.
8 int8_t windspeed = ((int16_t)(perlin8(SEGENV.aux0, SEGENV.aux1) - 127) * SEGMENT.custom2) >> 7;
8509
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 PartSys->applyForce(windspeed, 0);
8510 }
8511
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 SEGENV.step++;
8512
8513
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 18 times.
18 if (SEGMENT.check3) { //add turbulance (parameters and algorithm found by experimentation)
8514 ✗ if (SEGMENT.call % map(firespeed, 0, 255, 4, 15) == 0) {
8515 ✗ for (i = 0; i < PartSys->usedParticles; i++) {
8516 ✗ if (PartSys->particles[i].y < PartSys->maxY / 4) { // do not apply turbulance everywhere -> bottom quarter seems a good balance
8517 ✗ int32_t curl = ((int32_t)perlin8(PartSys->particles[i].x, PartSys->particles[i].y, SEGENV.step << 4) - 127);
8518 ✗ PartSys->particles[i].vx += (curl * (firespeed + 10)) >> 9;
8519 }
8520 }
8521 }
8522 }
8523
8524 18 uint8_t j = hw_random16(); // start with a random flame (so each flame gets the chance to emit a particle if available particles is smaller than number of flames)
8525
2/2
✓ Branch 0 taken 222 times.
✓ Branch 1 taken 18 times.
240 for (i = 0; i < percycle; i++) {
8526 222 j = (j + 1) % numFlames;
8527
1/2
✓ Branch 0 taken 222 times.
✗ Branch 1 not taken.
222 PartSys->flameEmit(PartSys->sources[j]);
8528 }
8529
8530
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->updateFire(SEGMENT.intensity, false); // update and render the fire
8531
8532
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 return FRAMETIME;
8533 }
8534 static const char _data_FX_MODE_PARTICLEFIRE[] PROGMEM = "PS Fire@Speed,Intensity,Flame Height,Wind,Spread,Smooth,Cylinder,Turbulence;;!;2;pal=35,sx=110,c1=110,c2=50,c3=31,o1=1";
8535
8536 /*
8537 PS Ballpit: particles falling down, user can enable these three options: X-wraparound, side bounce, ground bounce
8538 sliders control falling speed, intensity (number of particles spawned), inter-particle collision hardness (0 means no particle collisions) and render saturation
8539 this is quite versatile, can be made to look like rain or snow or confetti etc.
8540 Uses palette for particle color
8541 by DedeHai (Damian Schneider)
8542 */
8543 23 uint16_t mode_particlepit(void) {
8544 23 ParticleSystem2D *PartSys = nullptr;
8545
8546
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
8547
3/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4 times.
5 if (!initParticleSystem2D(PartSys, 0, 0, true, false)) // init
8548
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 return mode_static(); // allocation failed or not 2D
8549
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setKillOutOfBounds(true);
8550
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setGravity(); // enable with default gravity
8551
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setUsedParticles(170); // use 75% of available particles
8552 }
8553 else
8554
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem2D *>(SEGENV.data); // if not first call, just set the pointer to the PS
8555
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 18 times.
22 if (PartSys == nullptr)
8556
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 return mode_static(); // something went wrong, no data!
8557
8558
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
8559
8560
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setWrapX(SEGMENT.check1);
8561
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setBounceX(SEGMENT.check2);
8562
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setBounceY(SEGMENT.check3);
8563
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setWallHardness(min(SEGMENT.custom2, (uint8_t)150)); // limit to 100 min (if collisions are disabled, still want bouncy)
8564
4/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
✓ Branch 5 taken 4 times.
18 if (SEGMENT.custom2 > 0)
8565
3/6
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
14 PartSys->enableParticleCollisions(true, SEGMENT.custom2); // enable collisions and set particle collision hardness
8566 else
8567
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->enableParticleCollisions(false);
8568
8569 uint32_t i;
8570
12/18
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 7 times.
✓ Branch 9 taken 11 times.
✓ Branch 10 taken 7 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 7 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 6 times.
✓ Branch 15 taken 1 times.
✓ Branch 16 taken 6 times.
✓ Branch 17 taken 12 times.
18 if (SEGMENT.call % (128 - (SEGMENT.intensity >> 1)) == 0 && SEGMENT.intensity > 0) { // every nth frame emit particles, stop emitting if set to zero
8571
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 for (i = 0; i < PartSys->usedParticles; i++) { // emit particles
8572
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if (PartSys->particles[i].ttl == 0) { // find a dead particle
8573 // emit particle at random position over the top of the matrix (random16 is not random enough)
8574
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 PartSys->particles[i].ttl = 1500 - (SEGMENT.speed << 2) + hw_random16(500); // if speed is higher, make them die sooner
8575
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 PartSys->particles[i].x = hw_random(PartSys->maxX); //random(PartSys->maxX >> 1) + (PartSys->maxX >> 2);
8576 6 PartSys->particles[i].y = (PartSys->maxY << 1); // particles appear somewhere above the matrix, maximum is double the height
8577
4/8
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
✗ Branch 7 not taken.
6 PartSys->particles[i].vx = (int16_t)hw_random16(SEGMENT.speed >> 1) - (SEGMENT.speed >> 2); // side speed is +/-
8578
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 PartSys->particles[i].vy = map(SEGMENT.speed, 0, 255, -5, -100); // downward speed
8579 6 PartSys->particles[i].hue = hw_random16(); // set random color
8580 6 PartSys->particleFlags[i].collide = true; // enable collision for particle
8581
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 PartSys->particles[i].sat = ((SEGMENT.custom3) << 3) + 7;
8582 // set particle size
8583
4/6
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 2 times.
6 if (SEGMENT.custom1 == 255) {
8584
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setParticleSize(1); // set global size to 1 for advanced rendering (no single pixel particles)
8585
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 PartSys->advPartProps[i].size = hw_random16(SEGMENT.custom1); // set each particle to random size
8586 } else {
8587
3/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 PartSys->setParticleSize(SEGMENT.custom1); // set global size
8588 2 PartSys->advPartProps[i].size = 0; // use global size
8589 }
8590 6 break; // emit only one particle per round
8591 }
8592 }
8593 }
8594
8595
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 uint32_t frictioncoefficient = 1 + SEGMENT.check1; //need more friction if wrapX is set, see below note
8596
4/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 14 times.
18 if (SEGMENT.speed < 50) // for low speeds, apply more friction
8597
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 frictioncoefficient = 50 - SEGMENT.speed;
8598
8599
4/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 14 times.
18 if (SEGMENT.call % 6 == 0)// (3 + max(3, (SEGMENT.speed >> 2))) == 0) // note: if friction is too low, hard particles uncontrollably 'wander' left and right if wrapX is enabled
8600
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->applyFriction(frictioncoefficient);
8601
8602
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->update(); // update and render
8603
8604
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 return FRAMETIME;
8605 }
8606 static const char _data_FX_MODE_PARTICLEPIT[] PROGMEM = "PS Ballpit@Speed,Intensity,Size,Hardness,Saturation,Cylinder,Walls,Ground;;!;2;pal=11,sx=100,ix=220,c1=120,c2=130,c3=31,o3=1";
8607
8608 /*
8609 Particle Waterfall
8610 Uses palette for particle color, spray source at top emitting particles, many config options
8611 by DedeHai (Damian Schneider)
8612 */
8613 23 uint16_t mode_particlewaterfall(void) {
8614 23 ParticleSystem2D *PartSys = nullptr;
8615 uint8_t numSprays;
8616 23 uint32_t i = 0;
8617
8618
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
8619
3/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4 times.
5 if (!initParticleSystem2D(PartSys, 12)) // init, request 12 sources, no additional data needed
8620
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 return mode_static(); // allocation failed or not 2D
8621
8622
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setGravity(); // enable with default gforce
8623
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setKillOutOfBounds(true); // out of bounds particles dont return (except on top, taken care of by gravity setting)
8624
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setMotionBlur(190); // anable motion blur
8625
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setSmearBlur(30); // enable 2D blurring (smearing)
8626
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 4 times.
48 for (i = 0; i < PartSys->numSources; i++) {
8627 44 PartSys->sources[i].source.hue = i*90;
8628 44 PartSys->sources[i].sourceFlags.collide = true; // seeded particles will collide
8629 #ifdef ESP8266
8630 PartSys->sources[i].maxLife = 250; // lifetime in frames (ESP8266 has less particles, make them short lived to keep the water flowing)
8631 PartSys->sources[i].minLife = 100;
8632 #else
8633 44 PartSys->sources[i].maxLife = 400; // lifetime in frames
8634 44 PartSys->sources[i].minLife = 150;
8635 #endif
8636 }
8637 }
8638 else
8639
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem2D *>(SEGENV.data); // if not first call, just set the pointer to the PS
8640
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 18 times.
22 if (PartSys == nullptr)
8641
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 return mode_static(); // something went wrong, no data!
8642
8643 // Particle System settings
8644
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
8645
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setWrapX(SEGMENT.check1); // cylinder
8646
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setBounceX(SEGMENT.check2); // walls
8647
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setBounceY(SEGMENT.check3); // ground
8648
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setWallHardness(SEGMENT.custom2);
8649 18 numSprays = min((int32_t)PartSys->numSources, max(PartSys->maxXpixel / 6, (int32_t)2)); // number of sprays depends on segment width
8650
4/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
✓ Branch 5 taken 4 times.
18 if (SEGMENT.custom2 > 0) // collisions enabled
8651
3/6
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
14 PartSys->enableParticleCollisions(true, SEGMENT.custom2); // enable collisions and set particle collision hardness
8652 else {
8653
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->enableParticleCollisions(false);
8654
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setWallHardness(120); // set hardness (for ground bounce) to fixed value if not using collisions
8655 }
8656
8657
2/2
✓ Branch 0 taken 75 times.
✓ Branch 1 taken 18 times.
93 for (i = 0; i < numSprays; i++) {
8658
2/4
✓ Branch 0 taken 75 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 75 times.
✗ Branch 3 not taken.
75 PartSys->sources[i].source.hue += 1 + hw_random16(SEGMENT.custom1>>1); // change hue of spray source
8659 }
8660
8661
12/18
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 4 times.
✓ Branch 9 taken 14 times.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 4 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 3 times.
✓ Branch 15 taken 1 times.
✓ Branch 16 taken 3 times.
✓ Branch 17 taken 15 times.
18 if (SEGMENT.call % (12 - (SEGMENT.intensity >> 5)) == 0 && SEGMENT.intensity > 0) { // every nth frame, emit particles, do not emit if intensity is zero
8662
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 3 times.
15 for (i = 0; i < numSprays; i++) {
8663
2/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
12 PartSys->sources[i].vy = -SEGMENT.speed >> 3; // emitting speed, down
8664 //PartSys->sources[i].source.x = map(SEGMENT.custom3, 0, 31, 0, (PartSys->maxXpixel - numSprays * 2) * PS_P_RADIUS) + i * PS_P_RADIUS * 2; // emitter position
8665
2/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
12 PartSys->sources[i].source.x = map(SEGMENT.custom3, 0, 31, 0, (PartSys->maxXpixel - numSprays) * PS_P_RADIUS) + i * PS_P_RADIUS * 2; // emitter position
8666 12 PartSys->sources[i].source.y = PartSys->maxY + (PS_P_RADIUS * ((i<<2) + 4)); // source y position, few pixels above the top to increase spreading before entering the matrix
8667
2/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
12 PartSys->sources[i].var = (SEGMENT.custom1 >> 3); // emiting variation 0-32
8668
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 PartSys->sprayEmit(PartSys->sources[i]);
8669 }
8670 }
8671
8672
4/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 14 times.
18 if (SEGMENT.call % 20 == 0)
8673
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->applyFriction(1); // add just a tiny amount of friction to help smooth things
8674
8675
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->update(); // update and render
8676
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 return FRAMETIME;
8677 }
8678 static const char _data_FX_MODE_PARTICLEWATERFALL[] PROGMEM = "PS Waterfall@Speed,Intensity,Variation,Collide,Position,Cylinder,Walls,Ground;;!;2;pal=9,sx=15,ix=200,c1=32,c2=160,o3=1";
8679
8680 /*
8681 Particle Box, applies gravity to particles in either a random direction or random but only downwards (sloshing)
8682 Uses palette for particle color
8683 by DedeHai (Damian Schneider)
8684 */
8685 23 uint16_t mode_particlebox(void) {
8686 23 ParticleSystem2D *PartSys = nullptr;
8687 uint32_t i;
8688
8689
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
8690
3/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4 times.
5 if (!initParticleSystem2D(PartSys, 1)) // init
8691
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 return mode_static(); // allocation failed or not 2D
8692
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setBounceX(true);
8693
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setBounceY(true);
8694
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 SEGENV.aux0 = hw_random16(); // position in perlin noise
8695 }
8696 else
8697
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem2D *>(SEGENV.data); // if not first call, just set the pointer to the PS
8698
8699
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 18 times.
22 if (PartSys == nullptr)
8700
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 return mode_static(); // something went wrong, no data!
8701
8702
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
8703
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setParticleSize(SEGMENT.custom3<<3);
8704
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setWallHardness(min(SEGMENT.custom2, (uint8_t)200)); // wall hardness is 200 or more
8705
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->enableParticleCollisions(true, max(2, (int)SEGMENT.custom2)); // enable collisions and set particle collision hardness
8706
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setUsedParticles(map(SEGMENT.intensity, 0, 255, 2, 153)); // 1% - 60%
8707 // add in new particles if amount has changed
8708
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 2 times.
49 for (i = 0; i < PartSys->usedParticles; i++) {
8709
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 31 times.
47 if (PartSys->particles[i].ttl < 260) { // initialize handed over particles and dead particles
8710 16 PartSys->particles[i].ttl = 260; // full brigthness
8711 16 PartSys->particles[i].x = hw_random16(PartSys->maxX);
8712 16 PartSys->particles[i].y = hw_random16(PartSys->maxY);
8713 16 PartSys->particles[i].hue = hw_random8(); // make it colorful
8714 16 PartSys->particleFlags[i].perpetual = true; // never die
8715 16 PartSys->particleFlags[i].collide = true; // all particles colllide
8716 16 break; // only spawn one particle per frame for less chaotic transitions
8717 }
8718 }
8719
8720
12/18
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 11 times.
✓ Branch 9 taken 7 times.
✓ Branch 10 taken 11 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 11 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 10 times.
✓ Branch 15 taken 1 times.
✓ Branch 16 taken 10 times.
✓ Branch 17 taken 8 times.
18 if (SEGMENT.call % (((255 - SEGMENT.speed) >> 6) + 1) == 0 && SEGMENT.speed > 0) { // how often the force is applied depends on speed setting
8721 int32_t xgravity;
8722 int32_t ygravity;
8723
2/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 int32_t increment = (SEGMENT.speed >> 6) + 1;
8724
8725
3/6
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 10 times.
10 if (SEGMENT.check2) { // washing machine
8726 ✗ int speed = tristate_square8(strip().now >> 7, 90, 15) / ((400 - SEGMENT.speed) >> 3);
8727 ✗ SEGENV.aux0 += speed;
8728 ✗ if (speed == 0) SEGENV.aux0 = 190; //down (= 270°)
8729 }
8730 else
8731
2/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 SEGENV.aux0 -= increment;
8732
8733
3/6
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
10 if (SEGMENT.check1) { // random, use perlin noise
8734
3/6
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
10 xgravity = ((int16_t)perlin8(SEGENV.aux0) - 127);
8735
3/6
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
10 ygravity = ((int16_t)perlin8(SEGENV.aux0 + 10000) - 127);
8736 // scale the gravity force
8737
2/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 xgravity = (xgravity * SEGMENT.custom1) / 128;
8738
2/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 ygravity = (ygravity * SEGMENT.custom1) / 128;
8739 }
8740 else { // go in a circle
8741 ✗ xgravity = ((int32_t)(SEGMENT.custom1) * cos16_t(SEGENV.aux0 << 8)) / 0xFFFF;
8742 ✗ ygravity = ((int32_t)(SEGMENT.custom1) * sin16_t(SEGENV.aux0 << 8)) / 0xFFFF;
8743 }
8744
3/6
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 10 times.
10 if (SEGMENT.check3) { // sloshing, y force is always downwards
8745 ✗ if (ygravity > 0)
8746 ✗ ygravity = -ygravity;
8747 }
8748
8749
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 PartSys->applyForce(xgravity, ygravity);
8750 }
8751
8752
4/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 14 times.
18 if ((SEGMENT.call & 0x0F) == 0) // every 16th frame
8753
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->applyFriction(1);
8754
8755
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->update(); // update and render
8756
8757
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 return FRAMETIME;
8758 }
8759 static const char _data_FX_MODE_PARTICLEBOX[] PROGMEM = "PS Box@!,Particles,Tilt,Hardness,Size,Random,Washing Machine,Sloshing;;!;2;pal=53,ix=50,c3=1,o1=1";
8760
8761 /*
8762 Fuzzy Noise: Perlin noise 'gravity' mapping as in particles on 'noise hills' viewed from above
8763 calculates slope gradient at the particle positions and applies 'downhill' force, resulting in a fuzzy perlin noise display
8764 by DedeHai (Damian Schneider)
8765 */
8766 23 uint16_t mode_particleperlin(void) {
8767 23 ParticleSystem2D *PartSys = nullptr;
8768 uint32_t i;
8769
8770
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
8771
3/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4 times.
5 if (!initParticleSystem2D(PartSys, 1, 0, true)) // init with 1 source and advanced properties
8772
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 return mode_static(); // allocation failed or not 2D
8773
8774
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setKillOutOfBounds(true); // should never happen, but lets make sure there are no stray particles
8775
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setMotionBlur(230); // anable motion blur
8776
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setBounceY(true);
8777
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 SEGENV.aux0 = rand();
8778 }
8779 else
8780
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem2D *>(SEGENV.data); // if not first call, just set the pointer to the PS
8781
8782
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 18 times.
22 if (PartSys == nullptr)
8783
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 return mode_static(); // something went wrong, no data!
8784
8785
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
8786
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setWrapX(SEGMENT.check1);
8787
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setBounceX(!SEGMENT.check1);
8788
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setWallHardness(SEGMENT.custom1); // wall hardness
8789
5/10
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 18 times.
✗ Branch 9 not taken.
18 PartSys->enableParticleCollisions(SEGMENT.check3, SEGMENT.custom1); // enable collisions and set particle collision hardness
8790
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setUsedParticles(map(SEGMENT.intensity, 0, 255, 25, 128)); // min is 10%, max is 50%
8791
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setSmearBlur(SEGMENT.check2 * 15); // enable 2D blurring (smearing)
8792
8793 // apply 'gravity' from a 2D perlin noise map
8794
4/8
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
18 SEGENV.aux0 += 1 + (SEGMENT.speed >> 5); // noise z-position
8795 // update position in noise
8796
2/2
✓ Branch 0 taken 1957 times.
✓ Branch 1 taken 18 times.
1975 for (i = 0; i < PartSys->usedParticles; i++) {
8797
2/2
✓ Branch 0 taken 443 times.
✓ Branch 1 taken 1514 times.
1957 if (PartSys->particles[i].ttl == 0) { // revive dead particles (do not keep them alive forever, they can clump up, need to reseed)
8798 443 PartSys->particles[i].ttl = hw_random16(500) + 200;
8799
1/2
✓ Branch 0 taken 443 times.
✗ Branch 1 not taken.
443 PartSys->particles[i].x = hw_random(PartSys->maxX);
8800
1/2
✓ Branch 0 taken 443 times.
✗ Branch 1 not taken.
443 PartSys->particles[i].y = hw_random(PartSys->maxY);
8801 443 PartSys->particleFlags[i].collide = true; // particle colllides
8802 }
8803
2/4
✓ Branch 0 taken 1957 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1957 times.
✗ Branch 3 not taken.
1957 uint32_t scale = 16 - ((31 - SEGMENT.custom3) >> 1);
8804 1957 uint16_t xnoise = PartSys->particles[i].x / scale; // position in perlin noise, scaled by slider
8805 1957 uint16_t ynoise = PartSys->particles[i].y / scale;
8806
3/6
✓ Branch 0 taken 1957 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1957 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1957 times.
✗ Branch 5 not taken.
1957 int16_t baseheight = perlin8(xnoise, ynoise, SEGENV.aux0); // noise value at particle position
8807 1957 PartSys->particles[i].hue = baseheight; // color particles to perlin noise value
8808
4/6
✓ Branch 0 taken 1957 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1957 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 443 times.
✓ Branch 5 taken 1514 times.
1957 if (SEGMENT.call % 8 == 0) { // do not apply the force every frame, is too chaotic
8809
3/6
✓ Branch 0 taken 443 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 443 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 443 times.
✗ Branch 5 not taken.
443 int8_t xslope = (baseheight + (int16_t)perlin8(xnoise - 10, ynoise, SEGENV.aux0));
8810
3/6
✓ Branch 0 taken 443 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 443 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 443 times.
✗ Branch 5 not taken.
443 int8_t yslope = (baseheight + (int16_t)perlin8(xnoise, ynoise - 10, SEGENV.aux0));
8811
1/2
✓ Branch 0 taken 443 times.
✗ Branch 1 not taken.
443 PartSys->applyForce(i, xslope, yslope);
8812 }
8813 }
8814
8815
6/10
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 7 times.
✓ Branch 9 taken 11 times.
18 if (SEGMENT.call % (16 - (SEGMENT.custom2 >> 4)) == 0)
8816
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 PartSys->applyFriction(2);
8817
8818
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->update(); // update and render
8819
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 return FRAMETIME;
8820 }
8821 static const char _data_FX_MODE_PARTICLEPERLIN[] PROGMEM = "PS Fuzzy Noise@Speed,Particles,Bounce,Friction,Scale,Cylinder,Smear,Collide;;!;2;pal=64,sx=50,ix=200,c1=130,c2=30,c3=5,o3=1";
8822
8823 /*
8824 Particle smashing down like meteors and exploding as they hit the ground, has many parameters to play with
8825 by DedeHai (Damian Schneider)
8826 */
8827 #define NUMBEROFSOURCES 8
8828 23 uint16_t mode_particleimpact(void) {
8829 23 ParticleSystem2D *PartSys = nullptr;
8830 23 uint32_t i = 0;
8831 uint32_t numMeteors;
8832 PSsettings2D meteorsettings;
8833 23 meteorsettings.asByte = 0b00101000; // PS settings for meteors: bounceY and gravity enabled
8834
8835
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
8836
3/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4 times.
5 if (!initParticleSystem2D(PartSys, NUMBEROFSOURCES)) // init, no additional data needed
8837
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 return mode_static(); // allocation failed or not 2D
8838
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setKillOutOfBounds(true);
8839
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setGravity(); // enable default gravity
8840
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setBounceY(true); // always use ground bounce
8841
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setWallRoughness(220); // high roughness
8842 4 numMeteors = min(PartSys->numSources, (uint32_t)NUMBEROFSOURCES);
8843
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 4 times.
36 for (i = 0; i < numMeteors; i++) {
8844 32 PartSys->sources[i].source.ttl = hw_random16(10 * i); // set initial delay for meteors
8845 32 PartSys->sources[i].source.vy = 10; // at positive speeds, no particles are emitted and if particle dies, it will be relaunched
8846 }
8847 }
8848 else
8849
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem2D *>(SEGENV.data); // if not first call, just set the pointer to the PS
8850
8851
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 18 times.
22 if (PartSys == nullptr)
8852
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 return mode_static(); // something went wrong, no data!
8853
8854 // Particle System settings
8855
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
8856
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setWrapX(SEGMENT.check1);
8857
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setBounceX(SEGMENT.check2);
8858
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setMotionBlur(SEGMENT.custom3<<3);
8859
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 uint8_t hardness = map(SEGMENT.custom2, 0, 255, PS_P_MINSURFACEHARDNESS - 2, 255);
8860
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->setWallHardness(hardness);
8861
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->enableParticleCollisions(SEGMENT.check3, hardness); // enable collisions and set particle collision hardness
8862 18 numMeteors = min(PartSys->numSources, (uint32_t)NUMBEROFSOURCES);
8863 uint32_t emitparticles; // number of particles to emit for each rocket's state
8864
8865
2/2
✓ Branch 0 taken 144 times.
✓ Branch 1 taken 18 times.
162 for (i = 0; i < numMeteors; i++) {
8866 // determine meteor state by its speed:
8867
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 126 times.
144 if ( PartSys->sources[i].source.vy < 0) // moving down, emit sparks
8868 18 emitparticles = 1;
8869
1/2
✓ Branch 0 taken 126 times.
✗ Branch 1 not taken.
126 else if ( PartSys->sources[i].source.vy > 0) // moving up means meteor is on 'standby'
8870 126 emitparticles = 0;
8871 else { // speed is zero, explode!
8872 ✗ PartSys->sources[i].source.vy = 10; // set source speed positive so it goes into timeout and launches again
8873 ✗ emitparticles = map(SEGMENT.intensity, 0, 255, 10, hw_random16(PartSys->usedParticles>>2)); // defines the size of the explosion
8874 }
8875
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 144 times.
162 for (int e = emitparticles; e > 0; e--) {
8876
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->sprayEmit(PartSys->sources[i]);
8877 }
8878 }
8879
8880 // update the meteors, set the speed state
8881
2/2
✓ Branch 0 taken 144 times.
✓ Branch 1 taken 18 times.
162 for (i = 0; i < numMeteors; i++) {
8882
2/2
✓ Branch 0 taken 137 times.
✓ Branch 1 taken 7 times.
144 if (PartSys->sources[i].source.ttl) {
8883 137 PartSys->sources[i].source.ttl--; // note: this saves an if statement, but moving down particles age twice
8884
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 119 times.
137 if (PartSys->sources[i].source.vy < 0) { // move down
8885
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->applyGravity(PartSys->sources[i].source);
8886
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->particleMoveUpdate(PartSys->sources[i].source, PartSys->sources[i].sourceFlags, &meteorsettings);
8887
8888 // if source reaches the bottom, set speed to 0 so it will explode on next function call (handled above)
8889
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if (PartSys->sources[i].source.y < PS_P_RADIUS<<1) { // reached the bottom pixel on its way down
8890 ✗ PartSys->sources[i].source.vy = 0; // set speed zero so it will explode
8891 ✗ PartSys->sources[i].source.vx = 0;
8892 ✗ PartSys->sources[i].sourceFlags.collide = true;
8893 #ifdef ESP8266
8894 PartSys->sources[i].maxLife = 900;
8895 PartSys->sources[i].minLife = 100;
8896 #else
8897 ✗ PartSys->sources[i].maxLife = 1250;
8898 ✗ PartSys->sources[i].minLife = 250;
8899 #endif
8900 ✗ PartSys->sources[i].source.ttl = hw_random16((768 - (SEGMENT.speed << 1))) + 40; // standby time til next launch (in frames)
8901 ✗ PartSys->sources[i].vy = (SEGMENT.custom1 >> 2); // emitting speed y
8902 ✗ PartSys->sources[i].var = (SEGMENT.custom1 >> 2); // speed variation around vx,vy (+/- var)
8903 }
8904 }
8905 }
8906
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 else if (PartSys->sources[i].source.vy > 0) { // meteor is exploded and time is up (ttl==0 and positive speed), relaunch it
8907 // reinitialize meteor
8908 7 PartSys->sources[i].source.y = PartSys->maxY + (PS_P_RADIUS << 2); // start 4 pixels above the top
8909
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 PartSys->sources[i].source.x = hw_random(PartSys->maxX);
8910 7 PartSys->sources[i].source.vy = -hw_random16(30) - 30; // meteor downward speed
8911 7 PartSys->sources[i].source.vx = hw_random16(50) - 25; // TODO: make this dependent on position so they do not move out of frame
8912 7 PartSys->sources[i].source.hue = hw_random16(); // random color
8913 7 PartSys->sources[i].source.ttl = 500; // long life, will explode at bottom
8914 7 PartSys->sources[i].sourceFlags.collide = false; // trail particles will not collide
8915 7 PartSys->sources[i].maxLife = 300; // spark particle life
8916 7 PartSys->sources[i].minLife = 100;
8917 7 PartSys->sources[i].vy = -9; // emitting speed (down)
8918 7 PartSys->sources[i].var = 3; // speed variation around vx,vy (+/- var)
8919 }
8920 }
8921
8922
2/2
✓ Branch 0 taken 6776 times.
✓ Branch 1 taken 18 times.
6794 for (uint32_t i = 0; i < PartSys->usedParticles; i++) {
8923
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 6738 times.
6776 if (PartSys->particles[i].ttl > 5) PartSys->particles[i].ttl -= 5; //ttl is linked to brightness, this allows to use higher brightness but still a short spark lifespan
8924 }
8925
8926
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->update(); // update and render
8927
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 return FRAMETIME;
8928 }
8929 #undef NUMBEROFSOURCES
8930 static const char _data_FX_MODE_PARTICLEIMPACT[] PROGMEM = "PS Impact@Launches,!,Force,Hardness,Blur,Cylinder,Walls,Collide;;!;2;pal=0,sx=32,ix=85,c1=70,c2=130,c3=0,o3=1";
8931
8932 /*
8933 Particle Attractor, a particle attractor sits in the matrix center, a spray bounces around and seeds particles
8934 uses inverse square law like in planetary motion
8935 Uses palette for particle color
8936 by DedeHai (Damian Schneider)
8937 */
8938 23 uint16_t mode_particleattractor(void) {
8939 23 ParticleSystem2D *PartSys = nullptr;
8940 PSsettings2D sourcesettings;
8941 23 sourcesettings.asByte = 0b00001100; // PS settings for bounceY, bounceY used for source movement (it always bounces whereas particles do not)
8942 PSparticleFlags attractorFlags;
8943 23 attractorFlags.asByte = 0; // no flags set
8944 PSparticle *attractor; // particle pointer to the attractor
8945
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
8946
3/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4 times.
5 if (!initParticleSystem2D(PartSys, 1, sizeof(PSparticle), true)) // init using 1 source and advanced particle settings
8947
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 return mode_static(); // allocation failed or not 2D
8948 4 PartSys->sources[0].source.hue = hw_random16();
8949 4 PartSys->sources[0].source.vx = -7; // will collied with wall and get random bounce direction
8950 4 PartSys->sources[0].sourceFlags.collide = true; // seeded particles will collide
8951 4 PartSys->sources[0].sourceFlags.perpetual = true; //source does not age
8952 #ifdef ESP8266
8953 PartSys->sources[0].maxLife = 200; // lifetime in frames (ESP8266 has less particles)
8954 PartSys->sources[0].minLife = 30;
8955 #else
8956 4 PartSys->sources[0].maxLife = 350; // lifetime in frames
8957 4 PartSys->sources[0].minLife = 50;
8958 #endif
8959 4 PartSys->sources[0].var = 4; // emiting variation
8960
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setWallHardness(255); //bounce forever
8961
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setWallRoughness(200); //randomize wall bounce
8962 }
8963 else {
8964
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem2D *>(SEGENV.data); // if not first call, just set the pointer to the PS
8965 }
8966
8967
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 18 times.
22 if (PartSys == nullptr)
8968
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 return mode_static(); // something went wrong, no data!
8969
8970 // Particle System settings
8971
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
8972 18 attractor = reinterpret_cast<PSparticle *>(PartSys->PSdataEnd);
8973
8974
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setColorByAge(SEGMENT.check1);
8975
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setParticleSize(SEGMENT.custom1 >> 1); //set size globally
8976
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setUsedParticles(map(SEGMENT.intensity, 0, 255, 25, 190));
8977
8978
4/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 14 times.
18 if (SEGMENT.custom2 > 0) // collisions enabled
8979
3/6
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 PartSys->enableParticleCollisions(true, map(SEGMENT.custom2, 1, 255, 120, 255)); // enable collisions and set particle collision hardness
8980 else
8981
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 PartSys->enableParticleCollisions(false);
8982
8983
4/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 14 times.
18 if (SEGMENT.call == 0) {
8984 4 attractor->vx = PartSys->sources[0].source.vy; // set to spray movemement but reverse x and y
8985 4 attractor->vy = PartSys->sources[0].source.vx;
8986 }
8987
8988 // set attractor properties
8989 18 attractor->ttl = 100; // never dies
8990
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 18 times.
18 if (SEGMENT.check2) {
8991 ✗ if ((SEGMENT.call % 3) == 0) // move slowly
8992 ✗ PartSys->particleMoveUpdate(*attractor, attractorFlags, &sourcesettings); // move the attractor
8993 }
8994 else {
8995 18 attractor->x = PartSys->maxX >> 1; // set to center
8996 18 attractor->y = PartSys->maxY >> 1;
8997 }
8998
8999
4/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 14 times.
18 if (SEGMENT.call % 5 == 0)
9000 4 PartSys->sources[0].source.hue++;
9001
9002
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 SEGENV.aux0 += 256; // emitting angle, one full turn in 255 frames (0xFFFF is 360°)
9003
4/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 10 times.
✓ Branch 5 taken 8 times.
18 if (SEGMENT.call % 2 == 0) // alternate direction of emit
9004
3/6
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
10 PartSys->angleEmit(PartSys->sources[0], SEGENV.aux0, 12);
9005 else
9006
3/6
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 PartSys->angleEmit(PartSys->sources[0], SEGENV.aux0 + 0x7FFF, 12); // emit at 180° as well
9007 // apply force
9008
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 uint32_t strength = SEGMENT.speed;
9009 #ifdef USERMOD_AUDIOREACTIVE
9010 um_data_t *um_data;
9011 if (UsermodManager::getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { // AR active, do not use simulated data
9012 uint32_t volumeSmth = (uint32_t)(*(float*) um_data->u_data[0]); // 0-255
9013 strength = (SEGMENT.speed * volumeSmth) >> 8;
9014 }
9015 #endif
9016
2/2
✓ Branch 0 taken 2138 times.
✓ Branch 1 taken 18 times.
2156 for (uint32_t i = 0; i < PartSys->usedParticles; i++) {
9017
3/6
✓ Branch 0 taken 2138 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2138 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2138 times.
✗ Branch 5 not taken.
2138 PartSys->pointAttractor(i, *attractor, strength, SEGMENT.check3);
9018 }
9019
9020
9021
6/10
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 5 times.
✓ Branch 9 taken 13 times.
18 if (SEGMENT.call % (33 - SEGMENT.custom3) == 0)
9022
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 PartSys->applyFriction(2);
9023
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->particleMoveUpdate(PartSys->sources[0].source, PartSys->sources[0].sourceFlags, &sourcesettings); // move the source
9024
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->update(); // update and render
9025
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 return FRAMETIME;
9026 }
9027 static const char _data_FX_MODE_PARTICLEATTRACTOR[] PROGMEM = "PS Attractor@Mass,Particles,Size,Collide,Friction,AgeColor,Move,Swallow;;!;2;pal=9,sx=100,ix=82,c1=2,c2=0";
9028
9029 /*
9030 Particle Spray, just a particle spray with many parameters
9031 Uses palette for particle color
9032 by DedeHai (Damian Schneider)
9033 */
9034 23 uint16_t mode_particlespray(void) {
9035 23 ParticleSystem2D *PartSys = nullptr;
9036 23 const uint8_t hardness = 200; // collision hardness is fixed
9037
9038
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
9039
3/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4 times.
5 if (!initParticleSystem2D(PartSys, 1)) // init, no additional data needed
9040
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 return mode_static(); // allocation failed or not 2D
9041
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setKillOutOfBounds(true); // out of bounds particles dont return (except on top, taken care of by gravity setting)
9042
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setBounceY(true);
9043
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setMotionBlur(200); // anable motion blur
9044
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setSmearBlur(10); // anable motion blur
9045 4 PartSys->sources[0].source.hue = hw_random16();
9046 4 PartSys->sources[0].sourceFlags.collide = true; // seeded particles will collide (if enabled)
9047 4 PartSys->sources[0].var = 3;
9048 }
9049 else
9050
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem2D *>(SEGENV.data); // if not first call, just set the pointer to the PS
9051
9052
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 18 times.
22 if (PartSys == nullptr)
9053
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 return mode_static(); // something went wrong, no data!
9054
9055 // Particle System settings
9056
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
9057
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setBounceX(!SEGMENT.check2);
9058
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setWrapX(SEGMENT.check2);
9059
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->setWallHardness(hardness);
9060
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setGravity(8 * SEGMENT.check1); // enable gravity if checked (8 is default strength)
9061 //numSprays = min(PartSys->numSources, (uint8_t)1); // number of sprays
9062
9063
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 18 times.
18 if (SEGMENT.check3) // collisions enabled
9064 ✗ PartSys->enableParticleCollisions(true, hardness); // enable collisions and set particle collision hardness
9065 else
9066
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->enableParticleCollisions(false);
9067
9068 //position according to sliders
9069
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys->sources[0].source.x = map(SEGMENT.custom1, 0, 255, 0, PartSys->maxX);
9070
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys->sources[0].source.y = map(SEGMENT.custom2, 0, 255, 0, PartSys->maxY);
9071
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 uint16_t angle = (256 - (((int32_t)SEGMENT.custom3 + 1) << 3)) << 8;
9072
9073 #ifdef USERMOD_AUDIOREACTIVE
9074 um_data_t *um_data;
9075 if (UsermodManager::getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { // get AR data, do not use simulated data
9076 uint32_t volumeSmth = (uint8_t)(*(float*) um_data->u_data[0]); //0 to 255
9077 uint32_t volumeRaw = *(int16_t*)um_data->u_data[1]; //0 to 255
9078 PartSys->sources[0].minLife = 30;
9079
9080 if (SEGMENT.call % 20 == 0 || SEGMENT.call % (11 - volumeSmth / 25) == 0) { // defines interval of particle emit
9081 PartSys->sources[0].maxLife = (volumeSmth >> 1) + (SEGMENT.intensity >> 1); // lifetime in frames
9082 PartSys->sources[0].var = 1 + ((volumeRaw * SEGMENT.speed) >> 12);
9083 uint32_t emitspeed = (SEGMENT.speed >> 2) + (volumeRaw >> 3);
9084 PartSys->sources[0].source.hue += volumeSmth/30;
9085 PartSys->angleEmit(PartSys->sources[0], angle, emitspeed);
9086 }
9087 }
9088 else { //no AR data, fall back to normal mode
9089 // change source properties
9090 if (SEGMENT.call % (11 - (SEGMENT.intensity / 25)) == 0) { // every nth frame, cycle color and emit particles
9091 PartSys->sources[0].maxLife = 300 + SEGMENT.intensity; // lifetime in frames
9092 PartSys->sources[0].minLife = 150 + SEGMENT.intensity;
9093 PartSys->sources[0].source.hue++; // = hw_random16(); //change hue of spray source
9094 PartSys->angleEmit(PartSys->sources[0], angle, SEGMENT.speed >> 2);
9095 }
9096 }
9097 #else
9098 // change source properties
9099
6/10
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 7 times.
✓ Branch 9 taken 11 times.
18 if (SEGMENT.call % (11 - (SEGMENT.intensity / 25)) == 0) { // every nth frame, cycle color and emit particles
9100 7 PartSys->sources[0].maxLife = 300; // lifetime in frames. note: could be done in init part, but AR moderequires this to be dynamic
9101 7 PartSys->sources[0].minLife = 100;
9102 7 PartSys->sources[0].source.hue++; // = hw_random16(); //change hue of spray source
9103 // PartSys->sources[i].var = SEGMENT.custom3; // emiting variation = nozzle size (custom 3 goes from 0-32)
9104 // spray[j].source.hue = hw_random16(); //set random color for each particle (using palette)
9105
3/6
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
7 PartSys->angleEmit(PartSys->sources[0], angle, SEGMENT.speed >> 2);
9106 }
9107 #endif
9108
9109
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->update(); // update and render
9110
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 return FRAMETIME;
9111 }
9112 static const char _data_FX_MODE_PARTICLESPRAY[] PROGMEM = "PS Spray@Speed,!,Left/Right,Up/Down,Angle,Gravity,Cylinder/Square,Collide;;!;2v;pal=0,sx=150,ix=150,c1=220,c2=30,c3=21";
9113
9114
9115 /*
9116 Particle base Graphical Equalizer
9117 Uses palette for particle color
9118 by DedeHai (Damian Schneider)
9119 */
9120 23 uint16_t mode_particleGEQ(void) {
9121 23 ParticleSystem2D *PartSys = nullptr;
9122
9123
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
9124
3/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4 times.
5 if (!initParticleSystem2D(PartSys, 1))
9125
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 return mode_static(); // allocation failed or not 2D
9126
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setKillOutOfBounds(true);
9127
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setUsedParticles(170); // use 2/3 of available particles
9128 }
9129 else
9130
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem2D *>(SEGENV.data); // if not first call, just set the pointer to the PS
9131
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 18 times.
22 if (PartSys == nullptr)
9132
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 return mode_static(); // something went wrong, no data!
9133
9134 uint32_t i;
9135 // set particle system properties
9136
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
9137
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setWrapX(SEGMENT.check1);
9138
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setBounceX(SEGMENT.check2);
9139
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setBounceY(SEGMENT.check3);
9140 //PartSys->enableParticleCollisions(false);
9141
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setWallHardness(SEGMENT.custom2);
9142
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setGravity(SEGMENT.custom3 << 2); // set gravity strength
9143
9144
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 um_data_t *um_data = getAudioData();
9145 18 uint8_t *fftResult = (uint8_t *)um_data->u_data[2]; // 16 bins with FFT data, log mapped already, each band contains frequency amplitude 0-255
9146
9147 //map the bands into 16 positions on x axis, emit some particles according to frequency loudness
9148 18 i = 0;
9149 18 uint32_t binwidth = (PartSys->maxX + 1)>>4; //emit poisition variation for one bin (+/-) is equal to width/16 (for 16 bins)
9150
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 uint32_t threshold = 300 - SEGMENT.intensity;
9151 18 uint32_t emitparticles = 0;
9152
9153
2/2
✓ Branch 0 taken 288 times.
✓ Branch 1 taken 18 times.
306 for (uint32_t bin = 0; bin < 16; bin++) {
9154 288 uint32_t xposition = binwidth*bin + (binwidth>>1); // emit position according to frequency band
9155
2/4
✓ Branch 0 taken 288 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 288 times.
✗ Branch 3 not taken.
288 uint8_t emitspeed = ((uint32_t)fftResult[bin] * (uint32_t)SEGMENT.speed) >> 9; // emit speed according to loudness of band (127 max!)
9156 288 emitparticles = 0;
9157
9158
2/2
✓ Branch 0 taken 107 times.
✓ Branch 1 taken 181 times.
288 if (fftResult[bin] > threshold) {
9159 107 emitparticles = 1;// + (fftResult[bin]>>6);
9160 }
9161
2/2
✓ Branch 0 taken 167 times.
✓ Branch 1 taken 14 times.
181 else if (fftResult[bin] > 0) { // band has low volue
9162 167 uint32_t restvolume = ((threshold - fftResult[bin])>>2) + 2;
9163
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 161 times.
167 if (hw_random16() % restvolume == 0)
9164 6 emitparticles = 1;
9165 }
9166
9167
4/4
✓ Branch 0 taken 519 times.
✓ Branch 1 taken 44 times.
✓ Branch 2 taken 275 times.
✓ Branch 3 taken 244 times.
563 while (i < PartSys->usedParticles && emitparticles > 0) { // emit particles if there are any left, low frequencies take priority
9168
2/2
✓ Branch 0 taken 90 times.
✓ Branch 1 taken 185 times.
275 if (PartSys->particles[i].ttl == 0) { // find a dead particle
9169 //set particle properties TODO: could also use the spray...
9170
2/4
✓ Branch 0 taken 90 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 90 times.
✗ Branch 3 not taken.
90 PartSys->particles[i].ttl = 20 + map(SEGMENT.intensity, 0,255, emitspeed>>1, emitspeed + hw_random16(emitspeed)) ; // set particle alive, particle lifespan is in number of frames
9171 90 PartSys->particles[i].x = xposition + hw_random16(binwidth) - (binwidth>>1); // position randomly, deviating half a bin width
9172 90 PartSys->particles[i].y = PS_P_RADIUS; // start at the bottom (PS_P_RADIUS is minimum position a particle is fully in frame)
9173
4/8
✓ Branch 0 taken 90 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 90 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 90 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 90 times.
✗ Branch 7 not taken.
90 PartSys->particles[i].vx = hw_random16(SEGMENT.custom1>>1)-(SEGMENT.custom1>>2) ; //x-speed variation: +/- custom1/4
9174 90 PartSys->particles[i].vy = emitspeed;
9175 90 PartSys->particles[i].hue = (bin<<4) + hw_random16(17) - 8; // color from palette according to bin
9176 90 emitparticles--;
9177 }
9178 275 i++;
9179 }
9180 }
9181
9182
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->update(); // update and render
9183
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 return FRAMETIME;
9184 }
9185
9186 static const char _data_FX_MODE_PARTICLEGEQ[] PROGMEM = "PS GEQ 2D@Speed,Intensity,Diverge,Bounce,Gravity,Cylinder,Walls,Floor;;!;2f;pal=0,sx=155,ix=200,c1=0";
9187
9188 /*
9189 Particle rotating GEQ
9190 Particles sprayed from center with rotating spray
9191 Uses palette for particle color
9192 by DedeHai (Damian Schneider)
9193 */
9194 #define NUMBEROFSOURCES 16
9195 23 uint16_t mode_particlecenterGEQ(void) {
9196 23 ParticleSystem2D *PartSys = nullptr;
9197 uint8_t numSprays;
9198 uint32_t i;
9199
9200
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
9201
3/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4 times.
5 if (!initParticleSystem2D(PartSys, NUMBEROFSOURCES)) // init, request 16 sources
9202
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 return mode_static(); // allocation failed or not 2D
9203
9204 4 numSprays = min(PartSys->numSources, (uint32_t)NUMBEROFSOURCES);
9205
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 4 times.
60 for (i = 0; i < numSprays; i++) {
9206 56 PartSys->sources[i].source.x = (PartSys->maxX + 1) >> 1; // center
9207 56 PartSys->sources[i].source.y = (PartSys->maxY + 1) >> 1; // center
9208 56 PartSys->sources[i].source.hue = i * 16; // even color distribution
9209 56 PartSys->sources[i].maxLife = 400;
9210 56 PartSys->sources[i].minLife = 200;
9211 }
9212
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setKillOutOfBounds(true);
9213 }
9214 else
9215
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem2D *>(SEGENV.data); // if not first call, just set the pointer to the PS
9216
9217
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 18 times.
22 if (PartSys == nullptr)
9218
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 return mode_static(); // something went wrong, no data!
9219
9220
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
9221 18 numSprays = min(PartSys->numSources, (uint32_t)NUMBEROFSOURCES);
9222
9223
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 um_data_t *um_data = getAudioData();
9224 18 uint8_t *fftResult = (uint8_t *)um_data->u_data[2]; // 16 bins with FFT data, log mapped already, each band contains frequency amplitude 0-255
9225
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 uint32_t threshold = 300 - SEGMENT.intensity;
9226
9227
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 18 times.
18 if (SEGMENT.check2)
9228 ✗ SEGENV.aux0 += SEGMENT.custom1 << 2;
9229 else
9230
4/8
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
18 SEGENV.aux0 -= SEGMENT.custom1 << 2;
9231
9232 18 uint16_t angleoffset = (uint16_t)0xFFFF / (uint16_t)numSprays;
9233 18 uint32_t j = hw_random16(numSprays); // start with random spray so all get a chance to emit a particle if maximum number of particles alive is reached.
9234
2/2
✓ Branch 0 taken 248 times.
✓ Branch 1 taken 18 times.
266 for (i = 0; i < numSprays; i++) {
9235
12/18
✓ Branch 0 taken 248 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 248 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 248 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 248 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 104 times.
✓ Branch 9 taken 144 times.
✓ Branch 10 taken 104 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 104 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 64 times.
✓ Branch 15 taken 40 times.
✓ Branch 16 taken 64 times.
✓ Branch 17 taken 184 times.
248 if (SEGMENT.call % (32 - (SEGMENT.custom2 >> 3)) == 0 && SEGMENT.custom2 > 0)
9236
2/4
✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 64 times.
✗ Branch 3 not taken.
64 PartSys->sources[j].source.hue += 1 + (SEGMENT.custom2 >> 4);
9237
9238
2/4
✓ Branch 0 taken 248 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 248 times.
✗ Branch 3 not taken.
248 PartSys->sources[j].var = SEGMENT.custom3 >> 2;
9239
2/4
✓ Branch 0 taken 248 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 248 times.
✗ Branch 3 not taken.
248 int8_t emitspeed = 5 + (((uint32_t)fftResult[j] * ((uint32_t)SEGMENT.speed + 20)) >> 10); // emit speed according to loudness of band
9240
2/4
✓ Branch 0 taken 248 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 248 times.
✗ Branch 3 not taken.
248 uint16_t emitangle = j * angleoffset + SEGENV.aux0;
9241
9242 248 uint32_t emitparticles = 0;
9243
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 164 times.
248 if (fftResult[j] > threshold)
9244 84 emitparticles = 1;
9245
2/2
✓ Branch 0 taken 154 times.
✓ Branch 1 taken 10 times.
164 else if (fftResult[j] > 0) { // band has low value
9246 154 uint32_t restvolume = ((threshold - fftResult[j]) >> 2) + 2;
9247
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 146 times.
154 if (hw_random16() % restvolume == 0)
9248 8 emitparticles = 1;
9249 }
9250
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 156 times.
248 if (emitparticles)
9251
1/2
✓ Branch 0 taken 92 times.
✗ Branch 1 not taken.
92 PartSys->angleEmit(PartSys->sources[j], emitangle, emitspeed);
9252
9253 248 j = (j + 1) % numSprays;
9254 }
9255
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->update(); // update and render
9256
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 return FRAMETIME;
9257 }
9258 static const char _data_FX_MODE_PARTICLECIRCULARGEQ[] PROGMEM = "PS GEQ Nova@Speed,Intensity,Rotation Speed,Color Change,Nozzle,,Direction;;!;2f;pal=13,ix=180,c1=0,c2=0,c3=8";
9259
9260 /*
9261 Particle replacement of Ghost Rider by DedeHai (Damian Schneider), original FX by stepko adapted by Blaz Kristan (AKA blazoncek)
9262 */
9263 #define MAXANGLESTEP 2200 //32767 means 180°
9264 23 uint16_t mode_particleghostrider(void) {
9265 23 ParticleSystem2D *PartSys = nullptr;
9266 PSsettings2D ghostsettings;
9267 23 ghostsettings.asByte = 0b0000011; //enable wrapX and wrapY
9268
9269
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
9270
3/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4 times.
5 if (!initParticleSystem2D(PartSys, 1)) // init, no additional data needed
9271
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 return mode_static(); // allocation failed or not 2D
9272
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setKillOutOfBounds(true); // out of bounds particles dont return (except on top, taken care of by gravity setting)
9273 4 PartSys->sources[0].maxLife = 260; // lifetime in frames
9274 4 PartSys->sources[0].minLife = 250;
9275 4 PartSys->sources[0].source.x = hw_random16(PartSys->maxX);
9276 4 PartSys->sources[0].source.y = hw_random16(PartSys->maxY);
9277
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 SEGENV.step = hw_random16(MAXANGLESTEP) - (MAXANGLESTEP>>1); // angle increment
9278 }
9279 else {
9280
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem2D *>(SEGENV.data); // if not first call, just set the pointer to the PS
9281 }
9282
9283
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 18 times.
22 if (PartSys == nullptr)
9284
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 return mode_static(); // something went wrong, no data!
9285
9286
4/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 14 times.
18 if (SEGMENT.intensity > 0) { // spiraling
9287
3/6
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
4 if (SEGENV.aux1) {
9288 ✗ SEGENV.step += SEGMENT.intensity>>3;
9289 ✗ if ((int32_t)SEGENV.step > MAXANGLESTEP)
9290 ✗ SEGENV.aux1 = 0;
9291 }
9292 else {
9293
4/8
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
4 SEGENV.step -= SEGMENT.intensity>>3;
9294
3/6
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
4 if ((int32_t)SEGENV.step < -MAXANGLESTEP)
9295 ✗ SEGENV.aux1 = 1;
9296 }
9297 }
9298 // Particle System settings
9299
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
9300
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setMotionBlur(SEGMENT.custom1);
9301
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys->sources[0].var = SEGMENT.custom3 >> 1;
9302
9303 // color by age (PS 'color by age' always starts with hue = 255, don't want that here)
9304
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 if (SEGMENT.check1) {
9305
2/2
✓ Branch 0 taken 6776 times.
✓ Branch 1 taken 18 times.
6794 for (uint32_t i = 0; i < PartSys->usedParticles; i++) {
9306 6776 PartSys->particles[i].hue = PartSys->sources[0].source.hue + (PartSys->particles[i].ttl<<2);
9307 }
9308 }
9309
9310 // enable/disable walls
9311
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 ghostsettings.bounceX = SEGMENT.check2;
9312
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 ghostsettings.bounceY = SEGMENT.check2;
9313
9314
4/8
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
18 SEGENV.aux0 += (int32_t)SEGENV.step; // step is angle increment
9315
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 uint16_t emitangle = SEGENV.aux0 + 32767; // +180°
9316
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 int32_t speed = map(SEGMENT.speed, 0, 255, 12, 64);
9317
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->sources[0].source.vx = ((int32_t)cos16_t(SEGENV.aux0) * speed) / (int32_t)32767;
9318
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->sources[0].source.vy = ((int32_t)sin16_t(SEGENV.aux0) * speed) / (int32_t)32767;
9319 18 PartSys->sources[0].source.ttl = 500; // source never dies (note: setting 'perpetual' is not needed if replenished each frame)
9320
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->particleMoveUpdate(PartSys->sources[0].source, PartSys->sources[0].sourceFlags, &ghostsettings);
9321 // set head (steal one of the particles)
9322 18 PartSys->particles[PartSys->usedParticles-1].x = PartSys->sources[0].source.x;
9323 18 PartSys->particles[PartSys->usedParticles-1].y = PartSys->sources[0].source.y;
9324 18 PartSys->particles[PartSys->usedParticles-1].ttl = 255;
9325 18 PartSys->particles[PartSys->usedParticles-1].sat = 0; //white
9326 // emit two particles
9327
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->angleEmit(PartSys->sources[0], emitangle, speed);
9328
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->angleEmit(PartSys->sources[0], emitangle, speed);
9329
6/10
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 7 times.
✓ Branch 9 taken 11 times.
18 if (SEGMENT.call % (11 - (SEGMENT.custom2 / 25)) == 0) { // every nth frame, cycle color and emit particles
9330 7 PartSys->sources[0].source.hue++;
9331 }
9332
4/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 14 times.
18 if (SEGMENT.custom2 > 190) //fast color change
9333
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 PartSys->sources[0].source.hue += (SEGMENT.custom2 - 190) >> 2;
9334
9335
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->update(); // update and render
9336
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 return FRAMETIME;
9337 }
9338 static const char _data_FX_MODE_PARTICLEGHOSTRIDER[] PROGMEM = "PS Ghost Rider@Speed,Spiral,Blur,Color Cycle,Spread,AgeColor,Walls;;!;2;pal=1,sx=70,ix=0,c1=220,c2=30,c3=21,o1=1";
9339
9340 /*
9341 PS Blobs: large particles bouncing around, changing size and form
9342 Uses palette for particle color
9343 by DedeHai (Damian Schneider)
9344 */
9345 23 uint16_t mode_particleblobs(void) {
9346 23 ParticleSystem2D *PartSys = nullptr;
9347
9348
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) {
9349
3/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4 times.
5 if (!initParticleSystem2D(PartSys, 0, 0, true, true)) //init, no additional bytes, advanced size & size control
9350
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 return mode_static(); // allocation failed or not 2D
9351
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setBounceX(true);
9352
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setBounceY(true);
9353
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setWallHardness(255);
9354
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setWallRoughness(255);
9355
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setCollisionHardness(255);
9356 }
9357 else
9358
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem2D *>(SEGENV.data); // if not first call, just set the pointer to the PS
9359
9360
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 18 times.
22 if (PartSys == nullptr)
9361
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 return mode_static(); // something went wrong, no data!
9362
9363
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
9364
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setUsedParticles(map(SEGMENT.intensity, 0, 255, 25, 128)); // minimum 10%, maximum 50% of available particles (note: PS ensures at least 1)
9365
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->enableParticleCollisions(SEGMENT.check2);
9366
9367
2/2
✓ Branch 0 taken 187 times.
✓ Branch 1 taken 18 times.
205 for (uint32_t i = 0; i < PartSys->usedParticles; i++) { // update particles
9368
10/14
✓ Branch 0 taken 187 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 187 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 187 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 187 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 148 times.
✓ Branch 9 taken 39 times.
✓ Branch 10 taken 5 times.
✓ Branch 11 taken 143 times.
✓ Branch 12 taken 44 times.
✓ Branch 13 taken 143 times.
187 if (SEGENV.aux0 != SEGMENT.speed || PartSys->particles[i].ttl == 0) { // speed changed or dead
9369
4/8
✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 44 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 44 times.
✗ Branch 7 not taken.
44 PartSys->particles[i].vx = (int8_t)hw_random16(SEGMENT.speed >> 1) - (SEGMENT.speed >> 2); // +/- speed/4
9370
4/8
✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 44 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 44 times.
✗ Branch 7 not taken.
44 PartSys->particles[i].vy = (int8_t)hw_random16(SEGMENT.speed >> 1) - (SEGMENT.speed >> 2);
9371 }
9372
10/14
✓ Branch 0 taken 187 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 187 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 187 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 187 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 148 times.
✓ Branch 9 taken 39 times.
✓ Branch 10 taken 5 times.
✓ Branch 11 taken 143 times.
✓ Branch 12 taken 44 times.
✓ Branch 13 taken 143 times.
187 if (SEGENV.aux1 != SEGMENT.custom1 || PartSys->particles[i].ttl == 0) // size changed or dead
9373
4/8
✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 44 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 44 times.
✗ Branch 7 not taken.
44 PartSys->advPartSize[i].maxsize = 60 + (SEGMENT.custom1 >> 1) + hw_random16((SEGMENT.custom1 >> 2)); // set each particle to slightly randomized size
9374
9375 //PartSys->particles[i].perpetual = SEGMENT.check2; //infinite life if set
9376
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 143 times.
187 if (PartSys->particles[i].ttl == 0) { // find dead particle, renitialize
9377
2/4
✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
44 PartSys->particles[i].ttl = 300 + hw_random16(((uint16_t)SEGMENT.custom2 << 3) + 100);
9378
1/2
✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
44 PartSys->particles[i].x = hw_random(PartSys->maxX);
9379 44 PartSys->particles[i].y = hw_random16(PartSys->maxY);
9380 44 PartSys->particles[i].hue = hw_random16(); // set random color
9381 44 PartSys->particleFlags[i].collide = true; // enable collision for particle
9382 44 PartSys->advPartProps[i].size = 0; // start out small
9383 44 PartSys->advPartSize[i].asymmetry = hw_random16(220);
9384 44 PartSys->advPartSize[i].asymdir = hw_random16(255);
9385 // set advanced size control properties
9386 44 PartSys->advPartSize[i].grow = true;
9387 44 PartSys->advPartSize[i].growspeed = 1 + hw_random16(9);
9388 44 PartSys->advPartSize[i].shrinkspeed = 1 + hw_random16(9);
9389 44 PartSys->advPartSize[i].wobblespeed = 1 + hw_random16(3);
9390 }
9391 //PartSys->advPartSize[i].asymmetry++;
9392
2/4
✓ Branch 0 taken 187 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 187 times.
✗ Branch 3 not taken.
187 PartSys->advPartSize[i].pulsate = SEGMENT.check3;
9393
2/4
✓ Branch 0 taken 187 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 187 times.
✗ Branch 3 not taken.
187 PartSys->advPartSize[i].wobble = SEGMENT.check1;
9394 }
9395
4/8
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
18 SEGENV.aux0 = SEGMENT.speed; //write state back
9396
4/8
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
18 SEGENV.aux1 = SEGMENT.custom1;
9397
9398 #ifdef USERMOD_AUDIOREACTIVE
9399 um_data_t *um_data;
9400 if (UsermodManager::getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { // get AR data, do not use simulated data
9401 uint8_t volumeSmth = (uint8_t)(*(float*)um_data->u_data[0]);
9402 for (uint32_t i = 0; i < PartSys->usedParticles; i++) { // update particles
9403 if (SEGMENT.check3) //pulsate selected
9404 PartSys->advPartProps[i].size = volumeSmth;
9405 }
9406 }
9407 #endif
9408
9409
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setMotionBlur(((SEGMENT.custom3) << 3) + 7);
9410
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->update(); // update and render
9411
9412
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 return FRAMETIME;
9413 }
9414 static const char _data_FX_MODE_PARTICLEBLOBS[] PROGMEM = "PS Blobs@Speed,Blobs,Size,Life,Blur,Wobble,Collide,Pulsate;;!;2v;sx=30,ix=64,c1=200,c2=130,c3=0,o3=1";
9415
9416 /////////////////////////
9417 // PS Galaxy //
9418 /////////////////////////
9419 // Spiral galaxy with an emitter moving around, particles spiral into the center.
9420 // Also has a starfield mode where particles fly outwards from the center.
9421 // Ported from upstream WLED. By DedeHai (Damian Schneider)
9422 23 uint16_t mode_particlegalaxy(void) {
9423 23 ParticleSystem2D *PartSys = nullptr;
9424 PSsettings2D sourcesettings;
9425 23 sourcesettings.asByte = 0b00001100; // PS settings for bounceY, bounceY used for source movement (it always bounces whereas particles do not)
9426
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
9427
3/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4 times.
5 if (!initParticleSystem2D(PartSys, 1, 0, true)) // init using 1 source and advanced particle settings
9428
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 return mode_static(); // allocation failed or not 2D
9429 4 PartSys->sources[0].source.vx = -4; // will collide with wall and get random bounce direction
9430 4 PartSys->sources[0].source.x = PartSys->maxX >> 1; // start in the center
9431 4 PartSys->sources[0].source.y = PartSys->maxY >> 1;
9432 4 PartSys->sources[0].sourceFlags.perpetual = true; //source does not age
9433 4 PartSys->sources[0].maxLife = 4000; // lifetime in frames
9434 4 PartSys->sources[0].minLife = 800;
9435 4 PartSys->sources[0].source.hue = hw_random16(); // start with random color
9436
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setWallHardness(255); //bounce forever
9437
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 PartSys->setWallRoughness(200); //randomize wall bounce
9438 }
9439 else {
9440
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem2D *>(SEGENV.data); // if not first call, just set the pointer to the PS
9441 }
9442
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 18 times.
22 if (PartSys == nullptr)
9443
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 return mode_static(); // something went wrong, no data!
9444 // Particle System settings
9445
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
9446
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 uint8_t particlesize = SEGMENT.custom1;
9447
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->setParticleSize(particlesize); // set size globally
9448
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 PartSys->setMotionBlur(250 * SEGMENT.check3); // adds trails to single/quad pixel particles, no effect if size > 1
9449
9450
6/10
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 7 times.
✓ Branch 9 taken 11 times.
18 if ((SEGMENT.call % ((33 - SEGMENT.custom3) >> 1)) == 0) // change hue of emitted particles
9451 7 PartSys->sources[0].source.hue+=2;
9452
9453
4/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 12 times.
18 if (hw_random8() < (10 + (SEGMENT.intensity >> 1))) // 5%-55% chance to emit a particle in this frame
9454
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 PartSys->sprayEmit(PartSys->sources[0]);
9455
9456
4/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 12 times.
18 if ((SEGMENT.call & 0x3) == 0) // every 4th frame, move the emitter
9457
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 PartSys->particleMoveUpdate(PartSys->sources[0].source, PartSys->sources[0].sourceFlags, &sourcesettings);
9458
9459 // move alive particles in a spiral motion (or almost straight in fast starfield mode)
9460 18 int32_t centerx = PartSys->maxX >> 1; // center of matrix in subpixel coordinates
9461 18 int32_t centery = PartSys->maxY >> 1;
9462
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 18 times.
18 if (SEGMENT.check2) { // starfield mode
9463 ✗ PartSys->setKillOutOfBounds(true);
9464 ✗ PartSys->sources[0].var = 7; // emiting variation
9465 ✗ PartSys->sources[0].source.x = centerx; // set emitter to center
9466 ✗ PartSys->sources[0].source.y = centery;
9467 }
9468 else {
9469
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->setKillOutOfBounds(false);
9470 18 PartSys->sources[0].var = 1; // emiting variation
9471 }
9472
2/2
✓ Branch 0 taken 5664 times.
✓ Branch 1 taken 18 times.
5682 for (uint32_t i = 0; i < PartSys->usedParticles; i++) { //check all particles
9473
2/2
✓ Branch 0 taken 5649 times.
✓ Branch 1 taken 15 times.
5664 if (PartSys->particles[i].ttl == 0) continue; //skip dead particles
9474 // (dx/dy): vector pointing from particle to center
9475 15 int32_t dx = centerx - PartSys->particles[i].x;
9476 15 int32_t dy = centery - PartSys->particles[i].y;
9477 //speed towards center:
9478
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 int32_t distance = sqrt32_bw(dx * dx + dy * dy); // absolute distance to center
9479
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 if (distance < 20) distance = 20; // avoid division by zero, keep a minimum
9480 int32_t speedfactor;
9481
3/6
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 15 times.
15 if (SEGMENT.check2) { // starfield mode
9482 ✗ speedfactor = 1 + (1 + (SEGMENT.speed >> 1)) * distance; // speed increases towards edge
9483 //apply velocity
9484 ✗ PartSys->particles[i].x += (-speedfactor * dx) / 400000 - (dy >> 6);
9485 ✗ PartSys->particles[i].y += (-speedfactor * dy) / 400000 + (dx >> 6);
9486 }
9487 else {
9488
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
15 speedfactor = 2 + (((50 + SEGMENT.speed) << 6) / distance); // speed increases towards center
9489 // rotate clockwise
9490 15 int32_t tempVx = (-speedfactor * dy); // speed is orthogonal to center vector
9491 15 int32_t tempVy = (speedfactor * dx);
9492 //add speed towards center to make particles spiral in
9493 15 int vxc = (dx << 9) / (distance - 19); // subtract value from distance to make the pull-in force a bit stronger (helps on faster speeds)
9494 15 int vyc = (dy << 9) / (distance - 19);
9495 //apply velocity
9496 15 PartSys->particles[i].x += (tempVx + vxc) / 1024; // note: cannot use bit shift as that causes asymmetric rounding
9497 15 PartSys->particles[i].y += (tempVy + vyc) / 1024;
9498
9499
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 if (distance < 128) { // close to center
9500
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 if (PartSys->particles[i].ttl > 3)
9501 15 PartSys->particles[i].ttl -= 4; //age fast
9502 15 PartSys->particles[i].sat = distance << 1; // turn white towards center
9503 }
9504 }
9505
4/6
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7 times.
✓ Branch 5 taken 8 times.
15 if(SEGMENT.custom3 == 31) // color by age but mapped to 1024 as particles have a long life, since age is random, this gives more or less random colors
9506 7 PartSys->particles[i].hue = PartSys->particles[i].ttl >> 2;
9507
3/6
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 8 times.
8 else if(SEGMENT.custom3 == 0) // color by distance
9508 ✗ PartSys->particles[i].hue = map(distance, 20, (PartSys->maxX + PartSys->maxY) >> 2, 0, 180); // color by distance to center
9509 }
9510
9511
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->update(); // update and render
9512
9513
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 return FRAMETIME;
9514 }
9515 static const char _data_FX_MODE_PARTICLEGALAXY[] PROGMEM = "PS Galaxy@!,!,Size,,Color,,Starfield,Trace;;!;2;pal=59,sx=80,c1=1,c3=4";
9516
9517 #endif //WLED_DISABLE_PARTICLESYSTEM2D
9518 #endif // WLED_DISABLE_2D
9519
9520 ///////////////////////////
9521 // 1D Particle System FX //
9522 ///////////////////////////
9523
9524 #ifndef WLED_DISABLE_PARTICLESYSTEM1D
9525 /*
9526 Particle version of Drip and Rain
9527 Uses palette for particle color
9528 by DedeHai (Damian Schneider)
9529 */
9530 23 uint16_t mode_particleDrip(void) {
9531 23 ParticleSystem1D *PartSys = nullptr;
9532 //uint8_t numSprays;
9533
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
9534
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
5 if (!initParticleSystem1D(PartSys, 4)) // init
9535 ✗ return mode_static(); // allocation failed or single pixel
9536
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 PartSys->setKillOutOfBounds(true); // out of bounds particles dont return (except on top, taken care of by gravity setting)
9537 5 PartSys->sources[0].source.hue = hw_random16();
9538
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 SEGENV.aux1 = 0xFFFF; // invalidate
9539 }
9540 else
9541
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem1D *>(SEGENV.data); // if not first call, just set the pointer to the PS
9542
9543
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (PartSys == nullptr)
9544 ✗ return mode_static(); // something went wrong, no data!
9545
9546 // Particle System settings
9547
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
9548
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->setBounce(true);
9549
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->setWallHardness(50);
9550
9551
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setMotionBlur(SEGMENT.custom2); // anable motion blur
9552
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setGravity(SEGMENT.custom3 >> 1); // set gravity (8 is default strength)
9553
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setParticleSize(SEGMENT.check3); // 1 or 2 pixel rendering
9554
9555
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 23 times.
23 if (SEGMENT.check2) { //collisions enabled
9556 ✗ PartSys->enableParticleCollisions(true); //enable, full hardness
9557 }
9558 else
9559
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->enableParticleCollisions(false);
9560
9561 23 PartSys->sources[0].sourceFlags.collide = false; //drops do not collide
9562
9563
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 23 times.
23 if (SEGMENT.check1) { //rain mode, emit at random position, short life (3-8 seconds at 50fps)
9564 ✗ if (SEGMENT.custom1 == 0) //splash disabled, do not bounce raindrops
9565 ✗ PartSys->setBounce(false);
9566 ✗ PartSys->sources[0].var = 5;
9567 ✗ PartSys->sources[0].v = -(8 + (SEGMENT.speed >> 2)); //speed + var must be < 128, inverted speed (=down)
9568 // lifetime in frames
9569 ✗ PartSys->sources[0].minLife = 30;
9570 ✗ PartSys->sources[0].maxLife = 200;
9571 ✗ PartSys->sources[0].source.x = hw_random(PartSys->maxX); //random emit position
9572 }
9573 else { //drip
9574 23 PartSys->sources[0].var = 0;
9575
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 PartSys->sources[0].v = -(SEGMENT.speed >> 1); //speed + var must be < 128, inverted speed (=down)
9576 23 PartSys->sources[0].minLife = 3000;
9577 23 PartSys->sources[0].maxLife = 3000;
9578 23 PartSys->sources[0].source.x = PartSys->maxX - PS_P_RADIUS_1D;
9579 }
9580
9581
6/10
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 5 times.
✓ Branch 9 taken 18 times.
23 if (SEGENV.aux1 != SEGMENT.intensity) //slider changed
9582
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 SEGENV.aux0 = 1; //must not be zero or "% 0" happens below which crashes on ESP32
9583
9584
4/8
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
23 SEGENV.aux1 = SEGMENT.intensity; // save state
9585
9586 // every nth frame emit a particle
9587
6/10
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 6 times.
✓ Branch 9 taken 17 times.
23 if (SEGMENT.call % SEGENV.aux0 == 0) {
9588
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 int32_t interval = 300 / ((SEGMENT.intensity) + 1);
9589
3/6
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
6 SEGENV.aux0 = interval + hw_random(interval + 5);
9590 // if (SEGMENT.check1) // rain mode
9591 // PartSys->sources[0].source.hue = 0;
9592 // else
9593 6 PartSys->sources[0].source.hue = hw_random8(); //set random color TODO: maybe also not random but color cycling? need another slider or checkmark for this.
9594
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 PartSys->sprayEmit(PartSys->sources[0]);
9595 }
9596
9597
2/2
✓ Branch 0 taken 7536 times.
✓ Branch 1 taken 23 times.
7559 for (uint32_t i = 0; i < PartSys->usedParticles; i++) { //check all particles
9598
3/4
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 7511 times.
✓ Branch 2 taken 25 times.
✗ Branch 3 not taken.
7536 if (PartSys->particles[i].ttl && PartSys->particleFlags[i].collide == false) { // use collision flag to identify splash particles
9599
6/10
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 21 times.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 21 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 25 times.
25 if (SEGMENT.custom1 > 0 && PartSys->particles[i].x < (PS_P_RADIUS_1D << 1)) { //splash enabled and reached bottom
9600 ✗ PartSys->particles[i].ttl = 0; //kill origin particle
9601 ✗ PartSys->sources[0].maxLife = 80;
9602 ✗ PartSys->sources[0].minLife = 20;
9603 ✗ PartSys->sources[0].var = 10 + (SEGMENT.custom1 >> 3);
9604 ✗ PartSys->sources[0].v = 0;
9605 ✗ PartSys->sources[0].source.hue = PartSys->particles[i].hue;
9606 ✗ PartSys->sources[0].source.x = PS_P_RADIUS_1D;
9607 ✗ PartSys->sources[0].sourceFlags.collide = true; //splashes do collide if enabled
9608 ✗ for (int j = 0; j < 2 + (SEGMENT.custom1 >> 2); j++) {
9609 ✗ PartSys->sprayEmit(PartSys->sources[0]);
9610 }
9611 }
9612 }
9613
9614
3/6
✓ Branch 0 taken 7536 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7536 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 7536 times.
7536 if (SEGMENT.check1) { //rain mode, fade hue to max
9615 ✗ if (PartSys->particles[i].hue < 245)
9616 ✗ PartSys->particles[i].hue += 8;
9617 }
9618 //increase speed on high settings by calling the move function twice
9619
4/6
✓ Branch 0 taken 7536 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7536 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2048 times.
✓ Branch 5 taken 5488 times.
7536 if (SEGMENT.speed > 200)
9620
1/2
✓ Branch 0 taken 2048 times.
✗ Branch 1 not taken.
2048 PartSys->particleMoveUpdate(PartSys->particles[i], PartSys->particleFlags[i]);
9621 }
9622
9623
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->update(); // update and render
9624
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 return FRAMETIME;
9625 }
9626 static const char _data_FX_MODE_PARTICLEDRIP[] PROGMEM = "PS DripDrop@Speed,!,Splash,Blur,Gravity,Rain,PushSplash,Smooth;,!;!;1;pal=0,sx=150,ix=25,c1=220,c2=30,c3=21";
9627
9628
9629 /*
9630 Particle Replacement for "Bbouncing Balls by Aircoookie"
9631 Also replaces rolling balls and juggle (and maybe popcorn)
9632 Uses palette for particle color
9633 by DedeHai (Damian Schneider)
9634 */
9635 23 uint16_t mode_particlePinball(void) {
9636 23 ParticleSystem1D *PartSys = nullptr;
9637
9638
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
9639
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
5 if (!initParticleSystem1D(PartSys, 1, 128, 0, true)) // init
9640 ✗ return mode_static(); // allocation failed or is single pixel
9641 5 PartSys->sources[0].sourceFlags.collide = true; // seeded particles will collide (if enabled)
9642 5 PartSys->sources[0].source.x = PS_P_RADIUS_1D; //emit at bottom
9643
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 PartSys->setKillOutOfBounds(true); // out of bounds particles dont return
9644
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 SEGENV.aux0 = 1;
9645
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 SEGENV.aux1 = 5000; //set out of range to ensure uptate on first call
9646 }
9647 else
9648
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem1D *>(SEGENV.data); // if not first call, just set the pointer to the PS
9649
9650
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (PartSys == nullptr)
9651 ✗ return mode_static(); // something went wrong, no data!
9652
9653 // Particle System settings
9654 //uint32_t hardness = 240 + (SEGMENT.custom1>>4);
9655
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
9656
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setGravity(map(SEGMENT.custom3, 0 , 31, 0 , 16)); // set gravity (8 is default strength)
9657
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setBounce(SEGMENT.custom3); // disables bounce if no gravity is used
9658
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setMotionBlur(SEGMENT.custom2); // anable motion blur
9659
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->enableParticleCollisions(SEGMENT.check1, 255); // enable collisions and set particle collision to high hardness
9660
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setUsedParticles(SEGMENT.intensity);
9661
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setColorByPosition(SEGMENT.check3);
9662
9663 23 bool updateballs = false;
9664
12/22
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 23 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 23 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 23 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 23 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 23 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 23 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 5 times.
✓ Branch 21 taken 18 times.
23 if (SEGENV.aux1 != SEGMENT.speed + SEGMENT.intensity + SEGMENT.check2 + SEGMENT.custom1 + PartSys->usedParticles) { // user settings change or more particles are available
9665
4/8
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 5 times.
✗ Branch 7 not taken.
5 SEGENV.step = SEGMENT.call; // reset delay
9666 5 updateballs = true;
9667
4/6
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 1 times.
5 PartSys->sources[0].maxLife = SEGMENT.custom3 ? 5000 : 0xFFFF; // maximum lifetime in frames/2 (very long if not using gravity, this is enough to travel 4000 pixels at min speed)
9668 5 PartSys->sources[0].minLife = PartSys->sources[0].maxLife >> 1;
9669 }
9670
9671
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 23 times.
23 if (SEGMENT.check2) { //rolling balls
9672 ✗ PartSys->setGravity(0);
9673 ✗ PartSys->setWallHardness(255);
9674 ✗ int speedsum = 0;
9675 ✗ for (uint32_t i = 0; i < PartSys->usedParticles; i++) {
9676 ✗ PartSys->particles[i].ttl = 260; // keep particles alive
9677 ✗ if (updateballs) { //speed changed or particle is dead, set particle properties
9678 ✗ PartSys->particleFlags[i].collide = true;
9679 ✗ if (PartSys->particles[i].x == 0) { // still at initial position (when not switching from a PS)
9680 ✗ PartSys->particles[i].x = hw_random16(PartSys->maxX); // random initial position for all particles
9681 ✗ PartSys->particles[i].vx = (hw_random16() & 0x01) ? 1 : -1; // random initial direction
9682 }
9683 ✗ PartSys->particles[i].hue = hw_random8(); //set ball colors to random
9684 ✗ PartSys->advPartProps[i].sat = 255;
9685 ✗ PartSys->advPartProps[i].size = SEGMENT.custom1;
9686 }
9687 ✗ speedsum += abs(PartSys->particles[i].vx);
9688 }
9689 ✗ int32_t avgSpeed = speedsum / PartSys->usedParticles;
9690 ✗ int32_t setSpeed = 2 + (SEGMENT.speed >> 3);
9691 ✗ if (avgSpeed < setSpeed) { // if balls are slow, speed up some of them at random to keep the animation going
9692 ✗ for (int i = 0; i < setSpeed - avgSpeed; i++) {
9693 ✗ int idx = hw_random16(PartSys->usedParticles);
9694 ✗ PartSys->particles[idx].vx += PartSys->particles[idx].vx >= 0 ? 1 : -1; // add 1, keep direction
9695 }
9696 }
9697 ✗ else if (avgSpeed > setSpeed + 8) // if avg speed is too high, apply friction to slow them down
9698 ✗ PartSys->applyFriction(1);
9699 }
9700 else { //bouncing balls
9701
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->setWallHardness(220);
9702
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 PartSys->sources[0].var = SEGMENT.speed >> 3;
9703
4/8
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
23 int32_t newspeed = 2 + (SEGMENT.speed >> 1) - (SEGMENT.speed >> 3);
9704 23 PartSys->sources[0].v = newspeed;
9705 //check for balls that are 'laying on the ground' and remove them
9706
2/2
✓ Branch 0 taken 1887 times.
✓ Branch 1 taken 23 times.
1910 for (uint32_t i = 0; i < PartSys->usedParticles; i++) {
9707
7/10
✓ Branch 0 taken 1090 times.
✓ Branch 1 taken 797 times.
✓ Branch 2 taken 1090 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1090 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1090 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1090 times.
✓ Branch 9 taken 797 times.
1887 if (PartSys->particles[i].vx == 0 && PartSys->particles[i].x < (PS_P_RADIUS_1D + SEGMENT.custom1))
9708 1090 PartSys->particles[i].ttl = 0;
9709
2/2
✓ Branch 0 taken 415 times.
✓ Branch 1 taken 1472 times.
1887 if (updateballs) {
9710
2/4
✓ Branch 0 taken 415 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 415 times.
✗ Branch 3 not taken.
415 PartSys->advPartProps[i].size = SEGMENT.custom1;
9711
3/6
✓ Branch 0 taken 415 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 415 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 415 times.
415 if (SEGMENT.custom3 == 0) //gravity off, update speed
9712 ✗ PartSys->particles[i].vx = PartSys->particles[i].vx > 0 ? newspeed : -newspeed; //keep the direction
9713 }
9714 }
9715
9716 // every nth frame emit a ball
9717
6/10
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 5 times.
✓ Branch 9 taken 18 times.
23 if (SEGMENT.call > SEGENV.step) {
9718
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 int interval = 260 - ((int)SEGMENT.intensity);
9719
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 SEGENV.step += interval + hw_random16(interval);
9720 5 PartSys->sources[0].source.hue = hw_random16(); //set ball color
9721 5 PartSys->sources[0].sat = 255;
9722
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 PartSys->sources[0].size = SEGMENT.custom1;
9723
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 PartSys->sprayEmit(PartSys->sources[0]);
9724 }
9725 }
9726
10/20
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 23 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 23 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 23 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 23 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 23 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 23 times.
✗ Branch 19 not taken.
23 SEGENV.aux1 = SEGMENT.speed + SEGMENT.intensity + SEGMENT.check2 + SEGMENT.custom1 + PartSys->usedParticles;
9727
2/2
✓ Branch 0 taken 1887 times.
✓ Branch 1 taken 23 times.
1910 for (uint32_t i = 0; i < PartSys->usedParticles; i++) {
9728
1/2
✓ Branch 0 taken 1887 times.
✗ Branch 1 not taken.
1887 PartSys->particleMoveUpdate(PartSys->particles[i], PartSys->particleFlags[i]); // double the speed
9729 }
9730
9731
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->update(); // update and render
9732
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 return FRAMETIME;
9733 }
9734 static const char _data_FX_MODE_PSPINBALL[] PROGMEM = "PS Pinball@Speed,!,Size,Blur,Gravity,Collide,Rolling,Position Color;,!;!;1;pal=0,ix=220,c2=0,c3=8,o1=1";
9735
9736 /*
9737 Particle Replacement for original Dancing Shadows:
9738 "Spotlights moving back and forth that cast dancing shadows.
9739 Shine this through tree branches/leaves or other close-up objects that cast
9740 interesting shadows onto a ceiling or tarp.
9741 By Steve Pomeroy @xxv"
9742 Uses palette for particle color
9743 by DedeHai (Damian Schneider)
9744 */
9745 1283 uint16_t mode_particleDancingShadows(void) {
9746 1283 ParticleSystem1D *PartSys = nullptr;
9747
9748
4/6
✓ Branch 0 taken 1283 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1283 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 1275 times.
1283 if (SEGMENT.call == 0) { // initialization
9749
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 if (!initParticleSystem1D(PartSys, 1)) // init, one source
9750 ✗ return mode_static(); // allocation failed or is single pixel
9751 8 PartSys->sources[0].maxLife = 1000; //set long life (kill out of bounds is done in custom way)
9752 8 PartSys->sources[0].minLife = PartSys->sources[0].maxLife;
9753 }
9754 else {
9755
2/4
✓ Branch 0 taken 1275 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1275 times.
✗ Branch 3 not taken.
1275 PartSys = reinterpret_cast<ParticleSystem1D *>(SEGENV.data); // if not first call, just set the pointer to the PS
9756 }
9757
9758
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1283 times.
1283 if (PartSys == nullptr)
9759 ✗ return mode_static(); // something went wrong, no data!
9760
9761 // Particle System settings
9762
1/2
✓ Branch 0 taken 1283 times.
✗ Branch 1 not taken.
1283 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
9763
3/6
✓ Branch 0 taken 1283 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1283 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1283 times.
✗ Branch 5 not taken.
1283 PartSys->setMotionBlur(SEGMENT.custom1);
9764
3/6
✓ Branch 0 taken 1283 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1283 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1283 times.
1283 if (SEGMENT.check1)
9765 ✗ PartSys->setSmearBlur(120); // enable smear blur
9766 else
9767
1/2
✓ Branch 0 taken 1283 times.
✗ Branch 1 not taken.
1283 PartSys->setSmearBlur(0); // disable smear blur
9768
3/6
✓ Branch 0 taken 1283 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1283 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1283 times.
✗ Branch 5 not taken.
1283 PartSys->setParticleSize(SEGMENT.check3); // 1 or 2 pixel rendering
9769
3/6
✓ Branch 0 taken 1283 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1283 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1283 times.
✗ Branch 5 not taken.
1283 PartSys->setColorByPosition(SEGMENT.check2); // color fixed by position
9770
3/6
✓ Branch 0 taken 1283 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1283 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1283 times.
✗ Branch 5 not taken.
1283 PartSys->setUsedParticles(map(SEGMENT.intensity, 0, 255, 10, 255)); // set percentage of particles to use
9771
9772 1283 uint32_t deadparticles = 0;
9773 //kill out of bounds and moving away plus change color
9774
2/2
✓ Branch 0 taken 35251 times.
✓ Branch 1 taken 1283 times.
36534 for (uint32_t i = 0; i < PartSys->usedParticles; i++) {
9775
8/10
✓ Branch 0 taken 35251 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 35251 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4894 times.
✓ Branch 5 taken 30357 times.
✓ Branch 6 taken 863 times.
✓ Branch 7 taken 4031 times.
✓ Branch 8 taken 863 times.
✓ Branch 9 taken 34388 times.
35251 if (((SEGMENT.call & 0x07) == 0) && PartSys->particleFlags[i].outofbounds) { //check if out of bounds particle move away from strip, only update every 8th frame
9776
2/2
✓ Branch 0 taken 525 times.
✓ Branch 1 taken 338 times.
863 if ((int32_t)PartSys->particles[i].vx * PartSys->particles[i].x > 0) PartSys->particles[i].ttl = 0; //particle is moving away, kill it
9777 }
9778 35251 PartSys->particleFlags[i].perpetual = true; //particles do not age
9779
6/10
✓ Branch 0 taken 35251 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 35251 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 35251 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 35251 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 3583 times.
✓ Branch 9 taken 31668 times.
35251 if (SEGMENT.call % (32 / (1 + (SEGMENT.custom2 >> 3))) == 0)
9780
2/4
✓ Branch 0 taken 3583 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3583 times.
✗ Branch 3 not taken.
3583 PartSys->particles[i].hue += 2 + (SEGMENT.custom2 >> 5);
9781 //note: updating speed on the fly is not accurately possible, since it is unknown which particles are assigned to which spot
9782
6/10
✓ Branch 0 taken 35251 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 35251 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 35251 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 35251 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1076 times.
✓ Branch 9 taken 34175 times.
35251 if (SEGENV.aux0 != SEGMENT.speed) { //speed changed
9783 //update all particle speed by setting them to current value
9784
3/10
✗ Branch 0 not taken.
✓ Branch 1 taken 1076 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1076 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1076 times.
✗ Branch 9 not taken.
1076 PartSys->particles[i].vx = PartSys->particles[i].vx > 0 ? SEGMENT.speed >> 3 : -SEGMENT.speed >> 3;
9785 }
9786
2/2
✓ Branch 0 taken 10057 times.
✓ Branch 1 taken 25194 times.
35251 if (PartSys->particles[i].ttl == 0) deadparticles++; // count dead particles
9787 }
9788
4/8
✓ Branch 0 taken 1283 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1283 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1283 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1283 times.
✗ Branch 7 not taken.
1283 SEGENV.aux0 = SEGMENT.speed;
9789
9790 //generate a spotlight: generates particles just outside of view
9791
8/10
✓ Branch 0 taken 264 times.
✓ Branch 1 taken 1019 times.
✓ Branch 2 taken 264 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 264 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 99 times.
✓ Branch 7 taken 165 times.
✓ Branch 8 taken 99 times.
✓ Branch 9 taken 1184 times.
1283 if (deadparticles > 5 && (SEGMENT.call & 0x03) == 0) {
9792 //random color, random type
9793 99 uint32_t type = hw_random16(SPOT_TYPES_COUNT);
9794
4/8
✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 99 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 99 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 99 times.
✗ Branch 7 not taken.
99 int8_t speed = 2 + hw_random16(2 + (SEGMENT.speed >> 1)) + (SEGMENT.speed >> 4);
9795 99 int32_t width = hw_random16(1, 10);
9796 99 uint32_t ttl = 300; //ttl is particle brightness (below perpetual is set so it does not age, i.e. ttl stays at this value)
9797 int32_t position;
9798 //choose random start position, left and right from the segment
9799
2/2
✓ Branch 0 taken 55 times.
✓ Branch 1 taken 44 times.
99 if (hw_random() & 0x01) {
9800 55 position = PartSys->maxXpixel;
9801 55 speed = -speed;
9802 }
9803 else
9804 44 position = -width;
9805
9806 99 PartSys->sources[0].v = speed; //emitted particle speed
9807 99 PartSys->sources[0].source.hue = hw_random8(); //random spotlight color
9808
2/2
✓ Branch 0 taken 376 times.
✓ Branch 1 taken 99 times.
475 for (int32_t i = 0; i < width; i++) {
9809
2/2
✓ Branch 0 taken 355 times.
✓ Branch 1 taken 21 times.
376 if (width > 1) {
9810
6/7
✓ Branch 0 taken 99 times.
✓ Branch 1 taken 85 times.
✓ Branch 2 taken 90 times.
✓ Branch 3 taken 34 times.
✓ Branch 4 taken 22 times.
✓ Branch 5 taken 25 times.
✗ Branch 6 not taken.
355 switch (type) {
9811 99 case SPOT_TYPE_SOLID:
9812 //nothing to do
9813 99 break;
9814
9815 85 case SPOT_TYPE_GRADIENT:
9816 85 ttl = cubicwave8(map(i, 0, width - 1, 0, 255));
9817 85 ttl = ttl*ttl >> 8; //make gradient more pronounced
9818 85 break;
9819
9820 90 case SPOT_TYPE_2X_GRADIENT:
9821 90 ttl = cubicwave8(2 * map(i, 0, width - 1, 0, 255));
9822 90 ttl = ttl*ttl >> 8;
9823 90 break;
9824
9825 34 case SPOT_TYPE_2X_DOT:
9826
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 11 times.
34 if (i > 0) position++; //skip one pixel
9827 34 i++;
9828 34 break;
9829
9830 22 case SPOT_TYPE_3X_DOT:
9831
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 10 times.
22 if (i > 0) position += 2; //skip two pixels
9832 22 i+=2;
9833 22 break;
9834
9835 25 case SPOT_TYPE_4X_DOT:
9836
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 14 times.
25 if (i > 0) position += 3; //skip three pixels
9837 25 i+=3;
9838 25 break;
9839 }
9840 }
9841 //emit particle
9842 //set the particle source position:
9843 376 PartSys->sources[0].source.x = position * PS_P_RADIUS_1D;
9844
1/2
✓ Branch 0 taken 376 times.
✗ Branch 1 not taken.
376 int32_t partidx = PartSys->sprayEmit(PartSys->sources[0]);
9845
2/2
✓ Branch 0 taken 365 times.
✓ Branch 1 taken 11 times.
376 if (partidx >= 0) PartSys->particles[partidx].ttl = ttl; // -1 = no free particle
9846 376 position++; //do the next pixel
9847 }
9848 }
9849
9850
1/2
✓ Branch 0 taken 1283 times.
✗ Branch 1 not taken.
1283 PartSys->update(); // update and render
9851
9852
1/2
✓ Branch 0 taken 1283 times.
✗ Branch 1 not taken.
1283 return FRAMETIME;
9853 }
9854 static const char _data_FX_MODE_PARTICLEDANCINGSHADOWS[] PROGMEM = "PS Dancing Shadows@Speed,!,Blur,Color Cycle,,Smear,Position Color,Smooth;,!;!;1;sx=100,ix=180,c1=0,c2=0";
9855
9856 /*
9857 Particle Fireworks 1D replacement
9858 Uses palette for particle color
9859 by DedeHai (Damian Schneider)
9860 */
9861 23 uint16_t mode_particleFireworks1D(void) {
9862 23 ParticleSystem1D *PartSys = nullptr;
9863 uint8_t *forcecounter;
9864
9865
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
9866
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
5 if (!initParticleSystem1D(PartSys, 4, 150, 4, true)) // init advanced particle system
9867 ✗ if (!initParticleSystem1D(PartSys, 4, 150, 4, true)) // init advanced particle system
9868 ✗ return mode_static(); // allocation failed or is single pixel
9869
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 PartSys->setKillOutOfBounds(true);
9870 5 PartSys->sources[0].sourceFlags.custom1 = 1; // set rocket state to standby
9871 }
9872 else
9873
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem1D *>(SEGENV.data); // if not first call, just set the pointer to the PS
9874
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (PartSys == nullptr)
9875 ✗ return mode_static(); // something went wrong, no data!
9876
9877 // Particle System settings
9878
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
9879 23 forcecounter = PartSys->PSdataEnd;
9880
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setMotionBlur(SEGMENT.custom2); // anable motion blur
9881
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 int32_t gravity = (1 + (SEGMENT.speed >> 3)); // gravity value used for rocket speed calculation
9882
5/8
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 19 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
23 PartSys->setGravity(SEGMENT.speed ? gravity : 0); // set gravity
9883
9884
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if (PartSys->sources[0].sourceFlags.custom1 == 1) { // rocket is on standby
9885 5 PartSys->sources[0].source.ttl--;
9886
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (PartSys->sources[0].source.ttl == 0) { // time is up, relaunch
9887
9888
4/6
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 3 times.
5 if (hw_random8() < SEGMENT.custom1) // randomly choose direction according to slider, fire at start of segment if true
9889
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 SEGENV.aux0 = 1;
9890 else
9891
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 SEGENV.aux0 = 0;
9892
9893 5 PartSys->sources[0].sourceFlags.custom1 = 0; //flag used for rocket state
9894 5 PartSys->sources[0].source.hue = hw_random16(); // different color for each launch
9895 5 PartSys->sources[0].var = 10; // emit variation
9896 5 PartSys->sources[0].v = -10; // emit speed
9897 5 PartSys->sources[0].minLife = 30;
9898
3/6
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
5 PartSys->sources[0].maxLife = SEGMENT.check2 ? 400 : 60;
9899 5 PartSys->sources[0].source.x = 0; // start from bottom
9900 5 uint32_t speed = sqrt((gravity * ((PartSys->maxX >> 2) + hw_random16(PartSys->maxX >> 1))) >> 4); // set speed such that rocket explods in frame
9901 5 PartSys->sources[0].source.vx = min(speed, (uint32_t)127);
9902 5 PartSys->sources[0].source.ttl = 4000;
9903 5 PartSys->sources[0].sat = 30; // low saturation exhaust
9904
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 PartSys->sources[0].size = SEGMENT.check3; // single or double pixel rendering
9905 5 PartSys->sources[0].sourceFlags.reversegrav = false ; // normal gravity
9906
9907
4/6
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 3 times.
5 if (SEGENV.aux0) { // inverted rockets launch from end
9908 2 PartSys->sources[0].sourceFlags.reversegrav = true;
9909 2 PartSys->sources[0].source.x = PartSys->maxX; // start from top
9910 2 PartSys->sources[0].source.vx = -PartSys->sources[0].source.vx; // revert direction
9911 2 PartSys->sources[0].v = -PartSys->sources[0].v; // invert exhaust emit speed
9912 }
9913 }
9914 }
9915 else { // rocket is launched
9916 18 int32_t rocketgravity = -gravity;
9917 18 int32_t currentspeed = PartSys->sources[0].source.vx;
9918
4/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7 times.
✓ Branch 5 taken 11 times.
18 if (SEGENV.aux0) { // negative speed rocket
9919 7 rocketgravity = -rocketgravity;
9920 7 currentspeed = -currentspeed;
9921 }
9922
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->applyForce(PartSys->sources[0].source, rocketgravity, forcecounter[0]);
9923
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->particleMoveUpdate(PartSys->sources[0].source, PartSys->sources[0].sourceFlags);
9924
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 PartSys->particleMoveUpdate(PartSys->sources[0].source, PartSys->sources[0].sourceFlags); // increase rocket speed by calling the move function twice, also ages twice
9925
4/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7 times.
✓ Branch 5 taken 11 times.
18 uint32_t rocketheight = SEGENV.aux0 ? PartSys->maxX - PartSys->sources[0].source.x : PartSys->sources[0].source.x;
9926
9927
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
18 if (currentspeed < 0 && PartSys->sources[0].source.ttl > 50) // reached apogee
9928 ✗ PartSys->sources[0].source.ttl = min((uint32_t)50, rocketheight >> (PS_P_RADIUS_SHIFT_1D + 3)); // alive for a few more frames
9929
9930
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if (PartSys->sources[0].source.ttl < 2) { // explode
9931 ✗ PartSys->sources[0].sourceFlags.custom1 = 1; // set standby state
9932 ✗ PartSys->sources[0].var = 5 + ((((PartSys->maxX >> 1) + rocketheight) * (200 + SEGMENT.intensity)) / (PartSys->maxX << 2)); // set explosion particle speed
9933 ✗ PartSys->sources[0].minLife = 600;
9934 ✗ PartSys->sources[0].maxLife = 1300;
9935 ✗ PartSys->sources[0].source.ttl = 100 + hw_random16(64 - (SEGMENT.speed >> 2)); // standby time til next launch
9936 ✗ PartSys->sources[0].sat = SEGMENT.custom3 < 16 ? 10 + (SEGMENT.custom3 << 4) : 255; //color saturation
9937 ✗ PartSys->sources[0].size = SEGMENT.check3 ? hw_random16(SEGMENT.intensity) : 0; // random particle size in explosion
9938 ✗ uint32_t explosionsize = 8 + (PartSys->maxXpixel >> 2) + (PartSys->sources[0].source.x >> (PS_P_RADIUS_SHIFT_1D - 1));
9939 ✗ explosionsize += hw_random16((explosionsize * SEGMENT.intensity) >> 8);
9940 ✗ for (uint32_t e = 0; e < explosionsize; e++) { // emit explosion particles
9941 ✗ int idx = PartSys->sprayEmit(PartSys->sources[0]); // emit a particle
9942 ✗ if(SEGMENT.custom3 > 23) {
9943 ✗ if(SEGMENT.custom3 == 31) { // highest slider value
9944 ✗ PartSys->setColorByAge(SEGMENT.check1); // color by age if colorful mode is enabled
9945 ✗ PartSys->setColorByPosition(!SEGMENT.check1); // color by position otherwise
9946 }
9947 else { // if custom3 is set to high value (but not highest), set particle color by initial speed
9948 ✗ PartSys->particles[idx].hue = map(abs(PartSys->particles[idx].vx), 0, PartSys->sources[0].var, 0, 16 + hw_random16(200)); // set hue according to speed, use random amount of palette width
9949 ✗ PartSys->particles[idx].hue += PartSys->sources[0].source.hue; // add hue offset of the rocket (random starting color)
9950 }
9951 }
9952 else {
9953 ✗ if (SEGMENT.check1) // colorful mode
9954 ✗ PartSys->sources[0].source.hue = hw_random16(); //random color for each particle
9955 }
9956 }
9957 }
9958 }
9959
8/12
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
✓ Branch 5 taken 10 times.
✓ Branch 6 taken 13 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 13 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 13 times.
✓ Branch 11 taken 10 times.
23 if ((SEGMENT.call & 0x01) == 0 && PartSys->sources[0].sourceFlags.custom1 == false && PartSys->sources[0].source.ttl > 50) // every second frame and not in standby and not about to explode
9960
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 PartSys->sprayEmit(PartSys->sources[0]); // emit exhaust particle
9961
9962
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 15 times.
23 if ((SEGMENT.call & 0x03) == 0) // every fourth frame
9963
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 PartSys->applyFriction(1); // apply friction to all particles
9964
9965
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->update(); // update and render
9966
9967
2/2
✓ Branch 0 taken 3280 times.
✓ Branch 1 taken 23 times.
3303 for (uint32_t i = 0; i < PartSys->usedParticles; i++) {
9968
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 3258 times.
3280 if (PartSys->particles[i].ttl > 10) PartSys->particles[i].ttl -= 10; //ttl is linked to brightness, this allows to use higher brightness but still a short spark lifespan
9969 3258 else PartSys->particles[i].ttl = 0;
9970 }
9971
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 return FRAMETIME;
9972 }
9973 static const char _data_FX_MODE_PS_FIREWORKS1D[] PROGMEM = "PS Fireworks 1D@Gravity,Explosion,Firing side,Blur,Color,Colorful,Trail,Smooth;,!;!;1;c2=30,o1=1";
9974
9975 /*
9976 Particle based Sparkle effect
9977 Uses palette for particle color
9978 by DedeHai (Damian Schneider)
9979 */
9980 23 uint16_t mode_particleSparkler(void) {
9981 23 ParticleSystem1D *PartSys = nullptr;
9982 uint32_t numSparklers;
9983 PSsettings1D sparklersettings;
9984 23 sparklersettings.asByte = 0; // PS settings for sparkler (set below)
9985
9986
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
9987
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
5 if (!initParticleSystem1D(PartSys, 16, 128 ,0, true)) // init, no additional data needed
9988 ✗ return mode_static(); // allocation failed or is single pixel
9989 } else
9990
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem1D *>(SEGENV.data); // if not first call, just set the pointer to the PS
9991
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (PartSys == nullptr)
9992 ✗ return mode_static(); // something went wrong, no data!
9993
9994 // Particle System settings
9995
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
9996
9997
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 sparklersettings.wrap = !SEGMENT.check2;
9998
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 sparklersettings.bounce = SEGMENT.check2; // note: bounce always takes priority over wrap
9999
10000 23 numSparklers = PartSys->numSources;
10001
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setMotionBlur(SEGMENT.custom2); // anable motion blur/overlay
10002 //PartSys->setSmearBlur(SEGMENT.custom2); // anable smearing blur
10003
10004
2/2
✓ Branch 0 taken 368 times.
✓ Branch 1 taken 23 times.
391 for (uint32_t i = 0; i < numSparklers; i++) {
10005 368 PartSys->sources[i].source.hue = hw_random16();
10006 368 PartSys->sources[i].var = 0; // sparks stationary
10007
2/4
✓ Branch 0 taken 368 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 368 times.
✗ Branch 3 not taken.
368 PartSys->sources[i].minLife = 150 + SEGMENT.intensity;
10008
2/4
✓ Branch 0 taken 368 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 368 times.
✗ Branch 3 not taken.
368 PartSys->sources[i].maxLife = 250 + (SEGMENT.intensity << 1);
10009
2/4
✓ Branch 0 taken 368 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 368 times.
✗ Branch 3 not taken.
368 uint32_t speed = SEGMENT.speed >> 1;
10010
3/6
✓ Branch 0 taken 368 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 368 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 368 times.
368 if (SEGMENT.check1) // sparks move (slide option)
10011 ✗ PartSys->sources[i].var = SEGMENT.intensity >> 3;
10012 368 PartSys->sources[i].source.vx = speed; // update speed, do not change direction
10013 368 PartSys->sources[i].source.ttl = 400; // replenish its life (setting it perpetual uses more code)
10014
2/4
✓ Branch 0 taken 368 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 368 times.
✗ Branch 3 not taken.
368 PartSys->sources[i].sat = SEGMENT.custom1; // color saturation
10015
3/6
✓ Branch 0 taken 368 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 368 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 368 times.
368 PartSys->sources[i].size = SEGMENT.check3 ? 120 : 0;
10016
4/6
✓ Branch 0 taken 368 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 368 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 304 times.
✓ Branch 5 taken 64 times.
368 if (SEGMENT.speed == 255) // random position at highest speed setting
10017 304 PartSys->sources[i].source.x = hw_random16(PartSys->maxX);
10018 else
10019
1/2
✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
64 PartSys->particleMoveUpdate(PartSys->sources[i].source, PartSys->sources[i].sourceFlags, &sparklersettings); //move sparkler
10020 }
10021
10022
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 numSparklers = min(1 + (SEGMENT.custom3 >> 1), (int)numSparklers); // set used sparklers, 1 to 16
10023
10024
6/10
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 4 times.
✓ Branch 9 taken 19 times.
23 if (SEGENV.aux0 != SEGMENT.custom3) { //number of used sparklers changed, redistribute
10025
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 4 times.
28 for (uint32_t i = 1; i < numSparklers; i++) {
10026 24 PartSys->sources[i].source.x = (PartSys->sources[0].source.x + (PartSys->maxX / numSparklers) * i ) % PartSys->maxX; //distribute evenly
10027 }
10028 }
10029
4/8
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
23 SEGENV.aux0 = SEGMENT.custom3;
10030
10031
2/2
✓ Branch 0 taken 128 times.
✓ Branch 1 taken 23 times.
151 for (uint32_t i = 0; i < numSparklers; i++) {
10032
4/6
✓ Branch 0 taken 128 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 128 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 71 times.
✓ Branch 5 taken 57 times.
128 if (hw_random() % (((271 - SEGMENT.intensity) >> 4)) == 0)
10033
1/2
✓ Branch 0 taken 71 times.
✗ Branch 1 not taken.
71 PartSys->sprayEmit(PartSys->sources[i]); //emit a particle
10034 }
10035
10036
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->update(); // update and render
10037
10038
2/2
✓ Branch 0 taken 2824 times.
✓ Branch 1 taken 23 times.
2847 for (uint32_t i = 0; i < PartSys->usedParticles; i++) {
10039
6/10
✓ Branch 0 taken 2824 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2824 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 187 times.
✓ Branch 5 taken 2637 times.
✓ Branch 6 taken 187 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 187 times.
✗ Branch 9 not taken.
2824 if (PartSys->particles[i].ttl > (64 - (SEGMENT.intensity >> 2))) PartSys->particles[i].ttl -= (64 - (SEGMENT.intensity >> 2)); //ttl is linked to brightness, this allows to use higher brightness but still a short spark lifespan
10040 2637 else PartSys->particles[i].ttl = 0;
10041 }
10042
10043
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 return FRAMETIME;
10044 }
10045 static const char _data_FX_MODE_PS_SPARKLER[] PROGMEM = "PS Sparkler@Move,!,Saturation,Blur,Sparklers,Slide,Bounce,Large;,!;!;1;pal=0,sx=255,c1=0,c2=0,c3=6";
10046
10047 /*
10048 Particle based Hourglass, particles falling at defined intervals
10049 Uses palette for particle color
10050 by DedeHai (Damian Schneider)
10051 */
10052 23 uint16_t mode_particleHourglass(void) {
10053 23 ParticleSystem1D *PartSys = nullptr;
10054 23 constexpr int positionOffset = PS_P_RADIUS_1D / 2;; // resting position offset
10055 bool* direction;
10056 uint32_t* settingTracker;
10057
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
10058
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
5 if (!initParticleSystem1D(PartSys, 0, 255, 8, false)) // init
10059 ✗ return mode_static(); // allocation failed or is single pixel
10060
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 PartSys->setBounce(true);
10061
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 PartSys->setWallHardness(100);
10062 }
10063 else
10064
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem1D *>(SEGENV.data); // if not first call, just set the pointer to the PS
10065
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (PartSys == nullptr)
10066 ✗ return mode_static(); // something went wrong, no data!
10067
10068 // Particle System settings
10069
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
10070 23 settingTracker = reinterpret_cast<uint32_t *>(PartSys->PSdataEnd); //assign data pointer
10071 23 direction = reinterpret_cast<bool *>(PartSys->PSdataEnd + 4); //assign data pointer
10072
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setUsedParticles(1 + ((SEGMENT.intensity * 255) >> 8));
10073
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setMotionBlur(SEGMENT.custom2); // anable motion blur
10074
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setGravity(map(SEGMENT.custom3, 0, 31, 1, 30));
10075
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->enableParticleCollisions(true, 32); // hardness value found by experimentation on different settings
10076
10077
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 uint32_t colormode = SEGMENT.custom1 >> 5; // 0-7
10078
10079
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 19 times.
23 if (SEGMENT.intensity != *settingTracker) { // initialize
10080
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 *settingTracker = SEGMENT.intensity;
10081
2/2
✓ Branch 0 taken 1051 times.
✓ Branch 1 taken 4 times.
1055 for (uint32_t i = 0; i < PartSys->usedParticles; i++) {
10082 1051 PartSys->particleFlags[i].reversegrav = true; // resting particles dont fall
10083 1051 *direction = 0; // down
10084
2/4
✓ Branch 0 taken 1051 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1051 times.
✗ Branch 3 not taken.
1051 SEGENV.aux1 = 1; // initialize below
10085 }
10086
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 SEGENV.aux0 = PartSys->usedParticles - 1; // initial state, start with highest number particle
10087 }
10088
10089 // calculate target position depending on direction
10090 2117 auto calcTargetPos = [&](size_t i) {
10091
2/2
✓ Branch 0 taken 2102 times.
✓ Branch 1 taken 15 times.
2117 return PartSys->particleFlags[i].reversegrav ?
10092 2102 PartSys->maxX - i * PS_P_RADIUS_1D - positionOffset
10093 2117 : (PartSys->usedParticles - i) * PS_P_RADIUS_1D - positionOffset;
10094 23 };
10095
10096
10097
2/2
✓ Branch 0 taken 4759 times.
✓ Branch 1 taken 23 times.
4782 for (uint32_t i = 0; i < PartSys->usedParticles; i++) { // check if particle reached target position after falling
10098
3/4
✓ Branch 0 taken 1066 times.
✓ Branch 1 taken 3693 times.
✓ Branch 2 taken 1066 times.
✗ Branch 3 not taken.
4759 if (PartSys->particleFlags[i].fixed == false && abs(PartSys->particles[i].vx) < 5) {
10099 1066 int32_t targetposition = calcTargetPos(i);
10100 1066 bool closeToTarget = abs(targetposition - PartSys->particles[i].x) < 3 * PS_P_RADIUS_1D;
10101
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1058 times.
1066 if (closeToTarget) { // close to target and slow speed
10102 8 PartSys->particles[i].x = targetposition; // set exact position
10103 8 PartSys->particleFlags[i].fixed = true; // pin particle
10104 }
10105 }
10106
2/2
✓ Branch 0 taken 2048 times.
✓ Branch 1 taken 2711 times.
4759 if (colormode == 7)
10107
1/2
✓ Branch 0 taken 2048 times.
✗ Branch 1 not taken.
2048 PartSys->setColorByPosition(true); // color fixed by position
10108 else {
10109
1/2
✓ Branch 0 taken 2711 times.
✗ Branch 1 not taken.
2711 PartSys->setColorByPosition(false);
10110
2/4
✓ Branch 0 taken 2711 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2711 times.
✗ Branch 3 not taken.
2711 uint8_t basehue = ((SEGMENT.custom1 & 0x1F) << 3); // use 5 LSBs to select color
10111
2/7
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2695 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
2711 switch(colormode) {
10112 16 case 0: PartSys->particles[i].hue = 120; break; // fixed at 120, if flip is activated, this can make red and green (use palette 34)
10113 ✗ case 1: PartSys->particles[i].hue = basehue; break; // fixed selectable color
10114 ✗ case 2: // 2 colors inverleaved (same code as 3)
10115 ✗ case 3: PartSys->particles[i].hue = ((SEGMENT.custom1 & 0x1F) << 1) + (i % colormode)*74; break; // interleved colors (every 2 or 3 particles)
10116 2695 case 4: PartSys->particles[i].hue = basehue + (i * 255) / PartSys->usedParticles; break; // gradient palette colors
10117 ✗ case 5: PartSys->particles[i].hue = basehue + (i * 1024) / PartSys->usedParticles; break; // multi gradient palette colors
10118 ✗ case 6: PartSys->particles[i].hue = i + (strip().now >> 3); break; // disco! moving color gradient
10119 ✗ default: break;
10120 }
10121 }
10122
7/10
✓ Branch 0 taken 4759 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4759 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4759 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 27 times.
✓ Branch 7 taken 4732 times.
✓ Branch 8 taken 27 times.
✓ Branch 9 taken 4732 times.
4759 if (SEGMENT.check1 && !PartSys->particleFlags[i].reversegrav) // flip color when fallen
10123 27 PartSys->particles[i].hue += 120;
10124 }
10125
10126 // re-order particles in case collisions flipped particles (highest number index particle is on the "bottom")
10127
2/2
✓ Branch 0 taken 4736 times.
✓ Branch 1 taken 23 times.
4759 for (uint32_t i = 0; i < PartSys->usedParticles - 1; i++) {
10128
4/6
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 4731 times.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
4736 if (PartSys->particles[i].x < PartSys->particles[i+1].x && PartSys->particleFlags[i].fixed == false && PartSys->particleFlags[i+1].fixed == false) {
10129 ✗ std::swap(PartSys->particles[i].x, PartSys->particles[i+1].x);
10130 }
10131 }
10132
10133
10134
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 19 times.
23 if (SEGENV.aux1 == 1) { // last countdown call before dropping starts, reset all particles
10135
2/2
✓ Branch 0 taken 1051 times.
✓ Branch 1 taken 4 times.
1055 for (uint32_t i = 0; i < PartSys->usedParticles; i++) {
10136 1051 PartSys->particleFlags[i].collide = true;
10137 1051 PartSys->particleFlags[i].perpetual = true;
10138 1051 PartSys->particles[i].ttl = 260;
10139 1051 PartSys->particles[i].x = calcTargetPos(i);
10140 1051 PartSys->particleFlags[i].fixed = true;
10141 }
10142 }
10143
10144
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 17 times.
✓ Branch 5 taken 6 times.
23 if (SEGENV.aux1 == 0) { // countdown passed, run
10145
5/8
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
✓ Branch 7 taken 11 times.
17 if (strip().now >= SEGENV.step) { // drop a particle, do not drop more often than every second frame or particles tangle up quite badly
10146 // set next drop time
10147
5/10
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 6 times.
6 if (SEGMENT.check3 && *direction) // fast reset
10148 ✗ SEGENV.step = strip().now + 100; // drop one particle every 100ms
10149 else // normal interval
10150
5/10
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 6 times.
✗ Branch 9 not taken.
6 SEGENV.step = strip().now + max(20, SEGMENT.speed * 20); // map speed slider from 0.1s to 5s
10151
4/6
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 1 times.
6 if (SEGENV.aux0 < PartSys->usedParticles) {
10152
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 PartSys->particleFlags[SEGENV.aux0].reversegrav = *direction; // let this particle fall or rise
10153
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 PartSys->particleFlags[SEGENV.aux0].fixed = false; // unpin
10154 }
10155 else { // overflow
10156 1 *direction = !(*direction); // flip direction
10157
5/10
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
1 SEGENV.aux1 = SEGMENT.virtualLength() + 100; // set countdown
10158 }
10159
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 if (*direction == 0) // down, start dropping the highest number particle
10160
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 SEGENV.aux0--; // next particle
10161 else
10162
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 SEGENV.aux0++;
10163 }
10164 }
10165
3/6
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
6 else if (SEGMENT.check2) // auto reset
10166
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 SEGENV.aux1--; // countdown
10167
10168
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->update(); // update and render
10169
10170
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 return FRAMETIME;
10171 }
10172 static const char _data_FX_MODE_PS_HOURGLASS[] PROGMEM = "PS Hourglass@Interval,!,Color,Blur,Gravity,Colorflip,Start,Fast Reset;,!;!;1;pal=34,sx=50,ix=200,c1=140,c2=80,c3=4,o1=1,o2=1,o3=1";
10173
10174 /*
10175 Particle based Spray effect (like a volcano, possible replacement for popcorn)
10176 Uses palette for particle color
10177 by DedeHai (Damian Schneider)
10178 */
10179 23 uint16_t mode_particle1Dspray(void) {
10180 23 ParticleSystem1D *PartSys = nullptr;
10181
10182
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
10183
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
5 if (!initParticleSystem1D(PartSys, 1))
10184 ✗ return mode_static(); // allocation failed or is single pixel
10185
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 PartSys->setKillOutOfBounds(true);
10186
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 PartSys->setWallHardness(150);
10187
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 PartSys->setParticleSize(1);
10188 }
10189 else
10190
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem1D *>(SEGENV.data); // if not first call, just set the pointer to the PS
10191
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (PartSys == nullptr)
10192 ✗ return mode_static(); // something went wrong, no data!
10193
10194 // Particle System settings
10195
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
10196
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setBounce(SEGMENT.check2);
10197
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setMotionBlur(SEGMENT.custom2); // anable motion blur
10198
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 int32_t gravity = -((int32_t)SEGMENT.custom3 - 16); // gravity setting, 0-15 is positive (down), 17 - 31 is negative (up)
10199
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->setGravity(abs(gravity)); // use reversgrav setting to invert gravity (for proper 'floor' and out of bounce handling)
10200
10201
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 PartSys->sources[0].source.hue = SEGMENT.aux0; // hw_random16();
10202 23 PartSys->sources[0].var = 20;
10203 23 PartSys->sources[0].minLife = 200;
10204 23 PartSys->sources[0].maxLife = 400;
10205
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 PartSys->sources[0].source.x = map(SEGMENT.custom1, 0 , 255, 0, PartSys->maxX); // spray position
10206
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 PartSys->sources[0].v = map(SEGMENT.speed, 0 , 255, -127 + PartSys->sources[0].var, 127 - PartSys->sources[0].var); // particle emit speed
10207 23 PartSys->sources[0].sourceFlags.reversegrav = gravity < 0 ? true : false;
10208
10209
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 15 times.
23 if (hw_random() % (1 + ((255 - SEGMENT.intensity) >> 3)) == 0) {
10210
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 PartSys->sprayEmit(PartSys->sources[0]); // emit a particle
10211
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 SEGMENT.aux0++; // increment hue
10212 }
10213
10214 //update color settings
10215
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setColorByAge(SEGMENT.check1); // overruled by 'color by position'
10216
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setColorByPosition(SEGMENT.check3);
10217
2/2
✓ Branch 0 taken 7536 times.
✓ Branch 1 taken 23 times.
7559 for (uint i = 0; i < PartSys->usedParticles; i++) {
10218 7536 PartSys->particleFlags[i].reversegrav = PartSys->sources[0].sourceFlags.reversegrav; // update gravity direction
10219 }
10220
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->update(); // update and render
10221
10222
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 return FRAMETIME;
10223 }
10224 static const char _data_FX_MODE_PS_1DSPRAY[] PROGMEM = "PS Spray 1D@Speed(+/-),!,Position,Blur,Gravity(+/-),AgeColor,Bounce,Position Color;,!;!;1;sx=200,ix=220,c1=0,c2=0";
10225
10226 /*
10227 Particle based balance: particles move back and forth (1D pendent to 2D particle box)
10228 Uses palette for particle color
10229 by DedeHai (Damian Schneider)
10230 */
10231 23 uint16_t mode_particleBalance(void) {
10232 23 ParticleSystem1D *PartSys = nullptr;
10233 uint32_t i;
10234
10235
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
10236
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
5 if (!initParticleSystem1D(PartSys, 1, 128)) // init, no additional data needed, use half of max particles
10237 ✗ return mode_static(); // allocation failed or is single pixel
10238
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 PartSys->setParticleSize(1);
10239 }
10240 else
10241
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem1D *>(SEGENV.data); // if not first call, just set the pointer to the PS
10242
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (PartSys == nullptr)
10243 ✗ return mode_static(); // something went wrong, no data!
10244
10245 // Particle System settings
10246
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
10247
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setMotionBlur(SEGMENT.custom2); // enable motion blur
10248
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setBounce(!SEGMENT.check2);
10249
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setWrap(SEGMENT.check2);
10250
6/10
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 19 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 19 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 19 times.
✗ Branch 9 not taken.
23 uint8_t hardness = SEGMENT.custom1 > 0 ? map(SEGMENT.custom1, 0, 255, 50, 250) : 200; // set hardness, make the walls hard if collisions are disabled
10251
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->enableParticleCollisions(SEGMENT.custom1, hardness); // enable collisions if custom1 > 0
10252
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->setWallHardness(200);
10253
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setUsedParticles(map(SEGMENT.intensity, 0, 255, 10, 255));
10254
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (PartSys->usedParticles > SEGENV.aux1) { // more particles, reinitialize
10255
2/2
✓ Branch 0 taken 455 times.
✓ Branch 1 taken 5 times.
460 for (i = 0; i < PartSys->usedParticles; i++) {
10256 455 PartSys->particles[i].x = i * PS_P_RADIUS_1D;
10257 455 PartSys->particles[i].ttl = 300;
10258 455 PartSys->particleFlags[i].perpetual = true;
10259 455 PartSys->particleFlags[i].collide = true;
10260 }
10261 }
10262
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 SEGENV.aux1 = PartSys->usedParticles;
10263
10264 // re-order particles in case collisions flipped particles
10265
2/2
✓ Branch 0 taken 1981 times.
✓ Branch 1 taken 23 times.
2004 for (i = 0; i < PartSys->usedParticles - 1; i++) {
10266
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1981 times.
1981 if (PartSys->particles[i].x > PartSys->particles[i+1].x) {
10267 ✗ if (SEGMENT.check2) { // check for wrap around
10268 ✗ if (PartSys->particles[i].x - PartSys->particles[i+1].x > 3 * PS_P_RADIUS_1D)
10269 ✗ continue;
10270 }
10271 ✗ std::swap(PartSys->particles[i].x, PartSys->particles[i+1].x);
10272 }
10273 }
10274
10275
6/10
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 14 times.
✓ Branch 9 taken 9 times.
23 if (SEGMENT.call % (((255 - SEGMENT.speed) >> 6) + 1) == 0) { // how often the force is applied depends on speed setting
10276 int32_t xgravity;
10277
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 int32_t increment = (SEGMENT.speed >> 6) + 1;
10278
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 SEGENV.aux0 += increment;
10279
3/6
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 14 times.
14 if (SEGMENT.check3) // random, use perlin noise
10280 ✗ xgravity = ((int16_t)perlin8(SEGENV.aux0) - 128);
10281 else // sinusoidal
10282
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 xgravity = (int16_t)cos8(SEGENV.aux0) - 128;//((int32_t)(SEGMENT.custom3 << 2) * cos8(SEGENV.aux0)
10283 // scale the force
10284
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 xgravity = (xgravity * ((SEGMENT.custom3+1) << 2)) / 128; // xgravity: -127 to +127
10285
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 PartSys->applyForce(xgravity);
10286 }
10287
10288 23 uint32_t randomindex = hw_random16(PartSys->usedParticles);
10289 23 PartSys->particles[randomindex].vx = ((int32_t)PartSys->particles[randomindex].vx * 200) / 255; // apply friction to random particle to reduce clumping (without collisions)
10290
10291 //if (SEGMENT.check2 && (SEGMENT.call & 0x07) == 0) // no walls, apply friction to smooth things out
10292
10/14
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
✓ Branch 6 taken 5 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 5 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 4 times.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 22 times.
23 if ((SEGMENT.call & 0x0F) == 0 && SEGMENT.custom3 > 4) // apply friction every 16th frame to smooth things out (except for low tilt)
10293
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 PartSys->applyFriction(1); // apply friction to all particles
10294
10295 //update colors
10296
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setColorByPosition(SEGMENT.check1);
10297
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 23 times.
23 if (!SEGMENT.check1) {
10298 ✗ for (i = 0; i < PartSys->usedParticles; i++) {
10299 ✗ PartSys->particles[i].hue = (1024 * i) / PartSys->usedParticles; // color by particle index
10300 }
10301 }
10302
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->update(); // update and render
10303
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 return FRAMETIME;
10304 }
10305 static const char _data_FX_MODE_PS_BALANCE[] PROGMEM = "PS 1D Balance@!,!,Hardness,Blur,Tilt,Position Color,Wrap,Random;,!;!;1;pal=18,c2=0,c3=4,o1=1";
10306
10307 /*
10308 Particle based Chase effect
10309 Uses palette for particle color
10310 by DedeHai (Damian Schneider)
10311 */
10312 23 uint16_t mode_particleChase(void) {
10313 23 ParticleSystem1D *PartSys = nullptr;
10314
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
10315
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
5 if (!initParticleSystem1D(PartSys, 1, 255, 2, true)) // init
10316 ✗ return mode_static(); // allocation failed or is single pixel
10317
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 SEGENV.aux0 = 0xFFFF; // invalidate
10318 5 *PartSys->PSdataEnd = 1; // huedir
10319 5 *(PartSys->PSdataEnd + 1) = 1; // sizedir
10320 }
10321 else
10322
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem1D *>(SEGENV.data); // if not first call, just set the pointer to the PS
10323
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (PartSys == nullptr)
10324 ✗ return mode_static(); // something went wrong, no data!
10325 // Particle System settings
10326
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
10327
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setColorByPosition(SEGMENT.check3);
10328
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setMotionBlur(7 + ((SEGMENT.custom3) << 3)); // anable motion blur
10329
4/8
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
23 uint32_t numParticles = 1 + map(SEGMENT.intensity, 0, 255, 2, 255 / (1 + (SEGMENT.custom1 >> 6))); // depends on intensity and particle size (custom1), minimum 1
10330 23 numParticles = min(numParticles, PartSys->usedParticles); // limit to available particles
10331
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 int32_t huestep = 1 + ((((uint32_t)SEGMENT.custom2 << 19) / numParticles) >> 16); // hue increment
10332
14/28
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 23 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 23 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 23 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 23 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 23 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 23 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 23 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 23 times.
✗ Branch 23 not taken.
✓ Branch 24 taken 23 times.
✗ Branch 25 not taken.
✓ Branch 26 taken 23 times.
✗ Branch 27 not taken.
23 uint32_t settingssum = SEGMENT.speed + SEGMENT.intensity + SEGMENT.custom1 + SEGMENT.custom2 + SEGMENT.check1 + SEGMENT.check2 + SEGMENT.check3;
10333
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGENV.aux0 != settingssum) { // settings changed changed, update
10334
3/6
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
5 if (SEGMENT.check1)
10335 ✗ SEGENV.step = PartSys->advPartProps[0].size / 2 + (PartSys->maxX / numParticles);
10336 else
10337
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 SEGENV.step = (PartSys->maxX + (PS_P_RADIUS_1D << 5)) / numParticles; // spacing between particles
10338
2/2
✓ Branch 0 taken 1248 times.
✓ Branch 1 taken 5 times.
1253 for (int32_t i = 0; i < (int32_t)PartSys->usedParticles; i++) {
10339 1248 PartSys->advPartProps[i].sat = 255;
10340
2/4
✓ Branch 0 taken 1248 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1248 times.
✗ Branch 3 not taken.
1248 PartSys->particles[i].x = (i - 1) * SEGENV.step; // distribute evenly (starts out of frame for i=0)
10341
2/4
✓ Branch 0 taken 1248 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1248 times.
✗ Branch 3 not taken.
1248 PartSys->particles[i].vx = SEGMENT.speed >> 2;
10342
2/4
✓ Branch 0 taken 1248 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1248 times.
✗ Branch 3 not taken.
1248 PartSys->advPartProps[i].size = SEGMENT.custom1;
10343
4/6
✓ Branch 0 taken 1248 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1248 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 876 times.
✓ Branch 5 taken 372 times.
1248 if (SEGMENT.custom2 < 255)
10344 876 PartSys->particles[i].hue = i * huestep; // gradient distribution
10345 else
10346 372 PartSys->particles[i].hue = hw_random16();
10347 }
10348
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 SEGENV.aux0 = settingssum;
10349 }
10350
10351
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 23 times.
23 if(SEGMENT.check1) {
10352 ✗ huestep = 1 + (max((int)huestep, 3) * ((int(sin16_t(strip().now * 3) + 32767))) >> 15); // changes gradient spread (scale hue step)
10353 }
10354
10355 // wrap around (cannot use particle system wrap if distributing colors manually, it also wraps rendering which does not look good)
10356
2/2
✓ Branch 0 taken 5496 times.
✓ Branch 1 taken 23 times.
5519 for (int32_t i = (int32_t)PartSys->usedParticles - 1; i >= 0; i--) { // check from the back, last particle wraps first, multiple particles can overrun per frame
10357
2/2
✓ Branch 0 taken 1089 times.
✓ Branch 1 taken 4407 times.
5496 if (PartSys->particles[i].x > PartSys->maxX + PS_P_RADIUS_1D + PartSys->advPartProps[i].size) { // wrap it around
10358 1089 uint32_t nextindex = (i + 1) % PartSys->usedParticles;
10359
2/4
✓ Branch 0 taken 1089 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1089 times.
✗ Branch 3 not taken.
1089 PartSys->particles[i].x = PartSys->particles[nextindex].x - (int)SEGENV.step;
10360
3/6
✓ Branch 0 taken 1089 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1089 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1089 times.
1089 if(SEGMENT.check1) // playful mode, vary size
10361 ✗ PartSys->advPartProps[i].size = max(1 + (SEGMENT.custom1 >> 1), ((int(sin16_t(strip().now << 1) + 32767)) >> 8)); // cycle size
10362
4/6
✓ Branch 0 taken 1089 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1089 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 779 times.
✓ Branch 5 taken 310 times.
1089 if (SEGMENT.custom2 < 255)
10363 779 PartSys->particles[i].hue = PartSys->particles[nextindex].hue - huestep;
10364 else
10365 310 PartSys->particles[i].hue = hw_random16();
10366 }
10367 5496 PartSys->particles[i].ttl = 300; // reset ttl, cannot use perpetual because memmanager can change pointer at any time
10368 }
10369
10370
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 23 times.
23 if (SEGMENT.check1) { // playful mode, changes hue, size, speed, density dynamically
10371 ✗ int8_t* huedir = reinterpret_cast<int8_t *>(PartSys->PSdataEnd); //assign data pointer
10372 ✗ int8_t* stepdir = reinterpret_cast<int8_t *>(PartSys->PSdataEnd + 1);
10373 ✗ if(*stepdir == 0) *stepdir = 1; // initialize directions
10374 ✗ if(*huedir == 0) *huedir = 1;
10375 ✗ if (SEGENV.step >= (PartSys->advPartProps[0].size + PS_P_RADIUS_1D * 4) + PartSys->maxX / numParticles)
10376 ✗ *stepdir = -1; // increase density (decrease space between particles)
10377 ✗ else if (SEGENV.step <= (PartSys->advPartProps[0].size >> 1) + ((PartSys->maxX / numParticles)))
10378 ✗ *stepdir = 1; // decrease density
10379 ✗ if (SEGENV.aux1 > 512)
10380 ✗ *huedir = -1;
10381 ✗ else if (SEGENV.aux1 < 50)
10382 ✗ *huedir = 1;
10383 ✗ if (SEGMENT.call % (1024 / (1 + (SEGMENT.speed >> 2))) == 0)
10384 ✗ SEGENV.aux1 += *huedir;
10385 ✗ int8_t globalhuestep = 0; // global hue increment
10386 ✗ if (SEGMENT.call % (1 + (int(sin16_t(strip().now) + 32767) >> 12)) == 0)
10387 ✗ globalhuestep = 2; // global hue change to add some color variation
10388 ✗ if ((SEGMENT.call & 0x1F) == 0)
10389 ✗ SEGENV.step += *stepdir; // change density
10390 ✗ for(uint32_t i = 0; i < PartSys->usedParticles; i++) {
10391 ✗ PartSys->particles[i].hue -= globalhuestep; // shift global hue (both directions)
10392 ✗ PartSys->particles[i].vx = 1 + (SEGMENT.speed >> 2) + ((int32_t(sin16_t(strip().now >> 1) + 32767) * (SEGMENT.speed >> 2)) >> 16);
10393 }
10394 }
10395
10396
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setParticleSize(SEGMENT.custom1); // if custom1 == 0 this sets rendering size to one pixel
10397
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->update(); // update and render
10398
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 return FRAMETIME;
10399 }
10400 static const char _data_FX_MODE_PS_CHASE[] PROGMEM = "PS Chase@!,Density,Size,Hue,Blur,Playful,,Position Color;,!;!;1;pal=11,sx=50,c2=5,c3=0";
10401
10402 /*
10403 Particle Fireworks Starburst replacement (smoother rendering, more settings)
10404 Uses palette for particle color
10405 by DedeHai (Damian Schneider)
10406 */
10407 23 uint16_t mode_particleStarburst(void) {
10408 23 ParticleSystem1D *PartSys = nullptr;
10409
10410
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
10411
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
5 if (!initParticleSystem1D(PartSys, 1, 200, 0, true)) // init
10412 ✗ return mode_static(); // allocation failed or is single pixel
10413
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 PartSys->setKillOutOfBounds(true);
10414
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 PartSys->enableParticleCollisions(true, 200);
10415 5 PartSys->sources[0].source.ttl = 1; // set initial stanby time
10416 5 PartSys->sources[0].sat = 0; // emitted particles start out white
10417 }
10418 else
10419
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem1D *>(SEGENV.data); // if not first call, just set the pointer to the PS
10420
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (PartSys == nullptr)
10421 ✗ return mode_static(); // something went wrong, no data!
10422
10423 // Particle System settings
10424
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
10425
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setMotionBlur(SEGMENT.custom2); // anable motion blur
10426
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setGravity(SEGMENT.check1 * 8); // enable gravity
10427
10428
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if (PartSys->sources[0].source.ttl-- == 0) { // stanby time elapsed TODO: make it a timer?
10429
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 uint32_t explosionsize = 4 + hw_random16(SEGMENT.intensity >> 2);
10430 5 PartSys->sources[0].source.hue = hw_random16();
10431 5 PartSys->sources[0].var = 10 + (explosionsize << 1);
10432 5 PartSys->sources[0].minLife = 250;
10433 5 PartSys->sources[0].maxLife = 300;
10434
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 PartSys->sources[0].source.x = hw_random(PartSys->maxX); //random explosion position
10435
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 PartSys->sources[0].source.ttl = 10 + hw_random16(255 - SEGMENT.speed);
10436
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 PartSys->sources[0].size = SEGMENT.custom1; // Fragment size
10437
3/6
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
5 PartSys->setParticleSize(SEGMENT.custom1); // enable advanced size rendering
10438
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 PartSys->sources[0].sourceFlags.collide = SEGMENT.check3;
10439
2/2
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 5 times.
105 for (uint32_t e = 0; e < explosionsize; e++) { // emit particles
10440
3/6
✓ Branch 0 taken 100 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 100 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 100 times.
100 if (SEGMENT.check2)
10441 ✗ PartSys->sources[0].source.hue = hw_random16(); //random color for each particle
10442
1/2
✓ Branch 0 taken 100 times.
✗ Branch 1 not taken.
100 PartSys->sprayEmit(PartSys->sources[0]); //emit a particle
10443 }
10444 }
10445 //shrink all particles
10446
2/2
✓ Branch 0 taken 4336 times.
✓ Branch 1 taken 23 times.
4359 for (uint32_t i = 0; i < PartSys->usedParticles; i++) {
10447
2/2
✓ Branch 0 taken 328 times.
✓ Branch 1 taken 4008 times.
4336 if (PartSys->advPartProps[i].size)
10448 328 PartSys->advPartProps[i].size--;
10449
2/2
✓ Branch 0 taken 340 times.
✓ Branch 1 taken 3996 times.
4336 if (PartSys->advPartProps[i].sat < 251)
10450
2/4
✓ Branch 0 taken 340 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 340 times.
✗ Branch 3 not taken.
340 PartSys->advPartProps[i].sat += 1 + (SEGMENT.custom3 >> 2); //note: it should be >> 3, the >> 2 creates overflows resulting in blinking if custom3 > 27, which is a bonus feature
10451 }
10452
10453
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call % 5 == 0) {
10454
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 PartSys->applyFriction(1); //slow down particles
10455 }
10456
10457
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->update(); // update and render
10458
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 return FRAMETIME;
10459 }
10460 static const char _data_FX_MODE_PS_STARBURST[] PROGMEM = "PS Starburst@Chance,Fragments,Size,Blur,Cooling,Gravity,Colorful,Push;,!;!;1;pal=52,sx=150,ix=150,c1=120,c2=0,c3=21";
10461
10462 /*
10463 Particle based 1D GEQ effect, each frequency bin gets an emitter, distributed over the strip
10464 Uses palette for particle color
10465 by DedeHai (Damian Schneider)
10466 */
10467 23 uint16_t mode_particle1DGEQ(void) {
10468 23 ParticleSystem1D *PartSys = nullptr;
10469 uint32_t numSources;
10470 uint32_t i;
10471
10472
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
10473
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
5 if (!initParticleSystem1D(PartSys, 16, 255, 0, true)) // init, no additional data needed
10474 ✗ return mode_static(); // allocation failed or is single pixel
10475 }
10476 else
10477
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem1D *>(SEGENV.data); // if not first call, just set the pointer to the PS
10478
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (PartSys == nullptr)
10479 ✗ return mode_static(); // something went wrong, no data!
10480
10481 // Particle System settings
10482
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
10483 23 numSources = PartSys->numSources;
10484
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setMotionBlur(SEGMENT.custom2); // anable motion blur
10485
10486 23 uint32_t spacing = PartSys->maxX / numSources;
10487
2/2
✓ Branch 0 taken 368 times.
✓ Branch 1 taken 23 times.
391 for (i = 0; i < numSources; i++) {
10488 368 PartSys->sources[i].source.hue = i * 16; // hw_random16(); //TODO: make adjustable, maybe even colorcycle?
10489
2/4
✓ Branch 0 taken 368 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 368 times.
✗ Branch 3 not taken.
368 PartSys->sources[i].var = SEGMENT.speed >> 2;
10490
2/4
✓ Branch 0 taken 368 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 368 times.
✗ Branch 3 not taken.
368 PartSys->sources[i].minLife = 180 + (SEGMENT.intensity >> 1);
10491
2/4
✓ Branch 0 taken 368 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 368 times.
✗ Branch 3 not taken.
368 PartSys->sources[i].maxLife = 240 + SEGMENT.intensity;
10492 368 PartSys->sources[i].sat = 255;
10493
2/4
✓ Branch 0 taken 368 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 368 times.
✗ Branch 3 not taken.
368 PartSys->sources[i].size = SEGMENT.custom1;
10494 368 PartSys->sources[i].source.x = (spacing >> 1) + spacing * i; //distribute evenly
10495 }
10496
10497
2/2
✓ Branch 0 taken 5496 times.
✓ Branch 1 taken 23 times.
5519 for (i = 0; i < PartSys->usedParticles; i++) {
10498
2/2
✓ Branch 0 taken 319 times.
✓ Branch 1 taken 5177 times.
5496 if (PartSys->particles[i].ttl > 20) PartSys->particles[i].ttl -= 20; //ttl is linked to brightness, this allows to use higher brightness but still a short lifespan
10499 5177 else PartSys->particles[i].ttl = 0;
10500 }
10501
10502
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 um_data_t *um_data = getAudioData();
10503 23 uint8_t *fftResult = (uint8_t *)um_data->u_data[2]; // 16 bins with FFT data, log mapped already, each band contains frequency amplitude 0-255
10504
10505 //map the bands into 16 positions on x axis, emit some particles according to frequency loudness
10506 23 i = 0;
10507 23 uint32_t bin = hw_random16(numSources); //current bin , start with random one to distribute available particles fairly
10508
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 uint32_t threshold = 300 - SEGMENT.intensity;
10509
10510
2/2
✓ Branch 0 taken 368 times.
✓ Branch 1 taken 23 times.
391 for (i = 0; i < numSources; i++) {
10511 368 bin++;
10512 368 bin = bin % numSources;
10513 368 uint32_t emitparticle = 0;
10514 // uint8_t emitspeed = ((uint32_t)fftResult[bin] * (uint32_t)SEGMENT.speed) >> 10; // emit speed according to loudness of band (127 max!)
10515
2/2
✓ Branch 0 taken 156 times.
✓ Branch 1 taken 212 times.
368 if (fftResult[bin] > threshold) {
10516 156 emitparticle = 1;
10517 }
10518
2/2
✓ Branch 0 taken 194 times.
✓ Branch 1 taken 18 times.
212 else if (fftResult[bin] > 0) { // band has low volue
10519 194 uint32_t restvolume = ((threshold - fftResult[bin]) >> 2) + 2;
10520
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 175 times.
194 if (hw_random() % restvolume == 0) {
10521 19 emitparticle = 1;
10522 }
10523 }
10524
10525
2/2
✓ Branch 0 taken 175 times.
✓ Branch 1 taken 193 times.
368 if (emitparticle)
10526
1/2
✓ Branch 0 taken 175 times.
✗ Branch 1 not taken.
175 PartSys->sprayEmit(PartSys->sources[bin]);
10527 }
10528 //TODO: add color control?
10529
10530
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->update(); // update and render
10531
10532
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 return FRAMETIME;
10533 }
10534 static const char _data_FX_MODE_PS_1D_GEQ[] PROGMEM = "PS GEQ 1D@Speed,!,Size,Blur,,,,;,!;!;1f;pal=0,sx=50,ix=200,c1=0,c2=0,c3=0,o1=1,o2=1";
10535
10536 /*
10537 Particle based Fire effect
10538 Uses palette for particle color
10539 by DedeHai (Damian Schneider)
10540 */
10541 23 uint16_t mode_particleFire1D(void) {
10542 23 ParticleSystem1D *PartSys = nullptr;
10543
10544
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
10545
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
5 if (!initParticleSystem1D(PartSys, 5)) // init
10546 ✗ return mode_static(); // allocation failed or is single pixel
10547
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 PartSys->setKillOutOfBounds(true);
10548
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 PartSys->setParticleSize(1);
10549 }
10550 else
10551
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem1D *>(SEGENV.data); // if not first call, just set the pointer to the PS
10552
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (PartSys == nullptr)
10553 ✗ return mode_static(); // something went wrong, no data!
10554
10555 // Particle System settings
10556
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
10557
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setMotionBlur(128 + (SEGMENT.custom2 >> 1)); // enable motion blur
10558
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->setColorByAge(true);
10559 23 uint32_t emitparticles = 1;
10560 23 uint32_t j = hw_random16();
10561
2/2
✓ Branch 0 taken 69 times.
✓ Branch 1 taken 23 times.
92 for (uint i = 0; i < 3; i++) { // 3 base flames
10562
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 15 times.
69 if (PartSys->sources[i].source.ttl > 50)
10563 54 PartSys->sources[i].source.ttl -= 10; // TODO: in 2D making the source fade out slow results in much smoother flames, need to check if it can be done the same
10564 else
10565 15 PartSys->sources[i].source.ttl = 100 + hw_random16(200);
10566 }
10567
2/2
✓ Branch 0 taken 184 times.
✓ Branch 1 taken 23 times.
207 for (uint i = 0; i < PartSys->numSources; i++) {
10568 184 j = (j + 1) % PartSys->numSources;
10569 184 PartSys->sources[j].source.x = 0;
10570
2/4
✓ Branch 0 taken 184 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 184 times.
✗ Branch 3 not taken.
184 PartSys->sources[j].var = 2 + (SEGMENT.speed >> 4);
10571 // base flames
10572
2/2
✓ Branch 0 taken 115 times.
✓ Branch 1 taken 69 times.
184 if (j > 2) {
10573
2/4
✓ Branch 0 taken 115 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 115 times.
✗ Branch 3 not taken.
115 PartSys->sources[j].minLife = 150 + SEGMENT.intensity + (j << 2); // TODO: in 2D, min life is maxlife/2 and that looks very nice
10574
2/4
✓ Branch 0 taken 115 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 115 times.
✗ Branch 3 not taken.
115 PartSys->sources[j].maxLife = 200 + SEGMENT.intensity + (j << 3);
10575
2/4
✓ Branch 0 taken 115 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 115 times.
✗ Branch 3 not taken.
115 PartSys->sources[j].v = (SEGMENT.speed >> (2 + (j << 1)));
10576
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 92 times.
115 if (emitparticles) {
10577 23 emitparticles--;
10578
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->sprayEmit(PartSys->sources[j]); // emit a particle
10579 }
10580 }
10581 else {
10582
2/4
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 69 times.
✗ Branch 3 not taken.
69 PartSys->sources[j].minLife = PartSys->sources[j].source.ttl + SEGMENT.intensity;
10583 69 PartSys->sources[j].maxLife = PartSys->sources[j].minLife + 50;
10584
2/4
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 69 times.
✗ Branch 3 not taken.
69 PartSys->sources[j].v = SEGMENT.speed >> 2;
10585
4/6
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 69 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 30 times.
✓ Branch 5 taken 39 times.
69 if (SEGENV.call & 0x01) // every second frame
10586
1/2
✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
30 PartSys->sprayEmit(PartSys->sources[j]); // emit a particle
10587 }
10588 }
10589
10590
2/2
✓ Branch 0 taken 7536 times.
✓ Branch 1 taken 23 times.
7559 for (uint i = 0; i < PartSys->usedParticles; i++) {
10591 7536 PartSys->particles[i].x += PartSys->particles[i].ttl >> 7; // 'hot' particles are faster, apply some extra velocity
10592
4/6
✓ Branch 0 taken 7536 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7536 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 139 times.
✓ Branch 5 taken 7397 times.
7536 if (PartSys->particles[i].ttl > 3 + ((255 - SEGMENT.custom1) >> 1))
10593
2/4
✓ Branch 0 taken 139 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 139 times.
✗ Branch 3 not taken.
139 PartSys->particles[i].ttl -= map(SEGMENT.custom1, 0, 255, 1, 3); // age faster
10594 }
10595
10596
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->update(); // update and render
10597
10598
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 return FRAMETIME;
10599 }
10600 static const char _data_FX_MODE_PS_FIRE1D[] PROGMEM = "PS Fire 1D@!,!,Cooling,Blur;,!;!;1;pal=35,sx=100,ix=50,c1=80,c2=100,c3=28,o1=1,o2=1";
10601
10602 /*
10603 Particle based AR effect, swoop particles along the strip with selected frequency loudness
10604 by DedeHai (Damian Schneider)
10605 */
10606 23 uint16_t mode_particle1DsonicStream(void) {
10607 23 ParticleSystem1D *PartSys = nullptr;
10608
10609
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
10610
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
5 if (!initParticleSystem1D(PartSys, 1, 255, 0, true)) // init, no additional data needed
10611 ✗ return mode_static(); // allocation failed or is single pixel
10612
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 PartSys->setKillOutOfBounds(true);
10613 5 PartSys->sources[0].source.x = 0; // at start
10614 //PartSys->sources[1].source.x = PartSys->maxX; // at end
10615 5 PartSys->sources[0].var = 0;//SEGMENT.custom1 >> 3;
10616 }
10617 else
10618
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem1D *>(SEGENV.data); // if not first call, just set the pointer to the PS
10619
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (PartSys == nullptr)
10620 ✗ return mode_static(); // something went wrong, no data!
10621
10622 // Particle System settings
10623
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
10624
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setMotionBlur(20 + (SEGMENT.custom2 >> 1)); // anable motion blur
10625
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->setSmearBlur(200); // smooth out the edges
10626
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 PartSys->sources[0].v = 5 + (SEGMENT.speed >> 2);
10627
10628 // FFT processing
10629
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 um_data_t *um_data = getAudioData();
10630 23 uint8_t *fftResult = (uint8_t *)um_data->u_data[2]; // 16 bins with FFT data, log mapped already, each band contains frequency amplitude 0-255
10631 uint32_t loudness;
10632
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 uint32_t baseBin = SEGMENT.custom3 >> 1; // 0 - 15 map(SEGMENT.custom3, 0, 31, 0, 14);
10633
10634 23 loudness = fftResult[baseBin];// + fftResult[baseBin + 1];
10635
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 19 times.
23 if (baseBin > 12)
10636 4 loudness = loudness << 2; // double loudness for high frequencies (better detecion)
10637
10638
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 uint32_t threshold = 140 - (SEGMENT.intensity >> 1);
10639
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 if (SEGMENT.check2) { // enable low pass filter for dynamic threshold
10640
4/8
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
23 SEGMENT.step = (SEGMENT.step * 31500 + loudness * (32768 - 31500)) >> 15; // low pass filter for simple beat detection: add average to base threshold
10641
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 threshold = 20 + (threshold >> 1) + SEGMENT.step; // add average to threshold
10642 }
10643
10644 // color
10645
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 uint32_t hueincrement = (SEGMENT.custom1 >> 3); // 0-31
10646
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 19 times.
✓ Branch 5 taken 4 times.
23 PartSys->sources[0].sat = SEGMENT.custom1 > 0 ? 255 : 0; // color slider at zero: set to white
10647
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setColorByPosition(SEGMENT.custom1 == 255);
10648
10649 // particle manipulation
10650
2/2
✓ Branch 0 taken 5496 times.
✓ Branch 1 taken 23 times.
5519 for (uint32_t i = 0; i < PartSys->usedParticles; i++) {
10651
1/2
✓ Branch 0 taken 5496 times.
✗ Branch 1 not taken.
5496 if (PartSys->sources[0].sourceFlags.perpetual == false) { // age faster if not perpetual
10652
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 5476 times.
5496 if (PartSys->particles[i].ttl > 2) {
10653 20 PartSys->particles[i].ttl -= 2; //ttl is linked to brightness, this allows to use higher brightness but still a short lifespan
10654 }
10655 5476 else PartSys->particles[i].ttl = 0;
10656 }
10657
3/6
✓ Branch 0 taken 5496 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5496 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 5496 times.
5496 if (SEGMENT.check1) { // modulate colors by mid frequencies
10658 ✗ int mids = sqrt32_bw((int)fftResult[5] + (int)fftResult[6] + (int)fftResult[7] + (int)fftResult[8] + (int)fftResult[9] + (int)fftResult[10]); // average the mids, bin 5 is ~500Hz, bin 10 is ~2kHz (see audio_reactive.h)
10659 ✗ PartSys->particles[i].hue += (mids * perlin8(PartSys->particles[i].x << 2, SEGMENT.step << 2)) >> 9; // color by perlin noise from mid frequencies
10660 }
10661 }
10662
10663
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
23 if (loudness > threshold) {
10664
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 SEGMENT.aux0 += hueincrement; // change color
10665
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys->sources[0].minLife = 100 + (((unsigned)SEGMENT.intensity * loudness * loudness) >> 13);
10666 18 PartSys->sources[0].maxLife = PartSys->sources[0].minLife;
10667
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys->sources[0].source.hue = SEGMENT.aux0;
10668
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys->sources[0].size = SEGMENT.speed;
10669
10/14
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 13 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 13 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 5 times.
✓ Branch 11 taken 8 times.
✓ Branch 12 taken 10 times.
✓ Branch 13 taken 8 times.
18 if (PartSys->particles[SEGMENT.aux1].x > 3 * PS_P_RADIUS_1D || PartSys->particles[SEGMENT.aux1].ttl == 0) { // only emit if last particle is far enough away or dead
10670
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 int partindex = PartSys->sprayEmit(PartSys->sources[0]); // emit a particle
10671
3/6
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
10 if (partindex >= 0) SEGMENT.aux1 = partindex; // track last emitted particle
10672 }
10673 }
10674 5 else loudness = 0; // required for push mode
10675
10676
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->update(); // update and render (needs to be done before manipulation for initial particle spacing to be right)
10677
10678
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 23 times.
23 if (SEGMENT.check3) { // push mode
10679 ✗ PartSys->sources[0].sourceFlags.perpetual = true; // emitted particles dont age
10680 ✗ PartSys->applyFriction(1); //slow down particles
10681 ✗ int32_t movestep = (((int)SEGMENT.speed + 2) * loudness) >> 10;
10682 ✗ if (movestep) {
10683 ✗ for (uint32_t i = 0; i < PartSys->usedParticles; i++) {
10684 ✗ if (PartSys->particles[i].ttl) {
10685 ✗ PartSys->particles[i].x += movestep; // push particles
10686 ✗ PartSys->particles[i].vx = 10 + (SEGMENT.speed >> 4) ; // give particles some speed for smooth movement (friction will slow them down)
10687 }
10688 }
10689 }
10690 }
10691 else {
10692 23 PartSys->sources[0].sourceFlags.perpetual = false; // emitted particles age
10693 // move all particles (again) to allow faster speeds
10694
2/2
✓ Branch 0 taken 5496 times.
✓ Branch 1 taken 23 times.
5519 for (uint32_t i = 0; i < PartSys->usedParticles; i++) {
10695
2/2
✓ Branch 0 taken 1245 times.
✓ Branch 1 taken 4251 times.
5496 if (PartSys->particles[i].vx == 0)
10696 1245 PartSys->particles[i].vx = PartSys->sources[0].v; // move static particles (after disabling push mode)
10697
1/2
✓ Branch 0 taken 5496 times.
✗ Branch 1 not taken.
5496 PartSys->particleMoveUpdate(PartSys->particles[i], PartSys->particleFlags[i], nullptr, &PartSys->advPartProps[i]);
10698 }
10699 }
10700
10701
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 return FRAMETIME;
10702 }
10703 static const char _data_FX_MODE_PS_SONICSTREAM[] PROGMEM = "PS Sonic Stream@!,!,Color,Blur,Bin,Mod,Filter,Push;,!;!;1f;c3=0,o2=1";
10704
10705
10706 /*
10707 Particle based AR effect, creates exploding particles on beats
10708 by DedeHai (Damian Schneider)
10709 */
10710 23 uint16_t mode_particle1DsonicBoom(void) {
10711 23 ParticleSystem1D *PartSys = nullptr;
10712
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
10713
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
5 if (!initParticleSystem1D(PartSys, 1, 255, 0, true)) // init, no additional data needed
10714 ✗ return mode_static(); // allocation failed or is single pixel
10715
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 PartSys->setKillOutOfBounds(true);
10716 }
10717 else
10718
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem1D *>(SEGENV.data); // if not first call, just set the pointer to the PS
10719
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (PartSys == nullptr)
10720 ✗ return mode_static(); // something went wrong, no data!
10721
10722 // Particle System settings
10723
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
10724
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setMotionBlur(180 * SEGMENT.check3);
10725
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setSmearBlur(64 * SEGMENT.check3);
10726
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 PartSys->sources[0].var = map(SEGMENT.speed, 0, 255, 10, 127);
10727
10728 // FFT processing
10729
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 um_data_t *um_data = getAudioData();
10730 23 uint8_t *fftResult = (uint8_t *)um_data->u_data[2]; // 16 bins with FFT data, log mapped already, each band contains frequency amplitude 0-255
10731 uint32_t loudness;
10732
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 uint32_t baseBin = SEGMENT.custom3 >> 1; // 0 - 15 map(SEGMENT.custom3, 0, 31, 0, 14);
10733 23 loudness = fftResult[baseBin];// + fftResult[baseBin + 1];
10734
10735
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 19 times.
23 if (baseBin > 12)
10736 4 loudness = loudness << 2; // double loudness for high frequencies (better detecion)
10737
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 uint32_t threshold = 150 - (SEGMENT.intensity >> 1);
10738
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 if (SEGMENT.check2) { // enable low pass filter for dynamic threshold
10739
4/8
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
23 SEGMENT.step = (SEGMENT.step * 31500 + loudness * (32768 - 31500)) >> 15; // low pass filter for simple beat detection: add average to base threshold
10740
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 threshold = 20 + (threshold >> 1) + SEGMENT.step; // add average to threshold
10741 }
10742
10743 // particle manipulation
10744
2/2
✓ Branch 0 taken 5496 times.
✓ Branch 1 taken 23 times.
5519 for (uint32_t i = 0; i < PartSys->usedParticles; i++) {
10745
3/6
✓ Branch 0 taken 5496 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5496 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 5496 times.
5496 if (SEGMENT.check1) { // modulate colors by mid frequencies
10746 ✗ int mids = sqrt32_bw((int)fftResult[5] + (int)fftResult[6] + (int)fftResult[7] + (int)fftResult[8] + (int)fftResult[9] + (int)fftResult[10]); // average the mids, bin 5 is ~500Hz, bin 10 is ~2kHz (see audio_reactive.h)
10747 ✗ PartSys->particles[i].hue += (mids * perlin8(PartSys->particles[i].x << 2, SEGMENT.step << 2)) >> 9; // color by perlin noise from mid frequencies
10748 }
10749
2/2
✓ Branch 0 taken 374 times.
✓ Branch 1 taken 5122 times.
5496 if (PartSys->particles[i].ttl > 16) {
10750 374 PartSys->particles[i].ttl -= 16; //ttl is linked to brightness, this allows to use higher brightness but still a (very) short lifespan
10751 }
10752 }
10753
10754
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 7 times.
23 if (loudness > threshold) {
10755
4/6
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 11 times.
16 if (SEGMENT.aux1 == 0) { // edge detected, code only runs once per "beat"
10756 // update position
10757
4/6
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 1 times.
5 if (SEGMENT.custom2 < 128) // fixed position
10758
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 PartSys->sources[0].source.x = map(SEGMENT.custom2, 0, 127, 0, PartSys->maxX);
10759
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
1 else if (SEGMENT.custom2 < 255) { // advances on each "beat"
10760 ✗ int32_t step = PartSys->maxX / (((270 - SEGMENT.custom2) >> 3)); // step: 2 - 33 steps for full segment width
10761 ✗ PartSys->sources[0].source.x = (PartSys->sources[0].source.x + step) % PartSys->maxX;
10762 ✗ if (PartSys->sources[0].source.x < step) // align to be symmetrical by making the first position half a step from start
10763 ✗ PartSys->sources[0].source.x = step >> 1;
10764 }
10765 else // position set to max, use random postion per beat
10766
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 PartSys->sources[0].source.x = hw_random(PartSys->maxX);
10767
10768 // update color
10769 //PartSys->setColorByPosition(SEGMENT.custom1 == 255); // color slider at max: particle color by position
10770
4/6
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 1 times.
5 PartSys->sources[0].sat = SEGMENT.custom1 > 0 ? 255 : 0; // color slider at zero: set to white
10771
4/6
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 4 times.
5 if (SEGMENT.custom1 == 255) // emit color by position
10772
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 SEGMENT.aux0 = map(PartSys->sources[0].source.x , 0, PartSys->maxX, 0, 255);
10773
4/6
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 1 times.
4 else if (SEGMENT.custom1 > 0)
10774
4/8
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
3 SEGMENT.aux0 += (SEGMENT.custom1 >> 1); // change emit color per "beat"
10775 }
10776
2/4
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
16 SEGMENT.aux1 = 1; // track edge detection
10777
10778 16 PartSys->sources[0].minLife = 200;
10779
2/4
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
16 PartSys->sources[0].maxLife = PartSys->sources[0].minLife + (((unsigned)SEGMENT.intensity * loudness * loudness) >> 13);
10780
2/4
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
16 PartSys->sources[0].source.hue = SEGMENT.aux0;
10781 16 PartSys->sources[0].size = 1; //SEGMENT.speed>>3;
10782 16 uint32_t explosionsize = 4 + (PartSys->maxXpixel >> 2);
10783 16 explosionsize = hw_random16((explosionsize * loudness) >> 10);
10784
2/2
✓ Branch 0 taken 213 times.
✓ Branch 1 taken 16 times.
229 for (uint32_t e = 0; e < explosionsize; e++) { // emit explosion particles
10785
1/2
✓ Branch 0 taken 213 times.
✗ Branch 1 not taken.
213 PartSys->sprayEmit(PartSys->sources[0]); // emit a particle
10786 }
10787 }
10788 else
10789
2/4
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
7 SEGMENT.aux1 = 0; // reset edge detection
10790
10791
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->update(); // update and render (needs to be done before manipulation for initial particle spacing to be right)
10792
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 return FRAMETIME;
10793 }
10794 static const char _data_FX_MODE_PS_SONICBOOM[] PROGMEM = "PS Sonic Boom@!,!,Color,Position,Bin,Mod,Filter,Blur;,!;!;1f;c2=63,c3=0,o2=1";
10795
10796 /*
10797 Particles bound by springs
10798 by DedeHai (Damian Schneider)
10799 */
10800 23 uint16_t mode_particleSpringy(void) {
10801 23 ParticleSystem1D *PartSys = nullptr;
10802
3/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 18 times.
23 if (SEGMENT.call == 0) { // initialization
10803
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
5 if (!initParticleSystem1D(PartSys, 1, 128, 0, true)) // init
10804 ✗ return mode_static(); // allocation failed or is single pixel
10805
4/8
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 5 times.
✗ Branch 7 not taken.
5 SEGENV.aux0 = SEGENV.aux1 = 0xFFFF; // invalidate settings
10806 }
10807 else
10808
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 PartSys = reinterpret_cast<ParticleSystem1D *>(SEGENV.data); // if not first call, just set the pointer to the PS
10809
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (PartSys == nullptr)
10810 ✗ return mode_static(); // something went wrong, no data!
10811 // Particle System settings
10812
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->updateSystem(); // update system properties (dimensions and data pointers)
10813
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 PartSys->setMotionBlur(220 * SEGMENT.check1); // anable motion blur
10814
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->setSmearBlur(50); // smear a little
10815
7/14
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 23 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 23 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 23 times.
✗ Branch 13 not taken.
23 PartSys->setUsedParticles(map(SEGMENT.custom1, 0, 255, 30 >> SEGMENT.check2, 255 >> (SEGMENT.check2*2))); // depends on density and particle size
10816 // PartSys->enableParticleCollisions(true, 140); // enable particle collisions, can not be set too hard or impulses will not strech the springs if soft.
10817 23 int32_t springlength = PartSys->maxX / (PartSys->usedParticles); // spring length (spacing between particles)
10818
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 int32_t springK = map(SEGMENT.speed, 0, 255, 5, 35); // spring constant (stiffness)
10819
10820
4/8
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
23 uint32_t settingssum = SEGMENT.custom1 + SEGMENT.check2;
10821
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 18 times.
23 if (SEGENV.aux0 != settingssum) { // number of particles changed, update distribution
10822
2/2
✓ Branch 0 taken 357 times.
✓ Branch 1 taken 5 times.
362 for (int32_t i = 0; i < (int32_t)PartSys->usedParticles; i++) {
10823 357 PartSys->advPartProps[i].sat = 255; // full saturation
10824 //PartSys->particleFlags[i].collide = true; // enable collision for particles
10825 357 PartSys->particles[i].x = (i+1) * ((PartSys->maxX) / (PartSys->usedParticles)); // distribute
10826 //PartSys->particles[i].vx = 0; //reset speed
10827
3/6
✓ Branch 0 taken 357 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 357 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 357 times.
357 PartSys->advPartProps[i].size = SEGMENT.check2 ? 190 : 2; // set size, small or big
10828 }
10829
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 SEGENV.aux0 = settingssum;
10830 }
10831
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 int dxlimit = (2 + ((255 - SEGMENT.speed) >> 5)) * springlength; // limit for spring length to avoid overstretching
10832
10833 23 int springforce[PartSys->usedParticles]; // spring forces
10834 23 memset(springforce, 0, PartSys->usedParticles * sizeof(int32_t)); // reset spring forces
10835
10836 // calculate spring forces and limit particle positions
10837
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (PartSys->particles[0].x < -springlength)
10838 ✗ PartSys->particles[0].x = -springlength; // limit the spring length
10839
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 else if (PartSys->particles[0].x > dxlimit)
10840 ✗ PartSys->particles[0].x = dxlimit; // limit the spring length
10841 23 springforce[0] += ((springlength >> 1) - (PartSys->particles[0].x)) * springK; // first particle anchors to x=0
10842
10843
2/2
✓ Branch 0 taken 1552 times.
✓ Branch 1 taken 23 times.
1575 for (uint32_t i = 1; i < PartSys->usedParticles; i++) {
10844 // reorder particles if they are out of order to prevent chaos
10845
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 1537 times.
1552 if (PartSys->particles[i].x < PartSys->particles[i-1].x)
10846 15 std::swap(PartSys->particles[i].x, PartSys->particles[i-1].x); // swap particle positions to maintain order
10847 1552 int dx = PartSys->particles[i].x - PartSys->particles[i-1].x; // distance, always positive
10848
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1552 times.
1552 if (dx > dxlimit) { // limit the spring length
10849 ✗ PartSys->particles[i].x = PartSys->particles[i-1].x + dxlimit;
10850 ✗ dx = dxlimit;
10851 }
10852 1552 int dxleft = (springlength - dx); // offset from spring resting position
10853 1552 springforce[i] += dxleft * springK;
10854 1552 springforce[i-1] -= dxleft * springK;
10855
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 1529 times.
1552 if (i == (PartSys->usedParticles - 1)) {
10856
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (PartSys->particles[i].x >= PartSys->maxX + springlength)
10857 ✗ PartSys->particles[i].x = PartSys->maxX + springlength;
10858 23 int dxright = (springlength >> 1) - (PartSys->maxX - PartSys->particles[i].x); // last particle anchors to x=maxX
10859 23 springforce[i] -= dxright * springK;
10860 }
10861 }
10862 // apply spring forces to particles
10863
4/8
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
23 bool dampenoscillations = (SEGMENT.call % (9 - (SEGMENT.speed >> 5))) == 0; // dampen oscillation if particles are slow, more damping on stiffer springs
10864
2/2
✓ Branch 0 taken 1575 times.
✓ Branch 1 taken 23 times.
1598 for (uint32_t i = 0; i < PartSys->usedParticles; i++) {
10865 1575 springforce[i] = springforce[i] / 64; // scale spring force (cannot use shifts because of negative values)
10866 1575 int maxforce = 120; // limit spring force
10867
2/2
✓ Branch 0 taken 1572 times.
✓ Branch 1 taken 3 times.
1575 springforce[i] = springforce[i] > maxforce ? maxforce : springforce[i] < -maxforce ? -maxforce : springforce[i]; // limit spring force
10868
1/2
✓ Branch 0 taken 1575 times.
✗ Branch 1 not taken.
1575 PartSys->applyForce(PartSys->particles[i], springforce[i], PartSys->advPartProps[i].forcecounter);
10869 //dampen slow particles to avoid persisting oscillations on higher stiffness
10870
2/2
✓ Branch 0 taken 545 times.
✓ Branch 1 taken 1030 times.
1575 if (dampenoscillations) {
10871
3/4
✓ Branch 0 taken 545 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 534 times.
✓ Branch 3 taken 11 times.
545 if (abs(PartSys->particles[i].vx) < 3 && abs(springforce[i]) < (springK >> 2))
10872 534 PartSys->particles[i].vx = (PartSys->particles[i].vx * 254) / 256; // take out some energy
10873 }
10874 1575 PartSys->particles[i].ttl = 300; // reset ttl, cannot use perpetual
10875 }
10876
10877
8/14
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 23 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 23 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 6 times.
✓ Branch 13 taken 17 times.
23 if (SEGMENT.call % ((65 - ((SEGMENT.intensity * (1 + (SEGMENT.speed>>3))) >> 7))) == 0) // more damping for higher stiffness
10878
3/6
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
6 PartSys->applyFriction((SEGMENT.intensity >> 2));
10879
10880 // add a small resetting force so particles return to resting position even under high damping
10881
2/2
✓ Branch 0 taken 1529 times.
✓ Branch 1 taken 23 times.
1552 for (uint32_t i = 1; i < PartSys->usedParticles - 1; i++) {
10882 1529 int restposition = (springlength >> 1) + i * springlength; // resting position
10883 1529 int dx = restposition - PartSys->particles[i].x; // distance, always positive
10884
4/6
✓ Branch 0 taken 1521 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 1521 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1529 times.
✗ Branch 5 not taken.
1529 PartSys->applyForce(PartSys->particles[i], dx > 0 ? 1 : (dx < 0 ? -1 : 0), PartSys->advPartProps[i].forcecounter);
10885 }
10886
10887 // Modes
10888
3/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 23 times.
23 if (SEGMENT.check3) { // use AR, custom 3 becomes frequency band to use, applies velocity to center particle according to loudness
10889 ✗ um_data_t *um_data = getAudioData();
10890 ✗ uint8_t *fftResult = (uint8_t *)um_data->u_data[2]; // 16 bins with FFT data, log mapped already, each band contains frequency amplitude 0-255
10891 ✗ uint32_t baseBin = map(SEGMENT.custom3, 0, 31, 0, 14);
10892 ✗ uint32_t loudness = fftResult[baseBin] + fftResult[baseBin+1];
10893 ✗ uint32_t threshold = 80; //150 - (SEGMENT.intensity >> 1);
10894 ✗ if (loudness > threshold) {
10895 ✗ int offset = (PartSys->maxX >> 1) - PartSys->particles[PartSys->usedParticles>>1].x; // offset from center
10896 ✗ if (abs(offset) < PartSys->maxX >> 5) // push particle around in center sector
10897 ✗ PartSys->particles[PartSys->usedParticles>>1].vx = ((PartSys->particles[PartSys->usedParticles>>1].vx > 0 ? 1 : -1)) * (loudness >> 3);
10898 }
10899 }
10900 else{
10901
4/6
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 19 times.
23 if (SEGMENT.custom3 <= 10) { // periodic pulse: 0-5 apply at start, 6-10 apply at center
10902
5/8
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 3 times.
4 if (strip().now > SEGMENT.step) {
10903
5/14
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
1 int speed = (SEGMENT.custom3 > 5) ? (SEGMENT.custom3 - 6) : SEGMENT.custom3;
10904
5/10
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
1 SEGMENT.step = strip().now + 7500 - ((SEGMENT.speed << 3) + (speed << 10));
10905
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 int amplitude = 40 + (SEGMENT.custom1 >> 2);
10906
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
1 int index = (SEGMENT.custom3 > 5) ? (PartSys->usedParticles / 2) : 0; // center or start particle
10907 1 PartSys->particles[index].vx += amplitude;
10908 }
10909 }
10910
4/6
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 15 times.
✓ Branch 5 taken 4 times.
19 else if (SEGMENT.custom3 <= 30) { // sinusoidal wave: 11-20 apply at start, 21-30 apply at center
10911
3/6
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
15 int index = (SEGMENT.custom3 > 20) ? (PartSys->usedParticles / 2) : 0; // center or start particle
10912 15 int restposition = 0;
10913
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 if (index > 0) restposition = PartSys->maxX >> 1; // center
10914 //int amplitude = 5 + (SEGMENT.speed >> 3) + (SEGMENT.custom1 >> 2); // amplitude depends on density
10915
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
15 int amplitude = 5 + (SEGMENT.custom1 >> 2); // amplitude depends on density
10916
3/6
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
15 int speed = SEGMENT.custom3 - 10 - (index ? 10 : 0); // map 11-20 and 21-30 to 1-10
10917
3/6
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
15 int phase = strip().now * ((1 + (SEGMENT.speed >> 4)) * speed);
10918
3/6
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 15 times.
15 if (SEGMENT.check2) amplitude <<= 1; // double amplitude for XL particles
10919 //PartSys->applyForce(PartSys->particles[index], (sin16_t(phase) * amplitude) >> 15, PartSys->advPartProps[index].forcecounter); // apply acceleration
10920
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 PartSys->particles[index].x = restposition + ((sin16_t(phase) * amplitude) >> 12); // apply position
10921 }
10922 else {
10923
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (hw_random16() < 656) { // ~1% chance to add a pulse
10924 ✗ int amplitude = 60;
10925 ✗ if (SEGMENT.check2) amplitude <<= 1; // double amplitude for XL particles
10926 ✗ PartSys->particles[PartSys->usedParticles >> 1].vx += hw_random16(amplitude << 1) - amplitude; // apply acceleration
10927 }
10928 }
10929 }
10930
10931
2/2
✓ Branch 0 taken 1575 times.
✓ Branch 1 taken 23 times.
1598 for (uint32_t i = 0; i < PartSys->usedParticles; i++) {
10932
4/6
✓ Branch 0 taken 1575 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1575 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 752 times.
✓ Branch 5 taken 823 times.
1575 if (SEGMENT.custom2 == 255) { // map speed to hue
10933 752 int speedclr = ((int8_t(abs(PartSys->particles[i].vx))) >> 2) << 4; // scale for greater color variation, dump small values to avoid flickering
10934 //int speed = PartSys->particles[i].vx << 2; // +/- 512
10935
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 752 times.
752 if (speedclr > 240) speedclr = 240; // limit color to non-wrapping part of palette
10936 752 PartSys->particles[i].hue = speedclr;
10937 }
10938
3/6
✓ Branch 0 taken 823 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 823 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 823 times.
823 else if (SEGMENT.custom2 > 0)
10939 ✗ PartSys->particles[i].hue = i * (SEGMENT.custom2 >> 2); // gradient distribution
10940 else {
10941 // map hue to particle density
10942 int deviation;
10943
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 804 times.
823 if (i == 0) // First particle: measure density based on distance to anchor point
10944 19 deviation = springlength/2 - PartSys->particles[i].x;
10945
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 785 times.
804 else if (i == PartSys->usedParticles - 1) // Last particle: measure density based on distance to right boundary
10946 19 deviation = springlength/2 - (PartSys->maxX - PartSys->particles[i].x);
10947 else {
10948 // Middle particles: average of compression/expansion from both sides
10949 785 int leftDx = PartSys->particles[i].x - PartSys->particles[i-1].x;
10950 785 int rightDx = PartSys->particles[i+1].x - PartSys->particles[i].x;
10951 785 int avgDistance = (leftDx + rightDx) >> 1;
10952
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 776 times.
785 if (avgDistance < 0) avgDistance = 0; // avoid negative distances (not sure why this happens)
10953 785 deviation = (springlength - avgDistance);
10954 }
10955 823 deviation = constrain(deviation, -127, 112); // limit deviation to -127..112 (do not go intwo wrapping part of palette)
10956 823 PartSys->particles[i].hue = 127 + deviation; // map density to hue
10957 }
10958 }
10959
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 PartSys->update(); // update and render
10960
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 return FRAMETIME;
10961
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 }
10962 static const char _data_FX_MODE_PS_SPRINGY[] PROGMEM = "PS Springy@Stiffness,Damping,Density,Hue,Mode,Smear,XL,AR;,!;!;1f;pal=54,c2=0,c3=23";
10963
10964 #endif // WLED_DISABLE_PARTICLESYSTEM1D
10965
10966 //////////////////////////////////////////////////////////////////////////////////////////
10967 // mode data
10968 static const char _data_RESERVED[] PROGMEM = "RSVD";
10969
10970 // add (or replace reserved) effect mode and data into vector
10971 // use id==255 to find unallocated gaps (with "Reserved" data string)
10972 // if vector size() is smaller than id (single) data is appended at the end (regardless of id)
10973 // return the actual id used for the effect or 255 if the add failed.
10974 757988 uint8_t WS2812FX::addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name) {
10975
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 757988 times.
757988 if (id == 255) { // find empty slot
10976 ✗ for (size_t i=1; i<_mode.size(); i++) if (_modeData[i] == _data_RESERVED) { id = i; break; }
10977 }
10978
1/2
✓ Branch 0 taken 757988 times.
✗ Branch 1 not taken.
757988 if (id < _mode.size()) {
10979
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 757988 times.
757988 if (_modeData[id] != _data_RESERVED) return 255; // do not overwrite an already added effect
10980 757988 _mode[id] = mode_fn;
10981 757988 _modeData[id] = mode_name;
10982 757988 return id;
10983 ✗ } else if (_mode.size() < 255) { // 255 is reserved for indicating the effect wasn't added
10984 ✗ _mode.push_back(mode_fn);
10985 ✗ _modeData.push_back(mode_name);
10986 ✗ if (_modeCount < _mode.size()) _modeCount++;
10987 ✗ return _mode.size() - 1;
10988 } else {
10989 ✗ return 255; // The vector is full so return 255
10990 }
10991 }
10992
10993 3542 void WS2812FX::setupEffectData() {
10994 // Solid must be first! (assuming vector is empty upon call to setup)
10995
1/2
✓ Branch 0 taken 3542 times.
✗ Branch 1 not taken.
3542 _mode.push_back(&mode_static);
10996
1/2
✓ Branch 0 taken 3542 times.
✗ Branch 1 not taken.
3542 _modeData.push_back(_data_FX_MODE_STATIC);
10997 // fill reserved word in case there will be any gaps in the array
10998
2/2
✓ Branch 0 taken 775698 times.
✓ Branch 1 taken 3542 times.
779240 for (size_t i=1; i<_modeCount; i++) {
10999
1/2
✓ Branch 0 taken 775698 times.
✗ Branch 1 not taken.
775698 _mode.push_back(&mode_static);
11000
1/2
✓ Branch 0 taken 775698 times.
✗ Branch 1 not taken.
775698 _modeData.push_back(_data_RESERVED);
11001 }
11002 // now replace all pre-allocated effects
11003 // --- 1D non-audio effects ---
11004 3542 addEffect(FX_MODE_BLINK, &mode_blink, _data_FX_MODE_BLINK);
11005 3542 addEffect(FX_MODE_BREATH, &mode_breath, _data_FX_MODE_BREATH);
11006 3542 addEffect(FX_MODE_COLOR_WIPE, &mode_color_wipe, _data_FX_MODE_COLOR_WIPE);
11007 3542 addEffect(FX_MODE_COLOR_WIPE_RANDOM, &mode_color_wipe_random, _data_FX_MODE_COLOR_WIPE_RANDOM);
11008 3542 addEffect(FX_MODE_RANDOM_COLOR, &mode_random_color, _data_FX_MODE_RANDOM_COLOR);
11009 3542 addEffect(FX_MODE_COLOR_SWEEP, &mode_color_sweep, _data_FX_MODE_COLOR_SWEEP);
11010 3542 addEffect(FX_MODE_DYNAMIC, &mode_dynamic, _data_FX_MODE_DYNAMIC);
11011 3542 addEffect(FX_MODE_RAINBOW, &mode_rainbow, _data_FX_MODE_RAINBOW);
11012 3542 addEffect(FX_MODE_RAINBOW_CYCLE, &mode_rainbow_cycle, _data_FX_MODE_RAINBOW_CYCLE);
11013 3542 addEffect(FX_MODE_SCAN, &mode_scan, _data_FX_MODE_SCAN);
11014 3542 addEffect(FX_MODE_DUAL_SCAN, &mode_dual_scan, _data_FX_MODE_DUAL_SCAN);
11015 3542 addEffect(FX_MODE_FADE, &mode_fade, _data_FX_MODE_FADE);
11016 3542 addEffect(FX_MODE_THEATER_CHASE, &mode_theater_chase, _data_FX_MODE_THEATER_CHASE);
11017 3542 addEffect(FX_MODE_THEATER_CHASE_RAINBOW, &mode_theater_chase_rainbow, _data_FX_MODE_THEATER_CHASE_RAINBOW);
11018 3542 addEffect(FX_MODE_RUNNING_LIGHTS, &mode_running_lights, _data_FX_MODE_RUNNING_LIGHTS);
11019 3542 addEffect(FX_MODE_SAW, &mode_saw, _data_FX_MODE_SAW);
11020 3542 addEffect(FX_MODE_TWINKLE, &mode_twinkle, _data_FX_MODE_TWINKLE);
11021 3542 addEffect(FX_MODE_DISSOLVE, &mode_dissolve, _data_FX_MODE_DISSOLVE);
11022 3542 addEffect(FX_MODE_DISSOLVE_RANDOM, &mode_dissolve_random, _data_FX_MODE_DISSOLVE_RANDOM);
11023 3542 addEffect(FX_MODE_FLASH_SPARKLE, &mode_flash_sparkle, _data_FX_MODE_FLASH_SPARKLE);
11024 3542 addEffect(FX_MODE_HYPER_SPARKLE, &mode_hyper_sparkle, _data_FX_MODE_HYPER_SPARKLE);
11025 3542 addEffect(FX_MODE_STROBE, &mode_strobe, _data_FX_MODE_STROBE);
11026 3542 addEffect(FX_MODE_STROBE_RAINBOW, &mode_strobe_rainbow, _data_FX_MODE_STROBE_RAINBOW);
11027 3542 addEffect(FX_MODE_MULTI_STROBE, &mode_multi_strobe, _data_FX_MODE_MULTI_STROBE);
11028 3542 addEffect(FX_MODE_BLINK_RAINBOW, &mode_blink_rainbow, _data_FX_MODE_BLINK_RAINBOW);
11029 3542 addEffect(FX_MODE_ANDROID, &mode_android, _data_FX_MODE_ANDROID);
11030 3542 addEffect(FX_MODE_CHASE_COLOR, &mode_chase_color, _data_FX_MODE_CHASE_COLOR);
11031 3542 addEffect(FX_MODE_CHASE_RANDOM, &mode_chase_random, _data_FX_MODE_CHASE_RANDOM);
11032 3542 addEffect(FX_MODE_CHASE_RAINBOW, &mode_chase_rainbow, _data_FX_MODE_CHASE_RAINBOW);
11033 3542 addEffect(FX_MODE_CHASE_FLASH, &mode_chase_flash, _data_FX_MODE_CHASE_FLASH);
11034 3542 addEffect(FX_MODE_CHASE_FLASH_RANDOM, &mode_chase_flash_random, _data_FX_MODE_CHASE_FLASH_RANDOM);
11035 3542 addEffect(FX_MODE_CHASE_RAINBOW_WHITE, &mode_chase_rainbow_white, _data_FX_MODE_CHASE_RAINBOW_WHITE);
11036 3542 addEffect(FX_MODE_COLORFUL, &mode_colorful, _data_FX_MODE_COLORFUL);
11037 3542 addEffect(FX_MODE_TRAFFIC_LIGHT, &mode_traffic_light, _data_FX_MODE_TRAFFIC_LIGHT);
11038 3542 addEffect(FX_MODE_COLOR_SWEEP_RANDOM, &mode_color_sweep_random, _data_FX_MODE_COLOR_SWEEP_RANDOM);
11039 3542 addEffect(FX_MODE_RUNNING_COLOR, &mode_running_color, _data_FX_MODE_RUNNING_COLOR);
11040 3542 addEffect(FX_MODE_AURORA, &mode_aurora, _data_FX_MODE_AURORA);
11041 3542 addEffect(FX_MODE_RUNNING_RANDOM, &mode_running_random, _data_FX_MODE_RUNNING_RANDOM);
11042 3542 addEffect(FX_MODE_LARSON_SCANNER, &mode_larson_scanner, _data_FX_MODE_LARSON_SCANNER);
11043 3542 addEffect(FX_MODE_RAIN, &mode_rain, _data_FX_MODE_RAIN);
11044 3542 addEffect(FX_MODE_PRIDE_2015, &mode_pride_2015, _data_FX_MODE_PRIDE_2015);
11045 3542 addEffect(FX_MODE_COLORWAVES, &mode_colorwaves, _data_FX_MODE_COLORWAVES);
11046 3542 addEffect(FX_MODE_FIREWORKS, &mode_fireworks, _data_FX_MODE_FIREWORKS);
11047 3542 addEffect(FX_MODE_TETRIX, &mode_tetrix, _data_FX_MODE_TETRIX);
11048 3542 addEffect(FX_MODE_FIRE_FLICKER, &mode_fire_flicker, _data_FX_MODE_FIRE_FLICKER);
11049 3542 addEffect(FX_MODE_GRADIENT, &mode_gradient, _data_FX_MODE_GRADIENT);
11050 3542 addEffect(FX_MODE_LOADING, &mode_loading, _data_FX_MODE_LOADING);
11051 3542 addEffect(FX_MODE_FAIRY, &mode_fairy, _data_FX_MODE_FAIRY);
11052 3542 addEffect(FX_MODE_TWO_DOTS, &mode_two_dots, _data_FX_MODE_TWO_DOTS);
11053 3542 addEffect(FX_MODE_FAIRYTWINKLE, &mode_fairytwinkle, _data_FX_MODE_FAIRYTWINKLE);
11054 3542 addEffect(FX_MODE_RUNNING_DUAL, &mode_running_dual, _data_FX_MODE_RUNNING_DUAL);
11055 #ifdef WLED_ENABLE_GIF
11056 addEffect(FX_MODE_IMAGE, &mode_image, _data_FX_MODE_IMAGE);
11057 #endif
11058 3542 addEffect(FX_MODE_TRICOLOR_CHASE, &mode_tricolor_chase, _data_FX_MODE_TRICOLOR_CHASE);
11059 3542 addEffect(FX_MODE_TRICOLOR_WIPE, &mode_tricolor_wipe, _data_FX_MODE_TRICOLOR_WIPE);
11060 3542 addEffect(FX_MODE_TRICOLOR_FADE, &mode_tricolor_fade, _data_FX_MODE_TRICOLOR_FADE);
11061 3542 addEffect(FX_MODE_LIGHTNING, &mode_lightning, _data_FX_MODE_LIGHTNING);
11062 3542 addEffect(FX_MODE_ICU, &mode_icu, _data_FX_MODE_ICU);
11063 3542 addEffect(FX_MODE_DUAL_LARSON_SCANNER, &mode_dual_larson_scanner, _data_FX_MODE_DUAL_LARSON_SCANNER);
11064 3542 addEffect(FX_MODE_RANDOM_CHASE, &mode_random_chase, _data_FX_MODE_RANDOM_CHASE);
11065 3542 addEffect(FX_MODE_OSCILLATE, &mode_oscillate, _data_FX_MODE_OSCILLATE);
11066 3542 addEffect(FX_MODE_JUGGLE, &mode_juggle, _data_FX_MODE_JUGGLE);
11067 3542 addEffect(FX_MODE_PALETTE, &mode_palette, _data_FX_MODE_PALETTE);
11068 3542 addEffect(FX_MODE_BPM, &mode_bpm, _data_FX_MODE_BPM);
11069 3542 addEffect(FX_MODE_FILLNOISE8, &mode_fillnoise8, _data_FX_MODE_FILLNOISE8);
11070 3542 addEffect(FX_MODE_NOISE16_1, &mode_noise16_1, _data_FX_MODE_NOISE16_1);
11071 3542 addEffect(FX_MODE_NOISE16_2, &mode_noise16_2, _data_FX_MODE_NOISE16_2);
11072 3542 addEffect(FX_MODE_NOISE16_3, &mode_noise16_3, _data_FX_MODE_NOISE16_3);
11073 3542 addEffect(FX_MODE_NOISE16_4, &mode_noise16_4, _data_FX_MODE_NOISE16_4);
11074 3542 addEffect(FX_MODE_COLORTWINKLE, &mode_colortwinkle, _data_FX_MODE_COLORTWINKLE);
11075 3542 addEffect(FX_MODE_LAKE, &mode_lake, _data_FX_MODE_LAKE);
11076 3542 addEffect(FX_MODE_METEOR, &mode_meteor, _data_FX_MODE_METEOR);
11077 //addEffect(FX_MODE_METEOR_SMOOTH, &mode_meteor_smooth, _data_FX_MODE_METEOR_SMOOTH); // merged with mode_meteor
11078 3542 addEffect(FX_MODE_RAILWAY, &mode_railway, _data_FX_MODE_RAILWAY);
11079 3542 addEffect(FX_MODE_RIPPLE, &mode_ripple, _data_FX_MODE_RIPPLE);
11080 3542 addEffect(FX_MODE_TWINKLEFOX, &mode_twinklefox, _data_FX_MODE_TWINKLEFOX);
11081 3542 addEffect(FX_MODE_TWINKLECAT, &mode_twinklecat, _data_FX_MODE_TWINKLECAT);
11082 3542 addEffect(FX_MODE_HALLOWEEN_EYES, &mode_halloween_eyes, _data_FX_MODE_HALLOWEEN_EYES);
11083 3542 addEffect(FX_MODE_STATIC_PATTERN, &mode_static_pattern, _data_FX_MODE_STATIC_PATTERN);
11084 3542 addEffect(FX_MODE_TRI_STATIC_PATTERN, &mode_tri_static_pattern, _data_FX_MODE_TRI_STATIC_PATTERN);
11085 3542 addEffect(FX_MODE_SPOTS, &mode_spots, _data_FX_MODE_SPOTS);
11086 3542 addEffect(FX_MODE_SPOTS_FADE, &mode_spots_fade, _data_FX_MODE_SPOTS_FADE);
11087 3542 addEffect(FX_MODE_COMET, &mode_comet, _data_FX_MODE_COMET);
11088 #ifdef WLED_PS_DONT_REPLACE_FX
11089 3542 addEffect(FX_MODE_MULTI_COMET, &mode_multi_comet, _data_FX_MODE_MULTI_COMET);
11090 3542 addEffect(FX_MODE_ROLLINGBALLS, &rolling_balls, _data_FX_MODE_ROLLINGBALLS);
11091 3542 addEffect(FX_MODE_SHIMMER, &mode_shimmer, _data_FX_MODE_SHIMMER);
11092 3542 addEffect(FX_MODE_COPY, &mode_copy_segment, _data_FX_MODE_COPY);
11093 3542 addEffect(FX_MODE_COLORCLOUDS, &mode_ColorClouds, _data_FX_MODE_COLORCLOUDS);
11094 3542 addEffect(FX_MODE_PACMAN, &mode_pacman, _data_FX_MODE_PACMAN);
11095 3542 addEffect(FX_MODE_SLOW_TRANSITION, &mode_slow_transition, _data_FX_MODE_SLOW_TRANSITION);
11096 3542 addEffect(FX_MODE_SPARKLE, &mode_sparkle, _data_FX_MODE_SPARKLE);
11097 3542 addEffect(FX_MODE_GLITTER, &mode_glitter, _data_FX_MODE_GLITTER);
11098 3542 addEffect(FX_MODE_SOLID_GLITTER, &mode_solid_glitter, _data_FX_MODE_SOLID_GLITTER);
11099 3542 addEffect(FX_MODE_STARBURST, &mode_starburst, _data_FX_MODE_STARBURST);
11100 3542 addEffect(FX_MODE_DANCING_SHADOWS, &mode_dancing_shadows, _data_FX_MODE_DANCING_SHADOWS);
11101 3542 addEffect(FX_MODE_FIRE_2012, &mode_fire_2012, _data_FX_MODE_FIRE_2012);
11102 3542 addEffect(FX_MODE_EXPLODING_FIREWORKS, &mode_exploding_fireworks, _data_FX_MODE_EXPLODING_FIREWORKS);
11103 #endif
11104 3542 addEffect(FX_MODE_CANDLE, &mode_candle, _data_FX_MODE_CANDLE);
11105 3542 addEffect(FX_MODE_BOUNCINGBALLS, &mode_bouncing_balls, _data_FX_MODE_BOUNCINGBALLS);
11106 3542 addEffect(FX_MODE_POPCORN, &mode_popcorn, _data_FX_MODE_POPCORN);
11107 3542 addEffect(FX_MODE_DRIP, &mode_drip, _data_FX_MODE_DRIP);
11108 3542 addEffect(FX_MODE_SINELON, &mode_sinelon, _data_FX_MODE_SINELON);
11109 3542 addEffect(FX_MODE_SINELON_DUAL, &mode_sinelon_dual, _data_FX_MODE_SINELON_DUAL);
11110 3542 addEffect(FX_MODE_SINELON_RAINBOW, &mode_sinelon_rainbow, _data_FX_MODE_SINELON_RAINBOW);
11111 3542 addEffect(FX_MODE_PLASMA, &mode_plasma, _data_FX_MODE_PLASMA);
11112 3542 addEffect(FX_MODE_PERCENT, &mode_percent, _data_FX_MODE_PERCENT);
11113 3542 addEffect(FX_MODE_RIPPLE_RAINBOW, &mode_ripple_rainbow, _data_FX_MODE_RIPPLE_RAINBOW);
11114 3542 addEffect(FX_MODE_HEARTBEAT, &mode_heartbeat, _data_FX_MODE_HEARTBEAT);
11115 3542 addEffect(FX_MODE_PACIFICA, &mode_pacifica, _data_FX_MODE_PACIFICA);
11116 3542 addEffect(FX_MODE_CANDLE_MULTI, &mode_candle_multi, _data_FX_MODE_CANDLE_MULTI);
11117 3542 addEffect(FX_MODE_SUNRISE, &mode_sunrise, _data_FX_MODE_SUNRISE);
11118 3542 addEffect(FX_MODE_PHASED, &mode_phased, _data_FX_MODE_PHASED);
11119 3542 addEffect(FX_MODE_TWINKLEUP, &mode_twinkleup, _data_FX_MODE_TWINKLEUP);
11120 3542 addEffect(FX_MODE_NOISEPAL, &mode_noisepal, _data_FX_MODE_NOISEPAL);
11121 3542 addEffect(FX_MODE_SINEWAVE, &mode_sinewave, _data_FX_MODE_SINEWAVE);
11122 3542 addEffect(FX_MODE_PHASEDNOISE, &mode_phased_noise, _data_FX_MODE_PHASEDNOISE);
11123 3542 addEffect(FX_MODE_FLOW, &mode_flow, _data_FX_MODE_FLOW);
11124 3542 addEffect(FX_MODE_CHUNCHUN, &mode_chunchun, _data_FX_MODE_CHUNCHUN);
11125 3542 addEffect(FX_MODE_WASHING_MACHINE, &mode_washing_machine, _data_FX_MODE_WASHING_MACHINE);
11126 3542 addEffect(FX_MODE_BLENDS, &mode_blends, _data_FX_MODE_BLENDS);
11127 3542 addEffect(FX_MODE_TV_SIMULATOR, &mode_tv_simulator, _data_FX_MODE_TV_SIMULATOR);
11128 3542 addEffect(FX_MODE_DYNAMIC_SMOOTH, &mode_dynamic_smooth, _data_FX_MODE_DYNAMIC_SMOOTH);
11129
11130 // --- 1D audio effects ---
11131 3542 addEffect(FX_MODE_PIXELS, &mode_pixels, _data_FX_MODE_PIXELS);
11132 3542 addEffect(FX_MODE_PIXELWAVE, &mode_pixelwave, _data_FX_MODE_PIXELWAVE);
11133 3542 addEffect(FX_MODE_JUGGLES, &mode_juggles, _data_FX_MODE_JUGGLES);
11134 3542 addEffect(FX_MODE_MATRIPIX, &mode_matripix, _data_FX_MODE_MATRIPIX);
11135 3542 addEffect(FX_MODE_GRAVIMETER, &mode_gravimeter, _data_FX_MODE_GRAVIMETER);
11136 3542 addEffect(FX_MODE_PLASMOID, &mode_plasmoid, _data_FX_MODE_PLASMOID);
11137 3542 addEffect(FX_MODE_PUDDLES, &mode_puddles, _data_FX_MODE_PUDDLES);
11138 3542 addEffect(FX_MODE_MIDNOISE, &mode_midnoise, _data_FX_MODE_MIDNOISE);
11139 3542 addEffect(FX_MODE_NOISEMETER, &mode_noisemeter, _data_FX_MODE_NOISEMETER);
11140 3542 addEffect(FX_MODE_FREQWAVE, &mode_freqwave, _data_FX_MODE_FREQWAVE);
11141 3542 addEffect(FX_MODE_FREQMATRIX, &mode_freqmatrix, _data_FX_MODE_FREQMATRIX);
11142 3542 addEffect(FX_MODE_WATERFALL, &mode_waterfall, _data_FX_MODE_WATERFALL);
11143 3542 addEffect(FX_MODE_FREQPIXELS, &mode_freqpixels, _data_FX_MODE_FREQPIXELS);
11144 3542 addEffect(FX_MODE_NOISEFIRE, &mode_noisefire, _data_FX_MODE_NOISEFIRE);
11145 3542 addEffect(FX_MODE_PUDDLEPEAK, &mode_puddlepeak, _data_FX_MODE_PUDDLEPEAK);
11146 3542 addEffect(FX_MODE_NOISEMOVE, &mode_noisemove, _data_FX_MODE_NOISEMOVE);
11147 3542 addEffect(FX_MODE_PERLINMOVE, &mode_perlinmove, _data_FX_MODE_PERLINMOVE);
11148 3542 addEffect(FX_MODE_RIPPLEPEAK, &mode_ripplepeak, _data_FX_MODE_RIPPLEPEAK);
11149 3542 addEffect(FX_MODE_FREQMAP, &mode_freqmap, _data_FX_MODE_FREQMAP);
11150 3542 addEffect(FX_MODE_GRAVCENTER, &mode_gravcenter, _data_FX_MODE_GRAVCENTER);
11151 3542 addEffect(FX_MODE_GRAVCENTRIC, &mode_gravcentric, _data_FX_MODE_GRAVCENTRIC);
11152 3542 addEffect(FX_MODE_GRAVFREQ, &mode_gravfreq, _data_FX_MODE_GRAVFREQ);
11153 3542 addEffect(FX_MODE_DJLIGHT, &mode_DJLight, _data_FX_MODE_DJLIGHT);
11154 3542 addEffect(FX_MODE_BLURZ, &mode_blurz, _data_FX_MODE_BLURZ);
11155 3542 addEffect(FX_MODE_FLOWSTRIPE, &mode_FlowStripe, _data_FX_MODE_FLOWSTRIPE);
11156 3542 addEffect(FX_MODE_WAVESINS, &mode_wavesins, _data_FX_MODE_WAVESINS);
11157 3542 addEffect(FX_MODE_ROCKTAVES, &mode_rocktaves, _data_FX_MODE_ROCKTAVES);
11158
11159 // --- 2D effects ---
11160 #ifndef WLED_DISABLE_2D
11161 3542 addEffect(FX_MODE_2DPLASMAROTOZOOM, &mode_2Dplasmarotozoom, _data_FX_MODE_2DPLASMAROTOZOOM);
11162 3542 addEffect(FX_MODE_2DSPACESHIPS, &mode_2Dspaceships, _data_FX_MODE_2DSPACESHIPS);
11163 3542 addEffect(FX_MODE_2DCRAZYBEES, &mode_2Dcrazybees, _data_FX_MODE_2DCRAZYBEES);
11164
11165 #ifdef WLED_PS_DONT_REPLACE_FX
11166 3542 addEffect(FX_MODE_2DGHOSTRIDER, &mode_2Dghostrider, _data_FX_MODE_2DGHOSTRIDER);
11167 3542 addEffect(FX_MODE_2DBLOBS, &mode_2Dfloatingblobs, _data_FX_MODE_2DBLOBS);
11168 #endif
11169
11170 3542 addEffect(FX_MODE_2DSCROLLTEXT, &mode_2Dscrollingtext, _data_FX_MODE_2DSCROLLTEXT);
11171 3542 addEffect(FX_MODE_2DDRIFTROSE, &mode_2Ddriftrose, _data_FX_MODE_2DDRIFTROSE);
11172 3542 addEffect(FX_MODE_2DDISTORTIONWAVES, &mode_2Ddistortionwaves, _data_FX_MODE_2DDISTORTIONWAVES);
11173 3542 addEffect(FX_MODE_2DGEQ, &mode_2DGEQ, _data_FX_MODE_2DGEQ); // audio
11174 3542 addEffect(FX_MODE_2DNOISE, &mode_2Dnoise, _data_FX_MODE_2DNOISE);
11175 3542 addEffect(FX_MODE_2DFIRENOISE, &mode_2Dfirenoise, _data_FX_MODE_2DFIRENOISE);
11176 3542 addEffect(FX_MODE_2DSQUAREDSWIRL, &mode_2Dsquaredswirl, _data_FX_MODE_2DSQUAREDSWIRL);
11177
11178 //non audio
11179 3542 addEffect(FX_MODE_2DDNA, &mode_2Ddna, _data_FX_MODE_2DDNA);
11180 3542 addEffect(FX_MODE_2DMATRIX, &mode_2Dmatrix, _data_FX_MODE_2DMATRIX);
11181 3542 addEffect(FX_MODE_2DMETABALLS, &mode_2Dmetaballs, _data_FX_MODE_2DMETABALLS);
11182 3542 addEffect(FX_MODE_2DFUNKYPLANK, &mode_2DFunkyPlank, _data_FX_MODE_2DFUNKYPLANK); // audio
11183 3542 addEffect(FX_MODE_2DPULSER, &mode_2DPulser, _data_FX_MODE_2DPULSER);
11184 3542 addEffect(FX_MODE_2DDRIFT, &mode_2DDrift, _data_FX_MODE_2DDRIFT);
11185 3542 addEffect(FX_MODE_2DWAVERLY, &mode_2DWaverly, _data_FX_MODE_2DWAVERLY); // audio
11186 3542 addEffect(FX_MODE_2DSUNRADIATION, &mode_2DSunradiation, _data_FX_MODE_2DSUNRADIATION);
11187 3542 addEffect(FX_MODE_2DCOLOREDBURSTS, &mode_2DColoredBursts, _data_FX_MODE_2DCOLOREDBURSTS);
11188 3542 addEffect(FX_MODE_2DJULIA, &mode_2DJulia, _data_FX_MODE_2DJULIA);
11189 3542 addEffect(FX_MODE_2DGAMEOFLIFE, &mode_2Dgameoflife, _data_FX_MODE_2DGAMEOFLIFE);
11190 3542 addEffect(FX_MODE_2DTARTAN, &mode_2Dtartan, _data_FX_MODE_2DTARTAN);
11191 3542 addEffect(FX_MODE_2DPOLARLIGHTS, &mode_2DPolarLights, _data_FX_MODE_2DPOLARLIGHTS);
11192 3542 addEffect(FX_MODE_2DSWIRL, &mode_2DSwirl, _data_FX_MODE_2DSWIRL); // audio
11193 3542 addEffect(FX_MODE_2DLISSAJOUS, &mode_2DLissajous, _data_FX_MODE_2DLISSAJOUS);
11194 3542 addEffect(FX_MODE_2DFRIZZLES, &mode_2DFrizzles, _data_FX_MODE_2DFRIZZLES);
11195 3542 addEffect(FX_MODE_2DPLASMABALL, &mode_2DPlasmaball, _data_FX_MODE_2DPLASMABALL);
11196 3542 addEffect(FX_MODE_2DHIPHOTIC, &mode_2DHiphotic, _data_FX_MODE_2DHIPHOTIC);
11197 3542 addEffect(FX_MODE_2DSINDOTS, &mode_2DSindots, _data_FX_MODE_2DSINDOTS);
11198 3542 addEffect(FX_MODE_2DDNASPIRAL, &mode_2DDNASpiral, _data_FX_MODE_2DDNASPIRAL);
11199 3542 addEffect(FX_MODE_2DBLACKHOLE, &mode_2DBlackHole, _data_FX_MODE_2DBLACKHOLE);
11200 3542 addEffect(FX_MODE_2DSOAP, &mode_2Dsoap, _data_FX_MODE_2DSOAP);
11201 3542 addEffect(FX_MODE_2DOCTOPUS, &mode_2Doctopus, _data_FX_MODE_2DOCTOPUS);
11202 3542 addEffect(FX_MODE_2DWAVINGCELL, &mode_2Dwavingcell, _data_FX_MODE_2DWAVINGCELL);
11203 3542 addEffect(FX_MODE_2DAKEMI, &mode_2DAkemi, _data_FX_MODE_2DAKEMI); // audio
11204
11205 #ifndef WLED_DISABLE_PARTICLESYSTEM2D
11206 3542 addEffect(FX_MODE_PARTICLEVOLCANO, &mode_particlevolcano, _data_FX_MODE_PARTICLEVOLCANO);
11207 3542 addEffect(FX_MODE_PARTICLEFIRE, &mode_particlefire, _data_FX_MODE_PARTICLEFIRE);
11208 3542 addEffect(FX_MODE_PARTICLEFIREWORKS, &mode_particlefireworks, _data_FX_MODE_PARTICLEFIREWORKS);
11209 3542 addEffect(FX_MODE_PARTICLEVORTEX, &mode_particlevortex, _data_FX_MODE_PARTICLEVORTEX);
11210 3542 addEffect(FX_MODE_PARTICLEPERLIN, &mode_particleperlin, _data_FX_MODE_PARTICLEPERLIN);
11211 3542 addEffect(FX_MODE_PARTICLEPIT, &mode_particlepit, _data_FX_MODE_PARTICLEPIT);
11212 3542 addEffect(FX_MODE_PARTICLEBOX, &mode_particlebox, _data_FX_MODE_PARTICLEBOX);
11213 3542 addEffect(FX_MODE_PARTICLEATTRACTOR, &mode_particleattractor, _data_FX_MODE_PARTICLEATTRACTOR); // 872 bytes
11214 3542 addEffect(FX_MODE_PARTICLEIMPACT, &mode_particleimpact, _data_FX_MODE_PARTICLEIMPACT);
11215 3542 addEffect(FX_MODE_PARTICLEWATERFALL, &mode_particlewaterfall, _data_FX_MODE_PARTICLEWATERFALL);
11216 3542 addEffect(FX_MODE_PARTICLESPRAY, &mode_particlespray, _data_FX_MODE_PARTICLESPRAY);
11217 3542 addEffect(FX_MODE_PARTICLESGEQ, &mode_particleGEQ, _data_FX_MODE_PARTICLEGEQ);
11218 3542 addEffect(FX_MODE_PARTICLECENTERGEQ, &mode_particlecenterGEQ, _data_FX_MODE_PARTICLECIRCULARGEQ);
11219 3542 addEffect(FX_MODE_PARTICLEGHOSTRIDER, &mode_particleghostrider, _data_FX_MODE_PARTICLEGHOSTRIDER);
11220 3542 addEffect(FX_MODE_PARTICLEBLOBS, &mode_particleblobs, _data_FX_MODE_PARTICLEBLOBS);
11221 3542 addEffect(FX_MODE_PARTICLEGALAXY, &mode_particlegalaxy, _data_FX_MODE_PARTICLEGALAXY);
11222 #endif // WLED_DISABLE_PARTICLESYSTEM2D
11223 #endif // WLED_DISABLE_2D
11224
11225 #ifndef WLED_DISABLE_PARTICLESYSTEM1D
11226 3542 addEffect(FX_MODE_PSDRIP, &mode_particleDrip, _data_FX_MODE_PARTICLEDRIP);
11227 3542 addEffect(FX_MODE_PSPINBALL, &mode_particlePinball, _data_FX_MODE_PSPINBALL); //potential replacement for: bouncing balls, rollingballs, popcorn
11228 3542 addEffect(FX_MODE_PSDANCINGSHADOWS, &mode_particleDancingShadows, _data_FX_MODE_PARTICLEDANCINGSHADOWS);
11229 3542 addEffect(FX_MODE_PSFIREWORKS1D, &mode_particleFireworks1D, _data_FX_MODE_PS_FIREWORKS1D);
11230 3542 addEffect(FX_MODE_PSSPARKLER, &mode_particleSparkler, _data_FX_MODE_PS_SPARKLER);
11231 3542 addEffect(FX_MODE_PSHOURGLASS, &mode_particleHourglass, _data_FX_MODE_PS_HOURGLASS);
11232 3542 addEffect(FX_MODE_PS1DSPRAY, &mode_particle1Dspray, _data_FX_MODE_PS_1DSPRAY);
11233 3542 addEffect(FX_MODE_PSBALANCE, &mode_particleBalance, _data_FX_MODE_PS_BALANCE);
11234 3542 addEffect(FX_MODE_PSCHASE, &mode_particleChase, _data_FX_MODE_PS_CHASE);
11235 3542 addEffect(FX_MODE_PSSTARBURST, &mode_particleStarburst, _data_FX_MODE_PS_STARBURST);
11236 3542 addEffect(FX_MODE_PS1DGEQ, &mode_particle1DGEQ, _data_FX_MODE_PS_1D_GEQ);
11237 3542 addEffect(FX_MODE_PSFIRE1D, &mode_particleFire1D, _data_FX_MODE_PS_FIRE1D);
11238 3542 addEffect(FX_MODE_PS1DSONICSTREAM, &mode_particle1DsonicStream, _data_FX_MODE_PS_SONICSTREAM);
11239 3542 addEffect(FX_MODE_PS1DSONICBOOM, &mode_particle1DsonicBoom, _data_FX_MODE_PS_SONICBOOM);
11240 3542 addEffect(FX_MODE_PS1DSPRINGY, &mode_particleSpringy, _data_FX_MODE_PS_SPRINGY);
11241 #endif // WLED_DISABLE_PARTICLESYSTEM1D
11242
11243 3542 }
11244