diff --git a/wled00/FX.cpp b/wled00/FX.cpp index bbf1d007..d8265c29 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -3569,4 +3569,146 @@ uint16_t WS2812FX::mode_chunchun(void) setPixelColor(bird, c); } return FRAMETIME; -} \ No newline at end of file +} + + +typedef struct Spotlight { + float speed; + uint8_t colorIdx; + int16_t position; + unsigned long lastUpdateTime; + uint8_t width; + uint8_t type; +} spotlight; + +#define SPOT_TYPE_SOLID 0 +#define SPOT_TYPE_GRADIENT 1 +#define SPOT_TYPE_2X_GRADIENT 2 +#define SPOT_TYPE_2X_DOT 3 +#define SPOT_TYPE_3X_DOT 4 +#define SPOT_TYPE_4X_DOT 5 +#define SPOT_TYPES_COUNT 6 + +/* + * Spotlights moving back and forth that cast dancing shadows. + * Shine this through tree branches/leaves or other close-up objects that cast + * interesting shadows onto a ceiling or tarp. + * + * By Steve Pomeroy @xxv + */ +uint16_t WS2812FX::mode_dancing_shadows(void) +{ + uint8_t numSpotlights = map(SEGMENT.intensity, 0, 255, 2, 50); + bool initialize = SEGENV.aux0 != numSpotlights; + SEGENV.aux0 = numSpotlights; + + uint16_t dataSize = sizeof(spotlight) * numSpotlights; + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + Spotlight* spotlights = reinterpret_cast(SEGENV.data); + + fill(BLACK); + + unsigned long time = millis(); + bool respawn = false; + + for (uint8_t i = 0; i < numSpotlights; i++) { + if (!initialize) { + // advance the position of the spotlight + int16_t delta = (float)(time - spotlights[i].lastUpdateTime) * + (spotlights[i].speed * ((1.0 + SEGMENT.speed)/100.0)); + + if (abs(delta) >= 1) { + spotlights[i].position += delta; + spotlights[i].lastUpdateTime = time; + } + + respawn = (spotlights[i].speed > 0.0 && spotlights[i].position > (SEGLEN + 2)) + || (spotlights[i].speed < 0.0 && spotlights[i].position < -(spotlights[i].width + 2)); + } + + if (initialize || respawn) { + spotlights[i].colorIdx = random8(); + spotlights[i].width = random8(1, 10); + + spotlights[i].speed = 1.0/random8(4, 50); + + if (initialize) { + spotlights[i].position = random16(SEGLEN); + spotlights[i].speed *= random8(2) ? 1.0 : -1.0; + } else { + if (random8(2)) { + spotlights[i].position = SEGLEN + spotlights[i].width; + spotlights[i].speed *= -1.0; + }else { + spotlights[i].position = -spotlights[i].width; + } + } + + spotlights[i].lastUpdateTime = time; + spotlights[i].type = random8(SPOT_TYPES_COUNT); + } + + uint32_t color = color_from_palette(spotlights[i].colorIdx, false, false, 0); + int start = spotlights[i].position; + + if (spotlights[i].width <= 1) { + if (start >= 0 && start < SEGLEN) { + blendPixelColor(start, color, 128); + } + } else { + switch (spotlights[i].type) { + case SPOT_TYPE_SOLID: + for (uint8_t j = 0; j < spotlights[i].width; j++) { + if ((start + j) >= 0 && (start + j) < SEGLEN) { + blendPixelColor(start + j, color, 128); + } + } + break; + + case SPOT_TYPE_GRADIENT: + for (uint8_t j = 0; j < spotlights[i].width; j++) { + if ((start + j) >= 0 && (start + j) < SEGLEN) { + blendPixelColor(start + j, color, + cubicwave8(map(j, 0, spotlights[i].width - 1, 0, 255))); + } + } + break; + + case SPOT_TYPE_2X_GRADIENT: + for (uint8_t j = 0; j < spotlights[i].width; j++) { + if ((start + j) >= 0 && (start + j) < SEGLEN) { + blendPixelColor(start + j, color, + cubicwave8(2 * map(j, 0, spotlights[i].width - 1, 0, 255))); + } + } + break; + + case SPOT_TYPE_2X_DOT: + for (uint8_t j = 0; j < spotlights[i].width; j += 2) { + if ((start + j) >= 0 && (start + j) < SEGLEN) { + blendPixelColor(start + j, color, 128); + } + } + break; + + case SPOT_TYPE_3X_DOT: + for (uint8_t j = 0; j < spotlights[i].width; j += 3) { + if ((start + j) >= 0 && (start + j) < SEGLEN) { + blendPixelColor(start + j, color, 128); + } + } + break; + + case SPOT_TYPE_4X_DOT: + for (uint8_t j = 0; j < spotlights[i].width; j += 4) { + if ((start + j) >= 0 && (start + j) < SEGLEN) { + blendPixelColor(start + j, color, 128); + } + } + break; + } + } + } + + return FRAMETIME; +} diff --git a/wled00/FX.h b/wled00/FX.h index f1011822..05802b8f 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -102,7 +102,7 @@ #define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE ) #define IS_SELECTED ((SEGMENT.options & SELECTED ) == SELECTED ) -#define MODE_COUNT 112 +#define MODE_COUNT 113 #define FX_MODE_STATIC 0 #define FX_MODE_BLINK 1 @@ -216,6 +216,7 @@ #define FX_MODE_PHASEDNOISE 109 #define FX_MODE_FLOW 110 #define FX_MODE_CHUNCHUN 111 +#define FX_MODE_DANCING_SHADOWS 112 class WS2812FX { typedef uint16_t (WS2812FX::*mode_ptr)(void); @@ -419,6 +420,7 @@ class WS2812FX { _mode[FX_MODE_PHASEDNOISE] = &WS2812FX::mode_phased_noise; _mode[FX_MODE_FLOW] = &WS2812FX::mode_flow; _mode[FX_MODE_CHUNCHUN] = &WS2812FX::mode_chunchun; + _mode[FX_MODE_DANCING_SHADOWS] = &WS2812FX::mode_dancing_shadows; _brightness = DEFAULT_BRIGHTNESS; currentPalette = CRGBPalette16(CRGB::Black); @@ -617,7 +619,8 @@ class WS2812FX { mode_sinewave(void), mode_phased_noise(void), mode_flow(void), - mode_chunchun(void); + mode_chunchun(void), + mode_dancing_shadows(void); private: NeoPixelWrapper *bus; @@ -667,6 +670,8 @@ class WS2812FX { CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat); CRGB pacifica_one_layer(uint16_t i, CRGBPalette16& p, uint16_t cistart, uint16_t wavescale, uint8_t bri, uint16_t ioff); + + void blendPixelColor(uint16_t n, uint32_t color, uint8_t blend); uint32_t _lastPaletteChange = 0; uint32_t _lastShow = 0; @@ -702,7 +707,7 @@ const char JSON_mode_names[] PROGMEM = R"=====([ "Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle","Fireworks Starburst", "Fireworks 1D","Bouncing Balls","Sinelon","Sinelon Dual","Sinelon Rainbow","Popcorn","Drip","Plasma","Percent","Ripple Rainbow", "Heartbeat","Pacifica","Candle Multi", "Solid Glitter","Sunrise","Phased","Twinkleup","Noise Pal", "Sine","Phased Noise", -"Flow","Chunchun" +"Flow","Chunchun","Dancing Shadows" ])====="; diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 579a0cb6..d979dfcf 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -620,6 +620,14 @@ void WS2812FX::fill(uint32_t c) { } } +/* + * Blends the specified color with the existing pixel color. + */ +void WS2812FX::blendPixelColor(uint16_t n, uint32_t color, uint8_t blend) +{ + setPixelColor(n, color_blend(getPixelColor(n), color, blend)); +} + /* * fade out function, higher rate = quicker fade */