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 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>(
129 gfx::Tween::DoubleValueBetween(t
, start_value
, end_value
));
132 ///////////////////////////////////////////////////////////////////////////////
133 // InterpolatedRotation
136 InterpolatedRotation::InterpolatedRotation(float start_degrees
,
138 : InterpolatedTransform(),
139 start_degrees_(start_degrees
),
140 end_degrees_(end_degrees
) {
143 InterpolatedRotation::InterpolatedRotation(float start_degrees
,
147 : InterpolatedTransform(start_time
, end_time
),
148 start_degrees_(start_degrees
),
149 end_degrees_(end_degrees
) {
152 InterpolatedRotation::~InterpolatedRotation() {}
154 gfx::Transform
InterpolatedRotation::InterpolateButDoNotCompose(float t
) const {
155 gfx::Transform result
;
156 float interpolated_degrees
= ValueBetween(t
, start_degrees_
, end_degrees_
);
157 result
.Rotate(interpolated_degrees
);
158 if (t
== 0.0f
|| t
== 1.0f
)
159 MassageRotationIfMultipleOfNinetyDegrees(&result
, interpolated_degrees
);
163 ///////////////////////////////////////////////////////////////////////////////
164 // InterpolatedAxisAngleRotation
167 InterpolatedAxisAngleRotation::InterpolatedAxisAngleRotation(
168 const gfx::Vector3dF
& axis
,
171 : InterpolatedTransform(),
173 start_degrees_(start_degrees
),
174 end_degrees_(end_degrees
) {
177 InterpolatedAxisAngleRotation::InterpolatedAxisAngleRotation(
178 const gfx::Vector3dF
& axis
,
183 : InterpolatedTransform(start_time
, end_time
),
185 start_degrees_(start_degrees
),
186 end_degrees_(end_degrees
) {
189 InterpolatedAxisAngleRotation::~InterpolatedAxisAngleRotation() {}
192 InterpolatedAxisAngleRotation::InterpolateButDoNotCompose(float t
) const {
193 gfx::Transform result
;
194 result
.RotateAbout(axis_
, ValueBetween(t
, start_degrees_
, end_degrees_
));
198 ///////////////////////////////////////////////////////////////////////////////
202 InterpolatedScale::InterpolatedScale(float start_scale
, float end_scale
)
203 : InterpolatedTransform(),
204 start_scale_(gfx::Point3F(start_scale
, start_scale
, start_scale
)),
205 end_scale_(gfx::Point3F(end_scale
, end_scale
, end_scale
)) {
208 InterpolatedScale::InterpolatedScale(float start_scale
, float end_scale
,
209 float start_time
, float end_time
)
210 : InterpolatedTransform(start_time
, end_time
),
211 start_scale_(gfx::Point3F(start_scale
, start_scale
, start_scale
)),
212 end_scale_(gfx::Point3F(end_scale
, end_scale
, end_scale
)) {
215 InterpolatedScale::InterpolatedScale(const gfx::Point3F
& start_scale
,
216 const gfx::Point3F
& end_scale
)
217 : InterpolatedTransform(),
218 start_scale_(start_scale
),
219 end_scale_(end_scale
) {
222 InterpolatedScale::InterpolatedScale(const gfx::Point3F
& start_scale
,
223 const gfx::Point3F
& end_scale
,
226 : InterpolatedTransform(start_time
, end_time
),
227 start_scale_(start_scale
),
228 end_scale_(end_scale
) {
231 InterpolatedScale::~InterpolatedScale() {}
233 gfx::Transform
InterpolatedScale::InterpolateButDoNotCompose(float t
) const {
234 gfx::Transform result
;
235 float scale_x
= ValueBetween(t
, start_scale_
.x(), end_scale_
.x());
236 float scale_y
= ValueBetween(t
, start_scale_
.y(), end_scale_
.y());
237 // TODO(vollick) 3d xforms.
238 result
.Scale(scale_x
, scale_y
);
242 ///////////////////////////////////////////////////////////////////////////////
243 // InterpolatedTranslation
246 InterpolatedTranslation::InterpolatedTranslation(const gfx::Point
& start_pos
,
247 const gfx::Point
& end_pos
)
248 : InterpolatedTransform(),
249 start_pos_(start_pos
),
253 InterpolatedTranslation::InterpolatedTranslation(const gfx::Point
& start_pos
,
254 const gfx::Point
& end_pos
,
257 : InterpolatedTransform(start_time
, end_time
),
258 start_pos_(start_pos
),
262 InterpolatedTranslation::~InterpolatedTranslation() {}
265 InterpolatedTranslation::InterpolateButDoNotCompose(float t
) const {
266 gfx::Transform result
;
267 // TODO(vollick) 3d xforms.
268 result
.Translate(ValueBetween(t
, start_pos_
.x(), end_pos_
.x()),
269 ValueBetween(t
, start_pos_
.y(), end_pos_
.y()));
273 ///////////////////////////////////////////////////////////////////////////////
274 // InterpolatedConstantTransform
277 InterpolatedConstantTransform::InterpolatedConstantTransform(
278 const gfx::Transform
& transform
)
279 : InterpolatedTransform(),
280 transform_(transform
) {
284 InterpolatedConstantTransform::InterpolateButDoNotCompose(float t
) const {
288 InterpolatedConstantTransform::~InterpolatedConstantTransform() {}
290 ///////////////////////////////////////////////////////////////////////////////
291 // InterpolatedTransformAboutPivot
294 InterpolatedTransformAboutPivot::InterpolatedTransformAboutPivot(
295 const gfx::Point
& pivot
,
296 InterpolatedTransform
* transform
)
297 : InterpolatedTransform() {
298 Init(pivot
, transform
);
301 InterpolatedTransformAboutPivot::InterpolatedTransformAboutPivot(
302 const gfx::Point
& pivot
,
303 InterpolatedTransform
* transform
,
306 : InterpolatedTransform() {
307 Init(pivot
, transform
);
310 InterpolatedTransformAboutPivot::~InterpolatedTransformAboutPivot() {}
313 InterpolatedTransformAboutPivot::InterpolateButDoNotCompose(float t
) const {
314 if (transform_
.get()) {
315 return transform_
->Interpolate(t
);
317 return gfx::Transform();
320 void InterpolatedTransformAboutPivot::Init(const gfx::Point
& pivot
,
321 InterpolatedTransform
* xform
) {
322 gfx::Transform to_pivot
;
323 gfx::Transform from_pivot
;
324 to_pivot
.Translate(-pivot
.x(), -pivot
.y());
325 from_pivot
.Translate(pivot
.x(), pivot
.y());
327 scoped_ptr
<InterpolatedTransform
> pre_transform(
328 new InterpolatedConstantTransform(to_pivot
));
329 scoped_ptr
<InterpolatedTransform
> post_transform(
330 new InterpolatedConstantTransform(from_pivot
));
332 pre_transform
->SetChild(xform
);
333 xform
->SetChild(post_transform
.release());
334 transform_
.reset(pre_transform
.release());
337 InterpolatedMatrixTransform::InterpolatedMatrixTransform(
338 const gfx::Transform
& start_transform
,
339 const gfx::Transform
& end_transform
)
340 : InterpolatedTransform() {
341 Init(start_transform
, end_transform
);
344 InterpolatedMatrixTransform::InterpolatedMatrixTransform(
345 const gfx::Transform
& start_transform
,
346 const gfx::Transform
& end_transform
,
349 : InterpolatedTransform() {
350 Init(start_transform
, end_transform
);
353 InterpolatedMatrixTransform::~InterpolatedMatrixTransform() {}
356 InterpolatedMatrixTransform::InterpolateButDoNotCompose(float t
) const {
357 gfx::DecomposedTransform blended
;
358 bool success
= gfx::BlendDecomposedTransforms(&blended
,
363 return gfx::ComposeTransform(blended
);
366 void InterpolatedMatrixTransform::Init(const gfx::Transform
& start_transform
,
367 const gfx::Transform
& end_transform
) {
368 bool success
= gfx::DecomposeTransform(&start_decomp_
, start_transform
);
370 success
= gfx::DecomposeTransform(&end_decomp_
, end_transform
);