Reverse swipe

This commit is contained in:
Martin Bauer 2021-12-20 20:41:50 +01:00
parent bb5d3e1870
commit 74118fbeb7
10 changed files with 284 additions and 25 deletions

View File

@ -35,12 +35,15 @@ led_ring_effect_to_message_id = {
EffectCircularConfig: 2, EffectCircularConfig: 2,
EffectRandomTwoColorInterpolationConfig: 3, EffectRandomTwoColorInterpolationConfig: 3,
EffectSwipeAndChange: 4, EffectSwipeAndChange: 4,
EffectReverseSwipe: 5,
} }
mouse_led_effect_to_message_id = { mouse_led_effect_to_message_id = {
EffectStaticConfig: 5, EffectStaticConfig: 6,
EffectCircularConfig: 6, EffectCircularConfig: 7,
EffectRandomTwoColorInterpolationConfig: 7 EffectRandomTwoColorInterpolationConfig: 8,
EffectSwipeAndChange: 9,
EffectReverseSwipe: 10,
} }

View File

@ -67,7 +67,7 @@ class EffectStaticConfig:
class EffectAlexaSwipeConfig: class EffectAlexaSwipeConfig:
primary_color_width: float = 20 # in degrees primary_color_width: float = 20 # in degrees
transition_width: float = 30 # in degrees transition_width: float = 30 # in degrees
swipe_speed: float = 3 * 360 # in degrees per second swipe_speed: float = 2 * 360 # in degrees per second
bell_curve_width_in_leds: float = 3 bell_curve_width_in_leds: float = 3
start_position: float = 180 # in degrees start_position: float = 180 # in degrees
forward: bool = True forward: bool = True
@ -124,4 +124,17 @@ class EffectSwipeAndChange:
return self.swipe.as_bytes() + self.change.as_bytes() return self.swipe.as_bytes() + self.change.as_bytes()
def __repr__(self) -> str: def __repr__(self) -> str:
return f"Swipe and Change: \n {str(self.swipe)}\n {str(self.change)}" return f"Swipe and Change: \n {str(self.swipe)}\n {str(self.change)}"
@dataclass
class EffectReverseSwipe:
swipeSpeed: float = 2 * 360
bellCurveWidthInLeds: float = 3
startPosition: float = 180
def as_bytes(self) -> bytes:
return struct.pack("<fff", self.swipeSpeed, self.bellCurveWidthInLeds, self.startPosition)
def __repr__(self) -> str:
return f"Reverse swipe, speed {self.swipeSpeed}, width in leds {self.bellCurveWidthInLeds}, start position {self.startPosition}"

View File

