1// Copyright 2013 The Flutter 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 "flutter/fml/build_config.h"
6
7#if defined(OS_WIN)
8#define _USE_MATH_DEFINES
9#endif
10#include <cmath>
11
12#include "flutter/flow/matrix_decomposition.h"
13#include "gtest/gtest.h"
14
15namespace flutter {
16namespace testing {
17
18TEST(MatrixDecomposition, Rotation) {
19 SkM44 matrix;
20
21 const auto angle = M_PI_4;
22 matrix.setRotate({0.0, 0.0, 1.0}, angle);
23
24 flutter::MatrixDecomposition decomposition(matrix);
25 ASSERT_TRUE(decomposition.IsValid());
26
27 const auto sine = sin(angle * 0.5);
28
29 ASSERT_FLOAT_EQ(0, decomposition.rotation().x);
30 ASSERT_FLOAT_EQ(0, decomposition.rotation().y);
31 ASSERT_FLOAT_EQ(sine, decomposition.rotation().z);
32 ASSERT_FLOAT_EQ(cos(angle * 0.5), decomposition.rotation().w);
33}
34
35TEST(MatrixDecomposition, Scale) {
36 SkM44 matrix;
37
38 const auto scale = 5.0;
39 matrix.setScale(scale + 0, scale + 1, scale + 2);
40
41 flutter::MatrixDecomposition decomposition(matrix);
42 ASSERT_TRUE(decomposition.IsValid());
43
44 ASSERT_FLOAT_EQ(scale + 0, decomposition.scale().x);
45 ASSERT_FLOAT_EQ(scale + 1, decomposition.scale().y);
46 ASSERT_FLOAT_EQ(scale + 2, decomposition.scale().z);
47}
48
49TEST(MatrixDecomposition, Translate) {
50 SkM44 matrix;
51
52 const auto translate = 125.0;
53 matrix.setTranslate(translate + 0, translate + 1, translate + 2);
54
55 flutter::MatrixDecomposition decomposition(matrix);
56 ASSERT_TRUE(decomposition.IsValid());
57
58 ASSERT_FLOAT_EQ(translate + 0, decomposition.translation().x);
59 ASSERT_FLOAT_EQ(translate + 1, decomposition.translation().y);
60 ASSERT_FLOAT_EQ(translate + 2, decomposition.translation().z);
61}
62
63TEST(MatrixDecomposition, Combination) {
64 const auto rotation = M_PI_4;
65 const auto scale = 5;
66 const auto translate = 125.0;
67
68 SkM44 m1;
69 m1.setRotate({0, 0, 1}, rotation);
70
71 SkM44 m2;
72 m2.setScale(scale, scale, scale);
73
74 SkM44 m3;
75 m3.setTranslate(translate, translate, translate);
76
77 SkM44 combined = m3 * m2 * m1;
78
79 flutter::MatrixDecomposition decomposition(combined);
80 ASSERT_TRUE(decomposition.IsValid());
81
82 ASSERT_FLOAT_EQ(translate, decomposition.translation().x);
83 ASSERT_FLOAT_EQ(translate, decomposition.translation().y);
84 ASSERT_FLOAT_EQ(translate, decomposition.translation().z);
85
86 ASSERT_FLOAT_EQ(scale, decomposition.scale().x);
87 ASSERT_FLOAT_EQ(scale, decomposition.scale().y);
88 ASSERT_FLOAT_EQ(scale, decomposition.scale().z);
89
90 const auto sine = sin(rotation * 0.5);
91
92 ASSERT_FLOAT_EQ(0, decomposition.rotation().x);
93 ASSERT_FLOAT_EQ(0, decomposition.rotation().y);
94 ASSERT_FLOAT_EQ(sine, decomposition.rotation().z);
95 ASSERT_FLOAT_EQ(cos(rotation * 0.5), decomposition.rotation().w);
96}
97
98TEST(MatrixDecomposition, ScaleFloatError) {
99 constexpr float scale_increment = 0.00001f;
100 float scale = 0.0001f;
101 while (scale < 2.0f) {
102 SkM44 matrix;
103 matrix.setScale(scale, scale, 1.0f);
104
105 flutter::MatrixDecomposition decomposition3(matrix);
106 ASSERT_TRUE(decomposition3.IsValid());
107
108 ASSERT_FLOAT_EQ(scale, decomposition3.scale().x);
109 ASSERT_FLOAT_EQ(scale, decomposition3.scale().y);
110 ASSERT_FLOAT_EQ(1.f, decomposition3.scale().z);
111 ASSERT_FLOAT_EQ(0, decomposition3.rotation().x);
112 ASSERT_FLOAT_EQ(0, decomposition3.rotation().y);
113 ASSERT_FLOAT_EQ(0, decomposition3.rotation().z);
114 scale += scale_increment;
115 }
116
117 SkM44 matrix;
118 const auto scale1 = 1.7734375f;
119 matrix.setScale(scale1, scale1, 1.f);
120
121 // Bug upper bound (empirical)
122 const auto scale2 = 1.773437559603f;
123 SkM44 matrix2;
124 matrix2.setScale(scale2, scale2, 1.f);
125
126 // Bug lower bound (empirical)
127 const auto scale3 = 1.7734374403954f;
128 SkM44 matrix3;
129 matrix3.setScale(scale3, scale3, 1.f);
130
131 flutter::MatrixDecomposition decomposition(matrix);
132 ASSERT_TRUE(decomposition.IsValid());
133
134 flutter::MatrixDecomposition decomposition2(matrix2);
135 ASSERT_TRUE(decomposition2.IsValid());
136
137 flutter::MatrixDecomposition decomposition3(matrix3);
138 ASSERT_TRUE(decomposition3.IsValid());
139
140 ASSERT_FLOAT_EQ(scale1, decomposition.scale().x);
141 ASSERT_FLOAT_EQ(scale1, decomposition.scale().y);
142 ASSERT_FLOAT_EQ(1.f, decomposition.scale().z);
143 ASSERT_FLOAT_EQ(0, decomposition.rotation().x);
144 ASSERT_FLOAT_EQ(0, decomposition.rotation().y);
145 ASSERT_FLOAT_EQ(0, decomposition.rotation().z);
146
147 ASSERT_FLOAT_EQ(scale2, decomposition2.scale().x);
148 ASSERT_FLOAT_EQ(scale2, decomposition2.scale().y);
149 ASSERT_FLOAT_EQ(1.f, decomposition2.scale().z);
150 ASSERT_FLOAT_EQ(0, decomposition2.rotation().x);
151 ASSERT_FLOAT_EQ(0, decomposition2.rotation().y);
152 ASSERT_FLOAT_EQ(0, decomposition2.rotation().z);
153
154 ASSERT_FLOAT_EQ(scale3, decomposition3.scale().x);
155 ASSERT_FLOAT_EQ(scale3, decomposition3.scale().y);
156 ASSERT_FLOAT_EQ(1.f, decomposition3.scale().z);
157 ASSERT_FLOAT_EQ(0, decomposition3.rotation().x);
158 ASSERT_FLOAT_EQ(0, decomposition3.rotation().y);
159 ASSERT_FLOAT_EQ(0, decomposition3.rotation().z);
160}
161
162} // namespace testing
163} // namespace flutter
164