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);