/ src / display / led / effect / display_led_effect.h
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