1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 #include "platform/animation/TimingFunction.h"
8 #include "wtf/MathExtras.h"
12 String LinearTimingFunction::toString() const
17 double LinearTimingFunction::evaluate(double fraction, double) const
22 void LinearTimingFunction::range(double* minValue, double* maxValue) const
26 String CubicBezierTimingFunction::toString() const
28 switch (this->subType()) {
29 case CubicBezierTimingFunction::Ease:
31 case CubicBezierTimingFunction::EaseIn:
33 case CubicBezierTimingFunction::EaseOut:
35 case CubicBezierTimingFunction::EaseInOut:
37 case CubicBezierTimingFunction::Custom:
38 return "cubic-bezier(" + String::numberToStringECMAScript(this->x1()) + ", " +
39 String::numberToStringECMAScript(this->y1()) + ", " + String::numberToStringECMAScript(this->x2()) +
40 ", " + String::numberToStringECMAScript(this->y2()) + ")";
47 double CubicBezierTimingFunction::evaluate(double fraction, double accuracy) const
50 m_bezier = adoptPtr(new UnitBezier(m_x1, m_y1, m_x2, m_y2));
51 return m_bezier->solve(fraction, accuracy);
54 // This works by taking taking the derivative of the cubic bezier, on the y
55 // axis. We can then solve for where the derivative is zero to find the min
56 // and max distace along the line. We the have to solve those in terms of time
57 // rather than distance on the x-axis
58 void CubicBezierTimingFunction::range(double* minValue, double* maxValue) const
60 if (0 <= m_y1 && m_y2 < 1 && 0 <= m_y2 && m_y2 <= 1) {
64 double a = 3.0 * (m_y1 - m_y2) + 1.0;
65 double b = 2.0 * (m_y2 - 2.0 * m_y1);
68 if (std::abs(a) < std::numeric_limits<double>::epsilon()
69 && std::abs(b) < std::numeric_limits<double>::epsilon()) {
76 if (std::abs(a) < std::numeric_limits<double>::epsilon()) {
79 double discriminant = b * b - 4 * a * c;
82 double discriminantSqrt = sqrt(discriminant);
83 t1 = (-b + discriminantSqrt) / (2 * a);
84 t2 = (-b - discriminantSqrt) / (2 * a);
87 double solution1 = 0.0;
88 double solution2 = 0.0;
90 // If the solution is in the range [0,1] then we include it, otherwise we
93 m_bezier = adoptPtr(new UnitBezier(m_x1, m_y1, m_x2, m_y2));
95 // An interesting fact about these beziers is that they are only
96 // actually evaluated in [0,1]. After that we take the tangent at that point
97 // and linearly project it out.
99 solution1= m_bezier->sampleCurveY(t1);
101 if (0 < t2 && t2 < 1)
102 solution2 = m_bezier->sampleCurveY(t2);
104 // Since our input values can be out of the range 0->1 so we must also
105 // consider the minimum and maximum points.
106 double solutionMin = m_bezier->solve(*minValue, std::numeric_limits<double>::epsilon());
107 double solutionMax = m_bezier->solve(*maxValue, std::numeric_limits<double>::epsilon());
108 *minValue = std::min(std::min(solutionMin, solutionMax), 0.0);
109 *maxValue = std::max(std::max(solutionMin, solutionMax), 1.0);
110 *minValue = std::min(std::min(*minValue, solution1), solution2);
111 *maxValue = std::max(std::max(*maxValue, solution1), solution2);
114 String StepsTimingFunction::toString() const
116 const char* positionString = nullptr;
117 switch (stepAtPosition()) {
119 positionString = "start";
122 positionString = "middle";
125 positionString = "end";
129 StringBuilder builder;
130 if (this->numberOfSteps() == 1) {
131 builder.append("step-");
132 builder.append(positionString);
134 builder.append("steps(" + String::numberToStringECMAScript(this->numberOfSteps()) + ", ");
135 builder.append(positionString);
138 return builder.toString();
141 void StepsTimingFunction::range(double* minValue, double* maxValue) const
147 double StepsTimingFunction::evaluate(double fraction, double) const
149 double startOffset = 0;
150 switch (m_stepAtPosition) {
161 ASSERT_NOT_REACHED();
164 return clampTo(floor((m_steps * fraction) + startOffset) / m_steps, 0.0, 1.0);
168 bool operator==(const LinearTimingFunction& lhs, const TimingFunction& rhs)
170 return rhs.type() == TimingFunction::LinearFunction;
173 bool operator==(const CubicBezierTimingFunction& lhs, const TimingFunction& rhs)
175 if (rhs.type() != TimingFunction::CubicBezierFunction)
178 const CubicBezierTimingFunction& ctf = toCubicBezierTimingFunction(rhs);
179 if ((lhs.subType() == CubicBezierTimingFunction::Custom) && (ctf.subType() == CubicBezierTimingFunction::Custom))
180 return (lhs.x1() == ctf.x1()) && (lhs.y1() == ctf.y1()) && (lhs.x2() == ctf.x2()) && (lhs.y2() == ctf.y2());
182 return lhs.subType() == ctf.subType();
185 bool operator==(const StepsTimingFunction& lhs, const TimingFunction& rhs)
187 if (rhs.type() != TimingFunction::StepsFunction)
190 const StepsTimingFunction& stf = toStepsTimingFunction(rhs);
191 return (lhs.numberOfSteps() == stf.numberOfSteps()) && (lhs.stepAtPosition() == stf.stepAtPosition());
194 // The generic operator== *must* come after the
195 // non-generic operator== otherwise it will end up calling itself.
196 bool operator==(const TimingFunction& lhs, const TimingFunction& rhs)
198 switch (lhs.type()) {
199 case TimingFunction::LinearFunction: {
200 const LinearTimingFunction& linear = toLinearTimingFunction(lhs);
201 return (linear == rhs);
203 case TimingFunction::CubicBezierFunction: {
204 const CubicBezierTimingFunction& cubic = toCubicBezierTimingFunction(lhs);
205 return (cubic == rhs);
207 case TimingFunction::StepsFunction: {
208 const StepsTimingFunction& step = toStepsTimingFunction(lhs);
209 return (step == rhs);
212 ASSERT_NOT_REACHED();
217 // No need to define specific operator!= as they can all come via this function.
218 bool operator!=(const TimingFunction& lhs, const TimingFunction& rhs)
220 return !(lhs == rhs);