Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / platform / animation / TimingFunction.cpp
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.
4
5 #include "config.h"
6 #include "platform/animation/TimingFunction.h"
7
8 #include "wtf/MathExtras.h"
9
10 namespace blink {
11
12 String LinearTimingFunction::toString() const
13 {
14     return "linear";
15 }
16
17 double LinearTimingFunction::evaluate(double fraction, double) const
18 {
19     return fraction;
20 }
21
22 void LinearTimingFunction::range(double* minValue, double* maxValue) const
23 {
24 }
25
26 String CubicBezierTimingFunction::toString() const
27 {
28     switch (this->subType()) {
29     case CubicBezierTimingFunction::Ease:
30         return "ease";
31     case CubicBezierTimingFunction::EaseIn:
32         return "ease-in";
33     case CubicBezierTimingFunction::EaseOut:
34         return "ease-out";
35     case CubicBezierTimingFunction::EaseInOut:
36         return "ease-in-out";
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()) + ")";
41     default:
42         ASSERT_NOT_REACHED();
43     }
44     return "";
45 }
46
47 double CubicBezierTimingFunction::evaluate(double fraction, double accuracy) const
48 {
49     if (!m_bezier)
50         m_bezier = adoptPtr(new UnitBezier(m_x1, m_y1, m_x2, m_y2));
51     return m_bezier->solve(fraction, accuracy);
52 }
53
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
59 {
60     if (0 <= m_y1 && m_y2 < 1 && 0 <= m_y2 && m_y2 <= 1) {
61         return;
62     }
63
64     double a = 3.0 * (m_y1 - m_y2) + 1.0;
65     double b = 2.0 * (m_y2 - 2.0 * m_y1);
66     double c = m_y1;
67
68     if (std::abs(a) < std::numeric_limits<double>::epsilon()
69         && std::abs(b) < std::numeric_limits<double>::epsilon()) {
70         return;
71     }
72
73     double t1 = 0.0;
74     double t2 = 0.0;
75
76     if (std::abs(a) < std::numeric_limits<double>::epsilon()) {
77         t1 = -c / b;
78     } else {
79         double discriminant = b * b - 4 * a * c;
80         if (discriminant < 0)
81             return;
82         double discriminantSqrt = sqrt(discriminant);
83         t1 = (-b + discriminantSqrt) / (2 * a);
84         t2 = (-b - discriminantSqrt) / (2 * a);
85     }
86
87     double solution1 = 0.0;
88     double solution2 = 0.0;
89
90     // If the solution is in the range [0,1] then we include it, otherwise we
91     // ignore it.
92     if (!m_bezier)
93         m_bezier = adoptPtr(new UnitBezier(m_x1, m_y1, m_x2, m_y2));
94
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.
98     if (0 < t1 && t1 < 1)
99         solution1= m_bezier->sampleCurveY(t1);
100
101     if (0 < t2 && t2 < 1)
102         solution2 = m_bezier->sampleCurveY(t2);
103
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);
112 }
113
114 String StepsTimingFunction::toString() const
115 {
116     const char* positionString = nullptr;
117     switch (stepAtPosition()) {
118     case Start:
119         positionString = "start";
120         break;
121     case Middle:
122         positionString = "middle";
123         break;
124     case End:
125         positionString = "end";
126         break;
127     }
128
129     StringBuilder builder;
130     if (this->numberOfSteps() == 1) {
131         builder.append("step-");
132         builder.append(positionString);
133     } else {
134         builder.append("steps(" + String::numberToStringECMAScript(this->numberOfSteps()) + ", ");
135         builder.append(positionString);
136         builder.append(')');
137     }
138     return builder.toString();
139 }
140
141 void StepsTimingFunction::range(double* minValue, double* maxValue) const
142 {
143     *minValue = 0;
144     *maxValue = 1;
145 }
146
147 double StepsTimingFunction::evaluate(double fraction, double) const
148 {
149     double startOffset = 0;
150     switch (m_stepAtPosition) {
151     case Start:
152         startOffset = 1;
153         break;
154     case Middle:
155         startOffset = 0.5;
156         break;
157     case End:
158         startOffset = 0;
159         break;
160     default:
161         ASSERT_NOT_REACHED();
162         break;
163     }
164     return clampTo(floor((m_steps * fraction) + startOffset) / m_steps, 0.0, 1.0);
165 }
166
167 // Equals operators
168 bool operator==(const LinearTimingFunction& lhs, const TimingFunction& rhs)
169 {
170     return rhs.type() == TimingFunction::LinearFunction;
171 }
172
173 bool operator==(const CubicBezierTimingFunction& lhs, const TimingFunction& rhs)
174 {
175     if (rhs.type() != TimingFunction::CubicBezierFunction)
176         return false;
177
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());
181
182     return lhs.subType() == ctf.subType();
183 }
184
185 bool operator==(const StepsTimingFunction& lhs, const TimingFunction& rhs)
186 {
187     if (rhs.type() != TimingFunction::StepsFunction)
188         return false;
189
190     const StepsTimingFunction& stf = toStepsTimingFunction(rhs);
191     return (lhs.numberOfSteps() == stf.numberOfSteps()) && (lhs.stepAtPosition() == stf.stepAtPosition());
192 }
193
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)
197 {
198     switch (lhs.type()) {
199     case TimingFunction::LinearFunction: {
200         const LinearTimingFunction& linear = toLinearTimingFunction(lhs);
201         return (linear == rhs);
202     }
203     case TimingFunction::CubicBezierFunction: {
204         const CubicBezierTimingFunction& cubic = toCubicBezierTimingFunction(lhs);
205         return (cubic == rhs);
206     }
207     case TimingFunction::StepsFunction: {
208         const StepsTimingFunction& step = toStepsTimingFunction(lhs);
209         return (step == rhs);
210     }
211     default:
212         ASSERT_NOT_REACHED();
213     }
214     return false;
215 }
216
217 // No need to define specific operator!= as they can all come via this function.
218 bool operator!=(const TimingFunction& lhs, const TimingFunction& rhs)
219 {
220     return !(lhs == rhs);
221 }
222
223 } // namespace blink