@ -2,10 +2,11 @@ import asyncio
import serial_asyncio import serial_asyncio
from led_cmds import (ColorRGBW, ColorHSV, EffectStaticConfig, from led_cmds import (ColorRGBW, ColorHSV, EffectStaticConfig,
EffectRandomTwoColorInterpolationConfig, EffectAlexaSwipeConfig, EffectRandomTwoColorInterpolationConfig, EffectAlexaSwipeConfig,
EffectSwipeAndChange) EffectSwipeAndChange, EffectReverseSwipe)
from host_driver import MusicMouseProtocol, RfidTokenRead, RotaryEncoderEvent, ButtonEvent, TouchButton, TouchButtonPress, TouchButtonRelease from host_driver import MusicMouseProtocol, RfidTokenRead, RotaryEncoderEvent, ButtonEvent, TouchButton, TouchButtonPress, TouchButtonRelease
from player import AudioPlayer from player import AudioPlayer
from glob import glob from glob import glob
from copy import deepcopy
import os import os
MUSIC_FOLDER = "/home/martin/code/musicmouse/espmusicmouse/host_driver/music" MUSIC_FOLDER = "/home/martin/code/musicmouse/espmusicmouse/host_driver/music"
@ -75,12 +76,14 @@ def on_rfid(protocol, tagid):
if tagid == bytes.fromhex("0000000000"): if tagid == bytes.fromhex("0000000000"):
# Off # Off
if audio_player.is_playing(): if audio_player.is_playing():
ring_eff = EffectAlexaSwipeConfig() ring_eff = EffectReverseSwipe()
ring_eff.forward = False mouse_eff = EffectReverseSwipe()
mouse_eff.startPosition = 6 / 45 * 360
else: else:
ring_eff = EffectStaticConfig(ColorRGBW(0, 0, 0, 0)) ring_eff = EffectStaticConfig(ColorRGBW(0, 0, 0, 0))
mouse_eff = EffectStaticConfig(ColorRGBW(0, 0, 0, 0))
protocol.led_ring_effect(ring_eff) protocol.led_ring_effect(ring_eff)
protocol.mouse_led_effect(EffectStaticConfig(ColorRGBW(0, 0, 0, 0))) protocol.mouse_led_effect(mouse_eff)
audio_player.pause() audio_player.pause()
last_figure = current_figure last_figure = current_figure
@ -96,6 +99,8 @@ def on_rfid(protocol, tagid):
protocol.led_ring_effect(ring_eff) protocol.led_ring_effect(ring_eff)
mouse_eff = EffectStaticConfig(ccfg.background) mouse_eff = EffectStaticConfig(ccfg.background)
mouse_eff = deepcopy(ring_eff)
mouse_eff.swipe.start_position = 6 / 45 * 360
protocol.mouse_led_effect(mouse_eff) protocol.mouse_led_effect(mouse_eff)
if figure in playlists: if figure in playlists:
@ -127,8 +132,17 @@ def on_firmware_msg(protocol: MusicMouseProtocol, message):
*mouse_leds[message.touch_button])) *mouse_leds[message.touch_button]))
elif isinstance(message, TouchButtonRelease): elif isinstance(message, TouchButtonRelease):
ccfg = color_cfg[current_figure] ccfg = color_cfg[current_figure]
color = ccfg.background if audio_player.is_playing() else ColorRGBW(0, 0, 0, 0) eff_change = EffectRandomTwoColorInterpolationConfig()
protocol.mouse_led_effect(EffectStaticConfig(color, *mouse_leds[message.touch_button])) eff_static = EffectStaticConfig(ColorRGBW(0, 0, 0, 0), *mouse_leds[message.touch_button])
if audio_player.is_playing():
eff_static.color = ccfg.primary
protocol.mouse_led_effect(eff_static)
if audio_player.is_playing():
eff_change.color1 = ccfg.primary
eff_change.color2 = ccfg.secondary
eff_change.start_with_existing = True
protocol.mouse_led_effect(eff_change)
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()

View File

