1 /*
2  *  Copyright (C) 2005-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #pragma once
10 
11 ///////////////////////////////////////////////////////////////////////
12 // Tween.h
13 // A couple of tweening classes implemented in C++.
14 // ref: http://www.robertpenner.com/easing/
15 //
16 // Author: d4rk <d4rk@xbmc.org>
17 ///////////////////////////////////////////////////////////////////////
18 
19 
20 ///////////////////////////////////////////////////////////////////////
21 // Current list of classes:
22 //
23 // LinearTweener
24 // QuadTweener
25 // CubicTweener
26 // SineTweener
27 // CircleTweener
28 // BackTweener
29 // BounceTweener
30 // ElasticTweener
31 //
32 ///////////////////////////////////////////////////////////////////////
33 
34 #include <math.h>
35 
36 #ifndef M_PI
37 #define M_PI 3.14159265358979323846f
38 #endif
39 
40 enum TweenerType
41 {
42   EASE_IN,
43   EASE_OUT,
44   EASE_INOUT
45 };
46 
47 
48 class Tweener
49 {
50 public:
51   explicit Tweener(TweenerType tweenerType = EASE_OUT) { m_tweenerType = tweenerType; }
52   virtual ~Tweener() = default;
53 
SetEasing(TweenerType type)54   void SetEasing(TweenerType type) { m_tweenerType = type; }
55   virtual float Tween(float time, float start, float change, float duration)=0;
HasResumePoint()56   virtual bool HasResumePoint() const { return m_tweenerType == EASE_INOUT; }
57 protected:
58   TweenerType m_tweenerType;
59 };
60 
61 
62 class LinearTweener : public Tweener
63 {
64 public:
Tween(float time,float start,float change,float duration)65   float Tween(float time, float start, float change, float duration) override
66   {
67     return change * time / duration + start;
68   }
HasResumePoint()69   bool HasResumePoint() const override { return false; }
70 };
71 
72 
73 class QuadTweener : public Tweener
74 {
75 public:
76   explicit QuadTweener(float a = 1.0f) { _a=a; }
Tween(float time,float start,float change,float duration)77   float Tween(float time, float start, float change, float duration) override
78   {
79     switch (m_tweenerType)
80       {
81       case EASE_IN:
82         time /= duration;
83         return change * time * (_a * time + 1 - _a) + start;
84         break;
85 
86       case EASE_OUT:
87         time /= duration;
88         return -change * time * (_a * time - 1 - _a) + start;
89         break;
90 
91       case EASE_INOUT:
92         time /= duration/2;
93         if (time < 1)
94           return (change) * time * (_a * time + 1 - _a) + start;
95         time--;
96         return (-change) * time * (_a * time - 1 - _a) + start;
97         break;
98       }
99     return change * time * time + start;
100   }
101 private:
102   float _a;
103 };
104 
105 
106 class CubicTweener : public Tweener
107 {
108 public:
Tween(float time,float start,float change,float duration)109   float Tween(float time, float start, float change, float duration) override
110   {
111     switch (m_tweenerType)
112       {
113       case EASE_IN:
114         time /= duration;
115         return change * time * time * time + start;
116         break;
117 
118       case EASE_OUT:
119         time /= duration;
120         time--;
121         return change * (time * time * time + 1) + start;
122         break;
123 
124       case EASE_INOUT:
125         time /= duration/2;
126         if (time < 1)
127           return (change/2) * time * time * time + start;
128         time-=2;
129         return (change/2) * (time * time * time + 2) + start;
130         break;
131       }
132     return change * time * time + start;
133   }
134 };
135 
136 class CircleTweener : public Tweener
137 {
138 public:
Tween(float time,float start,float change,float duration)139   float Tween(float time, float start, float change, float duration) override
140   {
141     switch (m_tweenerType)
142       {
143       case EASE_IN:
144         time /= duration;
145         return (-change) * (sqrt(1 - time * time) - 1) + start;
146         break;
147 
148       case EASE_OUT:
149         time /= duration;
150         time--;
151         return change * sqrt(1 - time * time) + start;
152         break;
153 
154       case EASE_INOUT:
155         time /= duration/2;
156         if (time  < 1)
157           return (-change/2) * (sqrt(1 - time * time) - 1) + start;
158         time-=2;
159         return change/2 * (sqrt(1 - time * time) + 1) + start;
160         break;
161       }
162     return change * sqrt(1 - time * time) + start;
163   }
164 };
165 
166 class BackTweener : public Tweener
167 {
168 public:
169   explicit BackTweener(float s=1.70158) { _s=s; }
170 
Tween(float time,float start,float change,float duration)171   float Tween(float time, float start, float change, float duration) override
172   {
173     float s = _s;
174     switch (m_tweenerType)
175       {
176       case EASE_IN:
177         time /= duration;
178         return change * time * time * ((s + 1) * time - s) + start;
179         break;
180 
181       case EASE_OUT:
182         time /= duration;
183         time--;
184         return change * (time * time * ((s + 1) * time + s) + 1) + start;
185         break;
186 
187       case EASE_INOUT:
188         time /= duration/2;
189         s*=(1.525f);
190         if ((time ) < 1)
191         {
192           return (change/2) * (time * time * ((s + 1) * time - s)) + start;
193         }
194         time-=2;
195         return (change/2) * (time * time * ((s + 1) * time + s) + 2) + start;
196         break;
197       }
198     return change * ((time-1) * time * ((s + 1) * time + s) + 1) + start;
199   }
200 private:
201   float _s;
202 
203 };
204 
205 
206 class SineTweener : public Tweener
207 {
208 public:
Tween(float time,float start,float change,float duration)209   float Tween(float time, float start, float change, float duration) override
210   {
211     time /= duration;
212     switch (m_tweenerType)
213       {
214       case EASE_IN:
215         return change * (1 - cos(time * M_PI / 2.0f)) + start;
216         break;
217 
218       case EASE_OUT:
219         return change * sin(time * M_PI / 2.0f) + start;
220         break;
221 
222       case EASE_INOUT:
223         return change/2 * (1 - cos(M_PI * time)) + start;
224         break;
225       }
226     return (change/2) * (1 - cos(M_PI * time)) + start;
227   }
228 };
229 
230 
231 class BounceTweener : public Tweener
232 {
233 public:
Tween(float time,float start,float change,float duration)234   float Tween(float time, float start, float change, float duration) override
235   {
236     switch (m_tweenerType)
237       {
238       case EASE_IN:
239         return (change - easeOut(duration - time, 0, change, duration)) + start;
240         break;
241 
242       case EASE_OUT:
243         return easeOut(time, start, change, duration);
244         break;
245 
246       case EASE_INOUT:
247         if (time < duration/2)
248           return (change - easeOut (duration - (time * 2), 0, change, duration) + start) * .5f + start;
249         else
250           return (easeOut (time * 2 - duration, 0, change, duration) * .5f + change * .5f) + start;
251         break;
252       }
253 
254     return easeOut(time, start, change, duration);
255   }
256 protected:
easeOut(float time,float start,float change,float duration)257   static float easeOut(float time, float start, float change, float duration)
258   {
259     time /= duration;
260     if (time < (1/2.75)) {
261       return  change * (7.5625f * time * time) + start;
262     } else if (time < (2/2.75)) {
263       time -= (1.5f/2.75f);
264       return change * (7.5625f * time * time + .75f) + start;
265     } else if (time < (2.5/2.75)) {
266       time -= (2.25f/2.75f);
267       return change * (7.5625f * time * time + .9375f) + start;
268     } else {
269       time -= (2.625f/2.75f);
270       return change * (7.5625f * time * time + .984375f) + start;
271     }
272   }
273 };
274 
275 
276 class ElasticTweener : public Tweener
277 {
278 public:
279   ElasticTweener(float a=0.0, float p=0.0) { _a=a; _p=p; }
280 
Tween(float time,float start,float change,float duration)281   float Tween(float time, float start, float change, float duration) override
282   {
283     switch (m_tweenerType)
284       {
285       case EASE_IN:
286         return easeIn(time, start, change, duration);
287         break;
288 
289       case EASE_OUT:
290         return easeOut(time, start, change, duration);
291         break;
292 
293       case EASE_INOUT:
294         return easeInOut(time, start, change, duration);
295         break;
296       }
297     return easeOut(time, start, change, duration);
298   }
299 protected:
300   float _a;
301   float _p;
302 
easeIn(float time,float start,float change,float duration)303   float easeIn(float time, float start, float change, float duration) const
304   {
305     float s=0;
306     float a=_a;
307     float p=_p;
308 
309     if (time==0)
310       return start;
311     time /= duration;
312     if (time==1)
313         return start + change;
314     if (!p)
315       p=duration*.3f;
316     if (!a || a < fabs(change))
317     {
318       a = change;
319       s = p / 4.0f;
320     }
321     else
322     {
323       s = p / (2 * M_PI) * asin (change / a);
324     }
325     time--;
326     return -(a * pow(2.0f, 10*time) * sin((time * duration - s) * (2 * M_PI) / p )) + start;
327   }
328 
easeOut(float time,float start,float change,float duration)329   float easeOut(float time, float start, float change, float duration) const
330   {
331     float s=0;
332     float a=_a;
333     float p=_p;
334 
335     if (time==0)
336       return start;
337     time /= duration;
338     if (time==1)
339         return start + change;
340     if (!p)
341       p=duration*.3f;
342     if (!a || a < fabs(change))
343     {
344       a = change;
345       s = p / 4.0f;
346     }
347     else
348     {
349       s = p / (2 * M_PI) * asin (change / a);
350     }
351     return (a * pow(2.0f, -10*time) * sin((time * duration - s) * (2 * M_PI) / p )) + change + start;
352   }
353 
easeInOut(float time,float start,float change,float duration)354   float easeInOut(float time, float start, float change, float duration) const
355   {
356     float s=0;
357     float a=_a;
358     float p=_p;
359 
360     if (time==0)
361       return start;
362     time /= duration/2;
363     if (time==2)
364         return start + change;
365     if (!p)
366       p=duration*.3f*1.5f;
367     if (!a || a < fabs(change))
368     {
369       a = change;
370       s = p / 4.0f;
371     }
372     else
373     {
374       s = p / (2 * M_PI) * asin (change / a);
375     }
376 
377     if (time < 1)
378     {
379       time--;
380       return -.5f * (a * pow(2.0f, 10 * (time)) * sin((time * duration - s) * (2 * M_PI) / p )) + start;
381     }
382     time--;
383     return a * pow(2.0f, -10 * (time)) * sin((time * duration-s) * (2 * M_PI) / p ) * .5f + change + start;
384   }
385 };
386 
387