Alexa swipe effect

This commit is contained in:
Martin Bauer
2021-11-19 17:22:09 +01:00
parent 484db9bdfa
commit aa76900244
20 changed files with 342 additions and 894 deletions

View File

@@ -1,3 +1,5 @@
#ifndef PLATFORM_NATIVE
#include "drivers/Esp32DriverRGBW.h"
#include <driver/gpio.h>
@@ -103,3 +105,5 @@ bool Esp32DriverRGBW::waitForTransmissionToFinish(int waitMs)
else
return false;
}
#endif

View File

@@ -3,7 +3,6 @@
#include "helpers/ColorRGBW.h"
#include "Arduino.h" // TODO
#include <cstdint>
template <int TNumLeds>
@@ -12,16 +11,19 @@ class LedStripRGBW
public:
static constexpr int NUM_LEDS = TNumLeds;
static constexpr int normalizeIdx(int idx)
{
return (idx < 0) ? (idx + NUM_LEDS) : (idx >= NUM_LEDS ? idx - NUM_LEDS : idx);
}
void set(int idx, uint8_t r, uint8_t g, uint8_t b, uint8_t w)
{
// green: 0
// red: 8
// blue: 16
// white: 24
if (idx < 0 || idx >= NUM_LEDS)
Serial.printf("Out of bounds idx %i\n", idx);
else
data_[idx] = (g << 0) | (r << 8) | (b << 16) | (w << 24);
idx = normalizeIdx(idx);
data_[idx] = (g << 0) | (r << 8) | (b << 16) | (w << 24);
}
const uint32_t *rawData() const { return data_; }

View File

@@ -1,5 +1,7 @@
#pragma once
#ifndef PLATFORM_NATIVE
#include "containers/LedStripRGBW.h"
class Esp32DriverRGBW
@@ -17,3 +19,5 @@ private:
int rmtChannel_;
bool transmitting_;
};
#endif

View File