@ -55,7 +55,7 @@ public:
} }
} }
bool finished() { return finished_; } bool finished() const { return finished_; }
int operator()() int operator()()
{ {
@ -90,7 +90,7 @@ public:
private: private:
void getParams(float x, float &interpFac, float &brightness) void getParams(float x, float &interpFac, float &brightness)
{ {
brightness = (x < bellCurveWidth_) ? bellCurveApproximation(bellCurveWidth_ / 2 - x, invBellCurveWidth_) : 1.0f; brightness = stepFunction(x, bellCurveWidth_, invBellCurveWidth_);
if (x < primaryColorWidth_) if (x < primaryColorWidth_)
interpFac = 0.0f; interpFac = 0.0f;
else if (x > primaryColorWidth_ + transitionWidth_) else if (x > primaryColorWidth_ + transitionWidth_)

View File

@ -7,6 +7,7 @@ enum class EffectId
ALEXA_SWIPE, ALEXA_SWIPE,
RANDOM_TWO_COLOR_INTERPOLATION, RANDOM_TWO_COLOR_INTERPOLATION,
SWIPE_AND_CHANGE, // combination of ALEXA_SWIPE and RANDOM_TWO_COLOR_INTERPOLATION SWIPE_AND_CHANGE, // combination of ALEXA_SWIPE and RANDOM_TWO_COLOR_INTERPOLATION
REVERSE_SWIPE,
}; };
template <EffectId id> template <EffectId id>

View File

@ -0,0 +1,112 @@
#pragma once
#include "effects/Common.h"
#include "helpers/BellCurve.h"
#pragma pack(push, 1)
struct EffectReverseSwipeConfig
{
float swipeSpeed; // in degrees per second
float bellCurveWidthInLeds;
float startPosition;
};
#pragma pack(pop)
template <typename TLedStrip>
class EffectReverseSwipe
{
public:
static constexpr auto NUM_LEDS = numLeds<TLedStrip>();
static constexpr int DELAY_MS = 10;
using ConfigType = EffectReverseSwipeConfig;
EffectReverseSwipe(const EffectReverseSwipeConfig &cfg, TLedStrip &ledStrip)
: ledStrip_(ledStrip),
currentPosition_(float(NUM_LEDS) / 2 + 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),
finished_(false)
{
for (int i = 0; i < NUM_LEDS; ++i)
state_[i] = getLedRGBW(ledStrip_, i);
}
bool finished() const { return finished_; }
int operator()()
{
if (finished_)
return 60000;
const auto width = std::min(int(currentPosition_ + 1), int(NUM_LEDS / 2) + 1);
{
float brightness = stepFunction(currentPosition_, bellCurveWidth_, invBellCurveWidth_);
ColorRGBW &prevC = state_[ledStrip_.normalizeIdx(startPosition_)];
setLedRGBW(ledStrip_, startPosition_, prevC * brightness);
}
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;
float brightness = stepFunction(x, bellCurveWidth_, invBellCurveWidth_);
ColorRGBW &prevC1 = state_[ledStrip_.normalizeIdx(led1)];
ColorRGBW &prevC2 = state_[ledStrip_.normalizeIdx(led2)];
setLedRGBW(ledStrip_, led1, prevC1 * brightness);
setLedRGBW(ledStrip_, led2, prevC2 * brightness);
}
}
currentPosition_ -= speed_;
if (currentPosition_ < 0)
{
finished_ = true;
clear(ledStrip_);
}
return DELAY_MS;
}
private:
TLedStrip &ledStrip_;
float currentPosition_;
float bellCurveWidth_;
float invBellCurveWidth_;
float speed_;
int startPosition_;
bool finished_;
ColorRGBW state_[NUM_LEDS];
};
// Traits
template <>
struct EffectIdToConfig<EffectId::REVERSE_SWIPE>
{
using type = EffectReverseSwipeConfig;
};
template <>
struct EffectConfigToId<EffectReverseSwipeConfig>
{
static constexpr auto id = EffectId::REVERSE_SWIPE;
};
template <typename TLedStrip>
struct EffectIdToClass<EffectId::REVERSE_SWIPE, TLedStrip>
{
using type = EffectReverseSwipe<TLedStrip>;
};

View File

@ -16,4 +16,20 @@ static inline float bellCurveApproximation(float x, float inverseWidth)
const auto res = 1.0f + 0.27606958941084f * x3 - 0.80213917882168f * x2; const auto res = 1.0f + 0.27606958941084f * x3 - 0.80213917882168f * x2;
return res < 0.0f ? 0.0f : res; return res < 0.0f ? 0.0f : res;
}
// Function start at 0 (x=0) and goes smoothly up to 1 and arrives 1 at x=width
// inverse width has to be 1 / width (should be cached outside)
static inline float stepFunction(float x, float width, float inverseWidth)
{
if (x < 0.0f)
return 0.0f;
if (x >= width)
return 1.0f;
auto bellInvWidth = inverseWidth * 0.5f;
auto nx = (-x + width) * bellInvWidth * 4;
auto x2 = nx * nx;
auto x3 = x2 * nx;
return 1 + 0.2760695894 * x3 - 0.8021391 * x2;
} }

View File

