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 #include <algorithm> | |
6 #include <cmath> | |
7 #include <limits> | |
8 #include <vector> | |
9 | |
10 #include "cc/transform_operations.h" | |
11 | |
12 using WebKit::WebTransformationMatrix; | |
13 | |
14 namespace { | |
15 const double EPSILON = 1e-4; | |
jamesr
2013/01/14 22:03:00
use chromium style kEpsilon
it's pretty weird for
Ian Vollick
2013/01/15 15:02:38
I used this epsilon when checking if two rotations
| |
16 } | |
17 | |
18 namespace cc { | |
19 | |
20 struct TransformOperation { | |
21 enum Type { | |
22 TransformOperationTranslate, | |
23 TransformOperationRotate, | |
24 TransformOperationScale, | |
25 TransformOperationSkew, | |
26 TransformOperationPerspective, | |
27 TransformOperationMatrix, | |
28 TransformOperationIdentity | |
29 }; | |
30 | |
31 TransformOperation() | |
32 : type(TransformOperationIdentity) { | |
jamesr
2013/01/14 22:03:00
this should be indented 4 spaces from the previous
| |
33 } | |
34 | |
35 Type type; | |
36 WebKit::WebTransformationMatrix matrix; | |
37 | |
38 union { | |
39 double perspective_depth; | |
40 | |
41 struct { | |
42 double x, y; | |
43 } skew; | |
44 | |
45 struct { | |
46 double x, y, z; | |
47 } scale; | |
48 | |
49 struct { | |
50 double x, y, z; | |
51 } translate; | |
52 | |
53 struct { | |
54 struct { | |
55 double x, y, z; | |
56 } axis; | |
57 | |
58 double angle; | |
59 } rotate; | |
60 }; | |
61 | |
62 bool IsIdentity() const { return matrix.isIdentity(); } | |
63 }; | |
64 | |
65 TransformOperations::TransformOperations() { | |
66 Initialize(); | |
67 } | |
68 | |
69 TransformOperations::TransformOperations(const TransformOperations& other) { | |
70 Initialize(other); | |
71 } | |
72 | |
73 TransformOperations::~TransformOperations() { | |
74 } | |
75 | |
76 struct TransformOperationsPrivate { | |
77 std::vector<TransformOperation> operations; | |
78 }; | |
79 | |
80 WebTransformationMatrix TransformOperations::Apply() const { | |
81 WebTransformationMatrix to_return; | |
82 for (size_t i = 0; i < private_->operations.size(); ++i) | |
83 to_return.multiply(private_->operations[i].matrix); | |
84 return to_return; | |
85 } | |
86 | |
87 static bool IsIdentity(const TransformOperation* operation) { | |
88 return !operation || operation->IsIdentity(); | |
89 } | |
90 | |
91 static bool ShareSameAxis(const TransformOperation* from, | |
92 const TransformOperation* to, | |
93 double& axis_x, double& axis_y, double& axis_z, | |
94 double& angle_from) { | |
95 if (IsIdentity(from) && IsIdentity(to)) | |
96 return false; | |
97 | |
98 if (IsIdentity(from) && !IsIdentity(to)) { | |
99 axis_x = to->rotate.axis.x; | |
100 axis_y = to->rotate.axis.y; | |
101 axis_z = to->rotate.axis.z; | |
102 angle_from = 0; | |
103 return true; | |
104 } | |
105 | |
106 if (!IsIdentity(from) && IsIdentity(to)) { | |
107 axis_x = from->rotate.axis.x; | |
108 axis_y = from->rotate.axis.y; | |
109 axis_z = from->rotate.axis.z; | |
110 angle_from = from->rotate.angle; | |
111 return true; | |
112 } | |
113 | |
114 double length_2 = from->rotate.axis.x * from->rotate.axis.x + | |
115 from->rotate.axis.y * from->rotate.axis.y + | |
116 from->rotate.axis.z * from->rotate.axis.z; | |
117 double other_length_2 = to->rotate.axis.x * to->rotate.axis.x + | |
118 to->rotate.axis.y * to->rotate.axis.y + | |
119 to->rotate.axis.z * to->rotate.axis.z; | |
120 | |
121 if (length_2 <= EPSILON || other_length_2 <= EPSILON) | |
122 return false; | |
123 | |
124 double dot = to->rotate.axis.x * from->rotate.axis.x + | |
125 to->rotate.axis.y * from->rotate.axis.y + | |
126 to->rotate.axis.z * from->rotate.axis.z; | |
127 double error = std::fabs(1.0 - (dot * dot) / (length_2 * other_length_2)); | |
128 bool result = error < EPSILON; | |
129 if (result) { | |
130 axis_x = to->rotate.axis.x; | |
131 axis_y = to->rotate.axis.y; | |
132 axis_z = to->rotate.axis.z; | |
133 // If the axes are pointing in opposite directions, we need to reverse | |
134 // the angle. | |
135 angle_from = dot > 0 ? from->rotate.angle : -from->rotate.angle; | |
136 } | |
137 return result; | |
138 } | |
139 | |
140 static double BlendDoubles(double from, double to, double progress) { | |
141 return from * (1 - progress) + to * progress; | |
142 } | |
143 | |
144 static bool BlendTransformOperations(const TransformOperation* from, | |
145 const TransformOperation* to, | |
146 double progress, | |
147 WebTransformationMatrix& result) { | |
148 if (IsIdentity(from) && IsIdentity(to)) | |
149 return true; | |
150 | |
151 TransformOperation::Type interpolation_type = | |
152 TransformOperation::TransformOperationIdentity; | |
153 if (IsIdentity(to)) | |
154 interpolation_type = from->type; | |
155 else | |
156 interpolation_type = to->type; | |
157 | |
158 switch (interpolation_type) { | |
159 case TransformOperation::TransformOperationTranslate: { | |
160 double from_x = IsIdentity(from) ? 0 : from->translate.x; | |
161 double from_y = IsIdentity(from) ? 0 : from->translate.y; | |
162 double from_z = IsIdentity(from) ? 0 : from->translate.z; | |
163 double to_x = IsIdentity(to) ? 0 : to->translate.x; | |
164 double to_y = IsIdentity(to) ? 0 : to->translate.y; | |
165 double to_z = IsIdentity(to) ? 0 : to->translate.z; | |
166 result.translate3d(BlendDoubles(from_x, to_x, progress), | |
167 BlendDoubles(from_y, to_y, progress), | |
168 BlendDoubles(from_z, to_z, progress)); | |
169 break; | |
170 } | |
171 case TransformOperation::TransformOperationRotate: { | |
172 double axis_x = 0; | |
173 double axis_y = 0; | |
174 double axis_z = 1; | |
175 double from_angle = 0; | |
176 double to_angle = IsIdentity(to) ? 0 : to->rotate.angle; | |
177 if (ShareSameAxis(from, to, axis_x, axis_y, axis_z, from_angle)) | |
178 result.rotate3d(axis_x, axis_y, axis_z, | |
179 BlendDoubles(from_angle, to_angle, progress)); | |
180 else { | |
181 WebTransformationMatrix to_matrix; | |
182 if (!IsIdentity(to)) | |
183 to_matrix = to->matrix; | |
184 WebTransformationMatrix from_matrix; | |
185 if (!IsIdentity(from)) | |
186 from_matrix = from->matrix; | |
187 result = to_matrix; | |
188 if (!result.blend(from_matrix, progress)) | |
189 return false; | |
190 } | |
191 break; | |
192 } | |
193 case TransformOperation::TransformOperationScale: { | |
194 double from_x = IsIdentity(from) ? 1 : from->scale.x; | |
195 double from_y = IsIdentity(from) ? 1 : from->scale.y; | |
196 double from_z = IsIdentity(from) ? 1 : from->scale.z; | |
197 double to_x = IsIdentity(to) ? 1 : to->scale.x; | |
198 double to_y = IsIdentity(to) ? 1 : to->scale.y; | |
199 double to_z = IsIdentity(to) ? 1 : to->scale.z; | |
200 result.scale3d(BlendDoubles(from_x, to_x, progress), | |
201 BlendDoubles(from_y, to_y, progress), | |
202 BlendDoubles(from_z, to_z, progress)); | |
203 break; | |
204 } | |
205 case TransformOperation::TransformOperationSkew: { | |
206 double from_x = IsIdentity(from) ? 0 : from->skew.x; | |
207 double from_y = IsIdentity(from) ? 0 : from->skew.y; | |
208 double to_x = IsIdentity(to) ? 0 : to->skew.x; | |
209 double to_y = IsIdentity(to) ? 0 : to->skew.y; | |
210 result.skewX(BlendDoubles(from_x, to_x, progress)); | |
211 result.skewY(BlendDoubles(from_y, to_y, progress)); | |
212 break; | |
213 } | |
214 case TransformOperation::TransformOperationPerspective: { | |
215 double from_perspective_depth = IsIdentity(from) ? | |
216 std::numeric_limits<double>::max() : from->perspective_depth; | |
217 double to_perspective_depth = IsIdentity(to) ? | |
218 std::numeric_limits<double>::max() : to->perspective_depth; | |
219 result.applyPerspective( | |
220 BlendDoubles(from_perspective_depth, to_perspective_depth, progress)); | |
221 break; | |
222 } | |
223 case TransformOperation::TransformOperationMatrix: { | |
224 WebTransformationMatrix to_matrix; | |
225 if (!IsIdentity(to)) | |
226 to_matrix = to->matrix; | |
227 WebTransformationMatrix from_matrix; | |
228 if (!IsIdentity(from)) | |
229 from_matrix = from->matrix; | |
230 result = to_matrix; | |
231 if (!result.blend(from_matrix, progress)) | |
232 return false; | |
233 break; | |
234 } | |
235 case TransformOperation::TransformOperationIdentity: | |
236 // Do nothing. | |
237 break; | |
238 } | |
239 | |
240 return true; | |
241 } | |
242 | |
243 WebTransformationMatrix TransformOperations::Blend( | |
244 const TransformOperations& from, double progress) const { | |
245 WebTransformationMatrix to_return; | |
246 BlendInternal(from, progress, to_return); | |
247 return to_return; | |
248 } | |
249 | |
250 bool TransformOperations::MatchesTypes(const TransformOperations& other) const { | |
251 if (IsIdentity() || other.IsIdentity()) | |
252 return true; | |
253 | |
254 if (private_->operations.size() != other.private_->operations.size()) | |
255 return false; | |
256 | |
257 for (size_t i = 0; i < private_->operations.size(); ++i) { | |
258 if (private_->operations[i].type != other.private_->operations[i].type | |
259 && !private_->operations[i].IsIdentity() | |
260 && !other.private_->operations[i].IsIdentity()) | |
261 return false; | |
262 } | |
263 | |
264 return true; | |
265 } | |
266 | |
267 bool TransformOperations::CanBlendWith( | |
268 const TransformOperations& other) const { | |
269 WebTransformationMatrix dummy; | |
270 return BlendInternal(other, 0.5, dummy); | |
271 } | |
272 | |
273 void TransformOperations::AppendTranslate(double x, double y, double z) { | |
274 TransformOperation to_add; | |
275 to_add.matrix.translate3d(x, y, z); | |
276 to_add.type = TransformOperation::TransformOperationTranslate; | |
277 to_add.translate.x = x; | |
278 to_add.translate.y = y; | |
279 to_add.translate.z = z; | |
280 private_->operations.push_back(to_add); | |
281 } | |
282 | |
283 void TransformOperations::AppendRotate(double x, double y, double z, | |
284 double degrees) { | |
285 TransformOperation to_add; | |
286 to_add.matrix.rotate3d(x, y, z, degrees); | |
287 to_add.type = TransformOperation::TransformOperationRotate; | |
288 to_add.rotate.axis.x = x; | |
289 to_add.rotate.axis.y = y; | |
290 to_add.rotate.axis.z = z; | |
291 to_add.rotate.angle = degrees; | |
292 private_->operations.push_back(to_add); | |
293 } | |
294 | |
295 void TransformOperations::AppendScale(double x, double y, double z) { | |
296 TransformOperation to_add; | |
297 to_add.matrix.scale3d(x, y, z); | |
298 to_add.type = TransformOperation::TransformOperationScale; | |
299 to_add.scale.x = x; | |
300 to_add.scale.y = y; | |
301 to_add.scale.z = z; | |
302 private_->operations.push_back(to_add); | |
303 } | |
304 | |
305 void TransformOperations::AppendSkew(double x, double y) { | |
306 TransformOperation to_add; | |
307 to_add.matrix.skewX(x); | |
308 to_add.matrix.skewY(y); | |
309 to_add.type = TransformOperation::TransformOperationSkew; | |
310 to_add.skew.x = x; | |
311 to_add.skew.y = y; | |
312 private_->operations.push_back(to_add); | |
313 } | |
314 | |
315 void TransformOperations::AppendPerspective(double depth) { | |
316 TransformOperation to_add; | |
317 to_add.matrix.applyPerspective(depth); | |
318 to_add.type = TransformOperation::TransformOperationPerspective; | |
319 to_add.perspective_depth = depth; | |
320 private_->operations.push_back(to_add); | |
321 } | |
322 | |
323 void TransformOperations::AppendMatrix(const WebTransformationMatrix& matrix) { | |
324 TransformOperation to_add; | |
325 to_add.matrix = matrix; | |
326 to_add.type = TransformOperation::TransformOperationMatrix; | |
327 private_->operations.push_back(to_add); | |
328 } | |
329 | |
330 void TransformOperations::AppendIdentity() { | |
331 private_->operations.push_back(TransformOperation()); | |
332 } | |
333 | |
334 bool TransformOperations::IsIdentity() const { | |
335 for (size_t i = 0; i < private_->operations.size(); ++i) { | |
336 if (!private_->operations[i].IsIdentity()) | |
337 return false; | |
338 } | |
339 return true; | |
340 } | |
341 | |
342 void TransformOperations::Initialize() { | |
343 private_.reset(new TransformOperationsPrivate); | |
344 } | |
345 | |
346 void TransformOperations::Initialize(const TransformOperations& other) { | |
347 if (private_.get() != other.private_.get()) | |
348 private_.reset(new TransformOperationsPrivate(*other.private_.get())); | |
349 else | |
350 Initialize(); | |
351 } | |
352 | |
353 bool TransformOperations::BlendInternal(const TransformOperations& from, | |
354 double progress, | |
355 WebTransformationMatrix& result) const { | |
356 bool from_identity = from.IsIdentity(); | |
357 bool to_identity = IsIdentity(); | |
358 if (from_identity && to_identity) | |
359 return true; | |
360 | |
361 if (MatchesTypes(from)) { | |
362 size_t num_operations = | |
363 std::max(from_identity ? 0 : from.private_->operations.size(), | |
364 to_identity ? 0 : private_->operations.size()); | |
365 for (size_t i = 0; i < num_operations; ++i) { | |
366 WebTransformationMatrix blended; | |
367 if (!BlendTransformOperations( | |
368 from_identity ? 0 : &from.private_->operations[i], | |
369 to_identity ? 0 : &private_->operations[i], | |
370 progress, | |
371 blended)) | |
372 return false; | |
373 result.multiply(blended); | |
374 } | |
375 return true; | |
376 } | |
377 | |
378 result = Apply(); | |
379 WebTransformationMatrix from_transform = from.Apply(); | |
380 return result.blend(from_transform, progress); | |
381 } | |
382 | |
383 } // namespace cc | |
OLD | NEW |