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