Linux: Depend on liberation-fonts package for RPMs.
[chromium-blink-merge.git] / cc / base / math_util_unittest.cc
blob9d44fba8ac034b9a7637bff7e0f70408fef11ca4
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, MapEnclosingRectWithLargeTransforms) {
223 gfx::Rect input(1, 2, 100, 200);
224 gfx::Rect output;
226 gfx::Transform large_x_scale;
227 large_x_scale.Scale(SkDoubleToMScalar(1e37), 1.0);
229 gfx::Transform infinite_x_scale;
230 infinite_x_scale = large_x_scale * large_x_scale;
232 gfx::Transform large_y_scale;
233 large_y_scale.Scale(1.0, SkDoubleToMScalar(1e37));
235 gfx::Transform infinite_y_scale;
236 infinite_y_scale = large_y_scale * large_y_scale;
238 gfx::Transform rotation;
239 rotation.RotateAboutYAxis(170.0);
241 int max_int = std::numeric_limits<int>::max();
243 output = MathUtil::MapEnclosingClippedRect(large_x_scale, input);
244 EXPECT_EQ(gfx::Rect(max_int, 2, 0, 200), output);
246 output = MathUtil::MapEnclosingClippedRect(large_x_scale * rotation, input);
247 EXPECT_EQ(gfx::Rect(), output);
249 output = MathUtil::MapEnclosingClippedRect(infinite_x_scale, input);
250 EXPECT_EQ(gfx::Rect(max_int, 2, 0, 200), output);
252 output =
253 MathUtil::MapEnclosingClippedRect(infinite_x_scale * rotation, input);
254 EXPECT_EQ(gfx::Rect(), output);
256 output = MathUtil::MapEnclosingClippedRect(large_y_scale, input);
257 EXPECT_EQ(gfx::Rect(1, max_int, 100, 0), output);
259 output = MathUtil::MapEnclosingClippedRect(large_y_scale * rotation, input);
260 EXPECT_EQ(gfx::Rect(-100, max_int, 100, 0), output);
262 output = MathUtil::MapEnclosingClippedRect(infinite_y_scale, input);
263 EXPECT_EQ(gfx::Rect(1, max_int, 100, 0), output);
265 output =
266 MathUtil::MapEnclosingClippedRect(infinite_y_scale * rotation, input);
267 EXPECT_EQ(gfx::Rect(), output);
270 TEST(MathUtilTest, ProjectEnclosingRectWithLargeTransforms) {
271 gfx::Rect input(1, 2, 100, 200);
272 gfx::Rect output;
274 gfx::Transform large_x_scale;
275 large_x_scale.Scale(SkDoubleToMScalar(1e37), 1.0);
277 gfx::Transform infinite_x_scale;
278 infinite_x_scale = large_x_scale * large_x_scale;
280 gfx::Transform large_y_scale;
281 large_y_scale.Scale(1.0, SkDoubleToMScalar(1e37));
283 gfx::Transform infinite_y_scale;
284 infinite_y_scale = large_y_scale * large_y_scale;
286 gfx::Transform rotation;
287 rotation.RotateAboutYAxis(170.0);
289 int max_int = std::numeric_limits<int>::max();
291 output = MathUtil::ProjectEnclosingClippedRect(large_x_scale, input);
292 EXPECT_EQ(gfx::Rect(max_int, 2, 0, 200), output);
294 output =
295 MathUtil::ProjectEnclosingClippedRect(large_x_scale * rotation, input);
296 EXPECT_EQ(gfx::Rect(), output);
298 output = MathUtil::ProjectEnclosingClippedRect(infinite_x_scale, input);
299 EXPECT_EQ(gfx::Rect(max_int, 2, 0, 200), output);
301 output =
302 MathUtil::ProjectEnclosingClippedRect(infinite_x_scale * rotation, input);
303 EXPECT_EQ(gfx::Rect(), output);
305 output = MathUtil::ProjectEnclosingClippedRect(large_y_scale, input);
306 EXPECT_EQ(gfx::Rect(1, max_int, 100, 0), output);
308 output =
309 MathUtil::ProjectEnclosingClippedRect(large_y_scale * rotation, input);
310 EXPECT_EQ(gfx::Rect(-103, max_int, 102, 0), output);
312 output = MathUtil::ProjectEnclosingClippedRect(infinite_y_scale, input);
313 EXPECT_EQ(gfx::Rect(1, max_int, 100, 0), output);
315 output =
316 MathUtil::ProjectEnclosingClippedRect(infinite_y_scale * rotation, input);
317 EXPECT_EQ(gfx::Rect(), output);
320 TEST(MathUtilTest, RoundUp) {
321 for (int multiplier = 1; multiplier <= 10; ++multiplier) {
322 // Try attempts in descending order, so that we can
323 // determine the correct value before it's needed.
324 int correct;
325 for (int attempt = 5 * multiplier; attempt >= -5 * multiplier; --attempt) {
326 if ((attempt % multiplier) == 0)
327 correct = attempt;
328 EXPECT_EQ(correct, MathUtil::UncheckedRoundUp(attempt, multiplier))
329 << "attempt=" << attempt << " multiplier=" << multiplier;
333 for (unsigned multiplier = 1; multiplier <= 10; ++multiplier) {
334 // Try attempts in descending order, so that we can
335 // determine the correct value before it's needed.
336 unsigned correct;
337 for (unsigned attempt = 5 * multiplier; attempt > 0; --attempt) {
338 if ((attempt % multiplier) == 0)
339 correct = attempt;
340 EXPECT_EQ(correct, MathUtil::UncheckedRoundUp(attempt, multiplier))
341 << "attempt=" << attempt << " multiplier=" << multiplier;
343 EXPECT_EQ(0u, MathUtil::UncheckedRoundUp(0u, multiplier))
344 << "attempt=0 multiplier=" << multiplier;
348 TEST(MathUtilTest, RoundUpOverflow) {
349 // Rounding up 123 by 50 is 150, which overflows int8_t, but fits in uint8_t.
350 EXPECT_FALSE(MathUtil::VerifyRoundup<int8_t>(123, 50));
351 EXPECT_TRUE(MathUtil::VerifyRoundup<uint8_t>(123, 50));
354 TEST(MathUtilTest, RoundDown) {
355 for (int multiplier = 1; multiplier <= 10; ++multiplier) {
356 // Try attempts in ascending order, so that we can
357 // determine the correct value before it's needed.
358 int correct;
359 for (int attempt = -5 * multiplier; attempt <= 5 * multiplier; ++attempt) {
360 if ((attempt % multiplier) == 0)
361 correct = attempt;
362 EXPECT_EQ(correct, MathUtil::UncheckedRoundDown(attempt, multiplier))
363 << "attempt=" << attempt << " multiplier=" << multiplier;
367 for (unsigned multiplier = 1; multiplier <= 10; ++multiplier) {
368 // Try attempts in ascending order, so that we can
369 // determine the correct value before it's needed.
370 unsigned correct;
371 for (unsigned attempt = 0; attempt <= 5 * multiplier; ++attempt) {
372 if ((attempt % multiplier) == 0)
373 correct = attempt;
374 EXPECT_EQ(correct, MathUtil::UncheckedRoundDown(attempt, multiplier))
375 << "attempt=" << attempt << " multiplier=" << multiplier;
380 TEST(MathUtilTest, RoundDownUnderflow) {
381 // Rounding down -123 by 50 is -150, which underflows int8_t, but fits in
382 // int16_t.
383 EXPECT_FALSE(MathUtil::VerifyRoundDown<int8_t>(-123, 50));
384 EXPECT_TRUE(MathUtil::VerifyRoundDown<int16_t>(-123, 50));
387 } // namespace
388 } // namespace cc