submodule: add rive-cpp to rive-tizen as submodule
[platform/core/uifw/rive-tizen.git] / submodule / src / animation / cubic_interpolator.cpp
1 #include "animation/cubic_interpolator.hpp"
2 #include <cmath>
3
4 using namespace rive;
5
6 const int NewtonIterations = 4;
7 const float NewtonMinSlope = 0.001f;
8 const float SubdivisionPrecision = 0.0000001f;
9 const int SubdivisionMaxIterations = 10;
10
11 // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
12 static float calcBezier(float aT, float aA1, float aA2)
13 {
14         return (((1.0f - 3.0f * aA2 + 3.0f * aA1) * aT +
15                  (3.0f * aA2 - 6.0f * aA1)) *
16                     aT +
17                 (3.0f * aA1)) *
18                aT;
19 }
20
21 // Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2.
22 static float getSlope(float aT, float aA1, float aA2)
23 {
24         return 3.0f * (1.0f - 3.0f * aA2 + 3.0f * aA1) * aT * aT +
25                2.0f * (3.0f * aA2 - 6.0f * aA1) * aT + (3.0f * aA1);
26 }
27
28 StatusCode CubicInterpolator::onAddedDirty(CoreContext* context)
29 {
30         for (int i = 0; i < SplineTableSize; ++i)
31         {
32                 m_Values[i] = calcBezier(i * SampleStepSize, x1(), x2());
33         }
34         return StatusCode::Ok;
35 }
36
37 float CubicInterpolator::getT(float x) const
38 {
39         float intervalStart = 0.0f;
40         int currentSample = 1;
41         int lastSample = SplineTableSize - 1;
42
43         for (; currentSample != lastSample && m_Values[currentSample] <= x;
44              ++currentSample)
45         {
46                 intervalStart += SampleStepSize;
47         }
48         --currentSample;
49
50         // Interpolate to provide an initial guess for t
51         float dist = (x - m_Values[currentSample]) /
52                      (m_Values[currentSample + 1] - m_Values[currentSample]);
53         float guessForT = intervalStart + dist * SampleStepSize;
54
55         float _x1 = x1(), _x2 = x2();
56
57         float initialSlope = getSlope(guessForT, _x1, _x2);
58         if (initialSlope >= NewtonMinSlope)
59         {
60                 for (int i = 0; i < NewtonIterations; ++i)
61                 {
62                         float currentSlope = getSlope(guessForT, _x1, _x2);
63                         if (currentSlope == 0.0f)
64                         {
65                                 return guessForT;
66                         }
67                         float currentX = calcBezier(guessForT, _x1, _x2) - x;
68                         guessForT -= currentX / currentSlope;
69                 }
70                 return guessForT;
71         }
72         else if (initialSlope == 0.0f)
73         {
74                 return guessForT;
75         }
76         else
77         {
78                 float aB = intervalStart + SampleStepSize;
79                 float currentX, currentT;
80                 int i = 0;
81                 do
82                 {
83                         currentT = intervalStart + (aB - intervalStart) / 2.0f;
84                         currentX = calcBezier(currentT, _x1, _x2) - x;
85                         if (currentX > 0.0f)
86                         {
87                                 aB = currentT;
88                         }
89                         else
90                         {
91                                 intervalStart = currentT;
92                         }
93                 } while (std::abs(currentX) > SubdivisionPrecision &&
94                          ++i < SubdivisionMaxIterations);
95                 return currentT;
96         }
97 }
98
99 float CubicInterpolator::transform(float mix) const
100 {
101         return calcBezier(getT(mix), y1(), y2());
102 }