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 "cc/animation/transform_operations.h"
17 #include "ui/gfx/geometry/box_f.h"
18 #include "ui/gfx/geometry/vector3d_f.h"
19 #include "ui/gfx/transform_util.h"
22 const SkMScalar kAngleEpsilon
= 1e-4f
;
27 bool TransformOperation::IsIdentity() const {
28 return matrix
.IsIdentity();
31 static bool IsOperationIdentity(const TransformOperation
* operation
) {
32 return !operation
|| operation
->IsIdentity();
35 static bool ShareSameAxis(const TransformOperation
* from
,
36 const TransformOperation
* to
,
40 SkMScalar
* angle_from
) {
41 if (IsOperationIdentity(from
) && IsOperationIdentity(to
))
44 if (IsOperationIdentity(from
) && !IsOperationIdentity(to
)) {
45 *axis_x
= to
->rotate
.axis
.x
;
46 *axis_y
= to
->rotate
.axis
.y
;
47 *axis_z
= to
->rotate
.axis
.z
;
52 if (!IsOperationIdentity(from
) && IsOperationIdentity(to
)) {
53 *axis_x
= from
->rotate
.axis
.x
;
54 *axis_y
= from
->rotate
.axis
.y
;
55 *axis_z
= from
->rotate
.axis
.z
;
56 *angle_from
= from
->rotate
.angle
;
60 SkMScalar length_2
= from
->rotate
.axis
.x
* from
->rotate
.axis
.x
+
61 from
->rotate
.axis
.y
* from
->rotate
.axis
.y
+
62 from
->rotate
.axis
.z
* from
->rotate
.axis
.z
;
63 SkMScalar other_length_2
= to
->rotate
.axis
.x
* to
->rotate
.axis
.x
+
64 to
->rotate
.axis
.y
* to
->rotate
.axis
.y
+
65 to
->rotate
.axis
.z
* to
->rotate
.axis
.z
;
67 if (length_2
<= kAngleEpsilon
|| other_length_2
<= kAngleEpsilon
)
70 SkMScalar dot
= to
->rotate
.axis
.x
* from
->rotate
.axis
.x
+
71 to
->rotate
.axis
.y
* from
->rotate
.axis
.y
+
72 to
->rotate
.axis
.z
* from
->rotate
.axis
.z
;
74 std::abs(SK_MScalar1
- (dot
* dot
) / (length_2
* other_length_2
));
75 bool result
= error
< kAngleEpsilon
;
77 *axis_x
= to
->rotate
.axis
.x
;
78 *axis_y
= to
->rotate
.axis
.y
;
79 *axis_z
= to
->rotate
.axis
.z
;
80 // If the axes are pointing in opposite directions, we need to reverse
82 *angle_from
= dot
> 0 ? from
->rotate
.angle
: -from
->rotate
.angle
;
87 static SkMScalar
BlendSkMScalars(SkMScalar from
,
90 return from
* (1 - progress
) + to
* progress
;
93 bool TransformOperation::BlendTransformOperations(
94 const TransformOperation
* from
,
95 const TransformOperation
* to
,
97 gfx::Transform
* result
) {
98 if (IsOperationIdentity(from
) && IsOperationIdentity(to
))
101 TransformOperation::Type interpolation_type
=
102 TransformOperation::TransformOperationIdentity
;
103 if (IsOperationIdentity(to
))
104 interpolation_type
= from
->type
;
106 interpolation_type
= to
->type
;
108 switch (interpolation_type
) {
109 case TransformOperation::TransformOperationTranslate
: {
110 SkMScalar from_x
= IsOperationIdentity(from
) ? 0 : from
->translate
.x
;
111 SkMScalar from_y
= IsOperationIdentity(from
) ? 0 : from
->translate
.y
;
112 SkMScalar from_z
= IsOperationIdentity(from
) ? 0 : from
->translate
.z
;
113 SkMScalar to_x
= IsOperationIdentity(to
) ? 0 : to
->translate
.x
;
114 SkMScalar to_y
= IsOperationIdentity(to
) ? 0 : to
->translate
.y
;
115 SkMScalar to_z
= IsOperationIdentity(to
) ? 0 : to
->translate
.z
;
116 result
->Translate3d(BlendSkMScalars(from_x
, to_x
, progress
),
117 BlendSkMScalars(from_y
, to_y
, progress
),
118 BlendSkMScalars(from_z
, to_z
, progress
));
121 case TransformOperation::TransformOperationRotate
: {
122 SkMScalar axis_x
= 0;
123 SkMScalar axis_y
= 0;
124 SkMScalar axis_z
= 1;
125 SkMScalar from_angle
= 0;
126 SkMScalar to_angle
= IsOperationIdentity(to
) ? 0 : to
->rotate
.angle
;
127 if (ShareSameAxis(from
, to
, &axis_x
, &axis_y
, &axis_z
, &from_angle
)) {
128 result
->RotateAbout(gfx::Vector3dF(axis_x
, axis_y
, axis_z
),
129 BlendSkMScalars(from_angle
, to_angle
, progress
));
131 gfx::Transform to_matrix
;
132 if (!IsOperationIdentity(to
))
133 to_matrix
= to
->matrix
;
134 gfx::Transform from_matrix
;
135 if (!IsOperationIdentity(from
))
136 from_matrix
= from
->matrix
;
138 if (!result
->Blend(from_matrix
, progress
))
143 case TransformOperation::TransformOperationScale
: {
144 SkMScalar from_x
= IsOperationIdentity(from
) ? 1 : from
->scale
.x
;
145 SkMScalar from_y
= IsOperationIdentity(from
) ? 1 : from
->scale
.y
;
146 SkMScalar from_z
= IsOperationIdentity(from
) ? 1 : from
->scale
.z
;
147 SkMScalar to_x
= IsOperationIdentity(to
) ? 1 : to
->scale
.x
;
148 SkMScalar to_y
= IsOperationIdentity(to
) ? 1 : to
->scale
.y
;
149 SkMScalar to_z
= IsOperationIdentity(to
) ? 1 : to
->scale
.z
;
150 result
->Scale3d(BlendSkMScalars(from_x
, to_x
, progress
),
151 BlendSkMScalars(from_y
, to_y
, progress
),
152 BlendSkMScalars(from_z
, to_z
, progress
));
155 case TransformOperation::TransformOperationSkew
: {
156 SkMScalar from_x
= IsOperationIdentity(from
) ? 0 : from
->skew
.x
;
157 SkMScalar from_y
= IsOperationIdentity(from
) ? 0 : from
->skew
.y
;
158 SkMScalar to_x
= IsOperationIdentity(to
) ? 0 : to
->skew
.x
;
159 SkMScalar to_y
= IsOperationIdentity(to
) ? 0 : to
->skew
.y
;
160 result
->SkewX(BlendSkMScalars(from_x
, to_x
, progress
));
161 result
->SkewY(BlendSkMScalars(from_y
, to_y
, progress
));
164 case TransformOperation::TransformOperationPerspective
: {
165 SkMScalar from_perspective_depth
=
166 IsOperationIdentity(from
) ? std::numeric_limits
<SkMScalar
>::max()
167 : from
->perspective_depth
;
168 SkMScalar to_perspective_depth
=
169 IsOperationIdentity(to
) ? std::numeric_limits
<SkMScalar
>::max()
170 : to
->perspective_depth
;
171 if (from_perspective_depth
== 0.f
|| to_perspective_depth
== 0.f
)
174 SkMScalar blended_perspective_depth
= BlendSkMScalars(
175 1.f
/ from_perspective_depth
, 1.f
/ to_perspective_depth
, progress
);
177 if (blended_perspective_depth
== 0.f
)
180 result
->ApplyPerspectiveDepth(1.f
/ blended_perspective_depth
);
183 case TransformOperation::TransformOperationMatrix
: {
184 gfx::Transform to_matrix
;
185 if (!IsOperationIdentity(to
))
186 to_matrix
= to
->matrix
;
187 gfx::Transform from_matrix
;
188 if (!IsOperationIdentity(from
))
189 from_matrix
= from
->matrix
;
191 if (!result
->Blend(from_matrix
, progress
))
195 case TransformOperation::TransformOperationIdentity
:
203 // If p = (px, py) is a point in the plane being rotated about (0, 0, nz), this
204 // function computes the angles we would have to rotate from p to get to
205 // (length(p), 0), (-length(p), 0), (0, length(p)), (0, -length(p)). If nz is
206 // negative, these angles will need to be reversed.
207 static void FindCandidatesInPlane(float px
,
211 int* num_candidates
) {
212 double phi
= atan2(px
, py
);
215 for (int i
= 1; i
< *num_candidates
; ++i
)
216 candidates
[i
] = candidates
[i
- 1] + M_PI_2
;
218 for (int i
= 0; i
< *num_candidates
; ++i
)
219 candidates
[i
] *= -1.f
;
223 static float RadiansToDegrees(float radians
) {
224 return (180.f
* radians
) / M_PI
;
227 static float DegreesToRadians(float degrees
) {
228 return (M_PI
* degrees
) / 180.f
;
231 static void BoundingBoxForArc(const gfx::Point3F
& point
,
232 const TransformOperation
* from
,
233 const TransformOperation
* to
,
234 SkMScalar min_progress
,
235 SkMScalar max_progress
,
237 const TransformOperation
* exemplar
= from
? from
: to
;
238 gfx::Vector3dF
axis(exemplar
->rotate
.axis
.x
,
239 exemplar
->rotate
.axis
.y
,
240 exemplar
->rotate
.axis
.z
);
242 const bool x_is_zero
= axis
.x() == 0.f
;
243 const bool y_is_zero
= axis
.y() == 0.f
;
244 const bool z_is_zero
= axis
.z() == 0.f
;
246 // We will have at most 6 angles to test (excluding from->angle and
248 static const int kMaxNumCandidates
= 6;
249 double candidates
[kMaxNumCandidates
];
250 int num_candidates
= kMaxNumCandidates
;
252 if (x_is_zero
&& y_is_zero
&& z_is_zero
)
255 SkMScalar from_angle
= from
? from
->rotate
.angle
: 0.f
;
256 SkMScalar to_angle
= to
? to
->rotate
.angle
: 0.f
;
258 // If the axes of rotation are pointing in opposite directions, we need to
259 // flip one of the angles. Note, if both |from| and |to| exist, then axis will
260 // correspond to |from|.
262 gfx::Vector3dF
other_axis(
263 to
->rotate
.axis
.x
, to
->rotate
.axis
.y
, to
->rotate
.axis
.z
);
264 if (gfx::DotProduct(axis
, other_axis
) < 0.f
)
269 SkMScalarToFloat(BlendSkMScalars(from_angle
, to_angle
, min_progress
));
271 SkMScalarToFloat(BlendSkMScalars(from_angle
, to_angle
, max_progress
));
272 if (max_degrees
< min_degrees
)
273 std::swap(min_degrees
, max_degrees
);
275 gfx::Transform from_transform
;
276 from_transform
.RotateAbout(axis
, min_degrees
);
277 gfx::Transform to_transform
;
278 to_transform
.RotateAbout(axis
, max_degrees
);
282 gfx::Point3F point_rotated_from
= point
;
283 from_transform
.TransformPoint(&point_rotated_from
);
284 gfx::Point3F point_rotated_to
= point
;
285 to_transform
.TransformPoint(&point_rotated_to
);
287 box
->set_origin(point_rotated_from
);
288 box
->ExpandTo(point_rotated_to
);
290 if (x_is_zero
&& y_is_zero
) {
291 FindCandidatesInPlane(
292 point
.x(), point
.y(), axis
.z(), candidates
, &num_candidates
);
293 } else if (x_is_zero
&& z_is_zero
) {
294 FindCandidatesInPlane(
295 point
.z(), point
.x(), axis
.y(), candidates
, &num_candidates
);
296 } else if (y_is_zero
&& z_is_zero
) {
297 FindCandidatesInPlane(
298 point
.y(), point
.z(), axis
.x(), candidates
, &num_candidates
);
300 gfx::Vector3dF normal
= axis
;
301 normal
.Scale(1.f
/ normal
.Length());
303 // First, find center of rotation.
305 gfx::Vector3dF to_point
= point
- origin
;
306 gfx::Point3F center
=
307 origin
+ gfx::ScaleVector3d(normal
, gfx::DotProduct(to_point
, normal
));
309 // Now we need to find two vectors in the plane of rotation. One pointing
310 // towards point and another, perpendicular vector in the plane.
311 gfx::Vector3dF v1
= point
- center
;
312 float v1_length
= v1
.Length();
313 if (v1_length
== 0.f
)
316 v1
.Scale(1.f
/ v1_length
);
317 gfx::Vector3dF v2
= gfx::CrossProduct(normal
, v1
);
318 // v1 is the basis vector in the direction of the point.
319 // i.e. with a rotation of 0, v1 is our +x vector.
320 // v2 is a perpenticular basis vector of our plane (+y).
322 // Take the parametric equation of a circle.
323 // x = r*cos(t); y = r*sin(t);
324 // We can treat that as a circle on the plane v1xv2.
325 // From that we get the parametric equations for a circle on the
326 // plane in 3d space of:
327 // x(t) = r*cos(t)*v1.x + r*sin(t)*v2.x + cx
328 // y(t) = r*cos(t)*v1.y + r*sin(t)*v2.y + cy
329 // z(t) = r*cos(t)*v1.z + r*sin(t)*v2.z + cz
330 // Taking the derivative of (x, y, z) and solving for 0 gives us our
331 // maximum/minimum x, y, z values.
332 // x'(t) = r*cos(t)*v2.x - r*sin(t)*v1.x = 0
333 // tan(t) = v2.x/v1.x
334 // t = atan2(v2.x, v1.x) + n*M_PI;
335 candidates
[0] = atan2(v2
.x(), v1
.x());
336 candidates
[1] = candidates
[0] + M_PI
;
337 candidates
[2] = atan2(v2
.y(), v1
.y());
338 candidates
[3] = candidates
[2] + M_PI
;
339 candidates
[4] = atan2(v2
.z(), v1
.z());
340 candidates
[5] = candidates
[4] + M_PI
;
343 double min_radians
= DegreesToRadians(min_degrees
);
344 double max_radians
= DegreesToRadians(max_degrees
);
346 for (int i
= 0; i
< num_candidates
; ++i
) {
347 double radians
= candidates
[i
];
348 while (radians
< min_radians
)
349 radians
+= 2.0 * M_PI
;
350 while (radians
> max_radians
)
351 radians
-= 2.0 * M_PI
;
352 if (radians
< min_radians
)
355 gfx::Transform rotation
;
356 rotation
.RotateAbout(axis
, RadiansToDegrees(radians
));
357 gfx::Point3F rotated
= point
;
358 rotation
.TransformPoint(&rotated
);
360 box
->ExpandTo(rotated
);
364 bool TransformOperation::BlendedBoundsForBox(const gfx::BoxF
& box
,
365 const TransformOperation
* from
,
366 const TransformOperation
* to
,
367 SkMScalar min_progress
,
368 SkMScalar max_progress
,
370 bool is_identity_from
= IsOperationIdentity(from
);
371 bool is_identity_to
= IsOperationIdentity(to
);
372 if (is_identity_from
&& is_identity_to
) {
377 TransformOperation::Type interpolation_type
=
378 TransformOperation::TransformOperationIdentity
;
380 interpolation_type
= from
->type
;
382 interpolation_type
= to
->type
;
384 switch (interpolation_type
) {
385 case TransformOperation::TransformOperationIdentity
:
388 case TransformOperation::TransformOperationTranslate
:
389 case TransformOperation::TransformOperationSkew
:
390 case TransformOperation::TransformOperationPerspective
:
391 case TransformOperation::TransformOperationScale
: {
392 gfx::Transform from_transform
;
393 gfx::Transform to_transform
;
394 if (!BlendTransformOperations(from
, to
, min_progress
, &from_transform
) ||
395 !BlendTransformOperations(from
, to
, max_progress
, &to_transform
))
399 from_transform
.TransformBox(bounds
);
401 gfx::BoxF to_box
= box
;
402 to_transform
.TransformBox(&to_box
);
403 bounds
->ExpandTo(to_box
);
407 case TransformOperation::TransformOperationRotate
: {
408 SkMScalar axis_x
= 0;
409 SkMScalar axis_y
= 0;
410 SkMScalar axis_z
= 1;
411 SkMScalar from_angle
= 0;
412 if (!ShareSameAxis(from
, to
, &axis_x
, &axis_y
, &axis_z
, &from_angle
))
415 bool first_point
= true;
416 for (int i
= 0; i
< 8; ++i
) {
417 gfx::Point3F corner
= box
.origin();
418 corner
+= gfx::Vector3dF(i
& 1 ? box
.width() : 0.f
,
419 i
& 2 ? box
.height() : 0.f
,
420 i
& 4 ? box
.depth() : 0.f
);
421 gfx::BoxF box_for_arc
;
423 corner
, from
, to
, min_progress
, max_progress
, &box_for_arc
);
425 *bounds
= box_for_arc
;
427 bounds
->Union(box_for_arc
);
432 case TransformOperation::TransformOperationMatrix
: