Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / cc / base / math_util_unittest.cc
blobae0cf9860bdb57f8b45b9da16e9e40df3f5c7e37
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"
7 #include <cmath>
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"
16 namespace cc {
17 namespace {
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.
75 EXPECT_RECT_NEAR(
76 gfx::RectF(gfx::PointF(-100, -100), gfx::SizeF(90, 90)), result, 0.15f);
79 TEST(MathUtilTest, EnclosingRectOfVerticesUsesCorrectInitialBounds) {
80 gfx::PointF vertices[3];
81 int num_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.
91 gfx::RectF result =
92 MathUtil::ComputeEnclosingRectOfVertices(vertices, num_vertices);
94 EXPECT_FLOAT_RECT_EQ(gfx::RectF(gfx::PointF(-100, -100), gfx::SizeF(90, 90)),
95 result);
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));
114 EXPECT_FLOAT_EQ(
115 180, MathUtil::SmallestAngleBetweenVectors(test_vector, -test_vector));
117 // The test vector is at a known angle.
118 EXPECT_FLOAT_EQ(
119 45, std::floor(MathUtil::SmallestAngleBetweenVectors(test_vector, x)));
120 EXPECT_FLOAT_EQ(
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);
151 gfx::Rect output;
152 gfx::Transform transform;
154 // Identity.
155 output =
156 MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
157 EXPECT_EQ(input, output);
159 // Integer translate.
160 transform.Translate(2.0, 3.0);
161 output =
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);
167 output =
168 MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
169 EXPECT_EQ(gfx::Rect(4, 6, 2, 3), output);
171 // Scale.
172 transform = gfx::Transform();
173 transform.Scale(2.0, 3.0);
174 output =
175 MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
176 EXPECT_EQ(gfx::Rect(2, 6, 6, 12), output);
178 // Rotate Z.
179 transform = gfx::Transform();
180 transform.Translate(1.0, 2.0);
181 transform.RotateAboutZAxis(90.0);
182 transform.Translate(-1.0, -2.0);
183 output =
184 MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
185 EXPECT_EQ(gfx::Rect(-3, 2, 4, 3), output);
187 // Rotate X.
188 transform = gfx::Transform();
189 transform.RotateAboutXAxis(90.0);
190 output =
191 MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
192 EXPECT_TRUE(output.IsEmpty());
194 transform = gfx::Transform();
195 transform.RotateAboutXAxis(180.0);
196 output =
197 MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
198 EXPECT_EQ(gfx::Rect(1, -6, 3, 4), output);
200 // Rotate Y.
201 transform = gfx::Transform();
202 transform.RotateAboutYAxis(90.0);
203 output =
204 MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
205 EXPECT_TRUE(output.IsEmpty());
207 transform = gfx::Transform();
208 transform.RotateAboutYAxis(180.0);
209 output =
210 MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
211 EXPECT_EQ(gfx::Rect(-4, 2, 3, 4), output);
213 // Translate Z.
214 transform = gfx::Transform();
215 transform.ApplyPerspectiveDepth(10.0);
216 transform.Translate3d(0.0, 0.0, 5.0);
217 output =
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.
226 int correct;
227 for (int attempt = 5 * multiplier; attempt >= -5 * multiplier; --attempt) {
228 if ((attempt % multiplier) == 0)
229 correct = attempt;
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.
238 unsigned correct;
239 for (unsigned attempt = 5 * multiplier; attempt > 0; --attempt) {
240 if ((attempt % multiplier) == 0)
241 correct = attempt;
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.
260 int correct;
261 for (int attempt = -5 * multiplier; attempt <= 5 * multiplier; ++attempt) {
262 if ((attempt % multiplier) == 0)
263 correct = attempt;
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.
272 unsigned correct;
273 for (unsigned attempt = 0; attempt <= 5 * multiplier; ++attempt) {
274 if ((attempt % multiplier) == 0)
275 correct = attempt;
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
284 // int16_t.
285 EXPECT_FALSE(MathUtil::VerifyRoundDown<int8_t>(-123, 50));
286 EXPECT_TRUE(MathUtil::VerifyRoundDown<int16_t>(-123, 50));
289 } // namespace
290 } // namespace cc