- add sources.
[platform/framework/web/crosswalk.git] / src / ui / gfx / interpolated_transform.cc
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
20 bool IsMultipleOfNinetyDegrees(double degrees) {
21   double remainder = fabs(fmod(degrees, 90.0));
22   return remainder < EPSILON || 90.0 - remainder < EPSILON;
23 }
24
25 bool IsApproximatelyZero(double value) {
26   return fabs(value) < EPSILON;
27 }
28
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
33 // either 0, 1 or -1.
34 bool MassageRotationIfMultipleOfNinetyDegrees(gfx::Transform* rotation,
35                                               float degrees) {
36   if (!IsMultipleOfNinetyDegrees(degrees) || !rotation)
37     return false;
38
39   gfx::Transform transform;
40   SkMatrix44& m = transform.matrix();
41   float degrees_by_ninety = degrees / 90.0f;
42
43   int n = static_cast<int>(degrees_by_ninety > 0
44       ? floor(degrees_by_ninety + 0.5f)
45       : ceil(degrees_by_ninety - 0.5f));
46
47   n %= 4;
48   if (n < 0)
49     n += 4;
50
51   // n should now be in the range [0, 3]
52   if (n == 1) {
53     m.set3x3( 0,  1,  0,
54              -1,  0,  0,
55               0,  0,  1);
56   } else if (n == 2) {
57     m.set3x3(-1,  0,  0,
58               0, -1,  0,
59               0,  0,  1);
60   } else if (n == 3) {
61     m.set3x3( 0, -1,  0,
62               1,  0,  0,
63               0,  0,  1);
64   }
65
66   *rotation = transform;
67   return true;
68 }
69
70 } // namespace
71
72 namespace ui {
73
74 ///////////////////////////////////////////////////////////////////////////////
75 // InterpolatedTransform
76 //
77
78 InterpolatedTransform::InterpolatedTransform()
79     : start_time_(0.0f),
80       end_time_(1.0f),
81       reversed_(false) {
82 }
83
84 InterpolatedTransform::InterpolatedTransform(float start_time,
85                                              float end_time)
86     : start_time_(start_time),
87       end_time_(end_time),
88       reversed_(false) {
89 }
90
91 InterpolatedTransform::~InterpolatedTransform() {}
92
93 gfx::Transform InterpolatedTransform::Interpolate(float t) const {
94   if (reversed_)
95     t = 1.0f - t;
96   gfx::Transform result = InterpolateButDoNotCompose(t);
97   if (child_.get()) {
98     result.ConcatTransform(child_->Interpolate(t));
99   }
100   return result;
101 }
102
103 void InterpolatedTransform::SetChild(InterpolatedTransform* child) {
104   child_.reset(child);
105 }
106
107 inline float InterpolatedTransform::ValueBetween(float time,
108                                                  float start_value,
109                                                  float end_value) const {
110   // can't handle NaN
111   DCHECK(time == time && start_time_ == start_time_ && end_time_ == end_time_);
112   if (time != time || start_time_ != start_time_ || end_time_ != end_time_)
113     return start_value;
114
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_);
120
121   if (time < start_time_)
122     return start_value;
123
124   if (time >= end_time_)
125     return end_value;
126
127   float t = (time - start_time_) / (end_time_ - start_time_);
128   return static_cast<float>(
129       gfx::Tween::DoubleValueBetween(t, start_value, end_value));
130 }
131
132 ///////////////////////////////////////////////////////////////////////////////
133 // InterpolatedRotation
134 //
135
136 InterpolatedRotation::InterpolatedRotation(float start_degrees,
137                                            float end_degrees)
138     : InterpolatedTransform(),
139       start_degrees_(start_degrees),
140       end_degrees_(end_degrees) {
141 }
142
143 InterpolatedRotation::InterpolatedRotation(float start_degrees,
144                                            float end_degrees,
145                                            float start_time,
146                                            float end_time)
147     : InterpolatedTransform(start_time, end_time),
148       start_degrees_(start_degrees),
149       end_degrees_(end_degrees) {
150 }
151
152 InterpolatedRotation::~InterpolatedRotation() {}
153
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);
160   return result;
161 }
162
163 ///////////////////////////////////////////////////////////////////////////////
164 // InterpolatedAxisAngleRotation
165 //
166
167 InterpolatedAxisAngleRotation::InterpolatedAxisAngleRotation(
168     const gfx::Vector3dF& axis,
169     float start_degrees,
170     float end_degrees)
171     : InterpolatedTransform(),
172       axis_(axis),
173       start_degrees_(start_degrees),
174       end_degrees_(end_degrees) {
175 }
176
177 InterpolatedAxisAngleRotation::InterpolatedAxisAngleRotation(
178     const gfx::Vector3dF& axis,
179     float start_degrees,
180     float end_degrees,
181     float start_time,
182     float end_time)
183     : InterpolatedTransform(start_time, end_time),
184       axis_(axis),
185       start_degrees_(start_degrees),
186       end_degrees_(end_degrees) {
187 }
188
189 InterpolatedAxisAngleRotation::~InterpolatedAxisAngleRotation() {}
190
191 gfx::Transform
192 InterpolatedAxisAngleRotation::InterpolateButDoNotCompose(float t) const {
193   gfx::Transform result;
194   result.RotateAbout(axis_, ValueBetween(t, start_degrees_, end_degrees_));
195   return result;
196 }
197
198 ///////////////////////////////////////////////////////////////////////////////
199 // InterpolatedScale
200 //
201
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)) {
206 }
207
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)) {
213 }
214
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) {
220 }
221
222 InterpolatedScale::InterpolatedScale(const gfx::Point3F& start_scale,
223                                      const gfx::Point3F& end_scale,
224                                      float start_time,
225                                      float end_time)
226     : InterpolatedTransform(start_time, end_time),
227       start_scale_(start_scale),
228       end_scale_(end_scale) {
229 }
230
231 InterpolatedScale::~InterpolatedScale() {}
232
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);
239   return result;
240 }
241
242 ///////////////////////////////////////////////////////////////////////////////
243 // InterpolatedTranslation
244 //
245
246 InterpolatedTranslation::InterpolatedTranslation(const gfx::Point& start_pos,
247                                                  const gfx::Point& end_pos)
248     : InterpolatedTransform(),
249       start_pos_(start_pos),
250       end_pos_(end_pos) {
251 }
252
253 InterpolatedTranslation::InterpolatedTranslation(const gfx::Point& start_pos,
254                                                  const gfx::Point& end_pos,
255                                                  float start_time,
256                                                  float end_time)
257     : InterpolatedTransform(start_time, end_time),
258       start_pos_(start_pos),
259       end_pos_(end_pos) {
260 }
261
262 InterpolatedTranslation::~InterpolatedTranslation() {}
263
264 gfx::Transform
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()));
270   return result;
271 }
272
273 ///////////////////////////////////////////////////////////////////////////////
274 // InterpolatedConstantTransform
275 //
276
277 InterpolatedConstantTransform::InterpolatedConstantTransform(
278   const gfx::Transform& transform)
279     : InterpolatedTransform(),
280   transform_(transform) {
281 }
282
283 gfx::Transform
284 InterpolatedConstantTransform::InterpolateButDoNotCompose(float t) const {
285   return transform_;
286 }
287
288 InterpolatedConstantTransform::~InterpolatedConstantTransform() {}
289
290 ///////////////////////////////////////////////////////////////////////////////
291 // InterpolatedTransformAboutPivot
292 //
293
294 InterpolatedTransformAboutPivot::InterpolatedTransformAboutPivot(
295   const gfx::Point& pivot,
296   InterpolatedTransform* transform)
297     : InterpolatedTransform() {
298   Init(pivot, transform);
299 }
300
301 InterpolatedTransformAboutPivot::InterpolatedTransformAboutPivot(
302   const gfx::Point& pivot,
303   InterpolatedTransform* transform,
304   float start_time,
305   float end_time)
306     : InterpolatedTransform() {
307   Init(pivot, transform);
308 }
309
310 InterpolatedTransformAboutPivot::~InterpolatedTransformAboutPivot() {}
311
312 gfx::Transform
313 InterpolatedTransformAboutPivot::InterpolateButDoNotCompose(float t) const {
314   if (transform_.get()) {
315     return transform_->Interpolate(t);
316   }
317   return gfx::Transform();
318 }
319
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());
326
327   scoped_ptr<InterpolatedTransform> pre_transform(
328     new InterpolatedConstantTransform(to_pivot));
329   scoped_ptr<InterpolatedTransform> post_transform(
330     new InterpolatedConstantTransform(from_pivot));
331
332   pre_transform->SetChild(xform);
333   xform->SetChild(post_transform.release());
334   transform_.reset(pre_transform.release());
335 }
336
337 InterpolatedMatrixTransform::InterpolatedMatrixTransform(
338     const gfx::Transform& start_transform,
339     const gfx::Transform& end_transform)
340     : InterpolatedTransform() {
341   Init(start_transform, end_transform);
342 }
343
344 InterpolatedMatrixTransform::InterpolatedMatrixTransform(
345     const gfx::Transform& start_transform,
346     const gfx::Transform& end_transform,
347     float start_time,
348     float end_time)
349     : InterpolatedTransform() {
350   Init(start_transform, end_transform);
351 }
352
353 InterpolatedMatrixTransform::~InterpolatedMatrixTransform() {}
354
355 gfx::Transform
356 InterpolatedMatrixTransform::InterpolateButDoNotCompose(float t) const {
357   gfx::DecomposedTransform blended;
358   bool success = gfx::BlendDecomposedTransforms(&blended,
359                                                 end_decomp_,
360                                                 start_decomp_,
361                                                 t);
362   DCHECK(success);
363   return gfx::ComposeTransform(blended);
364 }
365
366 void InterpolatedMatrixTransform::Init(const gfx::Transform& start_transform,
367                                        const gfx::Transform& end_transform) {
368   bool success = gfx::DecomposeTransform(&start_decomp_, start_transform);
369   DCHECK(success);
370   success = gfx::DecomposeTransform(&end_decomp_, end_transform);
371   DCHECK(success);
372 }
373
374 } // namespace ui