1 #include "animation/cubic_interpolator.hpp"
6 const int NewtonIterations = 4;
7 const float NewtonMinSlope = 0.001f;
8 const float SubdivisionPrecision = 0.0000001f;
9 const int SubdivisionMaxIterations = 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)
14 return (((1.0f - 3.0f * aA2 + 3.0f * aA1) * aT +
15 (3.0f * aA2 - 6.0f * aA1)) *
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)
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);
28 StatusCode CubicInterpolator::onAddedDirty(CoreContext* context)
30 for (int i = 0; i < SplineTableSize; ++i)
32 m_Values[i] = calcBezier(i * SampleStepSize, x1(), x2());
34 return StatusCode::Ok;
37 float CubicInterpolator::getT(float x) const
39 float intervalStart = 0.0f;
40 int currentSample = 1;
41 int lastSample = SplineTableSize - 1;
43 for (; currentSample != lastSample && m_Values[currentSample] <= x;
46 intervalStart += SampleStepSize;
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;
55 float _x1 = x1(), _x2 = x2();
57 float initialSlope = getSlope(guessForT, _x1, _x2);
58 if (initialSlope >= NewtonMinSlope)
60 for (int i = 0; i < NewtonIterations; ++i)
62 float currentSlope = getSlope(guessForT, _x1, _x2);
63 if (currentSlope == 0.0f)
67 float currentX = calcBezier(guessForT, _x1, _x2) - x;
68 guessForT -= currentX / currentSlope;
72 else if (initialSlope == 0.0f)
78 float aB = intervalStart + SampleStepSize;
79 float currentX, currentT;
83 currentT = intervalStart + (aB - intervalStart) / 2.0f;
84 currentX = calcBezier(currentT, _x1, _x2) - x;
91 intervalStart = currentT;
93 } while (std::abs(currentX) > SubdivisionPrecision &&
94 ++i < SubdivisionMaxIterations);
99 float CubicInterpolator::transform(float mix) const
101 return calcBezier(getT(mix), y1(), y2());