1 // Copyright (c) 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.
5 #include "ui/gfx/interpolated_transform.h"
10 #define M_PI 3.14159265358979323846
13 #include "base/logging.h"
14 #include "ui/gfx/animation/tween.h"
18 static const double EPSILON
= 1e-6;
20 bool IsMultipleOfNinetyDegrees(double degrees
) {
21 double remainder
= fabs(fmod(degrees
, 90.0));
22 return remainder
< EPSILON
|| 90.0 - remainder
< EPSILON
;
25 // Returns false if |degrees| is not a multiple of ninety degrees or if
26 // |rotation| is NULL. It does not affect |rotation| in this case. Otherwise
27 // *rotation is set to be the appropriate sanitized rotation matrix. That is,
28 // the rotation matrix corresponding to |degrees| which has entries that are all
30 bool MassageRotationIfMultipleOfNinetyDegrees(gfx::Transform
* rotation
,
32 if (!IsMultipleOfNinetyDegrees(degrees
) || !rotation
)
35 gfx::Transform transform
;
36 SkMatrix44
& m
= transform
.matrix();
37 float degrees_by_ninety
= degrees
/ 90.0f
;
39 int n
= static_cast<int>(degrees_by_ninety
> 0
40 ? floor(degrees_by_ninety
+ 0.5f
)
41 : ceil(degrees_by_ninety
- 0.5f
));
47 // n should now be in the range [0, 3]
62 *rotation
= transform
;
70 ///////////////////////////////////////////////////////////////////////////////
71 // InterpolatedTransform
74 InterpolatedTransform::InterpolatedTransform()
80 InterpolatedTransform::InterpolatedTransform(float start_time
,
82 : start_time_(start_time
),
87 InterpolatedTransform::~InterpolatedTransform() {}
89 gfx::Transform
InterpolatedTransform::Interpolate(float t
) const {
92 gfx::Transform result
= InterpolateButDoNotCompose(t
);
94 result
.ConcatTransform(child_
->Interpolate(t
));
99 void InterpolatedTransform::SetChild(InterpolatedTransform
* child
) {
103 inline float InterpolatedTransform::ValueBetween(float time
,
105 float end_value
) const {
107 DCHECK(time
== time
&& start_time_
== start_time_
&& end_time_
== end_time_
);
108 if (time
!= time
|| start_time_
!= start_time_
|| end_time_
!= end_time_
)
111 // Ok if equal -- we'll get a step function. Note: if end_time_ ==
112 // start_time_ == x, then if none of the numbers are NaN, then it
113 // must be true that time < x or time >= x, so we will return early
114 // due to one of the following if statements.
115 DCHECK(end_time_
>= start_time_
);
117 if (time
< start_time_
)
120 if (time
>= end_time_
)
123 float t
= (time
- start_time_
) / (end_time_
- start_time_
);
124 return static_cast<float>(
125 gfx::Tween::DoubleValueBetween(t
, start_value
, end_value
));
128 ///////////////////////////////////////////////////////////////////////////////
129 // InterpolatedRotation
132 InterpolatedRotation::InterpolatedRotation(float start_degrees
,
134 : InterpolatedTransform(),
135 start_degrees_(start_degrees
),
136 end_degrees_(end_degrees
) {
139 InterpolatedRotation::InterpolatedRotation(float start_degrees
,
143 : InterpolatedTransform(start_time
, end_time
),
144 start_degrees_(start_degrees
),
145 end_degrees_(end_degrees
) {
148 InterpolatedRotation::~InterpolatedRotation() {}
150 gfx::Transform
InterpolatedRotation::InterpolateButDoNotCompose(float t
) const {
151 gfx::Transform result
;
152 float interpolated_degrees
= ValueBetween(t
, start_degrees_
, end_degrees_
);
153 result
.Rotate(interpolated_degrees
);
154 if (t
== 0.0f
|| t
== 1.0f
)
155 MassageRotationIfMultipleOfNinetyDegrees(&result
, interpolated_degrees
);
159 ///////////////////////////////////////////////////////////////////////////////
160 // InterpolatedAxisAngleRotation
163 InterpolatedAxisAngleRotation::InterpolatedAxisAngleRotation(
164 const gfx::Vector3dF
& axis
,
167 : InterpolatedTransform(),
169 start_degrees_(start_degrees
),
170 end_degrees_(end_degrees
) {
173 InterpolatedAxisAngleRotation::InterpolatedAxisAngleRotation(
174 const gfx::Vector3dF
& axis
,
179 : InterpolatedTransform(start_time
, end_time
),
181 start_degrees_(start_degrees
),
182 end_degrees_(end_degrees
) {
185 InterpolatedAxisAngleRotation::~InterpolatedAxisAngleRotation() {}
188 InterpolatedAxisAngleRotation::InterpolateButDoNotCompose(float t
) const {
189 gfx::Transform result
;
190 result
.RotateAbout(axis_
, ValueBetween(t
, start_degrees_
, end_degrees_
));
194 ///////////////////////////////////////////////////////////////////////////////
198 InterpolatedScale::InterpolatedScale(float start_scale
, float end_scale
)
199 : InterpolatedTransform(),
200 start_scale_(gfx::Point3F(start_scale
, start_scale
, start_scale
)),
201 end_scale_(gfx::Point3F(end_scale
, end_scale
, end_scale
)) {
204 InterpolatedScale::InterpolatedScale(float start_scale
, float end_scale
,
205 float start_time
, float end_time
)
206 : InterpolatedTransform(start_time
, end_time
),
207 start_scale_(gfx::Point3F(start_scale
, start_scale
, start_scale
)),
208 end_scale_(gfx::Point3F(end_scale
, end_scale
, end_scale
)) {
211 InterpolatedScale::InterpolatedScale(const gfx::Point3F
& start_scale
,
212 const gfx::Point3F
& end_scale
)
213 : InterpolatedTransform(),
214 start_scale_(start_scale
),
215 end_scale_(end_scale
) {
218 InterpolatedScale::InterpolatedScale(const gfx::Point3F
& start_scale
,
219 const gfx::Point3F
& end_scale
,
222 : InterpolatedTransform(start_time
, end_time
),
223 start_scale_(start_scale
),
224 end_scale_(end_scale
) {
227 InterpolatedScale::~InterpolatedScale() {}
229 gfx::Transform
InterpolatedScale::InterpolateButDoNotCompose(float t
) const {
230 gfx::Transform result
;
231 float scale_x
= ValueBetween(t
, start_scale_
.x(), end_scale_
.x());
232 float scale_y
= ValueBetween(t
, start_scale_
.y(), end_scale_
.y());
233 float scale_z
= ValueBetween(t
, start_scale_
.z(), end_scale_
.z());
234 result
.Scale3d(scale_x
, scale_y
, scale_z
);
238 ///////////////////////////////////////////////////////////////////////////////
239 // InterpolatedTranslation
242 InterpolatedTranslation::InterpolatedTranslation(const gfx::Point
& start_pos
,
243 const gfx::Point
& end_pos
)
244 : InterpolatedTransform(),
245 start_pos_(start_pos
),
249 InterpolatedTranslation::InterpolatedTranslation(const gfx::Point
& start_pos
,
250 const gfx::Point
& end_pos
,
253 : InterpolatedTransform(start_time
, end_time
),
254 start_pos_(start_pos
),
258 InterpolatedTranslation::InterpolatedTranslation(const gfx::Point3F
& start_pos
,
259 const gfx::Point3F
& end_pos
)
260 : InterpolatedTransform(), start_pos_(start_pos
), end_pos_(end_pos
) {
263 InterpolatedTranslation::InterpolatedTranslation(const gfx::Point3F
& start_pos
,
264 const gfx::Point3F
& end_pos
,
267 : InterpolatedTransform(start_time
, end_time
),
268 start_pos_(start_pos
),
272 InterpolatedTranslation::~InterpolatedTranslation() {}
275 InterpolatedTranslation::InterpolateButDoNotCompose(float t
) const {
276 gfx::Transform result
;
277 result
.Translate3d(ValueBetween(t
, start_pos_
.x(), end_pos_
.x()),
278 ValueBetween(t
, start_pos_
.y(), end_pos_
.y()),
279 ValueBetween(t
, start_pos_
.z(), end_pos_
.z()));
283 ///////////////////////////////////////////////////////////////////////////////
284 // InterpolatedConstantTransform
287 InterpolatedConstantTransform::InterpolatedConstantTransform(
288 const gfx::Transform
& transform
)
289 : InterpolatedTransform(),
290 transform_(transform
) {
294 InterpolatedConstantTransform::InterpolateButDoNotCompose(float t
) const {
298 InterpolatedConstantTransform::~InterpolatedConstantTransform() {}
300 ///////////////////////////////////////////////////////////////////////////////
301 // InterpolatedTransformAboutPivot
304 InterpolatedTransformAboutPivot::InterpolatedTransformAboutPivot(
305 const gfx::Point
& pivot
,
306 InterpolatedTransform
* transform
)
307 : InterpolatedTransform() {
308 Init(pivot
, transform
);
311 InterpolatedTransformAboutPivot::InterpolatedTransformAboutPivot(
312 const gfx::Point
& pivot
,
313 InterpolatedTransform
* transform
,
316 : InterpolatedTransform() {
317 Init(pivot
, transform
);
320 InterpolatedTransformAboutPivot::~InterpolatedTransformAboutPivot() {}
323 InterpolatedTransformAboutPivot::InterpolateButDoNotCompose(float t
) const {
324 if (transform_
.get()) {
325 return transform_
->Interpolate(t
);
327 return gfx::Transform();
330 void InterpolatedTransformAboutPivot::Init(const gfx::Point
& pivot
,
331 InterpolatedTransform
* xform
) {
332 gfx::Transform to_pivot
;
333 gfx::Transform from_pivot
;
334 to_pivot
.Translate(SkIntToMScalar(-pivot
.x()), SkIntToMScalar(-pivot
.y()));
335 from_pivot
.Translate(SkIntToMScalar(pivot
.x()), SkIntToMScalar(pivot
.y()));
337 scoped_ptr
<InterpolatedTransform
> pre_transform(
338 new InterpolatedConstantTransform(to_pivot
));
339 scoped_ptr
<InterpolatedTransform
> post_transform(
340 new InterpolatedConstantTransform(from_pivot
));
342 pre_transform
->SetChild(xform
);
343 xform
->SetChild(post_transform
.release());
344 transform_
.reset(pre_transform
.release());
347 InterpolatedMatrixTransform::InterpolatedMatrixTransform(
348 const gfx::Transform
& start_transform
,
349 const gfx::Transform
& end_transform
)
350 : InterpolatedTransform() {
351 Init(start_transform
, end_transform
);
354 InterpolatedMatrixTransform::InterpolatedMatrixTransform(
355 const gfx::Transform
& start_transform
,
356 const gfx::Transform
& end_transform
,
359 : InterpolatedTransform() {
360 Init(start_transform
, end_transform
);
363 InterpolatedMatrixTransform::~InterpolatedMatrixTransform() {}
366 InterpolatedMatrixTransform::InterpolateButDoNotCompose(float t
) const {
367 gfx::DecomposedTransform blended
;
368 bool success
= gfx::BlendDecomposedTransforms(&blended
,
373 return gfx::ComposeTransform(blended
);
376 void InterpolatedMatrixTransform::Init(const gfx::Transform
& start_transform
,
377 const gfx::Transform
& end_transform
) {
378 bool success
= gfx::DecomposeTransform(&start_decomp_
, start_transform
);
380 success
= gfx::DecomposeTransform(&end_decomp_
, end_transform
);