transform_operations_unittest.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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 <limits>
6
7#include "base/basictypes.h"
8#include "base/memory/scoped_vector.h"
9#include "cc/animation/transform_operations.h"
10#include "cc/test/geometry_test_utils.h"
11#include "testing/gtest/include/gtest/gtest.h"
12#include "ui/gfx/animation/tween.h"
13#include "ui/gfx/box_f.h"
14#include "ui/gfx/vector3d_f.h"
15
16namespace cc {
17namespace {
18
19TEST(TransformOperationTest, TransformTypesAreUnique) {
20  ScopedVector<TransformOperations> transforms;
21
22  TransformOperations* to_add = new TransformOperations();
23  to_add->AppendTranslate(1, 0, 0);
24  transforms.push_back(to_add);
25
26  to_add = new TransformOperations();
27  to_add->AppendRotate(0, 0, 1, 2);
28  transforms.push_back(to_add);
29
30  to_add = new TransformOperations();
31  to_add->AppendScale(2, 2, 2);
32  transforms.push_back(to_add);
33
34  to_add = new TransformOperations();
35  to_add->AppendSkew(1, 0);
36  transforms.push_back(to_add);
37
38  to_add = new TransformOperations();
39  to_add->AppendPerspective(800);
40  transforms.push_back(to_add);
41
42  for (size_t i = 0; i < transforms.size(); ++i) {
43    for (size_t j = 0; j < transforms.size(); ++j) {
44      bool matches_type = transforms[i]->MatchesTypes(*transforms[j]);
45      EXPECT_TRUE((i == j && matches_type) || !matches_type);
46    }
47  }
48}
49
50TEST(TransformOperationTest, MatchTypesSameLength) {
51  TransformOperations translates;
52  translates.AppendTranslate(1, 0, 0);
53  translates.AppendTranslate(1, 0, 0);
54  translates.AppendTranslate(1, 0, 0);
55
56  TransformOperations skews;
57  skews.AppendSkew(0, 2);
58  skews.AppendSkew(0, 2);
59  skews.AppendSkew(0, 2);
60
61  TransformOperations translates2;
62  translates2.AppendTranslate(0, 2, 0);
63  translates2.AppendTranslate(0, 2, 0);
64  translates2.AppendTranslate(0, 2, 0);
65
66  TransformOperations translates3 = translates2;
67
68  EXPECT_FALSE(translates.MatchesTypes(skews));
69  EXPECT_TRUE(translates.MatchesTypes(translates2));
70  EXPECT_TRUE(translates.MatchesTypes(translates3));
71}
72
73TEST(TransformOperationTest, MatchTypesDifferentLength) {
74  TransformOperations translates;
75  translates.AppendTranslate(1, 0, 0);
76  translates.AppendTranslate(1, 0, 0);
77  translates.AppendTranslate(1, 0, 0);
78
79  TransformOperations skews;
80  skews.AppendSkew(2, 0);
81  skews.AppendSkew(2, 0);
82
83  TransformOperations translates2;
84  translates2.AppendTranslate(0, 2, 0);
85  translates2.AppendTranslate(0, 2, 0);
86
87  EXPECT_FALSE(translates.MatchesTypes(skews));
88  EXPECT_FALSE(translates.MatchesTypes(translates2));
89}
90
91void GetIdentityOperations(ScopedVector<TransformOperations>* operations) {
92  TransformOperations* to_add = new TransformOperations();
93  operations->push_back(to_add);
94
95  to_add = new TransformOperations();
96  to_add->AppendTranslate(0, 0, 0);
97  operations->push_back(to_add);
98
99  to_add = new TransformOperations();
100  to_add->AppendTranslate(0, 0, 0);
101  to_add->AppendTranslate(0, 0, 0);
102  operations->push_back(to_add);
103
104  to_add = new TransformOperations();
105  to_add->AppendScale(1, 1, 1);
106  operations->push_back(to_add);
107
108  to_add = new TransformOperations();
109  to_add->AppendScale(1, 1, 1);
110  to_add->AppendScale(1, 1, 1);
111  operations->push_back(to_add);
112
113  to_add = new TransformOperations();
114  to_add->AppendSkew(0, 0);
115  operations->push_back(to_add);
116
117  to_add = new TransformOperations();
118  to_add->AppendSkew(0, 0);
119  to_add->AppendSkew(0, 0);
120  operations->push_back(to_add);
121
122  to_add = new TransformOperations();
123  to_add->AppendRotate(0, 0, 1, 0);
124  operations->push_back(to_add);
125
126  to_add = new TransformOperations();
127  to_add->AppendRotate(0, 0, 1, 0);
128  to_add->AppendRotate(0, 0, 1, 0);
129  operations->push_back(to_add);
130
131  to_add = new TransformOperations();
132  to_add->AppendMatrix(gfx::Transform());
133  operations->push_back(to_add);
134
135  to_add = new TransformOperations();
136  to_add->AppendMatrix(gfx::Transform());
137  to_add->AppendMatrix(gfx::Transform());
138  operations->push_back(to_add);
139}
140
141TEST(TransformOperationTest, IdentityAlwaysMatches) {
142  ScopedVector<TransformOperations> operations;
143  GetIdentityOperations(&operations);
144
145  for (size_t i = 0; i < operations.size(); ++i) {
146    for (size_t j = 0; j < operations.size(); ++j)
147      EXPECT_TRUE(operations[i]->MatchesTypes(*operations[j]));
148  }
149}
150
151TEST(TransformOperationTest, ApplyTranslate) {
152  SkMScalar x = 1;
153  SkMScalar y = 2;
154  SkMScalar z = 3;
155  TransformOperations operations;
156  operations.AppendTranslate(x, y, z);
157  gfx::Transform expected;
158  expected.Translate3d(x, y, z);
159  EXPECT_TRANSFORMATION_MATRIX_EQ(expected, operations.Apply());
160}
161
162TEST(TransformOperationTest, ApplyRotate) {
163  SkMScalar x = 1;
164  SkMScalar y = 2;
165  SkMScalar z = 3;
166  SkMScalar degrees = 80;
167  TransformOperations operations;
168  operations.AppendRotate(x, y, z, degrees);
169  gfx::Transform expected;
170  expected.RotateAbout(gfx::Vector3dF(x, y, z), degrees);
171  EXPECT_TRANSFORMATION_MATRIX_EQ(expected, operations.Apply());
172}
173
174TEST(TransformOperationTest, ApplyScale) {
175  SkMScalar x = 1;
176  SkMScalar y = 2;
177  SkMScalar z = 3;
178  TransformOperations operations;
179  operations.AppendScale(x, y, z);
180  gfx::Transform expected;
181  expected.Scale3d(x, y, z);
182  EXPECT_TRANSFORMATION_MATRIX_EQ(expected, operations.Apply());
183}
184
185TEST(TransformOperationTest, ApplySkew) {
186  SkMScalar x = 1;
187  SkMScalar y = 2;
188  TransformOperations operations;
189  operations.AppendSkew(x, y);
190  gfx::Transform expected;
191  expected.SkewX(x);
192  expected.SkewY(y);
193  EXPECT_TRANSFORMATION_MATRIX_EQ(expected, operations.Apply());
194}
195
196TEST(TransformOperationTest, ApplyPerspective) {
197  SkMScalar depth = 800;
198  TransformOperations operations;
199  operations.AppendPerspective(depth);
200  gfx::Transform expected;
201  expected.ApplyPerspectiveDepth(depth);
202  EXPECT_TRANSFORMATION_MATRIX_EQ(expected, operations.Apply());
203}
204
205TEST(TransformOperationTest, ApplyMatrix) {
206  SkMScalar dx = 1;
207  SkMScalar dy = 2;
208  SkMScalar dz = 3;
209  gfx::Transform expected_matrix;
210  expected_matrix.Translate3d(dx, dy, dz);
211  TransformOperations matrix_transform;
212  matrix_transform.AppendMatrix(expected_matrix);
213  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_matrix, matrix_transform.Apply());
214}
215
216TEST(TransformOperationTest, ApplyOrder) {
217  SkMScalar sx = 2;
218  SkMScalar sy = 4;
219  SkMScalar sz = 8;
220
221  SkMScalar dx = 1;
222  SkMScalar dy = 2;
223  SkMScalar dz = 3;
224
225  TransformOperations operations;
226  operations.AppendScale(sx, sy, sz);
227  operations.AppendTranslate(dx, dy, dz);
228
229  gfx::Transform expected_scale_matrix;
230  expected_scale_matrix.Scale3d(sx, sy, sz);
231
232  gfx::Transform expected_translate_matrix;
233  expected_translate_matrix.Translate3d(dx, dy, dz);
234
235  gfx::Transform expected_combined_matrix = expected_scale_matrix;
236  expected_combined_matrix.PreconcatTransform(expected_translate_matrix);
237
238  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_combined_matrix, operations.Apply());
239}
240
241TEST(TransformOperationTest, BlendOrder) {
242  SkMScalar sx1 = 2;
243  SkMScalar sy1 = 4;
244  SkMScalar sz1 = 8;
245
246  SkMScalar dx1 = 1;
247  SkMScalar dy1 = 2;
248  SkMScalar dz1 = 3;
249
250  SkMScalar sx2 = 4;
251  SkMScalar sy2 = 8;
252  SkMScalar sz2 = 16;
253
254  SkMScalar dx2 = 10;
255  SkMScalar dy2 = 20;
256  SkMScalar dz2 = 30;
257
258  TransformOperations operations_from;
259  operations_from.AppendScale(sx1, sy1, sz1);
260  operations_from.AppendTranslate(dx1, dy1, dz1);
261
262  TransformOperations operations_to;
263  operations_to.AppendScale(sx2, sy2, sz2);
264  operations_to.AppendTranslate(dx2, dy2, dz2);
265
266  gfx::Transform scale_from;
267  scale_from.Scale3d(sx1, sy1, sz1);
268  gfx::Transform translate_from;
269  translate_from.Translate3d(dx1, dy1, dz1);
270
271  gfx::Transform scale_to;
272  scale_to.Scale3d(sx2, sy2, sz2);
273  gfx::Transform translate_to;
274  translate_to.Translate3d(dx2, dy2, dz2);
275
276  SkMScalar progress = 0.25f;
277
278  gfx::Transform blended_scale = scale_to;
279  blended_scale.Blend(scale_from, progress);
280
281  gfx::Transform blended_translate = translate_to;
282  blended_translate.Blend(translate_from, progress);
283
284  gfx::Transform expected = blended_scale;
285  expected.PreconcatTransform(blended_translate);
286
287  EXPECT_TRANSFORMATION_MATRIX_EQ(
288      expected, operations_to.Blend(operations_from, progress));
289}
290
291static void CheckProgress(SkMScalar progress,
292                          const gfx::Transform& from_matrix,
293                          const gfx::Transform& to_matrix,
294                          const TransformOperations& from_transform,
295                          const TransformOperations& to_transform) {
296  gfx::Transform expected_matrix = to_matrix;
297  expected_matrix.Blend(from_matrix, progress);
298  EXPECT_TRANSFORMATION_MATRIX_EQ(
299      expected_matrix, to_transform.Blend(from_transform, progress));
300}
301
302TEST(TransformOperationTest, BlendProgress) {
303  SkMScalar sx = 2;
304  SkMScalar sy = 4;
305  SkMScalar sz = 8;
306  TransformOperations operations_from;
307  operations_from.AppendScale(sx, sy, sz);
308
309  gfx::Transform matrix_from;
310  matrix_from.Scale3d(sx, sy, sz);
311
312  sx = 4;
313  sy = 8;
314  sz = 16;
315  TransformOperations operations_to;
316  operations_to.AppendScale(sx, sy, sz);
317
318  gfx::Transform matrix_to;
319  matrix_to.Scale3d(sx, sy, sz);
320
321  CheckProgress(-1, matrix_from, matrix_to, operations_from, operations_to);
322  CheckProgress(0, matrix_from, matrix_to, operations_from, operations_to);
323  CheckProgress(0.25f, matrix_from, matrix_to, operations_from, operations_to);
324  CheckProgress(0.5f, matrix_from, matrix_to, operations_from, operations_to);
325  CheckProgress(1, matrix_from, matrix_to, operations_from, operations_to);
326  CheckProgress(2, matrix_from, matrix_to, operations_from, operations_to);
327}
328
329TEST(TransformOperationTest, BlendWhenTypesDoNotMatch) {
330  SkMScalar sx1 = 2;
331  SkMScalar sy1 = 4;
332  SkMScalar sz1 = 8;
333
334  SkMScalar dx1 = 1;
335  SkMScalar dy1 = 2;
336  SkMScalar dz1 = 3;
337
338  SkMScalar sx2 = 4;
339  SkMScalar sy2 = 8;
340  SkMScalar sz2 = 16;
341
342  SkMScalar dx2 = 10;
343  SkMScalar dy2 = 20;
344  SkMScalar dz2 = 30;
345
346  TransformOperations operations_from;
347  operations_from.AppendScale(sx1, sy1, sz1);
348  operations_from.AppendTranslate(dx1, dy1, dz1);
349
350  TransformOperations operations_to;
351  operations_to.AppendTranslate(dx2, dy2, dz2);
352  operations_to.AppendScale(sx2, sy2, sz2);
353
354  gfx::Transform from;
355  from.Scale3d(sx1, sy1, sz1);
356  from.Translate3d(dx1, dy1, dz1);
357
358  gfx::Transform to;
359  to.Translate3d(dx2, dy2, dz2);
360  to.Scale3d(sx2, sy2, sz2);
361
362  SkMScalar progress = 0.25f;
363
364  gfx::Transform expected = to;
365  expected.Blend(from, progress);
366
367  EXPECT_TRANSFORMATION_MATRIX_EQ(
368      expected, operations_to.Blend(operations_from, progress));
369}
370
371TEST(TransformOperationTest, LargeRotationsWithSameAxis) {
372  TransformOperations operations_from;
373  operations_from.AppendRotate(0, 0, 1, 0);
374
375  TransformOperations operations_to;
376  operations_to.AppendRotate(0, 0, 2, 360);
377
378  SkMScalar progress = 0.5f;
379
380  gfx::Transform expected;
381  expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 180);
382
383  EXPECT_TRANSFORMATION_MATRIX_EQ(
384      expected, operations_to.Blend(operations_from, progress));
385}
386
387TEST(TransformOperationTest, LargeRotationsWithSameAxisInDifferentDirection) {
388  TransformOperations operations_from;
389  operations_from.AppendRotate(0, 0, 1, 180);
390
391  TransformOperations operations_to;
392  operations_to.AppendRotate(0, 0, -1, 180);
393
394  SkMScalar progress = 0.5f;
395
396  gfx::Transform expected;
397
398  EXPECT_TRANSFORMATION_MATRIX_EQ(
399      expected, operations_to.Blend(operations_from, progress));
400}
401
402TEST(TransformOperationTest, LargeRotationsWithDifferentAxes) {
403  TransformOperations operations_from;
404  operations_from.AppendRotate(0, 0, 1, 175);
405
406  TransformOperations operations_to;
407  operations_to.AppendRotate(0, 1, 0, 175);
408
409  SkMScalar progress = 0.5f;
410  gfx::Transform matrix_from;
411  matrix_from.RotateAbout(gfx::Vector3dF(0, 0, 1), 175);
412
413  gfx::Transform matrix_to;
414  matrix_to.RotateAbout(gfx::Vector3dF(0, 1, 0), 175);
415
416  gfx::Transform expected = matrix_to;
417  expected.Blend(matrix_from, progress);
418
419  EXPECT_TRANSFORMATION_MATRIX_EQ(
420      expected, operations_to.Blend(operations_from, progress));
421}
422
423TEST(TransformOperationTest, BlendRotationFromIdentity) {
424  ScopedVector<TransformOperations> identity_operations;
425  GetIdentityOperations(&identity_operations);
426
427  for (size_t i = 0; i < identity_operations.size(); ++i) {
428    TransformOperations operations;
429    operations.AppendRotate(0, 0, 1, 360);
430
431    SkMScalar progress = 0.5f;
432
433    gfx::Transform expected;
434    expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 180);
435
436    EXPECT_TRANSFORMATION_MATRIX_EQ(
437        expected, operations.Blend(*identity_operations[i], progress));
438
439    progress = -0.5f;
440
441    expected.MakeIdentity();
442    expected.RotateAbout(gfx::Vector3dF(0, 0, 1), -180);
443
444    EXPECT_TRANSFORMATION_MATRIX_EQ(
445        expected, operations.Blend(*identity_operations[i], progress));
446
447    progress = 1.5f;
448
449    expected.MakeIdentity();
450    expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 540);
451
452    EXPECT_TRANSFORMATION_MATRIX_EQ(
453        expected, operations.Blend(*identity_operations[i], progress));
454  }
455}
456
457TEST(TransformOperationTest, BlendTranslationFromIdentity) {
458  ScopedVector<TransformOperations> identity_operations;
459  GetIdentityOperations(&identity_operations);
460
461  for (size_t i = 0; i < identity_operations.size(); ++i) {
462    TransformOperations operations;
463    operations.AppendTranslate(2, 2, 2);
464
465    SkMScalar progress = 0.5f;
466
467    gfx::Transform expected;
468    expected.Translate3d(1, 1, 1);
469
470    EXPECT_TRANSFORMATION_MATRIX_EQ(
471        expected, operations.Blend(*identity_operations[i], progress));
472
473    progress = -0.5f;
474
475    expected.MakeIdentity();
476    expected.Translate3d(-1, -1, -1);
477
478    EXPECT_TRANSFORMATION_MATRIX_EQ(
479        expected, operations.Blend(*identity_operations[i], progress));
480
481    progress = 1.5f;
482
483    expected.MakeIdentity();
484    expected.Translate3d(3, 3, 3);
485
486    EXPECT_TRANSFORMATION_MATRIX_EQ(
487        expected, operations.Blend(*identity_operations[i], progress));
488  }
489}
490
491TEST(TransformOperationTest, BlendScaleFromIdentity) {
492  ScopedVector<TransformOperations> identity_operations;
493  GetIdentityOperations(&identity_operations);
494
495  for (size_t i = 0; i < identity_operations.size(); ++i) {
496    TransformOperations operations;
497    operations.AppendScale(3, 3, 3);
498
499    SkMScalar progress = 0.5f;
500
501    gfx::Transform expected;
502    expected.Scale3d(2, 2, 2);
503
504    EXPECT_TRANSFORMATION_MATRIX_EQ(
505        expected, operations.Blend(*identity_operations[i], progress));
506
507    progress = -0.5f;
508
509    expected.MakeIdentity();
510    expected.Scale3d(0, 0, 0);
511
512    EXPECT_TRANSFORMATION_MATRIX_EQ(
513        expected, operations.Blend(*identity_operations[i], progress));
514
515    progress = 1.5f;
516
517    expected.MakeIdentity();
518    expected.Scale3d(4, 4, 4);
519
520    EXPECT_TRANSFORMATION_MATRIX_EQ(
521        expected, operations.Blend(*identity_operations[i], progress));
522  }
523}
524
525TEST(TransformOperationTest, BlendSkewFromIdentity) {
526  ScopedVector<TransformOperations> identity_operations;
527  GetIdentityOperations(&identity_operations);
528
529  for (size_t i = 0; i < identity_operations.size(); ++i) {
530    TransformOperations operations;
531    operations.AppendSkew(2, 2);
532
533    SkMScalar progress = 0.5f;
534
535    gfx::Transform expected;
536    expected.SkewX(1);
537    expected.SkewY(1);
538
539    EXPECT_TRANSFORMATION_MATRIX_EQ(
540        expected, operations.Blend(*identity_operations[i], progress));
541
542    progress = -0.5f;
543
544    expected.MakeIdentity();
545    expected.SkewX(-1);
546    expected.SkewY(-1);
547
548    EXPECT_TRANSFORMATION_MATRIX_EQ(
549        expected, operations.Blend(*identity_operations[i], progress));
550
551    progress = 1.5f;
552
553    expected.MakeIdentity();
554    expected.SkewX(3);
555    expected.SkewY(3);
556
557    EXPECT_TRANSFORMATION_MATRIX_EQ(
558        expected, operations.Blend(*identity_operations[i], progress));
559  }
560}
561
562TEST(TransformOperationTest, BlendPerspectiveFromIdentity) {
563  ScopedVector<TransformOperations> identity_operations;
564  GetIdentityOperations(&identity_operations);
565
566  for (size_t i = 0; i < identity_operations.size(); ++i) {
567    TransformOperations operations;
568    operations.AppendPerspective(1000);
569
570    SkMScalar progress = 0.5f;
571
572    gfx::Transform expected;
573    expected.ApplyPerspectiveDepth(500 +
574                                   0.5 * std::numeric_limits<SkMScalar>::max());
575
576    EXPECT_TRANSFORMATION_MATRIX_EQ(
577        expected, operations.Blend(*identity_operations[i], progress));
578  }
579}
580
581TEST(TransformOperationTest, BlendRotationToIdentity) {
582  ScopedVector<TransformOperations> identity_operations;
583  GetIdentityOperations(&identity_operations);
584
585  for (size_t i = 0; i < identity_operations.size(); ++i) {
586    TransformOperations operations;
587    operations.AppendRotate(0, 0, 1, 360);
588
589    SkMScalar progress = 0.5f;
590
591    gfx::Transform expected;
592    expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 180);
593
594    EXPECT_TRANSFORMATION_MATRIX_EQ(
595        expected, identity_operations[i]->Blend(operations, progress));
596  }
597}
598
599TEST(TransformOperationTest, BlendTranslationToIdentity) {
600  ScopedVector<TransformOperations> identity_operations;
601  GetIdentityOperations(&identity_operations);
602
603  for (size_t i = 0; i < identity_operations.size(); ++i) {
604    TransformOperations operations;
605    operations.AppendTranslate(2, 2, 2);
606
607    SkMScalar progress = 0.5f;
608
609    gfx::Transform expected;
610    expected.Translate3d(1, 1, 1);
611
612    EXPECT_TRANSFORMATION_MATRIX_EQ(
613        expected, identity_operations[i]->Blend(operations, progress));
614  }
615}
616
617TEST(TransformOperationTest, BlendScaleToIdentity) {
618  ScopedVector<TransformOperations> identity_operations;
619  GetIdentityOperations(&identity_operations);
620
621  for (size_t i = 0; i < identity_operations.size(); ++i) {
622    TransformOperations operations;
623    operations.AppendScale(3, 3, 3);
624
625    SkMScalar progress = 0.5f;
626
627    gfx::Transform expected;
628    expected.Scale3d(2, 2, 2);
629
630    EXPECT_TRANSFORMATION_MATRIX_EQ(
631        expected, identity_operations[i]->Blend(operations, progress));
632  }
633}
634
635TEST(TransformOperationTest, BlendSkewToIdentity) {
636  ScopedVector<TransformOperations> identity_operations;
637  GetIdentityOperations(&identity_operations);
638
639  for (size_t i = 0; i < identity_operations.size(); ++i) {
640    TransformOperations operations;
641    operations.AppendSkew(2, 2);
642
643    SkMScalar progress = 0.5f;
644
645    gfx::Transform expected;
646    expected.SkewX(1);
647    expected.SkewY(1);
648
649    EXPECT_TRANSFORMATION_MATRIX_EQ(
650        expected, identity_operations[i]->Blend(operations, progress));
651  }
652}
653
654TEST(TransformOperationTest, BlendPerspectiveToIdentity) {
655  ScopedVector<TransformOperations> identity_operations;
656  GetIdentityOperations(&identity_operations);
657
658  for (size_t i = 0; i < identity_operations.size(); ++i) {
659    TransformOperations operations;
660    operations.AppendPerspective(1000);
661
662    SkMScalar progress = 0.5f;
663
664    gfx::Transform expected;
665    expected.ApplyPerspectiveDepth(500 +
666                                   0.5 * std::numeric_limits<SkMScalar>::max());
667
668    EXPECT_TRANSFORMATION_MATRIX_EQ(
669        expected, identity_operations[i]->Blend(operations, progress));
670  }
671}
672
673TEST(TransformOperationTest, ExtrapolatePerspectiveBlending) {
674  TransformOperations operations1;
675  operations1.AppendPerspective(1000);
676
677  TransformOperations operations2;
678  operations2.AppendPerspective(500);
679
680  gfx::Transform expected;
681  expected.ApplyPerspectiveDepth(250);
682
683  EXPECT_TRANSFORMATION_MATRIX_EQ(
684      expected, operations1.Blend(operations2, -0.5));
685
686  expected.MakeIdentity();
687  expected.ApplyPerspectiveDepth(1250);
688
689  EXPECT_TRANSFORMATION_MATRIX_EQ(
690      expected, operations1.Blend(operations2, 1.5));
691}
692
693TEST(TransformOperationTest, ExtrapolateMatrixBlending) {
694  gfx::Transform transform1;
695  transform1.Translate3d(1, 1, 1);
696  TransformOperations operations1;
697  operations1.AppendMatrix(transform1);
698
699  gfx::Transform transform2;
700  transform2.Translate3d(3, 3, 3);
701  TransformOperations operations2;
702  operations2.AppendMatrix(transform2);
703
704  gfx::Transform expected;
705  EXPECT_TRANSFORMATION_MATRIX_EQ(
706      expected, operations1.Blend(operations2, 1.5));
707
708  expected.Translate3d(4, 4, 4);
709  EXPECT_TRANSFORMATION_MATRIX_EQ(
710      expected, operations1.Blend(operations2, -0.5));
711}
712
713TEST(TransformOperationTest, BlendedBoundsWhenTypesDoNotMatch) {
714  TransformOperations operations_from;
715  operations_from.AppendScale(2.0, 4.0, 8.0);
716  operations_from.AppendTranslate(1.0, 2.0, 3.0);
717
718  TransformOperations operations_to;
719  operations_to.AppendTranslate(10.0, 20.0, 30.0);
720  operations_to.AppendScale(4.0, 8.0, 16.0);
721
722  gfx::BoxF box(1.f, 1.f, 1.f);
723  gfx::BoxF bounds;
724
725  SkMScalar min_progress = 0.f;
726  SkMScalar max_progress = 1.f;
727
728  EXPECT_FALSE(operations_to.BlendedBoundsForBox(
729      box, operations_from, min_progress, max_progress, &bounds));
730}
731
732TEST(TransformOperationTest, BlendedBoundsForIdentity) {
733  TransformOperations operations_from;
734  operations_from.AppendIdentity();
735  TransformOperations operations_to;
736  operations_to.AppendIdentity();
737
738  gfx::BoxF box(1.f, 2.f, 3.f);
739  gfx::BoxF bounds;
740
741  SkMScalar min_progress = 0.f;
742  SkMScalar max_progress = 1.f;
743
744  EXPECT_TRUE(operations_to.BlendedBoundsForBox(
745      box, operations_from, min_progress, max_progress, &bounds));
746  EXPECT_EQ(box.ToString(), bounds.ToString());
747}
748
749TEST(TransformOperationTest, BlendedBoundsForTranslate) {
750  TransformOperations operations_from;
751  operations_from.AppendTranslate(3.0, -4.0, 2.0);
752  TransformOperations operations_to;
753  operations_to.AppendTranslate(7.0, 4.0, -2.0);
754
755  gfx::BoxF box(1.f, 2.f, 3.f, 4.f, 4.f, 4.f);
756  gfx::BoxF bounds;
757
758  SkMScalar min_progress = -0.5f;
759  SkMScalar max_progress = 1.5f;
760  EXPECT_TRUE(operations_to.BlendedBoundsForBox(
761      box, operations_from, min_progress, max_progress, &bounds));
762  EXPECT_EQ(gfx::BoxF(2.f, -6.f, -1.f, 12.f, 20.f, 12.f).ToString(),
763            bounds.ToString());
764
765  min_progress = 0.f;
766  max_progress = 1.f;
767  EXPECT_TRUE(operations_to.BlendedBoundsForBox(
768      box, operations_from, min_progress, max_progress, &bounds));
769  EXPECT_EQ(gfx::BoxF(4.f, -2.f, 1.f, 8.f, 12.f, 8.f).ToString(),
770            bounds.ToString());
771
772  TransformOperations identity;
773  EXPECT_TRUE(operations_to.BlendedBoundsForBox(
774        box, identity, min_progress, max_progress, &bounds));
775  EXPECT_EQ(gfx::BoxF(1.f, 2.f, 1.f, 11.f, 8.f, 6.f).ToString(),
776            bounds.ToString());
777
778  EXPECT_TRUE(identity.BlendedBoundsForBox(
779        box, operations_from, min_progress, max_progress, &bounds));
780  EXPECT_EQ(gfx::BoxF(1.f, -2.f, 3.f, 7.f, 8.f, 6.f).ToString(),
781            bounds.ToString());
782}
783
784TEST(TransformOperationTest, BlendedBoundsForScale) {
785  TransformOperations operations_from;
786  operations_from.AppendScale(3.0, 0.5, 2.0);
787  TransformOperations operations_to;
788  operations_to.AppendScale(7.0, 4.0, -2.0);
789
790  gfx::BoxF box(1.f, 2.f, 3.f, 4.f, 4.f, 4.f);
791  gfx::BoxF bounds;
792
793  SkMScalar min_progress = -0.5f;
794  SkMScalar max_progress = 1.5f;
795  EXPECT_TRUE(operations_to.BlendedBoundsForBox(
796      box, operations_from, min_progress, max_progress, &bounds));
797  EXPECT_EQ(gfx::BoxF(1.f, -7.5f, -28.f, 44.f, 42.f, 56.f).ToString(),
798            bounds.ToString());
799
800  min_progress = 0.f;
801  max_progress = 1.f;
802  EXPECT_TRUE(operations_to.BlendedBoundsForBox(
803      box, operations_from, min_progress, max_progress, &bounds));
804  EXPECT_EQ(gfx::BoxF(3.f, 1.f, -14.f, 32.f, 23.f, 28.f).ToString(),
805            bounds.ToString());
806
807  TransformOperations identity;
808  EXPECT_TRUE(operations_to.BlendedBoundsForBox(
809        box, identity, min_progress, max_progress, &bounds));
810  EXPECT_EQ(gfx::BoxF(1.f, 2.f, -14.f, 34.f, 22.f, 21.f).ToString(),
811            bounds.ToString());
812
813  EXPECT_TRUE(identity.BlendedBoundsForBox(
814        box, operations_from, min_progress, max_progress, &bounds));
815  EXPECT_EQ(gfx::BoxF(1.f, 1.f, 3.f, 14.f, 5.f, 11.f).ToString(),
816            bounds.ToString());
817}
818
819TEST(TransformOperationTest, BlendedBoundsWithZeroScale) {
820  TransformOperations zero_scale;
821  zero_scale.AppendScale(0.0, 0.0, 0.0);
822  TransformOperations non_zero_scale;
823  non_zero_scale.AppendScale(2.0, -4.0, 5.0);
824
825  gfx::BoxF box(1.f, 2.f, 3.f, 4.f, 4.f, 4.f);
826  gfx::BoxF bounds;
827
828  SkMScalar min_progress = 0.f;
829  SkMScalar max_progress = 1.f;
830  EXPECT_TRUE(zero_scale.BlendedBoundsForBox(
831      box, non_zero_scale, min_progress, max_progress, &bounds));
832  EXPECT_EQ(gfx::BoxF(0.f, -24.f, 0.f, 10.f, 24.f, 35.f).ToString(),
833            bounds.ToString());
834
835  EXPECT_TRUE(non_zero_scale.BlendedBoundsForBox(
836      box, zero_scale, min_progress, max_progress, &bounds));
837  EXPECT_EQ(gfx::BoxF(0.f, -24.f, 0.f, 10.f, 24.f, 35.f).ToString(),
838            bounds.ToString());
839
840  EXPECT_TRUE(zero_scale.BlendedBoundsForBox(
841      box, zero_scale, min_progress, max_progress, &bounds));
842  EXPECT_EQ(gfx::BoxF().ToString(), bounds.ToString());
843}
844
845TEST(TransformOperationTest, BlendedBoundsForRotationTrivial) {
846  TransformOperations operations_from;
847  operations_from.AppendRotate(0.f, 0.f, 1.f, 0.f);
848  TransformOperations operations_to;
849  operations_to.AppendRotate(0.f, 0.f, 1.f, 360.f);
850
851  float sqrt_2 = sqrt(2.f);
852  gfx::BoxF box(
853      -sqrt_2, -sqrt_2, 0.f, sqrt_2, sqrt_2, 0.f);
854  gfx::BoxF bounds;
855
856  // Since we're rotating 360 degrees, any box with dimensions between 0 and
857  // 2 * sqrt(2) should give the same result.
858  float sizes[] = { 0.f, 0.1f, sqrt_2, 2.f * sqrt_2 };
859  for (size_t i = 0; i < arraysize(sizes); ++i) {
860    box.set_size(sizes[i], sizes[i], 0.f);
861    SkMScalar min_progress = 0.f;
862    SkMScalar max_progress = 1.f;
863    EXPECT_TRUE(operations_to.BlendedBoundsForBox(
864        box, operations_from, min_progress, max_progress, &bounds));
865    EXPECT_EQ(gfx::BoxF(-2.f, -2.f, 0.f, 4.f, 4.f, 0.f).ToString(),
866              bounds.ToString());
867  }
868}
869
870TEST(TransformOperationTest, BlendedBoundsForRotationAllExtrema) {
871  // If the normal is out of the plane, we can have up to 6 extrema (a min/max
872  // in each dimension) between the endpoints of the arc. This test ensures that
873  // we consider all 6.
874  TransformOperations operations_from;
875  operations_from.AppendRotate(1.f, 1.f, 1.f, 30.f);
876  TransformOperations operations_to;
877  operations_to.AppendRotate(1.f, 1.f, 1.f, 390.f);
878
879  gfx::BoxF box(1.f, 0.f, 0.f, 0.f, 0.f, 0.f);
880  gfx::BoxF bounds;
881
882  float min = -1.f / 3.f;
883  float max = 1.f;
884  float size = max - min;
885  EXPECT_TRUE(operations_to.BlendedBoundsForBox(
886      box, operations_from, 0.f, 1.f, &bounds));
887  EXPECT_EQ(gfx::BoxF(min, min, min, size, size, size).ToString(),
888            bounds.ToString());
889}
890
891TEST(TransformOperationTest, BlendedBoundsForRotationPointOnAxis) {
892  // Checks that if the point to rotate is sitting on the axis of rotation, that
893  // it does not get affected.
894  TransformOperations operations_from;
895  operations_from.AppendRotate(1.f, 1.f, 1.f, 30.f);
896  TransformOperations operations_to;
897  operations_to.AppendRotate(1.f, 1.f, 1.f, 390.f);
898
899  gfx::BoxF box(1.f, 1.f, 1.f, 0.f, 0.f, 0.f);
900  gfx::BoxF bounds;
901
902  EXPECT_TRUE(operations_to.BlendedBoundsForBox(
903      box, operations_from, 0.f, 1.f, &bounds));
904  EXPECT_EQ(box.ToString(), bounds.ToString());
905}
906
907// This would have been best as anonymous structs, but |arraysize| does not get
908// along with anonymous structs (and using ARRAYSIZE_UNSAFE seemed like a worse
909// option).
910struct ProblematicAxisTest {
911  float x;
912  float y;
913  float z;
914  gfx::BoxF expected;
915};
916
917TEST(TransformOperationTest, BlendedBoundsForRotationProblematicAxes) {
918  // Zeros in the components of the axis of rotation turned out to be tricky to
919  // deal with in practice. This function tests some potentially problematic
920  // axes to ensure sane behavior.
921
922  // Some common values used in the expected boxes.
923  float dim1 = 0.292893f;
924  float dim2 = sqrt(2.f);
925  float dim3 = 2.f * dim2;
926
927  ProblematicAxisTest tests[] = {
928    { 0.f, 0.f, 0.f, gfx::BoxF(1.f, 1.f, 1.f, 0.f, 0.f, 0.f) },
929    { 1.f, 0.f, 0.f, gfx::BoxF(1.f, -dim2, -dim2, 0.f, dim3, dim3) },
930    { 0.f, 1.f, 0.f, gfx::BoxF(-dim2, 1.f, -dim2, dim3, 0.f, dim3) },
931    { 0.f, 0.f, 1.f, gfx::BoxF(-dim2, -dim2, 1.f, dim3, dim3, 0.f) },
932    { 1.f, 1.f, 0.f, gfx::BoxF(dim1, dim1, -1.f, dim2, dim2, 2.f) },
933    { 0.f, 1.f, 1.f, gfx::BoxF(-1.f, dim1, dim1, 2.f, dim2, dim2) },
934    { 1.f, 0.f, 1.f, gfx::BoxF(dim1, -1.f, dim1, dim2, 2.f, dim2) }
935  };
936
937  for (size_t i = 0; i < arraysize(tests); ++i) {
938    float x = tests[i].x;
939    float y = tests[i].y;
940    float z = tests[i].z;
941    TransformOperations operations_from;
942    operations_from.AppendRotate(x, y, z, 0.f);
943    TransformOperations operations_to;
944    operations_to.AppendRotate(x, y, z, 360.f);
945    gfx::BoxF box(1.f, 1.f, 1.f, 0.f, 0.f, 0.f);
946    gfx::BoxF bounds;
947
948    EXPECT_TRUE(operations_to.BlendedBoundsForBox(
949        box, operations_from, 0.f, 1.f, &bounds));
950    EXPECT_EQ(tests[i].expected.ToString(), bounds.ToString());
951  }
952}
953
954// These would have been best as anonymous structs, but |arraysize| does not get
955// along with anonymous structs (and using ARRAYSIZE_UNSAFE seemed like a worse
956// option).
957struct TestAxis {
958  float x;
959  float y;
960  float z;
961};
962
963struct TestAngles {
964  float theta_from;
965  float theta_to;
966};
967
968struct TestProgress {
969  float min_progress;
970  float max_progress;
971};
972
973TEST(TransformOperationsTest, BlendedBoundsForRotationEmpiricalTests) {
974  // Sets up various axis angle combinations, computes the bounding box and
975  // empirically tests that the transformed bounds are indeed contained by the
976  // computed bounding box.
977
978  TestAxis axes[] = {
979    { 1.f, 1.f, 1.f },
980    { -1.f, -1.f, -1.f },
981    { -1.f, 2.f, 3.f },
982    { 1.f, -2.f, 3.f },
983    { 1.f, 2.f, -3.f },
984    { 0.f, 0.f, 0.f },
985    { 1.f, 0.f, 0.f },
986    { 0.f, 1.f, 0.f },
987    { 0.f, 0.f, 1.f },
988    { 1.f, 1.f, 0.f },
989    { 0.f, 1.f, 1.f },
990    { 1.f, 0.f, 1.f },
991    { -1.f, 0.f, 0.f },
992    { 0.f, -1.f, 0.f },
993    { 0.f, 0.f, -1.f },
994    { -1.f, -1.f, 0.f },
995    { 0.f, -1.f, -1.f },
996    { -1.f, 0.f, -1.f }
997  };
998
999  TestAngles angles[] = {
1000    { 5.f, 10.f },
1001    { 10.f, 5.f },
1002    { 0.f, 360.f },
1003    { 20.f, 180.f },
1004    { -20.f, -180.f },
1005    { 180.f, -220.f },
1006    { 220.f, 320.f }
1007  };
1008
1009  // We can go beyond the range [0, 1] (the bezier might slide out of this range
1010  // at either end), but since the first and last knots are at (0, 0) and (1, 1)
1011  // we will never go within it, so these tests are sufficient.
1012  TestProgress progress[] = {
1013    { 0.f, 1.f },
1014    { -.25f, 1.25f },
1015  };
1016
1017  size_t num_steps = 150;
1018  for (size_t i = 0; i < arraysize(axes); ++i) {
1019    for (size_t j = 0; j < arraysize(angles); ++j) {
1020      for (size_t k = 0; k < arraysize(progress); ++k) {
1021        float x = axes[i].x;
1022        float y = axes[i].y;
1023        float z = axes[i].z;
1024        TransformOperations operations_from;
1025        operations_from.AppendRotate(x, y, z, angles[j].theta_from);
1026        TransformOperations operations_to;
1027        operations_to.AppendRotate(x, y, z, angles[j].theta_to);
1028
1029        gfx::BoxF box(2.f, 5.f, 6.f, 1.f, 3.f, 2.f);
1030        gfx::BoxF bounds;
1031
1032        EXPECT_TRUE(operations_to.BlendedBoundsForBox(box,
1033                                                      operations_from,
1034                                                      progress[k].min_progress,
1035                                                      progress[k].max_progress,
1036                                                      &bounds));
1037        bool first_point = true;
1038        gfx::BoxF empirical_bounds;
1039        for (size_t step = 0; step < num_steps; ++step) {
1040          float t = step / (num_steps - 1.f);
1041          t = gfx::Tween::FloatValueBetween(
1042              t, progress[k].min_progress, progress[k].max_progress);
1043          gfx::Transform partial_rotation =
1044              operations_to.Blend(operations_from, t);
1045
1046          for (int corner = 0; corner < 8; ++corner) {
1047            gfx::Point3F point = box.origin();
1048            point += gfx::Vector3dF(corner & 1 ? box.width() : 0.f,
1049                                    corner & 2 ? box.height() : 0.f,
1050                                    corner & 4 ? box.depth() : 0.f);
1051            partial_rotation.TransformPoint(&point);
1052            if (first_point) {
1053              empirical_bounds.set_origin(point);
1054              first_point = false;
1055            } else {
1056              empirical_bounds.ExpandTo(point);
1057            }
1058          }
1059        }
1060
1061        // Our empirical estimate will be a little rough since we're only doing
1062        // 100 samples.
1063        static const float kTolerance = 1e-2f;
1064        EXPECT_NEAR(empirical_bounds.x(), bounds.x(), kTolerance);
1065        EXPECT_NEAR(empirical_bounds.y(), bounds.y(), kTolerance);
1066        EXPECT_NEAR(empirical_bounds.z(), bounds.z(), kTolerance);
1067        EXPECT_NEAR(empirical_bounds.width(), bounds.width(), kTolerance);
1068        EXPECT_NEAR(empirical_bounds.height(), bounds.height(), kTolerance);
1069        EXPECT_NEAR(empirical_bounds.depth(), bounds.depth(), kTolerance);
1070      }
1071    }
1072  }
1073}
1074
1075TEST(TransformOperationTest, BlendedBoundsForSequence) {
1076  TransformOperations operations_from;
1077  operations_from.AppendTranslate(2.0, 4.0, -1.0);
1078  operations_from.AppendScale(-1.0, 2.0, 3.0);
1079  operations_from.AppendTranslate(1.0, -5.0, 1.0);
1080  TransformOperations operations_to;
1081  operations_to.AppendTranslate(6.0, -2.0, 3.0);
1082  operations_to.AppendScale(-3.0, -2.0, 5.0);
1083  operations_to.AppendTranslate(13.0, -1.0, 5.0);
1084
1085  gfx::BoxF box(1.f, 2.f, 3.f, 4.f, 4.f, 4.f);
1086  gfx::BoxF bounds;
1087
1088  SkMScalar min_progress = -0.5f;
1089  SkMScalar max_progress = 1.5f;
1090  EXPECT_TRUE(operations_to.BlendedBoundsForBox(
1091      box, operations_from, min_progress, max_progress, &bounds));
1092  EXPECT_EQ(gfx::BoxF(-57.f, -59.f, -1.f, 76.f, 112.f, 80.f).ToString(),
1093            bounds.ToString());
1094
1095  min_progress = 0.f;
1096  max_progress = 1.f;
1097  EXPECT_TRUE(operations_to.BlendedBoundsForBox(
1098      box, operations_from, min_progress, max_progress, &bounds));
1099  EXPECT_EQ(gfx::BoxF(-32.f, -25.f, 7.f, 42.f, 44.f, 48.f).ToString(),
1100            bounds.ToString());
1101
1102  TransformOperations identity;
1103  EXPECT_TRUE(operations_to.BlendedBoundsForBox(
1104        box, identity, min_progress, max_progress, &bounds));
1105  EXPECT_EQ(gfx::BoxF(-33.f, -13.f, 3.f, 57.f, 19.f, 52.f).ToString(),
1106            bounds.ToString());
1107
1108  EXPECT_TRUE(identity.BlendedBoundsForBox(
1109        box, operations_from, min_progress, max_progress, &bounds));
1110  EXPECT_EQ(gfx::BoxF(-7.f, -3.f, 2.f, 15.f, 23.f, 20.f).ToString(),
1111            bounds.ToString());
1112}
1113
1114}  // namespace
1115}  // namespace cc
1116