1 // Copyright 2012 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.
8 #include "base/logging.h"
9 #include "cc/animation/timing_function.h"
15 static const double kBezierEpsilon
= 1e-7;
16 static const int MAX_STEPS
= 30;
18 static double eval_bezier(double x1
, double x2
, double t
) {
19 const double x1_times_3
= 3.0 * x1
;
20 const double x2_times_3
= 3.0 * x2
;
21 const double h3
= x1_times_3
;
22 const double h1
= x1_times_3
- x2_times_3
+ 1.0;
23 const double h2
= x2_times_3
- 6.0 * x1
;
24 return t
* (t
* (t
* h1
+ h2
) + h3
);
27 static double bezier_interp(double x1
,
37 x1
= std::min(std::max(x1
, 0.0), 1.0);
38 x2
= std::min(std::max(x2
, 0.0), 1.0);
39 x
= std::min(std::max(x
, 0.0), 1.0);
41 // Step 1. Find the t corresponding to the given x. I.e., we want t such that
42 // eval_bezier(x1, x2, t) = x. There is a unique solution if x1 and x2 lie
45 // We're just going to do bisection for now (for simplicity), but we could
46 // easily do some newton steps if this turns out to be a bottleneck.
49 for (int i
= 0; i
< MAX_STEPS
; ++i
, step
*= 0.5) {
50 const double error
= eval_bezier(x1
, x2
, t
) - x
;
51 if (std::abs(error
) < kBezierEpsilon
)
53 t
+= error
> 0.0 ? -step
: step
;
56 // We should have terminated the above loop because we got close to x, not
57 // because we exceeded MAX_STEPS. Do a DCHECK here to confirm.
58 DCHECK_GT(kBezierEpsilon
, std::abs(eval_bezier(x1
, x2
, t
) - x
));
60 // Step 2. Return the interpolated y values at the t we computed above.
61 return eval_bezier(y1
, y2
, t
);
66 TimingFunction::TimingFunction() {}
68 TimingFunction::~TimingFunction() {}
70 double TimingFunction::Duration() const {
74 scoped_ptr
<CubicBezierTimingFunction
> CubicBezierTimingFunction::Create(
75 double x1
, double y1
, double x2
, double y2
) {
76 return make_scoped_ptr(new CubicBezierTimingFunction(x1
, y1
, x2
, y2
));
79 CubicBezierTimingFunction::CubicBezierTimingFunction(double x1
,
83 : x1_(x1
), y1_(y1
), x2_(x2
), y2_(y2
) {}
85 CubicBezierTimingFunction::~CubicBezierTimingFunction() {}
87 float CubicBezierTimingFunction::GetValue(double x
) const {
88 return static_cast<float>(bezier_interp(x1_
, y1_
, x2_
, y2_
, x
));
91 scoped_ptr
<AnimationCurve
> CubicBezierTimingFunction::Clone() const {
92 return make_scoped_ptr(
93 new CubicBezierTimingFunction(*this)).PassAs
<AnimationCurve
>();
96 void CubicBezierTimingFunction::Range(float* min
, float* max
) const {
99 if (0.f
<= y1_
&& y1_
< 1.f
&& 0.f
<= y2_
&& y2_
<= 1.f
)
102 // Represent the function's derivative in the form at^2 + bt + c.
103 float a
= 3.f
* (y1_
- y2_
) + 1.f
;
104 float b
= 2.f
* (y2_
- 2.f
* y1_
);
107 // Check if the derivative is constant.
108 if (std::abs(a
) < kBezierEpsilon
&&
109 std::abs(b
) < kBezierEpsilon
)
112 // Zeros of the function's derivative.
116 if (std::abs(a
) < kBezierEpsilon
) {
117 // The function's derivative is linear.
120 // The function's derivative is a quadratic. We find the zeros of this
121 // quadratic using the quadratic formula.
122 float discriminant
= b
* b
- 4 * a
* c
;
123 if (discriminant
< 0.f
)
125 float discriminant_sqrt
= sqrt(discriminant
);
126 t_1
= (-b
+ discriminant_sqrt
) / (2.f
* a
);
127 t_2
= (-b
- discriminant_sqrt
) / (2.f
* a
);
133 if (0.f
< t_1
&& t_1
< 1.f
)
134 sol_1
= eval_bezier(y1_
, y2_
, t_1
);
136 if (0.f
< t_2
&& t_2
< 1.f
)
137 sol_2
= eval_bezier(y1_
, y2_
, t_2
);
139 *min
= std::min(std::min(*min
, sol_1
), sol_2
);
140 *max
= std::max(std::max(*max
, sol_1
), sol_2
);
143 // These numbers come from
144 // http://www.w3.org/TR/css3-transitions/#transition-timing-function_tag.
145 scoped_ptr
<TimingFunction
> EaseTimingFunction::Create() {
146 return CubicBezierTimingFunction::Create(
147 0.25, 0.1, 0.25, 1.0).PassAs
<TimingFunction
>();
150 scoped_ptr
<TimingFunction
> EaseInTimingFunction::Create() {
151 return CubicBezierTimingFunction::Create(
152 0.42, 0.0, 1.0, 1.0).PassAs
<TimingFunction
>();
155 scoped_ptr
<TimingFunction
> EaseOutTimingFunction::Create() {
156 return CubicBezierTimingFunction::Create(
157 0.0, 0.0, 0.58, 1.0).PassAs
<TimingFunction
>();
160 scoped_ptr
<TimingFunction
> EaseInOutTimingFunction::Create() {
161 return CubicBezierTimingFunction::Create(
162 0.42, 0.0, 0.58, 1).PassAs
<TimingFunction
>();