- add sources.
[platform/framework/web/crosswalk.git] / src / cc / animation / transform_operation.cc
1 // Copyright 2013 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 // Needed on Windows to get |M_PI| from <cmath>
6 #ifdef _WIN32
7 #define _USE_MATH_DEFINES
8 #endif
9
10 #include <algorithm>
11 #include <cmath>
12 #include <limits>
13
14 #include "base/logging.h"
15 #include "cc/animation/transform_operation.h"
16 #include "ui/gfx/box_f.h"
17 #include "ui/gfx/vector3d_f.h"
18
19 namespace {
20 const SkMScalar kAngleEpsilon = 1e-4f;
21 }
22
23 namespace cc {
24
25 bool TransformOperation::IsIdentity() const {
26   return matrix.IsIdentity();
27 }
28
29 static bool IsOperationIdentity(const TransformOperation* operation) {
30   return !operation || operation->IsIdentity();
31 }
32
33 static bool ShareSameAxis(const TransformOperation* from,
34                           const TransformOperation* to,
35                           SkMScalar* axis_x,
36                           SkMScalar* axis_y,
37                           SkMScalar* axis_z,
38                           SkMScalar* angle_from) {
39   if (IsOperationIdentity(from) && IsOperationIdentity(to))
40     return false;
41
42   if (IsOperationIdentity(from) && !IsOperationIdentity(to)) {
43     *axis_x = to->rotate.axis.x;
44     *axis_y = to->rotate.axis.y;
45     *axis_z = to->rotate.axis.z;
46     *angle_from = 0;
47     return true;
48   }
49
50   if (!IsOperationIdentity(from) && IsOperationIdentity(to)) {
51     *axis_x = from->rotate.axis.x;
52     *axis_y = from->rotate.axis.y;
53     *axis_z = from->rotate.axis.z;
54     *angle_from = from->rotate.angle;
55     return true;
56   }
57
58   SkMScalar length_2 = from->rotate.axis.x * from->rotate.axis.x +
59                        from->rotate.axis.y * from->rotate.axis.y +
60                        from->rotate.axis.z * from->rotate.axis.z;
61   SkMScalar other_length_2 = to->rotate.axis.x * to->rotate.axis.x +
62                              to->rotate.axis.y * to->rotate.axis.y +
63                              to->rotate.axis.z * to->rotate.axis.z;
64
65   if (length_2 <= kAngleEpsilon || other_length_2 <= kAngleEpsilon)
66     return false;
67
68   SkMScalar dot = to->rotate.axis.x * from->rotate.axis.x +
69                   to->rotate.axis.y * from->rotate.axis.y +
70                   to->rotate.axis.z * from->rotate.axis.z;
71   SkMScalar error =
72       std::abs(SK_MScalar1 - (dot * dot) / (length_2 * other_length_2));
73   bool result = error < kAngleEpsilon;
74   if (result) {
75     *axis_x = to->rotate.axis.x;
76     *axis_y = to->rotate.axis.y;
77     *axis_z = to->rotate.axis.z;
78     // If the axes are pointing in opposite directions, we need to reverse
79     // the angle.
80     *angle_from = dot > 0 ? from->rotate.angle : -from->rotate.angle;
81   }
82   return result;
83 }
84
85 static SkMScalar BlendSkMScalars(SkMScalar from,
86                                  SkMScalar to,
87                                  SkMScalar progress) {
88   return from * (1 - progress) + to * progress;
89 }
90
91 bool TransformOperation::BlendTransformOperations(
92     const TransformOperation* from,
93     const TransformOperation* to,
94     SkMScalar progress,
95     gfx::Transform* result) {
96   if (IsOperationIdentity(from) && IsOperationIdentity(to))
97     return true;
98
99   TransformOperation::Type interpolation_type =
100       TransformOperation::TransformOperationIdentity;
101   if (IsOperationIdentity(to))
102     interpolation_type = from->type;
103   else
104     interpolation_type = to->type;
105
106   switch (interpolation_type) {
107   case TransformOperation::TransformOperationTranslate: {
108     SkMScalar from_x = IsOperationIdentity(from) ? 0 : from->translate.x;
109     SkMScalar from_y = IsOperationIdentity(from) ? 0 : from->translate.y;
110     SkMScalar from_z = IsOperationIdentity(from) ? 0 : from->translate.z;
111     SkMScalar to_x = IsOperationIdentity(to) ? 0 : to->translate.x;
112     SkMScalar to_y = IsOperationIdentity(to) ? 0 : to->translate.y;
113     SkMScalar to_z = IsOperationIdentity(to) ? 0 : to->translate.z;
114     result->Translate3d(BlendSkMScalars(from_x, to_x, progress),
115                         BlendSkMScalars(from_y, to_y, progress),
116                         BlendSkMScalars(from_z, to_z, progress));
117     break;
118   }
119   case TransformOperation::TransformOperationRotate: {
120     SkMScalar axis_x = 0;
121     SkMScalar axis_y = 0;
122     SkMScalar axis_z = 1;
123     SkMScalar from_angle = 0;
124     SkMScalar to_angle = IsOperationIdentity(to) ? 0 : to->rotate.angle;
125     if (ShareSameAxis(from, to, &axis_x, &axis_y, &axis_z, &from_angle)) {
126       result->RotateAbout(gfx::Vector3dF(axis_x, axis_y, axis_z),
127                           BlendSkMScalars(from_angle, to_angle, progress));
128     } else {
129       gfx::Transform to_matrix;
130       if (!IsOperationIdentity(to))
131         to_matrix = to->matrix;
132       gfx::Transform from_matrix;
133       if (!IsOperationIdentity(from))
134         from_matrix = from->matrix;
135       *result = to_matrix;
136       if (!result->Blend(from_matrix, progress))
137         return false;
138     }
139     break;
140   }
141   case TransformOperation::TransformOperationScale: {
142     SkMScalar from_x = IsOperationIdentity(from) ? 1 : from->scale.x;
143     SkMScalar from_y = IsOperationIdentity(from) ? 1 : from->scale.y;
144     SkMScalar from_z = IsOperationIdentity(from) ? 1 : from->scale.z;
145     SkMScalar to_x = IsOperationIdentity(to) ? 1 : to->scale.x;
146     SkMScalar to_y = IsOperationIdentity(to) ? 1 : to->scale.y;
147     SkMScalar to_z = IsOperationIdentity(to) ? 1 : to->scale.z;
148     result->Scale3d(BlendSkMScalars(from_x, to_x, progress),
149                     BlendSkMScalars(from_y, to_y, progress),
150                     BlendSkMScalars(from_z, to_z, progress));
151     break;
152   }
153   case TransformOperation::TransformOperationSkew: {
154     SkMScalar from_x = IsOperationIdentity(from) ? 0 : from->skew.x;
155     SkMScalar from_y = IsOperationIdentity(from) ? 0 : from->skew.y;
156     SkMScalar to_x = IsOperationIdentity(to) ? 0 : to->skew.x;
157     SkMScalar to_y = IsOperationIdentity(to) ? 0 : to->skew.y;
158     result->SkewX(BlendSkMScalars(from_x, to_x, progress));
159     result->SkewY(BlendSkMScalars(from_y, to_y, progress));
160     break;
161   }
162   case TransformOperation::TransformOperationPerspective: {
163     SkMScalar from_perspective_depth =
164         IsOperationIdentity(from) ? std::numeric_limits<SkMScalar>::max()
165                                   : from->perspective_depth;
166     SkMScalar to_perspective_depth =
167         IsOperationIdentity(to) ? std::numeric_limits<SkMScalar>::max()
168                                 : to->perspective_depth;
169     if (from_perspective_depth == 0.f || to_perspective_depth == 0.f)
170       return false;
171
172     SkMScalar blended_perspective_depth = BlendSkMScalars(
173         1.f / from_perspective_depth, 1.f / to_perspective_depth, progress);
174
175     if (blended_perspective_depth == 0.f)
176       return false;
177
178     result->ApplyPerspectiveDepth(1.f / blended_perspective_depth);
179     break;
180   }
181   case TransformOperation::TransformOperationMatrix: {
182     gfx::Transform to_matrix;
183     if (!IsOperationIdentity(to))
184       to_matrix = to->matrix;
185     gfx::Transform from_matrix;
186     if (!IsOperationIdentity(from))
187       from_matrix = from->matrix;
188     *result = to_matrix;
189     if (!result->Blend(from_matrix, progress))
190       return false;
191     break;
192   }
193   case TransformOperation::TransformOperationIdentity:
194     // Do nothing.
195     break;
196   }
197
198   return true;
199 }
200
201 static void ApplyScaleToBox(float x_scale,
202                             float y_scale,
203                             float z_scale,
204                             gfx::BoxF* box) {
205   if (x_scale < 0)
206     box->set_x(-box->right());
207   if (y_scale < 0)
208     box->set_y(-box->bottom());
209   if (z_scale < 0)
210     box->set_z(-box->front());
211   box->Scale(std::abs(x_scale), std::abs(y_scale), std::abs(z_scale));
212 }
213
214 static void UnionBoxWithZeroScale(gfx::BoxF* box) {
215   float min_x = std::min(box->x(), 0.f);
216   float min_y = std::min(box->y(), 0.f);
217   float min_z = std::min(box->z(), 0.f);
218   float max_x = std::max(box->right(), 0.f);
219   float max_y = std::max(box->bottom(), 0.f);
220   float max_z = std::max(box->front(), 0.f);
221   *box = gfx::BoxF(
222       min_x, min_y, min_z, max_x - min_x, max_y - min_y, max_z - min_z);
223 }
224
225 // If p = (px, py) is a point in the plane being rotated about (0, 0, nz), this
226 // function computes the angles we would have to rotate from p to get to
227 // (length(p), 0), (-length(p), 0), (0, length(p)), (0, -length(p)). If nz is
228 // negative, these angles will need to be reversed.
229 static void FindCandidatesInPlane(float px,
230                                   float py,
231                                   float nz,
232                                   double* candidates,
233                                   int* num_candidates) {
234   double phi = atan2(px, py);
235   *num_candidates = 4;
236   candidates[0] = phi;
237   for (int i = 1; i < *num_candidates; ++i)
238     candidates[i] = candidates[i - 1] + M_PI_2;
239   if (nz < 0.f) {
240     for (int i = 0; i < *num_candidates; ++i)
241       candidates[i] *= -1.f;
242   }
243 }
244
245 static float RadiansToDegrees(float radians) {
246   return (180.f * radians) / M_PI;
247 }
248
249 static float DegreesToRadians(float degrees) {
250   return (M_PI * degrees) / 180.f;
251 }
252
253 // Div by zero doesn't always result in Inf as you might hope, so we'll do this
254 // explicitly here.
255 static float SafeDivide(float numerator, float denominator) {
256   if (numerator == 0.f)
257     return 0.f;
258
259   if (denominator == 0.f) {
260     return numerator > 0.f ? std::numeric_limits<float>::infinity()
261                            : -std::numeric_limits<float>::infinity();
262   }
263
264   return numerator / denominator;
265 }
266
267 static void BoundingBoxForArc(const gfx::Point3F& point,
268                               const TransformOperation* from,
269                               const TransformOperation* to,
270                               SkMScalar min_progress,
271                               SkMScalar max_progress,
272                               gfx::BoxF* box) {
273   const TransformOperation* exemplar = from ? from : to;
274   gfx::Vector3dF axis(exemplar->rotate.axis.x,
275                       exemplar->rotate.axis.y,
276                       exemplar->rotate.axis.z);
277
278   const bool x_is_zero = axis.x() == 0.f;
279   const bool y_is_zero = axis.y() == 0.f;
280   const bool z_is_zero = axis.z() == 0.f;
281
282   // We will have at most 6 angles to test (excluding from->angle and
283   // to->angle).
284   static const int kMaxNumCandidates = 6;
285   double candidates[kMaxNumCandidates];
286   int num_candidates = kMaxNumCandidates;
287
288   if (x_is_zero && y_is_zero && z_is_zero)
289     return;
290
291   SkMScalar from_angle = from ? from->rotate.angle : 0.f;
292   SkMScalar to_angle = to ? to->rotate.angle : 0.f;
293
294   float min_degrees =
295       SkMScalarToFloat(BlendSkMScalars(from_angle, to_angle, min_progress));
296   float max_degrees =
297       SkMScalarToFloat(BlendSkMScalars(from_angle, to_angle, max_progress));
298   if (max_degrees < min_degrees)
299     std::swap(min_degrees, max_degrees);
300
301   gfx::Transform from_transform;
302   from_transform.RotateAbout(axis, min_degrees);
303   gfx::Transform to_transform;
304   to_transform.RotateAbout(axis, max_degrees);
305
306   *box = gfx::BoxF();
307
308   gfx::Point3F point_rotated_from = point;
309   from_transform.TransformPoint(&point_rotated_from);
310   gfx::Point3F point_rotated_to = point;
311   to_transform.TransformPoint(&point_rotated_to);
312
313   box->set_origin(point_rotated_from);
314   box->ExpandTo(point_rotated_to);
315
316   if (x_is_zero && y_is_zero) {
317     FindCandidatesInPlane(
318         point.x(), point.y(), axis.z(), candidates, &num_candidates);
319   } else if (x_is_zero && z_is_zero) {
320     FindCandidatesInPlane(
321         point.z(), point.x(), axis.y(), candidates, &num_candidates);
322   } else if (y_is_zero && z_is_zero) {
323     FindCandidatesInPlane(
324         point.y(), point.z(), axis.x(), candidates, &num_candidates);
325   } else {
326     gfx::Vector3dF normal = axis;
327     normal.Scale(1.f / normal.Length());
328
329     // First, find center of rotation.
330     gfx::Point3F origin;
331     gfx::Vector3dF to_point = point - origin;
332     gfx::Point3F center =
333         origin + gfx::ScaleVector3d(normal, gfx::DotProduct(to_point, normal));
334
335     // Now we need to find two vectors in the plane of rotation. One pointing
336     // towards point and another, perpendicular vector in the plane.
337     gfx::Vector3dF v1 = point - center;
338     float v1_length = v1.Length();
339     if (v1_length == 0.f)
340       return;
341
342     v1.Scale(1.f / v1_length);
343
344     gfx::Vector3dF v2 = gfx::CrossProduct(normal, v1);
345
346     // Now figure out where (1, 0, 0) and (0, 0, 1) project on the rotation
347     // plane.
348     gfx::Point3F px(1.f, 0.f, 0.f);
349     gfx::Vector3dF to_px = px - center;
350     gfx::Point3F px_projected =
351         px - gfx::ScaleVector3d(normal, gfx::DotProduct(to_px, normal));
352     gfx::Vector3dF vx = px_projected - origin;
353
354     gfx::Point3F pz(0.f, 0.f, 1.f);
355     gfx::Vector3dF to_pz = pz - center;
356     gfx::Point3F pz_projected =
357         pz - ScaleVector3d(normal, gfx::DotProduct(to_pz, normal));
358     gfx::Vector3dF vz = pz_projected - origin;
359
360     double phi_x = atan2(gfx::DotProduct(v2, vx), gfx::DotProduct(v1, vx));
361     double phi_z = atan2(gfx::DotProduct(v2, vz), gfx::DotProduct(v1, vz));
362
363     // NB: it is fine if the denominators here are zero and these values go to
364     // infinity; atan can handle it.
365     double tan_theta1 = SafeDivide(normal.y(), (normal.x() * normal.z()));
366     double tan_theta2 = SafeDivide(-normal.z(), (normal.x() * normal.y()));
367
368     candidates[0] = atan(tan_theta1) + phi_x;
369     candidates[1] = candidates[0] + M_PI;
370     candidates[2] = atan(tan_theta2) + phi_x;
371     candidates[3] = candidates[2] + M_PI;
372     candidates[4] = atan(-tan_theta1) + phi_z;
373     candidates[5] = candidates[4] + M_PI;
374   }
375
376   double min_radians = DegreesToRadians(min_degrees);
377   double max_radians = DegreesToRadians(max_degrees);
378
379   for (int i = 0; i < num_candidates; ++i) {
380     double radians = candidates[i];
381     while (radians < min_radians)
382       radians += 2.0 * M_PI;
383     while (radians > max_radians)
384       radians -= 2.0 * M_PI;
385     if (radians < min_radians)
386       continue;
387
388     gfx::Transform rotation;
389     rotation.RotateAbout(axis, RadiansToDegrees(radians));
390     gfx::Point3F rotated = point;
391     rotation.TransformPoint(&rotated);
392
393     box->ExpandTo(rotated);
394   }
395 }
396
397 bool TransformOperation::BlendedBoundsForBox(const gfx::BoxF& box,
398                                              const TransformOperation* from,
399                                              const TransformOperation* to,
400                                              SkMScalar min_progress,
401                                              SkMScalar max_progress,
402                                              gfx::BoxF* bounds) {
403   bool is_identity_from = IsOperationIdentity(from);
404   bool is_identity_to = IsOperationIdentity(to);
405   if (is_identity_from && is_identity_to) {
406     *bounds = box;
407     return true;
408   }
409
410   TransformOperation::Type interpolation_type =
411       TransformOperation::TransformOperationIdentity;
412   if (is_identity_to)
413     interpolation_type = from->type;
414   else
415     interpolation_type = to->type;
416
417   switch (interpolation_type) {
418     case TransformOperation::TransformOperationTranslate: {
419       SkMScalar from_x, from_y, from_z;
420       if (is_identity_from) {
421         from_x = from_y = from_z = 0.0;
422       } else {
423         from_x = from->translate.x;
424         from_y = from->translate.y;
425         from_z = from->translate.z;
426       }
427       SkMScalar to_x, to_y, to_z;
428       if (is_identity_to) {
429         to_x = to_y = to_z = 0.0;
430       } else {
431         to_x = to->translate.x;
432         to_y = to->translate.y;
433         to_z = to->translate.z;
434       }
435       *bounds = box;
436       *bounds += gfx::Vector3dF(BlendSkMScalars(from_x, to_x, min_progress),
437                                 BlendSkMScalars(from_y, to_y, min_progress),
438                                 BlendSkMScalars(from_z, to_z, min_progress));
439       gfx::BoxF bounds_max = box;
440       bounds_max += gfx::Vector3dF(BlendSkMScalars(from_x, to_x, max_progress),
441                                    BlendSkMScalars(from_y, to_y, max_progress),
442                                    BlendSkMScalars(from_z, to_z, max_progress));
443       bounds->Union(bounds_max);
444       return true;
445     }
446     case TransformOperation::TransformOperationScale: {
447       SkMScalar from_x, from_y, from_z;
448       if (is_identity_from) {
449         from_x = from_y = from_z = 1.0;
450       } else {
451         from_x = from->scale.x;
452         from_y = from->scale.y;
453         from_z = from->scale.z;
454       }
455       SkMScalar to_x, to_y, to_z;
456       if (is_identity_to) {
457         to_x = to_y = to_z = 1.0;
458       } else {
459         to_x = to->scale.x;
460         to_y = to->scale.y;
461         to_z = to->scale.z;
462       }
463       *bounds = box;
464       ApplyScaleToBox(
465           SkMScalarToFloat(BlendSkMScalars(from_x, to_x, min_progress)),
466           SkMScalarToFloat(BlendSkMScalars(from_y, to_y, min_progress)),
467           SkMScalarToFloat(BlendSkMScalars(from_z, to_z, min_progress)),
468           bounds);
469       gfx::BoxF bounds_max = box;
470       ApplyScaleToBox(
471           SkMScalarToFloat(BlendSkMScalars(from_x, to_x, max_progress)),
472           SkMScalarToFloat(BlendSkMScalars(from_y, to_y, max_progress)),
473           SkMScalarToFloat(BlendSkMScalars(from_z, to_z, max_progress)),
474           &bounds_max);
475       if (!bounds->IsEmpty() && !bounds_max.IsEmpty()) {
476         bounds->Union(bounds_max);
477       } else if (!bounds->IsEmpty()) {
478         UnionBoxWithZeroScale(bounds);
479       } else if (!bounds_max.IsEmpty()) {
480         UnionBoxWithZeroScale(&bounds_max);
481         *bounds = bounds_max;
482       }
483
484       return true;
485     }
486     case TransformOperation::TransformOperationIdentity:
487       *bounds = box;
488       return true;
489     case TransformOperation::TransformOperationRotate: {
490       bool first_point = true;
491       for (int i = 0; i < 8; ++i) {
492         gfx::Point3F corner = box.origin();
493         corner += gfx::Vector3dF(i & 1 ? box.width() : 0.f,
494                                  i & 2 ? box.height() : 0.f,
495                                  i & 4 ? box.depth() : 0.f);
496         gfx::BoxF box_for_arc;
497         BoundingBoxForArc(
498             corner, from, to, min_progress, max_progress, &box_for_arc);
499         if (first_point)
500           *bounds = box_for_arc;
501         else
502           bounds->Union(box_for_arc);
503         first_point = false;
504       }
505       return true;
506     }
507     case TransformOperation::TransformOperationSkew:
508     case TransformOperation::TransformOperationPerspective:
509     case TransformOperation::TransformOperationMatrix:
510       return false;
511   }
512   NOTREACHED();
513   return false;
514 }
515
516 }  // namespace cc