@@ -0,0 +1,142 @@
#pragma once
#include "effects/Common.h"
#include "helpers/ColorRGBW.h"
#include "helpers/ColorHSV.h"
#include "helpers/ColorConversions.h"
#include "helpers/BellCurve.h"
struct EffectAlexaSwipeConfig
{
float primaryColorWidth; // in degrees
float transitionWidth;
float swipeSpeed; // in degrees per second
float bellCurveWidthInLeds;
float startPosition;
ColorRGBW primaryColor;
ColorRGBW secondaryColor;
};
template <typename TLedStrip>
class EffectAlexaSwipe
{
public:
static constexpr auto NUM_LEDS = numLeds<TLedStrip>();
static constexpr int DELAY_MS = 10;
using ConfigType = EffectAlexaSwipeConfig;
EffectAlexaSwipe(const EffectAlexaSwipeConfig &cfg, TLedStrip &ledStrip)
: ledStrip_(ledStrip),
currentPosition_(0),
transitionWidth_(cfg.transitionWidth / 360.0f * NUM_LEDS),
invTransitionWidth_(1.0f / transitionWidth_),
primaryColorWidth_(cfg.primaryColorWidth / 360.0f * NUM_LEDS + cfg.bellCurveWidthInLeds),
bellCurveWidth_(cfg.bellCurveWidthInLeds),
invBellCurveWidth_(1.0f / cfg.bellCurveWidthInLeds),
speed_(cfg.swipeSpeed / 360 / 1000 * NUM_LEDS * DELAY_MS),
startPosition_(cfg.startPosition / 360.0f * NUM_LEDS),
primaryColor_(rgb2hsv(cfg.primaryColor)),
secondaryColor_(rgb2hsv(cfg.secondaryColor))
{
//#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()()
{
clear(ledStrip_);
const auto width = std::min(int(currentPosition_ + 1), int(NUM_LEDS / 2));
setLedRGBW(ledStrip_, startPosition_, getColor(currentPosition_));
for (int i = 1; i < width; ++i)
{
const float x = currentPosition_ - float(i);
if (x > 0.0f)
{
const int led1 = startPosition_ + i;
const int led2 = startPosition_ - i;
const ColorRGBW color = getColor(x);
//#ifndef PLATFORM_NATIVE
// Serial.printf("Setting %d and %d to %d, %d, %d\n", led1, led2, color.r, color.g, color.b);
//#endif
setLedRGBW(ledStrip_, led1, color);
setLedRGBW(ledStrip_, led2, color);
}
}
currentPosition_ += speed_;
currentPosition_ = std::min(currentPosition_, float(NUM_LEDS) / 2.0f + bellCurveWidth_ / 2);
return DELAY_MS;
}
//private:
void getParams(float x, float &interpFac, float &brightness)
{
brightness = (x < bellCurveWidth_) ? bellCurveApproximation(bellCurveWidth_ / 2 - x, invBellCurveWidth_) : 1.0f;
if (x < primaryColorWidth_)
interpFac = 0.0f;
else if (x > primaryColorWidth_ + transitionWidth_)
interpFac = 1.0f;
else
interpFac = (x - primaryColorWidth_) * invTransitionWidth_;
}
// x is positive distance from running front
ColorRGBW getColor(float x)
{
float interpFac;
float brightness;
getParams(x, interpFac, brightness);
ColorHSV result{
interpFac * secondaryColor_.h + (1.0f - interpFac) * primaryColor_.h,
interpFac * secondaryColor_.s + (1.0f - interpFac) * primaryColor_.s,
interpFac * secondaryColor_.v + (1.0f - interpFac) * primaryColor_.v};
result.v *= brightness;
const auto converted = hsv2rgb(result);
//#ifndef PLATFORM_NATIVE
// Serial.printf("Coord %f, interp %f, bright %f, h %f, s %f, v %f, r %d, g %d, b %d\n", x,
// interpFac, brightness, result.h, result.s, result.v, converted.r,
// converted.g, converted.b);
//#endif
return converted;
}
TLedStrip &ledStrip_;
// in number of leds
float currentPosition_;
float transitionWidth_;
float invTransitionWidth_;
float primaryColorWidth_;
float bellCurveWidth_;
float invBellCurveWidth_;
float speed_;
int startPosition_;
const ColorHSV primaryColor_;
const ColorHSV secondaryColor_;
};
// Traits
template <>
struct EffectIdToConfig<EffectId::ALEXA_SWIPE>
{
using type = EffectAlexaSwipeConfig;
};
template <>
struct EffectConfigToId<EffectAlexaSwipeConfig>
{
static constexpr auto id = EffectId::ALEXA_SWIPE;
};
template <typename TLedStrip>
struct EffectIdToClass<EffectId::ALEXA_SWIPE, TLedStrip>
{
using type = EffectAlexaSwipe<TLedStrip>;
};

View File

@@ -28,11 +28,6 @@ public:
{
}
static constexpr int normalizeIdx(int idx)
{
return (idx < 0) ? (idx + NUM_LEDS) : (idx >= NUM_LEDS ? idx - NUM_LEDS : idx);
}
int operator()()
{
int startLed = int(currentPosition_);
@@ -41,20 +36,20 @@ public:
clear(ledStrip_);
// center
setLedRGBW(ledStrip_, normalizeIdx(startLed),
setLedRGBW(ledStrip_, startLed,
config_.color * bellCurveApproximation(distDown, invWidth_));
// down
for (int i = 1; i < widthInLeds_ / 2 + 1; ++i)
{
setLedRGBW(ledStrip_, normalizeIdx(startLed - i),
setLedRGBW(ledStrip_, startLed - i,
config_.color * bellCurveApproximation(distDown + i, invWidth_));
}
// up
for (int i = 1; i < widthInLeds_ / 2 + 1; ++i)
{
setLedRGBW(ledStrip_, normalizeIdx(startLed + i),
setLedRGBW(ledStrip_, startLed + i,
config_.color * bellCurveApproximation(distUp + i - 1, invWidth_));
}
@@ -62,7 +57,6 @@ public:
if (currentPosition_ > NUM_LEDS)
currentPosition_ -= NUM_LEDS;
//Serial.printf("Current pos %f led %d width %d\n", currentPosition_, normalizeIdx(startLed), widthInLeds_ / 2 + 1);
return DELAY_MS;
}

View File

@@ -4,9 +4,7 @@ enum class EffectId
{
STATIC,
CIRCULAR,
CIRCLE_WAVE,
COLOR_FADE,
RAINBOW_FADE,
ALEXA_SWIPE,
};
template <EffectId id>

View File

@@ -1,3 +1,5 @@
#pragma once
#include <cstdint>
static inline float bellCurveApproximation(float x, float inverseWidth)

View File

@@ -0,0 +1,114 @@
#include "helpers/ColorHSV.h"
#include "helpers/ColorRGBW.h"
#include <cstdint>
#include <cmath>
#include <algorithm>
// https://stackoverflow.com/questions/3018313/algorithm-to-convert-rgb-to-hsv-and-hsv-to-rgb-in-range-0-255-for-both
inline ColorHSV rgb2hsv(const ColorRGBW &in)
{
ColorHSV out;
const float r = (float)(in.r) / 255.0f;
const float g = (float)(in.g) / 255.0f;
const float b = (float)(in.b) / 255.0f;
const float min = std::min(r, std::min(g, b));
const float max = std::max(r, std::max(g, b));
out.v = max; // v
const float delta = max - min;
if (delta < 0.00001f)
{
out.s = 0;
out.h = 0; // undefined, maybe nan?
return out;
}
if (max > 0.0f)
{ // NOTE: if Max is == 0, this divide would cause a crash
out.s = (delta / max); // s
}
else
{
// if max is 0, then r = g = b = 0
// s = 0, h is undefined
out.s = 0.0f;
out.h = 0.0f; //NAN; // its now undefined
return out;
}
if (r >= max) // > is bogus, just keeps compilor happy
out.h = (g - b) / delta; // between yellow & magenta
else if (g >= max)
out.h = 2.0f + (b - r) / delta; // between cyan & yellow
else
out.h = 4.0f + (r - g) / delta; // between magenta & cyan
out.h *= 60.0f; // degrees
if (out.h < 0.0f)
out.h += 360.0f;
return out;
}
ColorRGBW hsv2rgb(const ColorHSV &in)
{
int i;
ColorRGBW out;
out.w = 0;
if (in.s <= 0.0f)
{ // < is bogus, just shuts up warnings
out.r = (uint8_t)(in.v * 255.0f);
out.g = (uint8_t)(in.v * 255.0f);
out.b = (uint8_t)(in.v * 255.0f);
return out;
}
float hh = in.h;
if (hh >= 360.0f)
hh = 0.0f;
hh /= 60.0f;
i = (long)hh;
auto ff = hh - i;
float p = in.v * (1.0f - in.s);
float q = in.v * (1.0f - (in.s * ff));
float t = in.v * (1.0f - (in.s * (1.0f - ff)));
switch (i)
{
case 0:
out.r = (uint8_t)(in.v * 255.0f);
out.g = (uint8_t)(t * 255.0f);
out.b = (uint8_t)(p * 255.0f);
break;
case 1:
out.r = (uint8_t)(q * 255.0f);
out.g = (uint8_t)(in.v * 255.0f);
out.b = (uint8_t)(p * 255.0f);
break;
case 2:
out.r = (uint8_t)(p * 255.0f);
out.g = (uint8_t)(in.v * 255.0f);
out.b = (uint8_t)(t * 255.0f);
break;
case 3:
out.r = (uint8_t)(p * 255.0f);
out.g = (uint8_t)(q * 255.0f);
out.b = (uint8_t)(in.v * 255.0f);
break;
case 4:
out.r = (uint8_t)(t * 255.0f);
out.g = (uint8_t)(p * 255.0f);
out.b = (uint8_t)(in.v * 255.0f);
break;
case 5:
default:
out.r = (uint8_t)(in.v * 255.0f);
out.g = (uint8_t)(p * 255.0f);
out.b = (uint8_t)(q * 255.0f);
break;
}
return out;
}

View File

@@ -0,0 +1,8 @@
#pragma once
#include <cstdint>
struct ColorHSV
{
float h, s, v;
};