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