display_led_effect.h
1 #pragma once 2 3 #include "../display_led_rgb_led_data.h" 4 #include "display_led_effect_common.h" 5 6 namespace display::led::effect 7 { 8 class Effect 9 { 10 public: 11 Effect() = default; 12 Effect(float duration) : effectDuration(duration) { } 13 virtual ~Effect() = default; 14 Effect(const Effect&) = delete; 15 Effect& operator=(const Effect&) = delete; 16 17 virtual void Update(float timeDelta, RgbLedColorBufferDescriptor& colors) = 0; 18 virtual void Reset() { elapsedTime = 0; hasFinished = false; elapsedTimeSinceFinished = 0.0f; } 19 20 enum class WrapMode 21 { 22 Once, 23 Loop, 24 PingPong, 25 ClampForever 26 }; 27 28 enum class Direction 29 { 30 Forward, 31 Up = Forward, 32 Backward, 33 Down = Backward 34 }; 35 36 void SetTimeScale(float scale) { timeScale = scale; } 37 float GetTimeScale() { return timeScale; } 38 void SetDirection(Direction dir) { direction = dir; } 39 Direction GetDirection() { return direction; } 40 void SetWrapMode(WrapMode mode) { wrapMode = mode; } 41 WrapMode GetWrapMode() { return wrapMode; } 42 43 protected: 44 WrapMode wrapMode = WrapMode::Loop; 45 Direction direction = Direction::Forward; 46 float elapsedTime = 0.0f; 47 float effectDuration = 1.0f; 48 float timeScale = 1.0f; 49 50 bool hasFinished = false; 51 float elapsedTimeSinceFinished = 0; 52 static constexpr float AllowFinishedResendEverySeconds = 1.0f; 53 54 inline float GetEffectPlaybackPercentage() 55 { 56 return elapsedTime / effectDuration; 57 } 58 59 inline bool AddDeltaTime(float timeDelta) 60 { 61 // we're done, count elapsed time, 62 // and that's all we need to do. 63 if (hasFinished) 64 { 65 if (elapsedTimeSinceFinished < AllowFinishedResendEverySeconds) 66 { 67 elapsedTimeSinceFinished += timeDelta; 68 return false; 69 } 70 else 71 { 72 elapsedTimeSinceFinished -= AllowFinishedResendEverySeconds; 73 return true; 74 } 75 } 76 77 bool shouldWrap = false; 78 if (direction == Direction::Forward) 79 { 80 elapsedTime += timeDelta * timeScale; 81 shouldWrap = elapsedTime >= effectDuration; 82 } 83 else 84 { 85 elapsedTime -= timeDelta * timeScale; 86 shouldWrap = elapsedTime <= 0; 87 } 88 89 // we don't need to wrap, 90 // go aheada and return that we can update. 91 if (shouldWrap == false) 92 { 93 return true; 94 } 95 96 switch (wrapMode) 97 { 98 case WrapMode::Once: 99 elapsedTime = 0.0f; 100 elapsedTimeSinceFinished = 0.0f; 101 hasFinished = true; 102 break; 103 case WrapMode::Loop: 104 if (direction == Direction::Forward) 105 { 106 elapsedTime -= effectDuration; 107 } 108 else 109 { 110 elapsedTime += effectDuration; 111 } 112 break; 113 case WrapMode::PingPong: 114 if (direction == Direction::Forward) 115 { 116 direction = Direction::Backward; 117 float diff = (elapsedTime - effectDuration); 118 elapsedTime = effectDuration - diff; 119 } 120 else 121 { 122 direction = Direction::Forward; 123 elapsedTime = __builtin_fabs(elapsedTime); 124 } 125 break; 126 case WrapMode::ClampForever: 127 elapsedTime = effectDuration; 128 elapsedTimeSinceFinished = 0.0f; 129 hasFinished = true; 130 break; 131 } 132 133 return true; 134 } 135 }; 136 } // namespace display::led::effect