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 // TODO(vollick) 3d xforms.
234 result
.Scale(scale_x
, scale_y
);
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() {}
261 InterpolatedTranslation::InterpolateButDoNotCompose(float t
) const {
262 gfx::Transform result
;
263 // TODO(vollick) 3d xforms.
264 result
.Translate(ValueBetween(t
, start_pos_
.x(), end_pos_
.x()),
265 ValueBetween(t
, start_pos_
.y(), end_pos_
.y()));
269 ///////////////////////////////////////////////////////////////////////////////
270 // InterpolatedConstantTransform
273 InterpolatedConstantTransform::InterpolatedConstantTransform(
274 const gfx::Transform
& transform
)
275 : InterpolatedTransform(),
276 transform_(transform
) {
280 InterpolatedConstantTransform::InterpolateButDoNotCompose(float t
) const {
284 InterpolatedConstantTransform::~InterpolatedConstantTransform() {}
286 ///////////////////////////////////////////////////////////////////////////////
287 // InterpolatedTransformAboutPivot
290 InterpolatedTransformAboutPivot::InterpolatedTransformAboutPivot(
291 const gfx::Point
& pivot
,
292 InterpolatedTransform
* transform
)
293 : InterpolatedTransform() {
294 Init(pivot
, transform
);
297 InterpolatedTransformAboutPivot::InterpolatedTransformAboutPivot(
298 const gfx::Point
& pivot
,
299 InterpolatedTransform
* transform
,
302 : InterpolatedTransform() {
303 Init(pivot
, transform
);
306 InterpolatedTransformAboutPivot::~InterpolatedTransformAboutPivot() {}
309 InterpolatedTransformAboutPivot::InterpolateButDoNotCompose(float t
) const {
310 if (transform_
.get()) {
311 return transform_
->Interpolate(t
);
313 return gfx::Transform();
316 void InterpolatedTransformAboutPivot::Init(const gfx::Point
& pivot
,
317 InterpolatedTransform
* xform
) {
318 gfx::Transform to_pivot
;
319 gfx::Transform from_pivot
;
320 to_pivot
.Translate(-pivot
.x(), -pivot
.y());
321 from_pivot
.Translate(pivot
.x(), pivot
.y());
323 scoped_ptr
<InterpolatedTransform
> pre_transform(
324 new InterpolatedConstantTransform(to_pivot
));
325 scoped_ptr
<InterpolatedTransform
> post_transform(
326 new InterpolatedConstantTransform(from_pivot
));
328 pre_transform
->SetChild(xform
);
329 xform
->SetChild(post_transform
.release());
330 transform_
.reset(pre_transform
.release());
333 InterpolatedMatrixTransform::InterpolatedMatrixTransform(
334 const gfx::Transform
& start_transform
,
335 const gfx::Transform
& end_transform
)
336 : InterpolatedTransform() {
337 Init(start_transform
, end_transform
);
340 InterpolatedMatrixTransform::InterpolatedMatrixTransform(
341 const gfx::Transform
& start_transform
,
342 const gfx::Transform
& end_transform
,
345 : InterpolatedTransform() {
346 Init(start_transform
, end_transform
);
349 InterpolatedMatrixTransform::~InterpolatedMatrixTransform() {}
352 InterpolatedMatrixTransform::InterpolateButDoNotCompose(float t
) const {
353 gfx::DecomposedTransform blended
;
354 bool success
= gfx::BlendDecomposedTransforms(&blended
,
359 return gfx::ComposeTransform(blended
);
362 void InterpolatedMatrixTransform::Init(const gfx::Transform
& start_transform
,
363 const gfx::Transform
& end_transform
) {
364 bool success
= gfx::DecomposeTransform(&start_decomp_
, start_transform
);
366 success
= gfx::DecomposeTransform(&end_decomp_
, end_transform
);