@ -1,6 +1,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/ReverseSwipe.h"
#include "effects/RandomTwoColorInterpolation.h" #include "effects/RandomTwoColorInterpolation.h"
#include "effects/SwipeAndChange.h" #include "effects/SwipeAndChange.h"
@ -101,10 +102,12 @@ enum class MessageHostToFw : uint8_t
LED_WHEEL_EFFECT_CIRCULAR = 2, LED_WHEEL_EFFECT_CIRCULAR = 2,
LED_WHEEL_EFFECT_RANDOM_TWO_COLOR_INTERPOLATION = 3, LED_WHEEL_EFFECT_RANDOM_TWO_COLOR_INTERPOLATION = 3,
LED_WHEEL_EFFECT_SWIPE_AND_CHANGE = 4, LED_WHEEL_EFFECT_SWIPE_AND_CHANGE = 4,
MOUSE_LED_EFFECT_STATIC = 5, LED_WHEEL_EFFECT_REVERSE_SWIPE = 5,
MOUSE_LED_EFFECT_CIRCULAR = 6, MOUSE_LED_EFFECT_STATIC = 6,
MOUSE_LED_EFFECT_RANDOM_TWO_COLOR_INTERPOLATION = 7, MOUSE_LED_EFFECT_CIRCULAR = 7,
MOUSE_LED_EFFECT_RANDOM_TWO_COLOR_INTERPOLATION = 8,
MOUSE_LED_EFFECT_SWIPE_AND_CHANGE = 9,
MOUSE_LED_EFFECT_REVERSE_SWIPE = 10,
}; };
template <> template <>
@ -201,6 +204,12 @@ inline void handleIncomingMessagesFromHost(LedTask1 *ledTaskCircle, LedTask2 *le
auto cfg = reinterpret_cast<EffectSwipeAndChangeConfig *>(msgBuffer); auto cfg = reinterpret_cast<EffectSwipeAndChangeConfig *>(msgBuffer);
ledTaskCircle->startEffect(*cfg); ledTaskCircle->startEffect(*cfg);
} }
else if (msgType == MessageHostToFw::LED_WHEEL_EFFECT_REVERSE_SWIPE)
{
auto cfg = reinterpret_cast<EffectReverseSwipeConfig *>(msgBuffer);
ledTaskCircle->startEffect(*cfg);
}
//
else if (msgType == MessageHostToFw::MOUSE_LED_EFFECT_STATIC) else if (msgType == MessageHostToFw::MOUSE_LED_EFFECT_STATIC)
{ {
auto cfg = reinterpret_cast<EffectStaticConfig *>(msgBuffer); auto cfg = reinterpret_cast<EffectStaticConfig *>(msgBuffer);
@ -216,6 +225,16 @@ inline void handleIncomingMessagesFromHost(LedTask1 *ledTaskCircle, LedTask2 *le
auto cfg = reinterpret_cast<EffectRandomTwoColorInterpolationConfig *>(msgBuffer); auto cfg = reinterpret_cast<EffectRandomTwoColorInterpolationConfig *>(msgBuffer);
ledTaskMouse->startEffect(*cfg); ledTaskMouse->startEffect(*cfg);
} }
else if (msgType == MessageHostToFw::MOUSE_LED_EFFECT_SWIPE_AND_CHANGE)
{
auto cfg = reinterpret_cast<EffectSwipeAndChangeConfig *>(msgBuffer);
ledTaskMouse->startEffect(*cfg);
}
else if (msgType == MessageHostToFw::MOUSE_LED_EFFECT_REVERSE_SWIPE)
{
auto cfg = reinterpret_cast<EffectReverseSwipeConfig *>(msgBuffer);
ledTaskMouse->startEffect(*cfg);
}
else else
Serial.println("Unknown message type"); Serial.println("Unknown message type");
} }

View File

@ -80,6 +80,7 @@ void _led_task_func(void *params)
else if (dispatchEffectId<EffectId::ALEXA_SWIPE >(id, effectFunction, ledStrip, msgBuffer, effectStorage)) {} else if (dispatchEffectId<EffectId::ALEXA_SWIPE >(id, effectFunction, ledStrip, msgBuffer, effectStorage)) {}
else if (dispatchEffectId<EffectId::RANDOM_TWO_COLOR_INTERPOLATION>(id, effectFunction, ledStrip, msgBuffer, effectStorage)) {} else if (dispatchEffectId<EffectId::RANDOM_TWO_COLOR_INTERPOLATION>(id, effectFunction, ledStrip, msgBuffer, effectStorage)) {}
else if (dispatchEffectId<EffectId::SWIPE_AND_CHANGE >(id, effectFunction, ledStrip, msgBuffer, effectStorage)) {} else if (dispatchEffectId<EffectId::SWIPE_AND_CHANGE >(id, effectFunction, ledStrip, msgBuffer, effectStorage)) {}
else if (dispatchEffectId<EffectId::REVERSE_SWIPE >(id, effectFunction, ledStrip, msgBuffer, effectStorage)) {}
// clang-format on // clang-format on
timeoutMsForEffect = 0; timeoutMsForEffect = 0;

View File

@ -2,25 +2,105 @@
#include "containers/LedStripRGBW.h" #include "containers/LedStripRGBW.h"
#include "effects/AlexaSwipe.h" #include "effects/AlexaSwipe.h"
#include "effects/ReverseSwipe.h"
#include "helpers/ColorConversions.h"
#include <iostream> #include <iostream>
#include <vector> #include <vector>
#include <fstream>
template <typename T> template <typename T>
void printVec(const std::vector<T> &vec) std::ostream &operator<<(std::ostream &os, const std::vector<T> &vec)
{ {
std::cout << "["; os << "[";
for (const auto &e : vec) for (const auto &e : vec)
std::cout << e << ","; {
std::cout << "]\n"; if (std::is_same<uint8_t, T>::value)
os << int(e) << ",";
else
os << e << ",";
}
os << "]";
return os;
}
template <typename T>
std::ostream &operator<<(std::ostream &os, const std::vector<std::vector<T>> &vec)
{
os << "[";
for (const auto &e : vec)
os << e << ",";
os << "]";
return os;
}
template <typename TEffect, int NLeds>
void effectToFile(const std::string &filename, TEffect &effect, LedStripRGBW<NLeds> &strip, int calls = 100)
{
std::vector<std::vector<uint8_t>> vr(calls);
std::vector<std::vector<uint8_t>> vg(calls);
std::vector<std::vector<uint8_t>> vb(calls);
std::vector<std::vector<float>> vh(calls);
std::vector<std::vector<float>> vs(calls);
std::vector<std::vector<float>> vv(calls);
for (int time = 0; time < calls; ++time)
{
effect();
vr[time].resize(NLeds);
vg[time].resize(NLeds);
vb[time].resize(NLeds);
vh[time].resize(NLeds);
vs[time].resize(NLeds);
vv[time].resize(NLeds);
for (int i = 0; i < NLeds; ++i)
{
uint8_t r, g, b, w;
strip.getRGBW(i, r, g, b, w);
vr[time][i] = r;
vg[time][i] = g;
vb[time][i] = b;
auto hsv = rgb2hsv(ColorRGBW{r, g, b, w});
vh[time][i] = hsv.h;
vs[time][i] = hsv.s;
vv[time][i] = hsv.v;
}
}
std::ofstream fs(filename.c_str());
fs << "r = " << vr << "\n";
fs << "g = " << vg << "\n";
fs << "b = " << vb << "\n";
fs << "h = " << vh << "\n";
fs << "s = " << vs << "\n";
fs << "v = " << vv << "\n";
} }
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
auto cfg = EffectAlexaSwipeConfig{20, 20, 90, 5, 180, ColorRGBW{255, 0, 0, 0}, ColorRGBW{0, 0, 255, 0}}; {
LedStripRGBW<51> strip; auto cfg = EffectAlexaSwipeConfig{20.f, 20.f, 90.f, 5.f, 180, true, ColorRGBW{255, 0, 0, 0}, ColorRGBW{255, 0, 0, 0}};
LedStripRGBW<51> strip;
EffectAlexaSwipe<decltype(strip)> effect(cfg, strip); EffectAlexaSwipe<decltype(strip)> effect(cfg, strip);
effectToFile("swipe.py", effect, strip, 200);
}
{
auto cfg = EffectReverseSwipeConfig{360.f, 5.f, 180};
LedStripRGBW<51> strip;
for (int i = 0; i < strip.numLeds(); ++i)
setLedRGBW(strip, i, ColorRGBW{255, 255, 255, 0});
EffectReverseSwipe<decltype(strip)> effect(cfg, strip);
effectToFile("reverse_swipe.py", effect, strip, 200);
}
/*
effect.currentPosition_ = 150; effect.currentPosition_ = 150;
const auto numLeds = strip.numLeds() / 2; const auto numLeds = strip.numLeds() / 2;
@ -35,6 +115,6 @@ int main(int argc, char **argv)
printVec(interpolation); printVec(interpolation);
effect(); effect();
*/
return 0; return 0;
} }