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
toString() const12 String LinearTimingFunction::toString() const
13 {
14 return "linear";
15 }
16
evaluate(double fraction,double) const17 double LinearTimingFunction::evaluate(double fraction, double) const
18 {
19 return fraction;
20 }
21
range(double * minValue,double * maxValue) const22 void LinearTimingFunction::range(double* minValue, double* maxValue) const
23 {
24 }
25
toString() const26 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
evaluate(double fraction,double accuracy) const47 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
range(double * minValue,double * maxValue) const58 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
toString() const114 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
range(double * minValue,double * maxValue) const141 void StepsTimingFunction::range(double* minValue, double* maxValue) const
142 {
143 *minValue = 0;
144 *maxValue = 1;
145 }
146
evaluate(double fraction,double) const147 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
operator ==(const LinearTimingFunction & lhs,const TimingFunction & rhs)168 bool operator==(const LinearTimingFunction& lhs, const TimingFunction& rhs)
169 {
170 return rhs.type() == TimingFunction::LinearFunction;
171 }
172
operator ==(const CubicBezierTimingFunction & lhs,const TimingFunction & rhs)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
operator ==(const StepsTimingFunction & lhs,const TimingFunction & rhs)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.
operator ==(const TimingFunction & lhs,const TimingFunction & rhs)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.
operator !=(const TimingFunction & lhs,const TimingFunction & rhs)218 bool operator!=(const TimingFunction& lhs, const TimingFunction& rhs)
219 {
220 return !(lhs == rhs);
221 }
222
223 } // namespace blink
224