diff --git a/espmusicmouse/lib/ledtl/containers/LedStripRGBW.h b/espmusicmouse/lib/ledtl/containers/LedStripRGBW.h index 4420b92..7fde545 100644 --- a/espmusicmouse/lib/ledtl/containers/LedStripRGBW.h +++ b/espmusicmouse/lib/ledtl/containers/LedStripRGBW.h @@ -26,6 +26,15 @@ public: data_[idx] = (g << 0) | (r << 8) | (b << 16) | (w << 24); } + void getRGBW(int idx, uint8_t &r, uint8_t &g, uint8_t &b, uint8_t &w) + { + idx = normalizeIdx(idx); + g = (data_[idx] >> 0) & 0xff; + r = (data_[idx] >> 8) & 0xff; + b = (data_[idx] >> 16) & 0xff; + w = (data_[idx] >> 24) & 0xff; + } + const uint32_t *rawData() const { return data_; } constexpr static int numLeds() { return TNumLeds; } @@ -89,4 +98,12 @@ void clear(LedStripRGBW &s) { for (int i = 0; i < TNumLeds; ++i) s.set(i, 0, 0, 0, 0); -} \ No newline at end of file +} + +template +ColorRGBW getLedRGBW(LedStripRGBW &s, int idx) +{ + ColorRGBW res; + s.getRGBW(idx, res.r, res.g, res.b, res.w); + return res; +} diff --git a/espmusicmouse/lib/ledtl/effects/AlexaSwipe.h b/espmusicmouse/lib/ledtl/effects/AlexaSwipe.h index 5809793..38b20a0 100644 --- a/espmusicmouse/lib/ledtl/effects/AlexaSwipe.h +++ b/espmusicmouse/lib/ledtl/effects/AlexaSwipe.h @@ -50,10 +50,6 @@ public: currentPosition_ = float(NUM_LEDS) / 2.0f + bellCurveWidth_ / 2; direction_ = -1; } - //#ifndef PLATFORM_NATIVE - // Serial.printf("Primary color %f, %f, %f\n", primaryColor_.h, primaryColor_.s, primaryColor_.v); - // Serial.printf("Secondary color %f, %f, %f\n", secondaryColor_.h, secondaryColor_.s, secondaryColor_.v); - //#endif } int operator()() diff --git a/espmusicmouse/lib/ledtl/effects/Common.h b/espmusicmouse/lib/ledtl/effects/Common.h index 452e83c..d721432 100644 --- a/espmusicmouse/lib/ledtl/effects/Common.h +++ b/espmusicmouse/lib/ledtl/effects/Common.h @@ -5,6 +5,7 @@ enum class EffectId STATIC, CIRCULAR, ALEXA_SWIPE, + RANDOM_TWO_COLOR_INTERPOLATION }; template diff --git a/espmusicmouse/lib/ledtl/effects/RandomTwoColorInterpolation.h b/espmusicmouse/lib/ledtl/effects/RandomTwoColorInterpolation.h new file mode 100644 index 0000000..ebf9f24 --- /dev/null +++ b/espmusicmouse/lib/ledtl/effects/RandomTwoColorInterpolation.h @@ -0,0 +1,146 @@ + +#pragma once + +#include "effects/Common.h" +#include "helpers/ColorRGBW.h" +#include "helpers/ColorHSV.h" +#include "helpers/ColorConversions.h" + +struct EffectRandomTwoColorInterpolationConfig +{ + int cycleDurationMs; + bool startWithExisting; + int numSegments; + + ColorHSV color1; + ColorHSV color2; + + bool hue1Random; + bool hue2Random; +}; + +template +class EffectRandomTwoColorInterpolation +{ +public: + static constexpr auto NUM_LEDS = numLeds(); + static constexpr int DELAY_MS = 10; + + EffectRandomTwoColorInterpolation(const EffectRandomTwoColorInterpolationConfig &cfg, TLedStrip &ledStrip) + : ledStrip_(ledStrip), + config_(cfg), + progress_(0.0f) + { + currentColors_ = arr1; + nextColors_ = arr2; + + if (config_.startWithExisting) + for (int i = 0; i < NUM_LEDS; ++i) + currentColors_[i] = rgb2hsv(getLedRGBW(ledStrip_, i)); + else + randomizeColors(currentColors_); + + randomizeColors(nextColors_); + } + + int operator()() + { + for (int i = 0; i < NUM_LEDS; ++i) + setLedRGBW(ledStrip_, i, hsv2rgb(interpolate(currentColors_[i], nextColors_[i], progress_))); + + progress_ += (float(DELAY_MS) / config_.cycleDurationMs); + if (progress_ > 1) + { + progress_ = 0; + std::swap(currentColors_, nextColors_); + randomizeColors(nextColors_); + } + return DELAY_MS; + } + +private: + float randomFloat() + { + return float(esp_random()) / float(UINT32_MAX); + } + + ColorHSV randomColor() + { + ColorHSV color1 = config_.color1; + ColorHSV color2 = config_.color2; + + if (config_.hue1Random) + color1.h = randomFloat() * 360; + if (config_.hue2Random) + color2.h = randomFloat() * 360; + + float f = randomFloat(); + return interpolate(color1, color2, f); + } + + void randomizeColors(ColorHSV *arr) + { + int segmentLength = NUM_LEDS / config_.numSegments; + int lastSegmentLength = NUM_LEDS - (segmentLength * (config_.numSegments - 1)); + + ColorHSV firstColor = randomColor(); + + ColorHSV currentColor = firstColor; + ColorHSV nextColor = randomColor(); + + int position = random(0, NUM_LEDS); + const auto incrPosition = [&position]() + { + position = (position == NUM_LEDS - 1) ? 0 : position + 1; + }; + + for (int segmentIdx = 0; segmentIdx < config_.numSegments - 1; ++segmentIdx) + { + for (int i = 0; i < segmentLength; ++i) + { + float f = float(i) / float(segmentLength); + arr[position] = interpolate(currentColor, nextColor, f); + incrPosition(); + } + currentColor = nextColor; + nextColor = randomColor(); + } + // last segment + for (int i = 0; i < lastSegmentLength; ++i) + { + float f = float(i) / float(lastSegmentLength); + arr[position] = interpolate(currentColor, firstColor, f); + incrPosition(); + } + } + + TLedStrip &ledStrip_; + EffectRandomTwoColorInterpolationConfig config_; + + ColorHSV arr1[NUM_LEDS]; + ColorHSV arr2[NUM_LEDS]; + + ColorHSV *currentColors_; + ColorHSV *nextColors_; + + float progress_; +}; + +// Traits +template <> +struct EffectIdToConfig +{ + using type = EffectRandomTwoColorInterpolationConfig; +}; + +template <> +struct EffectConfigToId +{ + static constexpr auto id = EffectId::RANDOM_TWO_COLOR_INTERPOLATION; +}; + +template +struct EffectIdToClass +{ + using type = EffectRandomTwoColorInterpolation; +}; \ No newline at end of file diff --git a/espmusicmouse/lib/ledtl/helpers/ColorConversions.h b/espmusicmouse/lib/ledtl/helpers/ColorConversions.h index 4042636..7327105 100644 --- a/espmusicmouse/lib/ledtl/helpers/ColorConversions.h +++ b/espmusicmouse/lib/ledtl/helpers/ColorConversions.h @@ -1,3 +1,5 @@ +#pragma once + #include "helpers/ColorHSV.h" #include "helpers/ColorRGBW.h" diff --git a/espmusicmouse/lib/ledtl/helpers/ColorHSV.h b/espmusicmouse/lib/ledtl/helpers/ColorHSV.h index 10a353a..3fbe39f 100644 --- a/espmusicmouse/lib/ledtl/helpers/ColorHSV.h +++ b/espmusicmouse/lib/ledtl/helpers/ColorHSV.h @@ -5,4 +5,42 @@ struct ColorHSV { float h, s, v; -}; \ No newline at end of file +}; + +inline ColorHSV operator*(const ColorHSV &c, float scalar) +{ + + return {scalar * c.h, + scalar * c.s, + scalar * c.v}; +} + +inline ColorHSV operator*(float scalar, const ColorHSV &c) +{ + return {scalar * c.h, + scalar * c.s, + scalar * c.v}; +} + +inline ColorHSV operator+(const ColorHSV &c1, const ColorHSV &c2) +{ + return { + c1.h + c2.h, + c1.s + c2.s, + c1.v + c2.v}; +} + +inline ColorHSV interpolate(const ColorHSV &c1, const ColorHSV &c2, float f) +{ + return ColorHSV{ + (1.0f - f) * c1.h + f * c2.h, + (1.0f - f) * c1.s + f * c2.s, + (1.0f - f) * c1.v + f * c2.v}; +} + +#ifndef PLATFORM_NATIVE +inline void print(const char *prefix, const ColorHSV &c) +{ + Serial.printf("%s HSV(%f, %f, %f)\n", prefix, c.h, c.s, c.v); +} +#endif diff --git a/espmusicmouse/src/TaskLed.h b/espmusicmouse/src/TaskLed.h index 0da1c40..5230aeb 100644 --- a/espmusicmouse/src/TaskLed.h +++ b/espmusicmouse/src/TaskLed.h @@ -12,7 +12,7 @@ #include static constexpr int MAX_EFFECT_CONFIG_SIZE = 128; -static constexpr int MAX_EFFECT_CLASS_SIZE = 128; +static constexpr int MAX_EFFECT_CLASS_SIZE = 4 * 1024; template class LedTask @@ -75,9 +75,10 @@ void _led_task_func(void *params) TLedStrip &ledStrip = *(task->ledStrip_); // clang-format off - if (dispatchEffectId(id, effectFunction, ledStrip, msgBuffer, effectStorage)) { Serial.println("Parsed circular");} - else if (dispatchEffectId(id, effectFunction, ledStrip, msgBuffer, effectStorage)) { Serial.println("Parsed static");} - else if (dispatchEffectId(id, effectFunction, ledStrip, msgBuffer, effectStorage)) { Serial.println("Alexa swipe");} + if (dispatchEffectId(id, effectFunction, ledStrip, msgBuffer, effectStorage)) { Serial.println("Parsed circular");} + else if (dispatchEffectId(id, effectFunction, ledStrip, msgBuffer, effectStorage)) { Serial.println("Parsed static");} + else if (dispatchEffectId(id, effectFunction, ledStrip, msgBuffer, effectStorage)) { Serial.println("Alexa swipe");} + else if (dispatchEffectId(id, effectFunction, ledStrip, msgBuffer, effectStorage)) { Serial.println("random color ip");} // clang-format on timeoutMsForEffect = 0; diff --git a/espmusicmouse/src/main.cpp b/espmusicmouse/src/main.cpp index 7a36dda..a358fb7 100644 --- a/espmusicmouse/src/main.cpp +++ b/espmusicmouse/src/main.cpp @@ -9,6 +9,7 @@ #include "effects/Circular.h" #include "effects/Static.h" #include "effects/AlexaSwipe.h" +#include "effects/RandomTwoColorInterpolation.h" #include "TaskLed.h" @@ -37,6 +38,9 @@ void tag_handler(uint8_t *sn) fox = true; //ledTask.startEffect(EffectCircularConfig{2 * 360, 180, ColorRGBW{0, 0, 255, 0}}); ledTask.startEffect(EffectAlexaSwipeConfig{20, 30, 3 * 360, 3, 180, true, ColorRGBW{0, 255, 0, 0}, ColorRGBW{0, 0, 255, 0}}); + delay(1000); + ledTask.startEffect(EffectRandomTwoColorInterpolationConfig{6000, true, 6, rgb2hsv(ColorRGBW{128, 0, 0, 0}), + rgb2hsv(ColorRGBW{0, 0, 128, 0}), true, true}); } if (sn[4] == 0xf0) {