1 #include "animation/cubic_interpolator.hpp"
2 #include "artboard.hpp"
3 #include "importers/artboard_importer.hpp"
4 #include "importers/import_stack.hpp"
9 const int NewtonIterations = 4;
10 const float NewtonMinSlope = 0.001f;
11 const float SubdivisionPrecision = 0.0000001f;
12 const int SubdivisionMaxIterations = 10;
14 // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
15 static float calcBezier(float aT, float aA1, float aA2)
17 return (((1.0f - 3.0f * aA2 + 3.0f * aA1) * aT +
18 (3.0f * aA2 - 6.0f * aA1)) *
24 // Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2.
25 static float getSlope(float aT, float aA1, float aA2)
27 return 3.0f * (1.0f - 3.0f * aA2 + 3.0f * aA1) * aT * aT +
28 2.0f * (3.0f * aA2 - 6.0f * aA1) * aT + (3.0f * aA1);
31 StatusCode CubicInterpolator::onAddedDirty(CoreContext* context)
33 for (int i = 0; i < SplineTableSize; ++i)
35 m_Values[i] = calcBezier(i * SampleStepSize, x1(), x2());
37 return StatusCode::Ok;
40 float CubicInterpolator::getT(float x) const
42 float intervalStart = 0.0f;
43 int currentSample = 1;
44 int lastSample = SplineTableSize - 1;
46 for (; currentSample != lastSample && m_Values[currentSample] <= x;
49 intervalStart += SampleStepSize;
53 // Interpolate to provide an initial guess for t
54 float dist = (x - m_Values[currentSample]) /
55 (m_Values[currentSample + 1] - m_Values[currentSample]);
56 float guessForT = intervalStart + dist * SampleStepSize;
58 float _x1 = x1(), _x2 = x2();
60 float initialSlope = getSlope(guessForT, _x1, _x2);
61 if (initialSlope >= NewtonMinSlope)
63 for (int i = 0; i < NewtonIterations; ++i)
65 float currentSlope = getSlope(guessForT, _x1, _x2);
66 if (currentSlope == 0.0f)
70 float currentX = calcBezier(guessForT, _x1, _x2) - x;
71 guessForT -= currentX / currentSlope;
75 else if (initialSlope == 0.0f)
81 float aB = intervalStart + SampleStepSize;
82 float currentX, currentT;
86 currentT = intervalStart + (aB - intervalStart) / 2.0f;
87 currentX = calcBezier(currentT, _x1, _x2) - x;
94 intervalStart = currentT;
96 } while (std::abs(currentX) > SubdivisionPrecision &&
97 ++i < SubdivisionMaxIterations);
102 float CubicInterpolator::transform(float mix) const
104 return calcBezier(getT(mix), y1(), y2());
107 StatusCode CubicInterpolator::import(ImportStack& importStack)
109 auto artboardImporter =
110 importStack.latest<ArtboardImporter>(ArtboardBase::typeKey);
111 if (artboardImporter == nullptr)
113 return StatusCode::MissingObject;
115 artboardImporter->addComponent(this);
116 return Super::import(importStack);