1 // Copyright 2012 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 #include "cc/base/math_util.h"
9 #include "cc/test/geometry_test_utils.h"
10 #include "testing/gmock/include/gmock/gmock.h"
11 #include "testing/gtest/include/gtest/gtest.h"
12 #include "ui/gfx/geometry/rect.h"
13 #include "ui/gfx/geometry/rect_f.h"
14 #include "ui/gfx/transform.h"
19 TEST(MathUtilTest
, ProjectionOfPerpendicularPlane
) {
20 // In this case, the m33() element of the transform becomes zero, which could
21 // cause a divide-by-zero when projecting points/quads.
23 gfx::Transform transform
;
24 transform
.MakeIdentity();
25 transform
.matrix().set(2, 2, 0);
27 gfx::RectF rect
= gfx::RectF(0, 0, 1, 1);
28 gfx::RectF projected_rect
= MathUtil::ProjectClippedRect(transform
, rect
);
30 EXPECT_EQ(0, projected_rect
.x());
31 EXPECT_EQ(0, projected_rect
.y());
32 EXPECT_TRUE(projected_rect
.IsEmpty());
35 TEST(MathUtilTest
, ProjectionOfAlmostPerpendicularPlane
) {
36 // In this case, the m33() element of the transform becomes almost zero, which
37 // could cause a divide-by-zero when projecting points/quads.
39 gfx::Transform transform
;
40 // The transform is from an actual test page:
41 // [ +1.0000 +0.0000 -1.0000 +3144132.0000
42 // +0.0000 +1.0000 +0.0000 +0.0000
43 // +16331238407143424.0000 +0.0000 -0.0000 +51346917453137000267776.0000
44 // +0.0000 +0.0000 +0.0000 +1.0000 ]
45 transform
.MakeIdentity();
46 transform
.matrix().set(0, 2, static_cast<SkMScalar
>(-1));
47 transform
.matrix().set(0, 3, static_cast<SkMScalar
>(3144132.0));
48 transform
.matrix().set(2, 0, static_cast<SkMScalar
>(16331238407143424.0));
49 transform
.matrix().set(2, 2, static_cast<SkMScalar
>(-1e-33));
50 transform
.matrix().set(2, 3,
51 static_cast<SkMScalar
>(51346917453137000267776.0));
53 gfx::RectF rect
= gfx::RectF(0, 0, 1, 1);
54 gfx::RectF projected_rect
= MathUtil::ProjectClippedRect(transform
, rect
);
56 EXPECT_EQ(0, projected_rect
.x());
57 EXPECT_EQ(0, projected_rect
.y());
58 EXPECT_TRUE(projected_rect
.IsEmpty()) << projected_rect
.ToString();
61 TEST(MathUtilTest
, EnclosingClippedRectUsesCorrectInitialBounds
) {
62 HomogeneousCoordinate
h1(-100, -100, 0, 1);
63 HomogeneousCoordinate
h2(-10, -10, 0, 1);
64 HomogeneousCoordinate
h3(10, 10, 0, -1);
65 HomogeneousCoordinate
h4(100, 100, 0, -1);
67 // The bounds of the enclosing clipped rect should be -100 to -10 for both x
68 // and y. However, if there is a bug where the initial xmin/xmax/ymin/ymax are
69 // initialized to numeric_limits<float>::min() (which is zero, not -flt_max)
70 // then the enclosing clipped rect will be computed incorrectly.
71 gfx::RectF result
= MathUtil::ComputeEnclosingClippedRect(h1
, h2
, h3
, h4
);
73 // Due to floating point math in ComputeClippedPointForEdge this result
74 // is fairly imprecise. 0.15f was empirically determined.
76 gfx::RectF(gfx::PointF(-100, -100), gfx::SizeF(90, 90)), result
, 0.15f
);
79 TEST(MathUtilTest
, EnclosingRectOfVerticesUsesCorrectInitialBounds
) {
80 gfx::PointF vertices
[3];
83 vertices
[0] = gfx::PointF(-10, -100);
84 vertices
[1] = gfx::PointF(-100, -10);
85 vertices
[2] = gfx::PointF(-30, -30);
87 // The bounds of the enclosing rect should be -100 to -10 for both x and y.
88 // However, if there is a bug where the initial xmin/xmax/ymin/ymax are
89 // initialized to numeric_limits<float>::min() (which is zero, not -flt_max)
90 // then the enclosing clipped rect will be computed incorrectly.
92 MathUtil::ComputeEnclosingRectOfVertices(vertices
, num_vertices
);
94 EXPECT_FLOAT_RECT_EQ(gfx::RectF(gfx::PointF(-100, -100), gfx::SizeF(90, 90)),
98 TEST(MathUtilTest
, SmallestAngleBetweenVectors
) {
99 gfx::Vector2dF
x(1, 0);
100 gfx::Vector2dF
y(0, 1);
101 gfx::Vector2dF
test_vector(0.5, 0.5);
103 // Orthogonal vectors are at an angle of 90 degress.
104 EXPECT_EQ(90, MathUtil::SmallestAngleBetweenVectors(x
, y
));
106 // A vector makes a zero angle with itself.
107 EXPECT_EQ(0, MathUtil::SmallestAngleBetweenVectors(x
, x
));
108 EXPECT_EQ(0, MathUtil::SmallestAngleBetweenVectors(y
, y
));
109 EXPECT_EQ(0, MathUtil::SmallestAngleBetweenVectors(test_vector
, test_vector
));
111 // Parallel but reversed vectors are at 180 degrees.
112 EXPECT_FLOAT_EQ(180, MathUtil::SmallestAngleBetweenVectors(x
, -x
));
113 EXPECT_FLOAT_EQ(180, MathUtil::SmallestAngleBetweenVectors(y
, -y
));
115 180, MathUtil::SmallestAngleBetweenVectors(test_vector
, -test_vector
));
117 // The test vector is at a known angle.
119 45, std::floor(MathUtil::SmallestAngleBetweenVectors(test_vector
, x
)));
121 45, std::floor(MathUtil::SmallestAngleBetweenVectors(test_vector
, y
)));
124 TEST(MathUtilTest
, VectorProjection
) {
125 gfx::Vector2dF
x(1, 0);
126 gfx::Vector2dF
y(0, 1);
127 gfx::Vector2dF
test_vector(0.3f
, 0.7f
);
129 // Orthogonal vectors project to a zero vector.
130 EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), MathUtil::ProjectVector(x
, y
));
131 EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), MathUtil::ProjectVector(y
, x
));
133 // Projecting a vector onto the orthonormal basis gives the corresponding
134 // component of the vector.
135 EXPECT_VECTOR_EQ(gfx::Vector2dF(test_vector
.x(), 0),
136 MathUtil::ProjectVector(test_vector
, x
));
137 EXPECT_VECTOR_EQ(gfx::Vector2dF(0, test_vector
.y()),
138 MathUtil::ProjectVector(test_vector
, y
));
140 // Finally check than an arbitrary vector projected to another one gives a
141 // vector parallel to the second vector.
142 gfx::Vector2dF
target_vector(0.5, 0.2f
);
143 gfx::Vector2dF projected_vector
=
144 MathUtil::ProjectVector(test_vector
, target_vector
);
145 EXPECT_EQ(projected_vector
.x() / target_vector
.x(),
146 projected_vector
.y() / target_vector
.y());
149 TEST(MathUtilTest
, MapEnclosedRectWith2dAxisAlignedTransform
) {
150 gfx::Rect
input(1, 2, 3, 4);
152 gfx::Transform transform
;
156 MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform
, input
);
157 EXPECT_EQ(input
, output
);
159 // Integer translate.
160 transform
.Translate(2.0, 3.0);
162 MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform
, input
);
163 EXPECT_EQ(gfx::Rect(3, 5, 3, 4), output
);
165 // Non-integer translate.
166 transform
.Translate(0.5, 0.5);
168 MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform
, input
);
169 EXPECT_EQ(gfx::Rect(4, 6, 2, 3), output
);
172 transform
= gfx::Transform();
173 transform
.Scale(2.0, 3.0);
175 MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform
, input
);
176 EXPECT_EQ(gfx::Rect(2, 6, 6, 12), output
);
179 transform
= gfx::Transform();
180 transform
.Translate(1.0, 2.0);
181 transform
.RotateAboutZAxis(90.0);
182 transform
.Translate(-1.0, -2.0);
184 MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform
, input
);
185 EXPECT_EQ(gfx::Rect(-3, 2, 4, 3), output
);
188 transform
= gfx::Transform();
189 transform
.RotateAboutXAxis(90.0);
191 MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform
, input
);
192 EXPECT_TRUE(output
.IsEmpty());
194 transform
= gfx::Transform();
195 transform
.RotateAboutXAxis(180.0);
197 MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform
, input
);
198 EXPECT_EQ(gfx::Rect(1, -6, 3, 4), output
);
201 transform
= gfx::Transform();
202 transform
.RotateAboutYAxis(90.0);
204 MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform
, input
);
205 EXPECT_TRUE(output
.IsEmpty());
207 transform
= gfx::Transform();
208 transform
.RotateAboutYAxis(180.0);
210 MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform
, input
);
211 EXPECT_EQ(gfx::Rect(-4, 2, 3, 4), output
);
214 transform
= gfx::Transform();
215 transform
.ApplyPerspectiveDepth(10.0);
216 transform
.Translate3d(0.0, 0.0, 5.0);
218 MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform
, input
);
219 EXPECT_EQ(gfx::Rect(2, 4, 6, 8), output
);
222 TEST(MathUtilTest
, RoundUp
) {
223 for (int multiplier
= 1; multiplier
<= 10; ++multiplier
) {
224 // Try attempts in descending order, so that we can
225 // determine the correct value before it's needed.
227 for (int attempt
= 5 * multiplier
; attempt
>= -5 * multiplier
; --attempt
) {
228 if ((attempt
% multiplier
) == 0)
230 EXPECT_EQ(correct
, MathUtil::UncheckedRoundUp(attempt
, multiplier
))
231 << "attempt=" << attempt
<< " multiplier=" << multiplier
;
235 for (unsigned multiplier
= 1; multiplier
<= 10; ++multiplier
) {
236 // Try attempts in descending order, so that we can
237 // determine the correct value before it's needed.
239 for (unsigned attempt
= 5 * multiplier
; attempt
> 0; --attempt
) {
240 if ((attempt
% multiplier
) == 0)
242 EXPECT_EQ(correct
, MathUtil::UncheckedRoundUp(attempt
, multiplier
))
243 << "attempt=" << attempt
<< " multiplier=" << multiplier
;
245 EXPECT_EQ(0u, MathUtil::UncheckedRoundUp(0u, multiplier
))
246 << "attempt=0 multiplier=" << multiplier
;
250 TEST(MathUtilTest
, RoundUpOverFlow
) {
251 // Rounding up 123 by 50 is 150, which overflows int8_t, but fits in uint8_t.
252 EXPECT_FALSE(MathUtil::VerifyRoundup
<int8_t>(123, 50));
253 EXPECT_TRUE(MathUtil::VerifyRoundup
<uint8_t>(123, 50));
256 TEST(MathUtilTest
, RoundDown
) {
257 for (int multiplier
= 1; multiplier
<= 10; ++multiplier
) {
258 // Try attempts in ascending order, so that we can
259 // determine the correct value before it's needed.
261 for (int attempt
= -5 * multiplier
; attempt
<= 5 * multiplier
; ++attempt
) {
262 if ((attempt
% multiplier
) == 0)
264 EXPECT_EQ(correct
, MathUtil::UncheckedRoundDown(attempt
, multiplier
))
265 << "attempt=" << attempt
<< " multiplier=" << multiplier
;
269 for (unsigned multiplier
= 1; multiplier
<= 10; ++multiplier
) {
270 // Try attempts in ascending order, so that we can
271 // determine the correct value before it's needed.
273 for (unsigned attempt
= 0; attempt
<= 5 * multiplier
; ++attempt
) {
274 if ((attempt
% multiplier
) == 0)
276 EXPECT_EQ(correct
, MathUtil::UncheckedRoundDown(attempt
, multiplier
))
277 << "attempt=" << attempt
<< " multiplier=" << multiplier
;
282 TEST(MathUtilTest
, RoundDownUnderFlow
) {
283 // Rounding down -123 by 50 is -150, which underflows int8_t, but fits in
285 EXPECT_FALSE(MathUtil::VerifyRoundDown
<int8_t>(-123, 50));
286 EXPECT_TRUE(MathUtil::VerifyRoundDown
<int16_t>(-123, 50));