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/base/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 bool IsApproximatelyZero(double value
) {
26 return fabs(value
) < EPSILON
;
29 // Returns false if |degrees| is not a multiple of ninety degrees or if
30 // |rotation| is NULL. It does not affect |rotation| in this case. Otherwise
31 // *rotation is set to be the appropriate sanitized rotation matrix. That is,
32 // the rotation matrix corresponding to |degrees| which has entries that are all
34 bool MassageRotationIfMultipleOfNinetyDegrees(gfx::Transform
* rotation
,
36 if (!IsMultipleOfNinetyDegrees(degrees
) || !rotation
)
39 gfx::Transform transform
;
40 SkMatrix44
& m
= transform
.matrix();
41 float degrees_by_ninety
= degrees
/ 90.0f
;
43 int n
= static_cast<int>(degrees_by_ninety
> 0
44 ? floor(degrees_by_ninety
+ 0.5f
)
45 : ceil(degrees_by_ninety
- 0.5f
));
51 // n should now be in the range [0, 3]
66 *rotation
= transform
;
74 ///////////////////////////////////////////////////////////////////////////////
75 // InterpolatedTransform
78 InterpolatedTransform::InterpolatedTransform()
84 InterpolatedTransform::InterpolatedTransform(float start_time
,
86 : start_time_(start_time
),
91 InterpolatedTransform::~InterpolatedTransform() {}
93 gfx::Transform
InterpolatedTransform::Interpolate(float t
) const {
96 gfx::Transform result
= InterpolateButDoNotCompose(t
);
98 result
.ConcatTransform(child_
->Interpolate(t
));
103 void InterpolatedTransform::SetChild(InterpolatedTransform
* child
) {
107 inline float InterpolatedTransform::ValueBetween(float time
,
109 float end_value
) const {
111 DCHECK(time
== time
&& start_time_
== start_time_
&& end_time_
== end_time_
);
112 if (time
!= time
|| start_time_
!= start_time_
|| end_time_
!= end_time_
)
115 // Ok if equal -- we'll get a step function. Note: if end_time_ ==
116 // start_time_ == x, then if none of the numbers are NaN, then it
117 // must be true that time < x or time >= x, so we will return early
118 // due to one of the following if statements.
119 DCHECK(end_time_
>= start_time_
);
121 if (time
< start_time_
)
124 if (time
>= end_time_
)
127 float t
= (time
- start_time_
) / (end_time_
- start_time_
);
128 return static_cast<float>(Tween::ValueBetween(t
, start_value
, end_value
));
131 ///////////////////////////////////////////////////////////////////////////////
132 // InterpolatedRotation
135 InterpolatedRotation::InterpolatedRotation(float start_degrees
,
137 : InterpolatedTransform(),
138 start_degrees_(start_degrees
),
139 end_degrees_(end_degrees
) {
142 InterpolatedRotation::InterpolatedRotation(float start_degrees
,
146 : InterpolatedTransform(start_time
, end_time
),
147 start_degrees_(start_degrees
),
148 end_degrees_(end_degrees
) {
151 InterpolatedRotation::~InterpolatedRotation() {}
153 gfx::Transform
InterpolatedRotation::InterpolateButDoNotCompose(float t
) const {
154 gfx::Transform result
;
155 float interpolated_degrees
= ValueBetween(t
, start_degrees_
, end_degrees_
);
156 result
.Rotate(interpolated_degrees
);
157 if (t
== 0.0f
|| t
== 1.0f
)
158 MassageRotationIfMultipleOfNinetyDegrees(&result
, interpolated_degrees
);
162 ///////////////////////////////////////////////////////////////////////////////
163 // InterpolatedAxisAngleRotation
166 InterpolatedAxisAngleRotation::InterpolatedAxisAngleRotation(
167 const gfx::Vector3dF
& axis
,
170 : InterpolatedTransform(),
172 start_degrees_(start_degrees
),
173 end_degrees_(end_degrees
) {
176 InterpolatedAxisAngleRotation::InterpolatedAxisAngleRotation(
177 const gfx::Vector3dF
& axis
,
182 : InterpolatedTransform(start_time
, end_time
),
184 start_degrees_(start_degrees
),
185 end_degrees_(end_degrees
) {
188 InterpolatedAxisAngleRotation::~InterpolatedAxisAngleRotation() {}
191 InterpolatedAxisAngleRotation::InterpolateButDoNotCompose(float t
) const {
192 gfx::Transform result
;
193 result
.RotateAbout(axis_
, ValueBetween(t
, start_degrees_
, end_degrees_
));
197 ///////////////////////////////////////////////////////////////////////////////
201 InterpolatedScale::InterpolatedScale(float start_scale
, float end_scale
)
202 : InterpolatedTransform(),
203 start_scale_(gfx::Point3F(start_scale
, start_scale
, start_scale
)),
204 end_scale_(gfx::Point3F(end_scale
, end_scale
, end_scale
)) {
207 InterpolatedScale::InterpolatedScale(float start_scale
, float end_scale
,
208 float start_time
, float end_time
)
209 : InterpolatedTransform(start_time
, end_time
),
210 start_scale_(gfx::Point3F(start_scale
, start_scale
, start_scale
)),
211 end_scale_(gfx::Point3F(end_scale
, end_scale
, end_scale
)) {
214 InterpolatedScale::InterpolatedScale(const gfx::Point3F
& start_scale
,
215 const gfx::Point3F
& end_scale
)
216 : InterpolatedTransform(),
217 start_scale_(start_scale
),
218 end_scale_(end_scale
) {
221 InterpolatedScale::InterpolatedScale(const gfx::Point3F
& start_scale
,
222 const gfx::Point3F
& end_scale
,
225 : InterpolatedTransform(start_time
, end_time
),
226 start_scale_(start_scale
),
227 end_scale_(end_scale
) {
230 InterpolatedScale::~InterpolatedScale() {}
232 gfx::Transform
InterpolatedScale::InterpolateButDoNotCompose(float t
) const {
233 gfx::Transform result
;
234 float scale_x
= ValueBetween(t
, start_scale_
.x(), end_scale_
.x());
235 float scale_y
= ValueBetween(t
, start_scale_
.y(), end_scale_
.y());
236 // TODO(vollick) 3d xforms.
237 result
.Scale(scale_x
, scale_y
);
241 ///////////////////////////////////////////////////////////////////////////////
242 // InterpolatedTranslation
245 InterpolatedTranslation::InterpolatedTranslation(const gfx::Point
& start_pos
,
246 const gfx::Point
& end_pos
)
247 : InterpolatedTransform(),
248 start_pos_(start_pos
),
252 InterpolatedTranslation::InterpolatedTranslation(const gfx::Point
& start_pos
,
253 const gfx::Point
& end_pos
,
256 : InterpolatedTransform(start_time
, end_time
),
257 start_pos_(start_pos
),
261 InterpolatedTranslation::~InterpolatedTranslation() {}
264 InterpolatedTranslation::InterpolateButDoNotCompose(float t
) const {
265 gfx::Transform result
;
266 // TODO(vollick) 3d xforms.
267 result
.Translate(ValueBetween(t
, start_pos_
.x(), end_pos_
.x()),
268 ValueBetween(t
, start_pos_
.y(), end_pos_
.y()));
272 ///////////////////////////////////////////////////////////////////////////////
273 // InterpolatedConstantTransform
276 InterpolatedConstantTransform::InterpolatedConstantTransform(
277 const gfx::Transform
& transform
)
278 : InterpolatedTransform(),
279 transform_(transform
) {
283 InterpolatedConstantTransform::InterpolateButDoNotCompose(float t
) const {
287 InterpolatedConstantTransform::~InterpolatedConstantTransform() {}
289 ///////////////////////////////////////////////////////////////////////////////
290 // InterpolatedTransformAboutPivot
293 InterpolatedTransformAboutPivot::InterpolatedTransformAboutPivot(
294 const gfx::Point
& pivot
,
295 InterpolatedTransform
* transform
)
296 : InterpolatedTransform() {
297 Init(pivot
, transform
);
300 InterpolatedTransformAboutPivot::InterpolatedTransformAboutPivot(
301 const gfx::Point
& pivot
,
302 InterpolatedTransform
* transform
,
305 : InterpolatedTransform() {
306 Init(pivot
, transform
);
309 InterpolatedTransformAboutPivot::~InterpolatedTransformAboutPivot() {}
312 InterpolatedTransformAboutPivot::InterpolateButDoNotCompose(float t
) const {
313 if (transform_
.get()) {
314 return transform_
->Interpolate(t
);
316 return gfx::Transform();
319 void InterpolatedTransformAboutPivot::Init(const gfx::Point
& pivot
,
320 InterpolatedTransform
* xform
) {
321 gfx::Transform to_pivot
;
322 gfx::Transform from_pivot
;
323 to_pivot
.Translate(-pivot
.x(), -pivot
.y());
324 from_pivot
.Translate(pivot
.x(), pivot
.y());
326 scoped_ptr
<InterpolatedTransform
> pre_transform(
327 new InterpolatedConstantTransform(to_pivot
));
328 scoped_ptr
<InterpolatedTransform
> post_transform(
329 new InterpolatedConstantTransform(from_pivot
));
331 pre_transform
->SetChild(xform
);
332 xform
->SetChild(post_transform
.release());
333 transform_
.reset(pre_transform
.release());
336 InterpolatedMatrixTransform::InterpolatedMatrixTransform(
337 const gfx::Transform
& start_transform
,
338 const gfx::Transform
& end_transform
)
339 : InterpolatedTransform() {
340 Init(start_transform
, end_transform
);
343 InterpolatedMatrixTransform::InterpolatedMatrixTransform(
344 const gfx::Transform
& start_transform
,
345 const gfx::Transform
& end_transform
,
348 : InterpolatedTransform() {
349 Init(start_transform
, end_transform
);
352 InterpolatedMatrixTransform::~InterpolatedMatrixTransform() {}
355 InterpolatedMatrixTransform::InterpolateButDoNotCompose(float t
) const {
356 gfx::DecomposedTransform blended
;
357 bool success
= gfx::BlendDecomposedTransforms(&blended
,
362 return gfx::ComposeTransform(blended
);
365 void InterpolatedMatrixTransform::Init(const gfx::Transform
& start_transform
,
366 const gfx::Transform
& end_transform
) {
367 bool success
= gfx::DecomposeTransform(&start_decomp_
, start_transform
);
369 success
= gfx::DecomposeTransform(&end_decomp_
, end_transform
);