Color interpolation effect

This commit is contained in:
Martin Bauer 2021-11-19 20:26:56 +01:00
parent 0bbb5d278c
commit da9a96fdfa
8 changed files with 215 additions and 10 deletions

View File

@ -26,6 +26,15 @@ public:
data_[idx] = (g << 0) | (r << 8) | (b << 16) | (w << 24); 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_; } const uint32_t *rawData() const { return data_; }
constexpr static int numLeds() { return TNumLeds; } constexpr static int numLeds() { return TNumLeds; }
@ -89,4 +98,12 @@ void clear(LedStripRGBW<TNumLeds> &s)
{ {
for (int i = 0; i < TNumLeds; ++i) for (int i = 0; i < TNumLeds; ++i)
s.set(i, 0, 0, 0, 0); s.set(i, 0, 0, 0, 0);
} }
template <int TNumLeds>
ColorRGBW getLedRGBW(LedStripRGBW<TNumLeds> &s, int idx)
{
ColorRGBW res;
s.getRGBW(idx, res.r, res.g, res.b, res.w);
return res;
}

View File

@ -50,10 +50,6 @@ public:
currentPosition_ = float(NUM_LEDS) / 2.0f + bellCurveWidth_ / 2; currentPosition_ = float(NUM_LEDS) / 2.0f + bellCurveWidth_ / 2;
direction_ = -1; 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()() int operator()()

View File

@ -5,6 +5,7 @@ enum class EffectId
STATIC, STATIC,
CIRCULAR, CIRCULAR,
ALEXA_SWIPE, ALEXA_SWIPE,
RANDOM_TWO_COLOR_INTERPOLATION
}; };
template <EffectId id> template <EffectId id>

View File

@ -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 <typename TLedStrip>
class EffectRandomTwoColorInterpolation
{
public:
static constexpr auto NUM_LEDS = numLeds<TLedStrip>();
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<EffectId::RANDOM_TWO_COLOR_INTERPOLATION>
{
using type = EffectRandomTwoColorInterpolationConfig;
};
template <>
struct EffectConfigToId<EffectRandomTwoColorInterpolationConfig>
{
static constexpr auto id = EffectId::RANDOM_TWO_COLOR_INTERPOLATION;
};
template <typename TLedStrip>
struct EffectIdToClass<EffectId::RANDOM_TWO_COLOR_INTERPOLATION, TLedStrip>
{
using type = EffectRandomTwoColorInterpolation<TLedStrip>;
};

View File

@ -1,3 +1,5 @@
#pragma once
#include "helpers/ColorHSV.h" #include "helpers/ColorHSV.h"
#include "helpers/ColorRGBW.h" #include "helpers/ColorRGBW.h"

View File

@ -5,4 +5,42 @@
struct ColorHSV struct ColorHSV
{ {
float h, s, v; float h, s, v;
}; };
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

View File

@ -12,7 +12,7 @@
#include <cstring> #include <cstring>
static constexpr int MAX_EFFECT_CONFIG_SIZE = 128; 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 <typename TLedStrip> template <typename TLedStrip>
class LedTask class LedTask
@ -75,9 +75,10 @@ void _led_task_func(void *params)
TLedStrip &ledStrip = *(task->ledStrip_); TLedStrip &ledStrip = *(task->ledStrip_);
// clang-format off // clang-format off
if (dispatchEffectId<EffectId::CIRCULAR >(id, effectFunction, ledStrip, msgBuffer, effectStorage)) { Serial.println("Parsed circular");} if (dispatchEffectId<EffectId::CIRCULAR >(id, effectFunction, ledStrip, msgBuffer, effectStorage)) { Serial.println("Parsed circular");}
else if (dispatchEffectId<EffectId::STATIC >(id, effectFunction, ledStrip, msgBuffer, effectStorage)) { Serial.println("Parsed static");} else if (dispatchEffectId<EffectId::STATIC >(id, effectFunction, ledStrip, msgBuffer, effectStorage)) { Serial.println("Parsed static");}
else if (dispatchEffectId<EffectId::ALEXA_SWIPE>(id, effectFunction, ledStrip, msgBuffer, effectStorage)) { Serial.println("Alexa swipe");} else if (dispatchEffectId<EffectId::ALEXA_SWIPE >(id, effectFunction, ledStrip, msgBuffer, effectStorage)) { Serial.println("Alexa swipe");}
else if (dispatchEffectId<EffectId::RANDOM_TWO_COLOR_INTERPOLATION>(id, effectFunction, ledStrip, msgBuffer, effectStorage)) { Serial.println("random color ip");}
// clang-format on // clang-format on
timeoutMsForEffect = 0; timeoutMsForEffect = 0;

View File

@ -9,6 +9,7 @@
#include "effects/Circular.h" #include "effects/Circular.h"
#include "effects/Static.h" #include "effects/Static.h"
#include "effects/AlexaSwipe.h" #include "effects/AlexaSwipe.h"
#include "effects/RandomTwoColorInterpolation.h"
#include "TaskLed.h" #include "TaskLed.h"
@ -37,6 +38,9 @@ void tag_handler(uint8_t *sn)
fox = true; fox = true;
//ledTask.startEffect(EffectCircularConfig{2 * 360, 180, ColorRGBW{0, 0, 255, 0}}); //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}}); 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) if (sn[4] == 0xf0)
{ {