FastLED 3.9.15
Loading...
Searching...
No Matches
animartrix.hpp
Go to the documentation of this file.
1#pragma once
2
3// FastLED Adapter for the animartrix fx library.
4// Original concept by Stefan Petrick 2023.
5// Adapted to C++ by Netmindz 2023.
6// Adapted to FastLED by Zach Vorhies 2024.
7// Refactored to Context + free functors 2026.
8// Fixed-point optimization migration 2026.
9//
10// Licensed under the MIT License.
11// See LICENSE file in the root of this repository.
12
13#include "crgb.h"
14#include "fl/log/log.h"
15#include "fl/stl/memory.h"
16#include "fl/stl/unique_ptr.h"
17#include "fl/math/xymap.h"
18#include "fl/stl/vector.h"
19#include "fl/stl/pair.h"
20#include "fl/stl/string.h"
21#include "fl/fx/fx2d.h"
22#include "eorder.h"
23#include "pixel_controller.h"
24
26#include "fl/stl/noexcept.h"
27
28namespace fl {
29
31
87
88// Viz factory function signature: returns a heap-allocated IAnimartrix2Viz.
90
91// Animation entry: maps enum to name and a factory that creates the visualizer.
97
98// Factory helper: creates a heap-allocated instance of any IAnimartrix2Viz subclass.
99template<typename T>
100static IAnimartrix2Viz* makeViz() { return new T(); } // ok bare allocation
101
102// Select between floating-point and fixed-point animartrix visualizers.
103// Define FL_ANIMARTRIX_USE_FIXED_POINT=0 to force floating-point, or =1 to force fixed-point.
104// Default: fixed-point on all platforms except ESP32-P4 (which has a hardware FPU).
105#ifndef FL_ANIMARTRIX_USE_FIXED_POINT
106#if defined(FL_IS_ESP_32P4)
107#define FL_ANIMARTRIX_USE_FIXED_POINT 0
108#else
109#define FL_ANIMARTRIX_USE_FIXED_POINT 1
110#endif
111#endif
112
113// Helper macro: selects the correct viz class based on FL_ANIMARTRIX_USE_FIXED_POINT.
114#if FL_ANIMARTRIX_USE_FIXED_POINT
115#define ANIMARTRIX_VIZ(FloatClass, FPClass) &makeViz<FPClass>
116#else
117#define ANIMARTRIX_VIZ(FloatClass, FPClass) &makeViz<FloatClass>
118#endif
119
120// The animation dispatch table.
121// Uses fixed-point (_FP) versions by default for ~2-3x speedup on MCUs without FPU.
122// On ESP32-P4 (hardware FPU), uses floating-point versions instead.
123// To override at compile time: #define FL_ANIMARTRIX_USE_FIXED_POINT 0 or 1
124// To override a specific entry at runtime, use Animartrix::setVizFactory().
143 {AnimartrixAnim::CALEIDO1, "CALEIDO1", ANIMARTRIX_VIZ(Caleido3, Caleido3_FP)}, // Note: same as original - Caleido1 maps to Caleido3
178};
179
180#undef ANIMARTRIX_VIZ
181
183 int id;
184 const char* name;
185};
186
187inline fl::string getAnimartrixName(int animation) {
188 if (animation < 0 || animation >= static_cast<int>(AnimartrixAnim::NUM_ANIMATIONS)) {
189 return "UNKNOWN";
190 }
191 return ANIMATION_TABLE[animation].name;
192}
193
194inline int getAnimartrixCount() {
195 return static_cast<int>(AnimartrixAnim::NUM_ANIMATIONS);
196}
197
199 if (index < 0 || index >= static_cast<int>(AnimartrixAnim::NUM_ANIMATIONS)) {
200 return {-1, "UNKNOWN"};
201 }
202 return {static_cast<int>(ANIMATION_TABLE[index].anim),
203 ANIMATION_TABLE[index].name};
204}
205
206// XYMap callback adapter
207inline u16 xyMapCallbackAdapter(u16 x, u16 y, void *userData) {
208 Fx2d *self = static_cast<Fx2d *>(userData);
209 return self->xyMap(x, y);
210}
211
212class Animartrix : public Fx2d {
213 public:
214 Animartrix(const XYMap &xyMap, AnimartrixAnim which_animation)
215 : Fx2d(xyMap) {
216 mCurrentAnimation = which_animation;
217 // convertToLookUpTable() is deferred to draw() to avoid
218 // cross-DLL static initialization order issues on Windows.
219 }
220
222
223 void draw(DrawContext ctx) override {
224 if (!mXyMap.isLUT()) {
225 mXyMap.convertToLookUpTable();
226 }
227 // Set up context for rendering
228 mCtx.leds = ctx.leds;
229 mCtx.xyMapFn = &xyMapCallbackAdapter;
230 mCtx.xyMapUserData = this;
231
232 // Initialize or reinitialize engine on animation change
233 const bool anim_changed = (mPrevAnimation != mCurrentAnimation);
234 if (anim_changed) {
237 }
238 if (mCtx.mEngine == nullptr) {
240 }
241
242 // Create (or recreate after animation change) the visualizer instance.
243 if (anim_changed || !mViz) {
245 }
246
247 setTime(mCtx, ctx.now);
248
249 // Dispatch to the selected animation.
250 // Custom viz takes precedence; otherwise use the table-created viz.
251 if (mCustomViz) {
252 mCustomViz->draw(mCtx);
253 } else if (mViz) {
254 mViz->draw(mCtx);
255 }
256
257 // Apply color order if not RGB
258 if (mColorOrder != RGB) {
259 for (int i = 0; i < mXyMap.getTotal(); ++i) {
260 CRGB &pixel = ctx.leds[i];
261 const u8 b0_index = RGB_BYTE0(mColorOrder);
262 const u8 b1_index = RGB_BYTE1(mColorOrder);
263 const u8 b2_index = RGB_BYTE2(mColorOrder);
264 pixel = CRGB(pixel.raw[b0_index], pixel.raw[b1_index],
265 pixel.raw[b2_index]);
266 }
267 }
268
269 mCtx.leds = fl::span<CRGB>();
270 }
271
272 int fxNum() const { return static_cast<int>(AnimartrixAnim::NUM_ANIMATIONS); }
273
274 void fxSet(int fx) {
275 int curr = fxGet();
276 if (fx < 0) {
277 fx = curr + fx;
278 if (fx < 0) {
279 fx = static_cast<int>(AnimartrixAnim::NUM_ANIMATIONS) - 1;
280 }
281 }
282 fx = fx % static_cast<int>(AnimartrixAnim::NUM_ANIMATIONS);
283 mCurrentAnimation = static_cast<AnimartrixAnim>(fx);
284 FL_DBG("Setting animation to " << getAnimartrixName(static_cast<int>(mCurrentAnimation)));
285 }
286
287 int fxGet() const { return static_cast<int>(mCurrentAnimation); }
288
289 fl::string fxName() const override { return "Animartrix:"; }
290
291 void fxNext(int fx = 1) { fxSet(fxGet() + fx); }
292
293 void setColorOrder(EOrder order) { mColorOrder = order; }
294 EOrder getColorOrder() const { return mColorOrder; }
295
302 // Search for existing override
303 for (auto &ovr : mFactoryOverrides) {
304 if (ovr.first == anim) {
305 ovr.second = factory;
306 // Force recreation on next draw
307 mViz.reset();
308 return;
309 }
310 }
311 if (factory) {
312 mFactoryOverrides.push_back(fl::make_pair(anim, factory));
313 mViz.reset();
314 }
315 }
316
322 mCustomViz.reset(viz);
323 mViz.reset();
324 }
325
328 for (int i = 0; i < static_cast<int>(AnimartrixAnim::NUM_ANIMATIONS); i++) {
331 }
332 return list;
333 }
334
335 private:
336 // Create a viz instance for the given animation, checking overrides first.
338 // Custom viz takes precedence over everything
339 if (mCustomViz) {
340 // Return a non-owning "alias" — mCustomViz owns the lifetime.
341 // We return nullptr here; draw() will use mCustomViz directly.
342 return nullptr;
343 }
344 // Check per-animation factory overrides
345 for (const auto &ovr : mFactoryOverrides) {
346 if (ovr.first == anim && ovr.second) {
347 return ovr.second();
348 }
349 }
350 // Default: look up in ANIMATION_TABLE
351 for (const auto &entry : ANIMATION_TABLE) {
352 if (entry.anim == anim) {
353 return entry.factory();
354 }
355 }
356 return nullptr;
357 }
358
361 EOrder mColorOrder = EOrder::RGB;
364 fl::unique_ptr<IAnimartrix2Viz> mCustomViz; // External viz, owns lifetime
366};
367
368} // namespace fl
#define ANIMARTRIX_VIZ(FloatClass, FPClass)
void fxNext(int fx=1)
void setVizFactory(AnimartrixAnim anim, AnimartrixVizFactory factory)
Override the factory for a specific built-in animation enum.
Animartrix(const Animartrix &) FL_NOEXCEPT=delete
AnimartrixAnim mPrevAnimation
fl::unique_ptr< IAnimartrix2Viz > mCustomViz
void draw(DrawContext ctx) override
void setColorOrder(EOrder order)
Animartrix(const XYMap &xyMap, AnimartrixAnim which_animation)
fl::vector< fl::pair< AnimartrixAnim, AnimartrixVizFactory > > mFactoryOverrides
AnimartrixAnim mCurrentAnimation
fl::unique_ptr< IAnimartrix2Viz > mViz
static fl::vector< fl::pair< int, fl::string > > getAnimationList()
int fxGet() const
void fxSet(int fx)
int fxNum() const
void setCustomViz(IAnimartrix2Viz *viz)
Set a fully custom viz that bypasses the animation enum entirely.
EOrder getColorOrder() const
fl::string fxName() const override
IAnimartrix2Viz * createViz(AnimartrixAnim anim)
XYMap mXyMap
Definition fx2d.h:30
Fx2d(const XYMap &xyMap)
Definition fx2d.h:19
u16 getWidth() const
Definition fx2d.h:24
u16 getHeight() const
Definition fx2d.h:23
u16 xyMap(u16 x, u16 y) const
Definition fx2d.h:20
void push_back(const T &value)
Definition list.h:371
A doubly-linked list container.
Definition list.h:21
#define FL_DBG
Definition log.h:388
Centralized logging categories for FastLED hardware interfaces and subsystems.
fl::string getAnimartrixName(int animation)
unsigned char u8
Definition stdint.h:131
fl::CRGB CRGB
Definition video.h:15
static const AnimartrixEntry ANIMATION_TABLE[]
IAnimartrix2Viz *(*)() AnimartrixVizFactory
void init(Context &ctx, int w, int h)
Definition engine.h:133
AnimartrixAnimInfo getAnimartrixInfo(int index)
void setTime(Context &ctx, fl::u32 t)
Definition engine.h:143
u16 xyMapCallbackAdapter(u16 x, u16 y, void *userData)
AnimartrixAnim
int getAnimartrixCount()
static IAnimartrix2Viz * makeViz()
pair< typename fl::decay< T1 >::type, typename fl::decay< T2 >::type > make_pair(T1 &&t, T2 &&u) FL_NOEXCEPT
Definition pair.h:95
EOrder
RGB color channel orderings, used when instantiating controllers to determine what order the controll...
Definition eorder.h:13
@ RGB
Red, Green, Blue (0012)
Definition eorder.h:14
Base definition for an LED controller.
Definition crgb.hpp:179
AnimartrixVizFactory factory
const char * name
AnimartrixAnim anim
#define RGB_BYTE2(RO)
Gets the color channel for byte 2.
#define RGB_BYTE1(RO)
Gets the color channel for byte 1.
#define RGB_BYTE0(RO)
Gets the color channel for byte 0.
Low level pixel data writing class.
#define FL_NOEXCEPT
#define FASTLED_SHARED_PTR(type)
Definition shared_ptr.h:535
Representation of an 8-bit RGB pixel (Red, Green, Blue)
Definition crgb.h:38
fl::span< CRGB > leds