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.
5 // Needed on Windows to get |M_PI| from <cmath>
7 #define _USE_MATH_DEFINES
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"
20 const SkMScalar kAngleEpsilon = 1e-4f;
25 bool TransformOperation::IsIdentity() const {
26 return matrix.IsIdentity();
29 static bool IsOperationIdentity(const TransformOperation* operation) {
30 return !operation || operation->IsIdentity();
33 static bool ShareSameAxis(const TransformOperation* from,
34 const TransformOperation* to,
38 SkMScalar* angle_from) {
39 if (IsOperationIdentity(from) && IsOperationIdentity(to))
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;
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;
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;
65 if (length_2 <= kAngleEpsilon || other_length_2 <= kAngleEpsilon)
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;
72 std::abs(SK_MScalar1 - (dot * dot) / (length_2 * other_length_2));
73 bool result = error < kAngleEpsilon;
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
80 *angle_from = dot > 0 ? from->rotate.angle : -from->rotate.angle;
85 static SkMScalar BlendSkMScalars(SkMScalar from,
88 return from * (1 - progress) + to * progress;
91 bool TransformOperation::BlendTransformOperations(
92 const TransformOperation* from,
93 const TransformOperation* to,
95 gfx::Transform* result) {
96 if (IsOperationIdentity(from) && IsOperationIdentity(to))
99 TransformOperation::Type interpolation_type =
100 TransformOperation::TransformOperationIdentity;
101 if (IsOperationIdentity(to))
102 interpolation_type = from->type;
104 interpolation_type = to->type;
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));
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));
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;
136 if (!result->Blend(from_matrix, progress))
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));
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));
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)
172 SkMScalar blended_perspective_depth = BlendSkMScalars(
173 1.f / from_perspective_depth, 1.f / to_perspective_depth, progress);
175 if (blended_perspective_depth == 0.f)
178 result->ApplyPerspectiveDepth(1.f / blended_perspective_depth);
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;
189 if (!result->Blend(from_matrix, progress))
193 case TransformOperation::TransformOperationIdentity:
201 static void ApplyScaleToBox(float x_scale,
206 box->set_x(-box->right());
208 box->set_y(-box->bottom());
210 box->set_z(-box->front());
211 box->Scale(std::abs(x_scale), std::abs(y_scale), std::abs(z_scale));
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);
222 min_x, min_y, min_z, max_x - min_x, max_y - min_y, max_z - min_z);
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,
233 int* num_candidates) {
234 double phi = atan2(px, py);
237 for (int i = 1; i < *num_candidates; ++i)
238 candidates[i] = candidates[i - 1] + M_PI_2;
240 for (int i = 0; i < *num_candidates; ++i)
241 candidates[i] *= -1.f;
245 static float RadiansToDegrees(float radians) {
246 return (180.f * radians) / M_PI;
249 static float DegreesToRadians(float degrees) {
250 return (M_PI * degrees) / 180.f;
253 // Div by zero doesn't always result in Inf as you might hope, so we'll do this
255 static float SafeDivide(float numerator, float denominator) {
256 if (numerator == 0.f)
259 if (denominator == 0.f) {
260 return numerator > 0.f ? std::numeric_limits<float>::infinity()
261 : -std::numeric_limits<float>::infinity();
264 return numerator / denominator;
267 static void BoundingBoxForArc(const gfx::Point3F& point,
268 const TransformOperation* from,
269 const TransformOperation* to,
270 SkMScalar min_progress,
271 SkMScalar max_progress,
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);
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;
282 // We will have at most 6 angles to test (excluding from->angle and
284 static const int kMaxNumCandidates = 6;
285 double candidates[kMaxNumCandidates];
286 int num_candidates = kMaxNumCandidates;
288 if (x_is_zero && y_is_zero && z_is_zero)
291 SkMScalar from_angle = from ? from->rotate.angle : 0.f;
292 SkMScalar to_angle = to ? to->rotate.angle : 0.f;
295 SkMScalarToFloat(BlendSkMScalars(from_angle, to_angle, min_progress));
297 SkMScalarToFloat(BlendSkMScalars(from_angle, to_angle, max_progress));
298 if (max_degrees < min_degrees)
299 std::swap(min_degrees, max_degrees);
301 gfx::Transform from_transform;
302 from_transform.RotateAbout(axis, min_degrees);
303 gfx::Transform to_transform;
304 to_transform.RotateAbout(axis, max_degrees);
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);
313 box->set_origin(point_rotated_from);
314 box->ExpandTo(point_rotated_to);
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);
326 gfx::Vector3dF normal = axis;
327 normal.Scale(1.f / normal.Length());
329 // First, find center of rotation.
331 gfx::Vector3dF to_point = point - origin;
332 gfx::Point3F center =
333 origin + gfx::ScaleVector3d(normal, gfx::DotProduct(to_point, normal));
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)
342 v1.Scale(1.f / v1_length);
344 gfx::Vector3dF v2 = gfx::CrossProduct(normal, v1);
346 // Now figure out where (1, 0, 0) and (0, 0, 1) project on the rotation
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;
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;
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));
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()));
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;
376 double min_radians = DegreesToRadians(min_degrees);
377 double max_radians = DegreesToRadians(max_degrees);
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)
388 gfx::Transform rotation;
389 rotation.RotateAbout(axis, RadiansToDegrees(radians));
390 gfx::Point3F rotated = point;
391 rotation.TransformPoint(&rotated);
393 box->ExpandTo(rotated);
397 bool TransformOperation::BlendedBoundsForBox(const gfx::BoxF& box,
398 const TransformOperation* from,
399 const TransformOperation* to,
400 SkMScalar min_progress,
401 SkMScalar max_progress,
403 bool is_identity_from = IsOperationIdentity(from);
404 bool is_identity_to = IsOperationIdentity(to);
405 if (is_identity_from && is_identity_to) {
410 TransformOperation::Type interpolation_type =
411 TransformOperation::TransformOperationIdentity;
413 interpolation_type = from->type;
415 interpolation_type = to->type;
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;
423 from_x = from->translate.x;
424 from_y = from->translate.y;
425 from_z = from->translate.z;
427 SkMScalar to_x, to_y, to_z;
428 if (is_identity_to) {
429 to_x = to_y = to_z = 0.0;
431 to_x = to->translate.x;
432 to_y = to->translate.y;
433 to_z = to->translate.z;
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);
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;
451 from_x = from->scale.x;
452 from_y = from->scale.y;
453 from_z = from->scale.z;
455 SkMScalar to_x, to_y, to_z;
456 if (is_identity_to) {
457 to_x = to_y = to_z = 1.0;
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)),
469 gfx::BoxF bounds_max = box;
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)),
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;
486 case TransformOperation::TransformOperationIdentity:
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;
498 corner, from, to, min_progress, max_progress, &box_for_arc);
500 *bounds = box_for_arc;
502 bounds->Union(box_for_arc);
507 case TransformOperation::TransformOperationSkew:
508 case TransformOperation::TransformOperationPerspective:
509 case TransformOperation::TransformOperationMatrix: