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.
4
5 #include "ui/gfx/interpolated_transform.h"
6
7 #include <cmath>
8
9 #ifndef M_PI
10 #define M_PI 3.14159265358979323846
11 #endif
12
13 #include "base/logging.h"
14 #include "ui/gfx/animation/tween.h"
15
16 namespace {
17
18 static const double EPSILON = 1e-6;
19
IsMultipleOfNinetyDegrees(double degrees)20 bool IsMultipleOfNinetyDegrees(double degrees) {
21 double remainder = fabs(fmod(degrees, 90.0));
22 return remainder < EPSILON || 90.0 - remainder < EPSILON;
23 }
24
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
29 // either 0, 1 or -1.
MassageRotationIfMultipleOfNinetyDegrees(gfx::Transform * rotation,float degrees)30 bool MassageRotationIfMultipleOfNinetyDegrees(gfx::Transform* rotation,
31 float degrees) {
32 if (!IsMultipleOfNinetyDegrees(degrees) || !rotation)
33 return false;
34
35 gfx::Transform transform;
36 SkMatrix44& m = transform.matrix();
37 float degrees_by_ninety = degrees / 90.0f;
38
39 int n = static_cast<int>(degrees_by_ninety > 0
40 ? floor(degrees_by_ninety + 0.5f)
41 : ceil(degrees_by_ninety - 0.5f));
42
43 n %= 4;
44 if (n < 0)
45 n += 4;
46
47 // n should now be in the range [0, 3]
48 if (n == 1) {
49 m.set3x3( 0, 1, 0,
50 -1, 0, 0,
51 0, 0, 1);
52 } else if (n == 2) {
53 m.set3x3(-1, 0, 0,
54 0, -1, 0,
55 0, 0, 1);
56 } else if (n == 3) {
57 m.set3x3( 0, -1, 0,
58 1, 0, 0,
59 0, 0, 1);
60 }
61
62 *rotation = transform;
63 return true;
64 }
65
66 } // namespace
67
68 namespace ui {
69
70 ///////////////////////////////////////////////////////////////////////////////
71 // InterpolatedTransform
72 //
73
InterpolatedTransform()74 InterpolatedTransform::InterpolatedTransform()
75 : start_time_(0.0f),
76 end_time_(1.0f),
77 reversed_(false) {
78 }
79
InterpolatedTransform(float start_time,float end_time)80 InterpolatedTransform::InterpolatedTransform(float start_time,
81 float end_time)
82 : start_time_(start_time),
83 end_time_(end_time),
84 reversed_(false) {
85 }
86
~InterpolatedTransform()87 InterpolatedTransform::~InterpolatedTransform() {}
88
Interpolate(float t) const89 gfx::Transform InterpolatedTransform::Interpolate(float t) const {
90 if (reversed_)
91 t = 1.0f - t;
92 gfx::Transform result = InterpolateButDoNotCompose(t);
93 if (child_.get()) {
94 result.ConcatTransform(child_->Interpolate(t));
95 }
96 return result;
97 }
98
SetChild(InterpolatedTransform * child)99 void InterpolatedTransform::SetChild(InterpolatedTransform* child) {
100 child_.reset(child);
101 }
102
ValueBetween(float time,float start_value,float end_value) const103 inline float InterpolatedTransform::ValueBetween(float time,
104 float start_value,
105 float end_value) const {
106 // can't handle NaN
107 DCHECK(time == time && start_time_ == start_time_ && end_time_ == end_time_);
108 if (time != time || start_time_ != start_time_ || end_time_ != end_time_)
109 return start_value;
110
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_);
116
117 if (time < start_time_)
118 return start_value;
119
120 if (time >= end_time_)
121 return end_value;
122
123 float t = (time - start_time_) / (end_time_ - start_time_);
124 return static_cast<float>(
125 gfx::Tween::DoubleValueBetween(t, start_value, end_value));
126 }
127
128 ///////////////////////////////////////////////////////////////////////////////
129 // InterpolatedRotation
130 //
131
InterpolatedRotation(float start_degrees,float end_degrees)132 InterpolatedRotation::InterpolatedRotation(float start_degrees,
133 float end_degrees)
134 : InterpolatedTransform(),
135 start_degrees_(start_degrees),
136 end_degrees_(end_degrees) {
137 }
138
InterpolatedRotation(float start_degrees,float end_degrees,float start_time,float end_time)139 InterpolatedRotation::InterpolatedRotation(float start_degrees,
140 float end_degrees,
141 float start_time,
142 float end_time)
143 : InterpolatedTransform(start_time, end_time),
144 start_degrees_(start_degrees),
145 end_degrees_(end_degrees) {
146 }
147
~InterpolatedRotation()148 InterpolatedRotation::~InterpolatedRotation() {}
149
InterpolateButDoNotCompose(float t) const150 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);
156 return result;
157 }
158
159 ///////////////////////////////////////////////////////////////////////////////
160 // InterpolatedAxisAngleRotation
161 //
162
InterpolatedAxisAngleRotation(const gfx::Vector3dF & axis,float start_degrees,float end_degrees)163 InterpolatedAxisAngleRotation::InterpolatedAxisAngleRotation(
164 const gfx::Vector3dF& axis,
165 float start_degrees,
166 float end_degrees)
167 : InterpolatedTransform(),
168 axis_(axis),
169 start_degrees_(start_degrees),
170 end_degrees_(end_degrees) {
171 }
172
InterpolatedAxisAngleRotation(const gfx::Vector3dF & axis,float start_degrees,float end_degrees,float start_time,float end_time)173 InterpolatedAxisAngleRotation::InterpolatedAxisAngleRotation(
174 const gfx::Vector3dF& axis,
175 float start_degrees,
176 float end_degrees,
177 float start_time,
178 float end_time)
179 : InterpolatedTransform(start_time, end_time),
180 axis_(axis),
181 start_degrees_(start_degrees),
182 end_degrees_(end_degrees) {
183 }
184
~InterpolatedAxisAngleRotation()185 InterpolatedAxisAngleRotation::~InterpolatedAxisAngleRotation() {}
186
187 gfx::Transform
InterpolateButDoNotCompose(float t) const188 InterpolatedAxisAngleRotation::InterpolateButDoNotCompose(float t) const {
189 gfx::Transform result;
190 result.RotateAbout(axis_, ValueBetween(t, start_degrees_, end_degrees_));
191 return result;
192 }
193
194 ///////////////////////////////////////////////////////////////////////////////
195 // InterpolatedScale
196 //
197
InterpolatedScale(float start_scale,float end_scale)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)) {
202 }
203
InterpolatedScale(float start_scale,float end_scale,float start_time,float end_time)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)) {
209 }
210
InterpolatedScale(const gfx::Point3F & start_scale,const gfx::Point3F & 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) {
216 }
217
InterpolatedScale(const gfx::Point3F & start_scale,const gfx::Point3F & end_scale,float start_time,float end_time)218 InterpolatedScale::InterpolatedScale(const gfx::Point3F& start_scale,
219 const gfx::Point3F& end_scale,
220 float start_time,
221 float end_time)
222 : InterpolatedTransform(start_time, end_time),
223 start_scale_(start_scale),
224 end_scale_(end_scale) {
225 }
226
~InterpolatedScale()227 InterpolatedScale::~InterpolatedScale() {}
228
InterpolateButDoNotCompose(float t) const229 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);
235 return result;
236 }
237
238 ///////////////////////////////////////////////////////////////////////////////
239 // InterpolatedTranslation
240 //
241
InterpolatedTranslation(const gfx::Point & start_pos,const gfx::Point & end_pos)242 InterpolatedTranslation::InterpolatedTranslation(const gfx::Point& start_pos,
243 const gfx::Point& end_pos)
244 : InterpolatedTransform(),
245 start_pos_(start_pos),
246 end_pos_(end_pos) {
247 }
248
InterpolatedTranslation(const gfx::Point & start_pos,const gfx::Point & end_pos,float start_time,float end_time)249 InterpolatedTranslation::InterpolatedTranslation(const gfx::Point& start_pos,
250 const gfx::Point& end_pos,
251 float start_time,
252 float end_time)
253 : InterpolatedTransform(start_time, end_time),
254 start_pos_(start_pos),
255 end_pos_(end_pos) {
256 }
257
~InterpolatedTranslation()258 InterpolatedTranslation::~InterpolatedTranslation() {}
259
260 gfx::Transform
InterpolateButDoNotCompose(float t) const261 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()));
266 return result;
267 }
268
269 ///////////////////////////////////////////////////////////////////////////////
270 // InterpolatedConstantTransform
271 //
272
InterpolatedConstantTransform(const gfx::Transform & transform)273 InterpolatedConstantTransform::InterpolatedConstantTransform(
274 const gfx::Transform& transform)
275 : InterpolatedTransform(),
276 transform_(transform) {
277 }
278
279 gfx::Transform
InterpolateButDoNotCompose(float t) const280 InterpolatedConstantTransform::InterpolateButDoNotCompose(float t) const {
281 return transform_;
282 }
283
~InterpolatedConstantTransform()284 InterpolatedConstantTransform::~InterpolatedConstantTransform() {}
285
286 ///////////////////////////////////////////////////////////////////////////////
287 // InterpolatedTransformAboutPivot
288 //
289
InterpolatedTransformAboutPivot(const gfx::Point & pivot,InterpolatedTransform * transform)290 InterpolatedTransformAboutPivot::InterpolatedTransformAboutPivot(
291 const gfx::Point& pivot,
292 InterpolatedTransform* transform)
293 : InterpolatedTransform() {
294 Init(pivot, transform);
295 }
296
InterpolatedTransformAboutPivot(const gfx::Point & pivot,InterpolatedTransform * transform,float start_time,float end_time)297 InterpolatedTransformAboutPivot::InterpolatedTransformAboutPivot(
298 const gfx::Point& pivot,
299 InterpolatedTransform* transform,
300 float start_time,
301 float end_time)
302 : InterpolatedTransform() {
303 Init(pivot, transform);
304 }
305
~InterpolatedTransformAboutPivot()306 InterpolatedTransformAboutPivot::~InterpolatedTransformAboutPivot() {}
307
308 gfx::Transform
InterpolateButDoNotCompose(float t) const309 InterpolatedTransformAboutPivot::InterpolateButDoNotCompose(float t) const {
310 if (transform_.get()) {
311 return transform_->Interpolate(t);
312 }
313 return gfx::Transform();
314 }
315
Init(const gfx::Point & pivot,InterpolatedTransform * xform)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());
322
323 scoped_ptr<InterpolatedTransform> pre_transform(
324 new InterpolatedConstantTransform(to_pivot));
325 scoped_ptr<InterpolatedTransform> post_transform(
326 new InterpolatedConstantTransform(from_pivot));
327
328 pre_transform->SetChild(xform);
329 xform->SetChild(post_transform.release());
330 transform_.reset(pre_transform.release());
331 }
332
InterpolatedMatrixTransform(const gfx::Transform & start_transform,const gfx::Transform & end_transform)333 InterpolatedMatrixTransform::InterpolatedMatrixTransform(
334 const gfx::Transform& start_transform,
335 const gfx::Transform& end_transform)
336 : InterpolatedTransform() {
337 Init(start_transform, end_transform);
338 }
339
InterpolatedMatrixTransform(const gfx::Transform & start_transform,const gfx::Transform & end_transform,float start_time,float end_time)340 InterpolatedMatrixTransform::InterpolatedMatrixTransform(
341 const gfx::Transform& start_transform,
342 const gfx::Transform& end_transform,
343 float start_time,
344 float end_time)
345 : InterpolatedTransform() {
346 Init(start_transform, end_transform);
347 }
348
~InterpolatedMatrixTransform()349 InterpolatedMatrixTransform::~InterpolatedMatrixTransform() {}
350
351 gfx::Transform
InterpolateButDoNotCompose(float t) const352 InterpolatedMatrixTransform::InterpolateButDoNotCompose(float t) const {
353 gfx::DecomposedTransform blended;
354 bool success = gfx::BlendDecomposedTransforms(&blended,
355 end_decomp_,
356 start_decomp_,
357 t);
358 DCHECK(success);
359 return gfx::ComposeTransform(blended);
360 }
361
Init(const gfx::Transform & start_transform,const gfx::Transform & end_transform)362 void InterpolatedMatrixTransform::Init(const gfx::Transform& start_transform,
363 const gfx::Transform& end_transform) {
364 bool success = gfx::DecomposeTransform(&start_decomp_, start_transform);
365 DCHECK(success);
366 success = gfx::DecomposeTransform(&end_decomp_, end_transform);
367 DCHECK(success);
368 }
369
370 } // namespace ui
371