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#include <cmath>
6#include <limits>
7
8#include "cc/animation/transform_operation.h"
9#include "ui/gfx/vector3d_f.h"
10
11namespace {
12const double kAngleEpsilon = 1e-4;
13}
14
15namespace cc {
16
17bool TransformOperation::IsIdentity() const {
18  return matrix.IsIdentity();
19}
20
21static bool IsOperationIdentity(const TransformOperation* operation) {
22  return !operation || operation->IsIdentity();
23}
24
25static bool ShareSameAxis(const TransformOperation* from,
26                          const TransformOperation* to,
27                          double* axis_x,
28                          double* axis_y,
29                          double* axis_z,
30                          double* angle_from) {
31  if (IsOperationIdentity(from) && IsOperationIdentity(to))
32    return false;
33
34  if (IsOperationIdentity(from) && !IsOperationIdentity(to)) {
35    *axis_x = to->rotate.axis.x;
36    *axis_y = to->rotate.axis.y;
37    *axis_z = to->rotate.axis.z;
38    *angle_from = 0;
39    return true;
40  }
41
42  if (!IsOperationIdentity(from) && IsOperationIdentity(to)) {
43    *axis_x = from->rotate.axis.x;
44    *axis_y = from->rotate.axis.y;
45    *axis_z = from->rotate.axis.z;
46    *angle_from = from->rotate.angle;
47    return true;
48  }
49
50  double length_2 = from->rotate.axis.x * from->rotate.axis.x +
51                    from->rotate.axis.y * from->rotate.axis.y +
52                    from->rotate.axis.z * from->rotate.axis.z;
53  double other_length_2 = to->rotate.axis.x * to->rotate.axis.x +
54                          to->rotate.axis.y * to->rotate.axis.y +
55                          to->rotate.axis.z * to->rotate.axis.z;
56
57  if (length_2 <= kAngleEpsilon || other_length_2 <= kAngleEpsilon)
58    return false;
59
60  double dot = to->rotate.axis.x * from->rotate.axis.x +
61               to->rotate.axis.y * from->rotate.axis.y +
62               to->rotate.axis.z * from->rotate.axis.z;
63  double error = std::abs(1.0 - (dot * dot) / (length_2 * other_length_2));
64  bool result = error < kAngleEpsilon;
65  if (result) {
66    *axis_x = to->rotate.axis.x;
67    *axis_y = to->rotate.axis.y;
68    *axis_z = to->rotate.axis.z;
69    // If the axes are pointing in opposite directions, we need to reverse
70    // the angle.
71    *angle_from = dot > 0 ? from->rotate.angle : -from->rotate.angle;
72  }
73  return result;
74}
75
76static double BlendDoubles(double from, double to, double progress) {
77  return from * (1 - progress) + to * progress;
78}
79
80bool TransformOperation::BlendTransformOperations(
81    const TransformOperation* from,
82    const TransformOperation* to,
83    double progress,
84    gfx::Transform* result) {
85  if (IsOperationIdentity(from) && IsOperationIdentity(to))
86    return true;
87
88  TransformOperation::Type interpolation_type =
89      TransformOperation::TransformOperationIdentity;
90  if (IsOperationIdentity(to))
91    interpolation_type = from->type;
92  else
93    interpolation_type = to->type;
94
95  switch (interpolation_type) {
96  case TransformOperation::TransformOperationTranslate: {
97    double from_x = IsOperationIdentity(from) ? 0 : from->translate.x;
98    double from_y = IsOperationIdentity(from) ? 0 : from->translate.y;
99    double from_z = IsOperationIdentity(from) ? 0 : from->translate.z;
100    double to_x = IsOperationIdentity(to) ? 0 : to->translate.x;
101    double to_y = IsOperationIdentity(to) ? 0 : to->translate.y;
102    double to_z = IsOperationIdentity(to) ? 0 : to->translate.z;
103    result->Translate3d(BlendDoubles(from_x, to_x, progress),
104                        BlendDoubles(from_y, to_y, progress),
105                        BlendDoubles(from_z, to_z, progress));
106    break;
107  }
108  case TransformOperation::TransformOperationRotate: {
109    double axis_x = 0;
110    double axis_y = 0;
111    double axis_z = 1;
112    double from_angle = 0;
113    double to_angle = IsOperationIdentity(to) ? 0 : to->rotate.angle;
114    if (ShareSameAxis(from, to, &axis_x, &axis_y, &axis_z, &from_angle)) {
115      result->RotateAbout(gfx::Vector3dF(axis_x, axis_y, axis_z),
116                          BlendDoubles(from_angle, to_angle, progress));
117    } else {
118      gfx::Transform to_matrix;
119      if (!IsOperationIdentity(to))
120        to_matrix = to->matrix;
121      gfx::Transform from_matrix;
122      if (!IsOperationIdentity(from))
123        from_matrix = from->matrix;
124      *result = to_matrix;
125      if (!result->Blend(from_matrix, progress))
126        return false;
127    }
128    break;
129  }
130  case TransformOperation::TransformOperationScale: {
131    double from_x = IsOperationIdentity(from) ? 1 : from->scale.x;
132    double from_y = IsOperationIdentity(from) ? 1 : from->scale.y;
133    double from_z = IsOperationIdentity(from) ? 1 : from->scale.z;
134    double to_x = IsOperationIdentity(to) ? 1 : to->scale.x;
135    double to_y = IsOperationIdentity(to) ? 1 : to->scale.y;
136    double to_z = IsOperationIdentity(to) ? 1 : to->scale.z;
137    result->Scale3d(BlendDoubles(from_x, to_x, progress),
138                    BlendDoubles(from_y, to_y, progress),
139                    BlendDoubles(from_z, to_z, progress));
140    break;
141  }
142  case TransformOperation::TransformOperationSkew: {
143    double from_x = IsOperationIdentity(from) ? 0 : from->skew.x;
144    double from_y = IsOperationIdentity(from) ? 0 : from->skew.y;
145    double to_x = IsOperationIdentity(to) ? 0 : to->skew.x;
146    double to_y = IsOperationIdentity(to) ? 0 : to->skew.y;
147    result->SkewX(BlendDoubles(from_x, to_x, progress));
148    result->SkewY(BlendDoubles(from_y, to_y, progress));
149    break;
150  }
151  case TransformOperation::TransformOperationPerspective: {
152    double from_perspective_depth = IsOperationIdentity(from) ?
153        std::numeric_limits<double>::max() : from->perspective_depth;
154    double to_perspective_depth = IsOperationIdentity(to) ?
155        std::numeric_limits<double>::max() : to->perspective_depth;
156    result->ApplyPerspectiveDepth(
157        BlendDoubles(from_perspective_depth, to_perspective_depth, progress));
158    break;
159  }
160  case TransformOperation::TransformOperationMatrix: {
161    gfx::Transform to_matrix;
162    if (!IsOperationIdentity(to))
163      to_matrix = to->matrix;
164    gfx::Transform from_matrix;
165    if (!IsOperationIdentity(from))
166      from_matrix = from->matrix;
167    *result = to_matrix;
168    if (!result->Blend(from_matrix, progress))
169      return false;
170    break;
171  }
172  case TransformOperation::TransformOperationIdentity:
173    // Do nothing.
174    break;
175  }
176
177  return true;
178}
179
180}  // namespace cc
181