OLD | NEW |
| (Empty) |
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 "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" | |
20 | |
21 namespace { | |
22 const SkMScalar kAngleEpsilon = 1e-4f; | |
23 } | |
24 | |
25 namespace cc { | |
26 | |
27 bool TransformOperation::IsIdentity() const { | |
28 return matrix.IsIdentity(); | |
29 } | |
30 | |
31 static bool IsOperationIdentity(const TransformOperation* operation) { | |
32 return !operation || operation->IsIdentity(); | |
33 } | |
34 | |
35 static bool ShareSameAxis(const TransformOperation* from, | |
36 const TransformOperation* to, | |
37 SkMScalar* axis_x, | |
38 SkMScalar* axis_y, | |
39 SkMScalar* axis_z, | |
40 SkMScalar* angle_from) { | |
41 if (IsOperationIdentity(from) && IsOperationIdentity(to)) | |
42 return false; | |
43 | |
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; | |
48 *angle_from = 0; | |
49 return true; | |
50 } | |
51 | |
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; | |
57 return true; | |
58 } | |
59 | |
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; | |
66 | |
67 if (length_2 <= kAngleEpsilon || other_length_2 <= kAngleEpsilon) | |
68 return false; | |
69 | |
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; | |
73 SkMScalar error = | |
74 std::abs(SK_MScalar1 - (dot * dot) / (length_2 * other_length_2)); | |
75 bool result = error < kAngleEpsilon; | |
76 if (result) { | |
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 | |
81 // the angle. | |
82 *angle_from = dot > 0 ? from->rotate.angle : -from->rotate.angle; | |
83 } | |
84 return result; | |
85 } | |
86 | |
87 static SkMScalar BlendSkMScalars(SkMScalar from, | |
88 SkMScalar to, | |
89 SkMScalar progress) { | |
90 return from * (1 - progress) + to * progress; | |
91 } | |
92 | |
93 bool TransformOperation::BlendTransformOperations( | |
94 const TransformOperation* from, | |
95 const TransformOperation* to, | |
96 SkMScalar progress, | |
97 gfx::Transform* result) { | |
98 if (IsOperationIdentity(from) && IsOperationIdentity(to)) | |
99 return true; | |
100 | |
101 TransformOperation::Type interpolation_type = | |
102 TransformOperation::TRANSFORM_OPERATION_IDENTITY; | |
103 if (IsOperationIdentity(to)) | |
104 interpolation_type = from->type; | |
105 else | |
106 interpolation_type = to->type; | |
107 | |
108 switch (interpolation_type) { | |
109 case TransformOperation::TRANSFORM_OPERATION_TRANSLATE: { | |
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)); | |
119 break; | |
120 } | |
121 case TransformOperation::TRANSFORM_OPERATION_ROTATE: { | |
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)); | |
130 } else { | |
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; | |
137 *result = to_matrix; | |
138 if (!result->Blend(from_matrix, progress)) | |
139 return false; | |
140 } | |
141 break; | |
142 } | |
143 case TransformOperation::TRANSFORM_OPERATION_SCALE: { | |
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)); | |
153 break; | |
154 } | |
155 case TransformOperation::TRANSFORM_OPERATION_SKEW: { | |
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)); | |
162 break; | |
163 } | |
164 case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE: { | |
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) | |
172 return false; | |
173 | |
174 SkMScalar blended_perspective_depth = BlendSkMScalars( | |
175 1.f / from_perspective_depth, 1.f / to_perspective_depth, progress); | |
176 | |
177 if (blended_perspective_depth == 0.f) | |
178 return false; | |
179 | |
180 result->ApplyPerspectiveDepth(1.f / blended_perspective_depth); | |
181 break; | |
182 } | |
183 case TransformOperation::TRANSFORM_OPERATION_MATRIX: { | |
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; | |
190 *result = to_matrix; | |
191 if (!result->Blend(from_matrix, progress)) | |
192 return false; | |
193 break; | |
194 } | |
195 case TransformOperation::TRANSFORM_OPERATION_IDENTITY: | |
196 // Do nothing. | |
197 break; | |
198 } | |
199 | |
200 return true; | |
201 } | |
202 | |
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, | |
208 float py, | |
209 float nz, | |
210 double* candidates, | |
211 int* num_candidates) { | |
212 double phi = atan2(px, py); | |
213 *num_candidates = 4; | |
214 candidates[0] = phi; | |
215 for (int i = 1; i < *num_candidates; ++i) | |
216 candidates[i] = candidates[i - 1] + M_PI_2; | |
217 if (nz < 0.f) { | |
218 for (int i = 0; i < *num_candidates; ++i) | |
219 candidates[i] *= -1.f; | |
220 } | |
221 } | |
222 | |
223 static float RadiansToDegrees(float radians) { | |
224 return (180.f * radians) / M_PI; | |
225 } | |
226 | |
227 static float DegreesToRadians(float degrees) { | |
228 return (M_PI * degrees) / 180.f; | |
229 } | |
230 | |
231 static void BoundingBoxForArc(const gfx::Point3F& point, | |
232 const TransformOperation* from, | |
233 const TransformOperation* to, | |
234 SkMScalar min_progress, | |
235 SkMScalar max_progress, | |
236 gfx::BoxF* box) { | |
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); | |
241 | |
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; | |
245 | |
246 // We will have at most 6 angles to test (excluding from->angle and | |
247 // to->angle). | |
248 static const int kMaxNumCandidates = 6; | |
249 double candidates[kMaxNumCandidates]; | |
250 int num_candidates = kMaxNumCandidates; | |
251 | |
252 if (x_is_zero && y_is_zero && z_is_zero) | |
253 return; | |
254 | |
255 SkMScalar from_angle = from ? from->rotate.angle : 0.f; | |
256 SkMScalar to_angle = to ? to->rotate.angle : 0.f; | |
257 | |
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|. | |
261 if (from && to) { | |
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) | |
265 to_angle *= -1.f; | |
266 } | |
267 | |
268 float min_degrees = | |
269 SkMScalarToFloat(BlendSkMScalars(from_angle, to_angle, min_progress)); | |
270 float max_degrees = | |
271 SkMScalarToFloat(BlendSkMScalars(from_angle, to_angle, max_progress)); | |
272 if (max_degrees < min_degrees) | |
273 std::swap(min_degrees, max_degrees); | |
274 | |
275 gfx::Transform from_transform; | |
276 from_transform.RotateAbout(axis, min_degrees); | |
277 gfx::Transform to_transform; | |
278 to_transform.RotateAbout(axis, max_degrees); | |
279 | |
280 *box = gfx::BoxF(); | |
281 | |
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); | |
286 | |
287 box->set_origin(point_rotated_from); | |
288 box->ExpandTo(point_rotated_to); | |
289 | |
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); | |
299 } else { | |
300 gfx::Vector3dF normal = axis; | |
301 normal.Scale(1.f / normal.Length()); | |
302 | |
303 // First, find center of rotation. | |
304 gfx::Point3F origin; | |
305 gfx::Vector3dF to_point = point - origin; | |
306 gfx::Point3F center = | |
307 origin + gfx::ScaleVector3d(normal, gfx::DotProduct(to_point, normal)); | |
308 | |
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) | |
314 return; | |
315 | |
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). | |
321 | |
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; | |
341 } | |
342 | |
343 double min_radians = DegreesToRadians(min_degrees); | |
344 double max_radians = DegreesToRadians(max_degrees); | |
345 | |
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) | |
353 continue; | |
354 | |
355 gfx::Transform rotation; | |
356 rotation.RotateAbout(axis, RadiansToDegrees(radians)); | |
357 gfx::Point3F rotated = point; | |
358 rotation.TransformPoint(&rotated); | |
359 | |
360 box->ExpandTo(rotated); | |
361 } | |
362 } | |
363 | |
364 bool TransformOperation::BlendedBoundsForBox(const gfx::BoxF& box, | |
365 const TransformOperation* from, | |
366 const TransformOperation* to, | |
367 SkMScalar min_progress, | |
368 SkMScalar max_progress, | |
369 gfx::BoxF* bounds) { | |
370 bool is_identity_from = IsOperationIdentity(from); | |
371 bool is_identity_to = IsOperationIdentity(to); | |
372 if (is_identity_from && is_identity_to) { | |
373 *bounds = box; | |
374 return true; | |
375 } | |
376 | |
377 TransformOperation::Type interpolation_type = | |
378 TransformOperation::TRANSFORM_OPERATION_IDENTITY; | |
379 if (is_identity_to) | |
380 interpolation_type = from->type; | |
381 else | |
382 interpolation_type = to->type; | |
383 | |
384 switch (interpolation_type) { | |
385 case TransformOperation::TRANSFORM_OPERATION_IDENTITY: | |
386 *bounds = box; | |
387 return true; | |
388 case TransformOperation::TRANSFORM_OPERATION_TRANSLATE: | |
389 case TransformOperation::TRANSFORM_OPERATION_SKEW: | |
390 case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE: | |
391 case TransformOperation::TRANSFORM_OPERATION_SCALE: { | |
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)) | |
396 return false; | |
397 | |
398 *bounds = box; | |
399 from_transform.TransformBox(bounds); | |
400 | |
401 gfx::BoxF to_box = box; | |
402 to_transform.TransformBox(&to_box); | |
403 bounds->ExpandTo(to_box); | |
404 | |
405 return true; | |
406 } | |
407 case TransformOperation::TRANSFORM_OPERATION_ROTATE: { | |
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)) | |
413 return false; | |
414 | |
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; | |
422 BoundingBoxForArc( | |
423 corner, from, to, min_progress, max_progress, &box_for_arc); | |
424 if (first_point) | |
425 *bounds = box_for_arc; | |
426 else | |
427 bounds->Union(box_for_arc); | |
428 first_point = false; | |
429 } | |
430 return true; | |
431 } | |
432 case TransformOperation::TRANSFORM_OPERATION_MATRIX: | |
433 return false; | |
434 } | |
435 NOTREACHED(); | |
436 return false; | |
437 } | |
438 | |
439 } // namespace cc | |
OLD | NEW |