diff --git a/animation_test/CMakeLists.txt b/animation_test/CMakeLists.txt new file mode 100644 index 0000000..7c6eb63 --- /dev/null +++ b/animation_test/CMakeLists.txt @@ -0,0 +1,15 @@ +project(animation_test) +cmake_minimum_required(VERSION 3.5) + +set(SOURCES + src/Fader.cpp + src/Animation/ImageScrollerAnimation.cpp + src/main.cpp + + src/Animation/Animation.h + src/Fader.h + src/Animation/ImageScrollerAnimation.h +) + +add_executable(animation_test ${SOURCES}) +target_include_directories(animation_test PUBLIC src) diff --git a/animation_test/src/Animation/Animation.h b/animation_test/src/Animation/Animation.h new file mode 100644 index 0000000..0d2e52b --- /dev/null +++ b/animation_test/src/Animation/Animation.h @@ -0,0 +1,53 @@ +#pragma once + +#include + +#include "Fader.h" + +class Animation +{ + public: + Animation(Fader *fader) + : m_fader(fader), m_running(true) + {} + + /*! + * The animation's main function. Will be called once per frame. + * + * \param frame The current frame since the animation's start. + */ + virtual void loop(uint64_t frame) = 0; + + /*! + * Stop the animation. After this function is called, the animation code + * might start some transition to black (all LEDs off). + */ + virtual void stop(void) + { + m_running = false; + } + + /*! + * A function for checking wether the animation has stopped. This must + * return true after the stop transition has finished. + * + * \returns True if all activity of the animation code has stopped. + */ + virtual bool finished(void) + { + return !m_running; + } + + /*! + * Reset function of the animation code. This should clear any + * internal state. + */ + virtual void reset(void) + { + m_running = true; + }; + + protected: + Fader *m_fader; + bool m_running; +}; diff --git a/animation_test/src/Fader.cpp b/animation_test/src/Fader.cpp new file mode 100644 index 0000000..424ce79 --- /dev/null +++ b/animation_test/src/Fader.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include + +#include + +#include +#include + +#include "Fader.h" + +Fader::Fader(std::size_t nstrips, std::size_t nmodules_per_strip) + : m_strips(nstrips), + m_modulesPerStrip(nmodules_per_strip) +{ + m_fd = socket(AF_INET, SOCK_DGRAM, 0); +} + +void Fader::set_color(uint32_t strip, uint32_t module, const Fader::Color &color) +{ + m_commands.emplace_back(Command{ + SET_COLOUR, + static_cast(strip), + static_cast(module), + color + }); +} + +void Fader::add_color(uint32_t strip, uint32_t module, const Fader::Color &color) +{ + m_commands.emplace_back(Command{ + ADD_COLOUR, + static_cast(strip), + static_cast(module), + color + }); +} + +void Fader::fade_color(uint32_t strip, uint32_t module, const Fader::Color &color) +{ + m_commands.emplace_back(Command{ + FADE_COLOUR, + static_cast(strip), + static_cast(module), + color + }); +} + +void Fader::set_color(const Fader::Color &color) +{ + for(std::size_t strip = 0; strip < m_strips; strip++) { + for(std::size_t module = 0; module < m_modulesPerStrip; module++) { + set_color(strip, module, color); + } + } +} + +void Fader::add_color(const Fader::Color &color) +{ + for(std::size_t strip = 0; strip < m_strips; strip++) { + for(std::size_t module = 0; module < m_modulesPerStrip; module++) { + add_color(strip, module, color); + } + } +} + +void Fader::fade_color(const Fader::Color &color) +{ + for(std::size_t strip = 0; strip < m_strips; strip++) { + for(std::size_t module = 0; module < m_modulesPerStrip; module++) { + fade_color(strip, module, color); + } + } +} + +void Fader::set_fadestep(uint8_t newFadestep) +{ + m_commands.emplace_back(Command{ + SET_FADESTEP, + 0, + 0, + Color{newFadestep, 0, 0, 0} + }); +} + +void Fader::update(void) +{ + // TODO: convert the m_commands vector to an UDP packet and send it + + std::vector packet; + + for(auto &command : m_commands) { + packet.push_back(command.action); + packet.push_back(command.strip); + packet.push_back(command.module); + packet.push_back(command.color.r); + packet.push_back(command.color.g); + packet.push_back(command.color.b); + packet.push_back(command.color.w); + } + + sockaddr_in target_addr; + + target_addr.sin_family = AF_INET; + target_addr.sin_port = htons(2703); + target_addr.sin_addr.s_addr = inet_addr("10.42.7.145"); + + int ret = sendto(m_fd, packet.data(), packet.size(), 0, reinterpret_cast(&target_addr), sizeof(target_addr)); + if(ret == -1) { + std::cerr << "sendto failed: " << strerror(errno) << std::endl; + } + + m_commands.clear(); +} diff --git a/animation_test/src/Fader.h b/animation_test/src/Fader.h new file mode 100644 index 0000000..f8138bd --- /dev/null +++ b/animation_test/src/Fader.h @@ -0,0 +1,82 @@ +#pragma once + +#include + +#include + +class Fader +{ + public: + struct Color { + int16_t r, g, b, w; + + Color(int16_t ir = 0, int16_t ig = 0, int16_t ib = 0, int16_t iw = 0) : r(ir), g(ig), b(ib), w(iw) {} + + void operator += (const Color &color) + { + this->r += color.r; + this->g += color.g; + this->b += color.b; + this->w += color.w; + } + + void normalize(void) + { + if(r > 255) { r = 255; } else if (r < 0) { r = 0; }; + if(g > 255) { g = 255; } else if (g < 0) { g = 0; }; + if(b > 255) { b = 255; } else if (b < 0) { b = 0; }; + if(w > 255) { w = 255; } else if (w < 0) { w = 0; }; + } + + void gamma_correct(void) + { + r = r * r / 255; + g = g * g / 255; + b = b * b / 255; + w = w * w / 255; + } + }; + + + Fader(std::size_t nstrips, std::size_t nmodules_per_strip); + + void set_color(uint32_t strip, uint32_t module, const Color &color); + void fade_color(uint32_t strip, uint32_t module, const Color &color); + void add_color(uint32_t strip, uint32_t module, const Color &color); + void set_color(const Color &color); // for all LEDs + void fade_color(const Color &color); // for all LEDs + void add_color(const Color &color); // for all LEDs + void set_fadestep(uint8_t newFadestep); + + void update(void); + + /*! + * \returns Whether any color value has changed since the last call to getColorValues(). + */ + bool something_changed(void) const {return !m_commands.empty(); }; + + std::size_t modules_per_strip(void) { return m_modulesPerStrip; } + std::size_t strips(void) { return m_strips; } + + private: + enum Action { + SET_COLOUR = 0, + FADE_COLOUR = 1, + ADD_COLOUR = 2, + SET_FADESTEP = 3 + }; + + struct Command { + enum Action action; + uint8_t strip; + uint8_t module; + Color color; + }; + + int m_fd; + + std::size_t m_strips; + std::size_t m_modulesPerStrip; + + std::vector m_commands; +}; diff --git a/animation_test/src/main.cpp b/animation_test/src/main.cpp new file mode 100644 index 0000000..d19ca0d --- /dev/null +++ b/animation_test/src/main.cpp @@ -0,0 +1,51 @@ +#include + +#include + +#include "Fader.h" +#include "Animation/ImageScrollerAnimation.h" + +int main(void) +{ + Fader fader(8, 16); + + ImageScrollerAnimation::Image img(12, 3); + img.pixel( 0 + 1, 0).r = 48; + img.pixel( 0 + 2, 1).r = 48; + img.pixel( 0 + 0, 2).r = 48; + img.pixel( 0 + 1, 2).r = 48; + img.pixel( 0 + 2, 2).r = 48; + img.pixel( 3 + 1, 0).g = 48; + img.pixel( 3 + 2, 1).g = 48; + img.pixel( 3 + 0, 2).g = 48; + img.pixel( 3 + 1, 2).g = 48; + img.pixel( 3 + 2, 2).g = 48; + img.pixel( 6 + 1, 0).b = 48; + img.pixel( 6 + 2, 1).b = 48; + img.pixel( 6 + 0, 2).b = 48; + img.pixel( 6 + 1, 2).b = 48; + img.pixel( 6 + 2, 2).b = 48; + img.pixel( 9 + 1, 0).w = 48; + img.pixel( 9 + 2, 1).w = 48; + img.pixel( 9 + 0, 2).w = 48; + img.pixel( 9 + 1, 2).w = 48; + img.pixel( 9 + 2, 2).w = 48; + + Animation *animation = new ImageScrollerAnimation(&fader, &img); + + for(uint64_t frame = 0; !animation->finished(); frame++) { + animation->loop(frame); + + if(fader.something_changed()) { + fader.update(); + } + + if(frame == 3000) { + animation->stop(); + } + + usleep(100000); + }; + + delete animation; +}