blob: c1548d9aa7cb8c03bc6ae310149e632cb3d433f3 [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cobalt/math/rect.h"
#include <limits>
#include "base/basictypes.h"
#include "cobalt/math/box_f.h"
#include "cobalt/math/rect_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cobalt {
namespace math {
#define EXPECT_RECTF_EQ(a, b) EXPECT_PRED_FORMAT2(AssertRectFloatEqual, a, b)
namespace {
bool FloatAlmostEqual(float a, float b) {
// FloatLE is the gtest predicate for less than or almost equal to.
return ::testing::FloatLE("a", "b", a, b) &&
::testing::FloatLE("b", "a", b, a);
}
} // namespace
::testing::AssertionResult AssertRectFloatEqual(const char* lhs_expr,
const char* rhs_expr,
const RectF& lhs,
const RectF& rhs) {
if (FloatAlmostEqual(lhs.x(), rhs.x()) &&
FloatAlmostEqual(lhs.y(), rhs.y()) &&
FloatAlmostEqual(lhs.width(), rhs.width()) &&
FloatAlmostEqual(lhs.height(), rhs.height())) {
return ::testing::AssertionSuccess();
}
return ::testing::AssertionFailure()
<< "Value of: " << rhs_expr << "\n Actual: " << rhs.ToString()
<< "\nExpected: " << lhs_expr << "\nWhich is: " << lhs.ToString();
}
TEST(RectTest, Contains) {
static const struct ContainsCase {
int rect_x;
int rect_y;
int rect_width;
int rect_height;
int point_x;
int point_y;
bool contained;
} contains_cases[] = {
{0, 0, 10, 10, 0, 0, true},
{0, 0, 10, 10, 5, 5, true},
{0, 0, 10, 10, 9, 9, true},
{0, 0, 10, 10, 5, 10, false},
{0, 0, 10, 10, 10, 5, false},
{0, 0, 10, 10, -1, -1, false},
{0, 0, 10, 10, 50, 50, false},
#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
{0, 0, -10, -10, 0, 0, false},
#endif
};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(contains_cases); ++i) {
const ContainsCase& value = contains_cases[i];
Rect rect(value.rect_x, value.rect_y, value.rect_width, value.rect_height);
EXPECT_EQ(value.contained, rect.Contains(value.point_x, value.point_y));
}
}
TEST(RectTest, Intersects) {
static const struct {
int x1; // rect 1
int y1;
int w1;
int h1;
int x2; // rect 2
int y2;
int w2;
int h2;
bool intersects;
} tests[] = {{0, 0, 0, 0, 0, 0, 0, 0, false},
{0, 0, 0, 0, -10, -10, 20, 20, false},
{-10, 0, 0, 20, 0, -10, 20, 0, false},
{0, 0, 10, 10, 0, 0, 10, 10, true},
{0, 0, 10, 10, 10, 10, 10, 10, false},
{10, 10, 10, 10, 0, 0, 10, 10, false},
{10, 10, 10, 10, 5, 5, 10, 10, true},
{10, 10, 10, 10, 15, 15, 10, 10, true},
{10, 10, 10, 10, 20, 15, 10, 10, false},
{10, 10, 10, 10, 21, 15, 10, 10, false}};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
EXPECT_EQ(tests[i].intersects, r1.Intersects(r2));
EXPECT_EQ(tests[i].intersects, r2.Intersects(r1));
}
}
TEST(RectTest, Intersect) {
static const struct {
int x1; // rect 1
int y1;
int w1;
int h1;
int x2; // rect 2
int y2;
int w2;
int h2;
int x3; // rect 3: the union of rects 1 and 2
int y3;
int w3;
int h3;
} tests[] = {{0, 0, 0, 0, // zeros
0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 4, 4, // equal
0, 0, 4, 4, 0, 0, 4, 4},
{0, 0, 4, 4, // neighboring
4, 4, 4, 4, 0, 0, 0, 0},
{0, 0, 4, 4, // overlapping corners
2, 2, 4, 4, 2, 2, 2, 2},
{0, 0, 4, 4, // T junction
3, 1, 4, 2, 3, 1, 1, 2},
{3, 0, 2, 2, // gap
0, 0, 2, 2, 0, 0, 0, 0}};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
Rect r3(tests[i].x3, tests[i].y3, tests[i].w3, tests[i].h3);
Rect ir = IntersectRects(r1, r2);
EXPECT_EQ(r3.x(), ir.x());
EXPECT_EQ(r3.y(), ir.y());
EXPECT_EQ(r3.width(), ir.width());
EXPECT_EQ(r3.height(), ir.height());
}
}
TEST(RectTest, Union) {
static const struct Test {
int x1; // rect 1
int y1;
int w1;
int h1;
int x2; // rect 2
int y2;
int w2;
int h2;
int x3; // rect 3: the union of rects 1 and 2
int y3;
int w3;
int h3;
} tests[] = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 4, 4, 0, 0, 4, 4, 0, 0, 4, 4},
{0, 0, 4, 4, 4, 4, 4, 4, 0, 0, 8, 8},
{0, 0, 4, 4, 0, 5, 4, 4, 0, 0, 4, 9},
{0, 0, 2, 2, 3, 3, 2, 2, 0, 0, 5, 5},
{3, 3, 2, 2, // reverse r1 and r2 from previous test
0, 0, 2, 2, 0, 0, 5, 5},
{0, 0, 0, 0, // union with empty rect
2, 2, 2, 2, 2, 2, 2, 2}};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
Rect r3(tests[i].x3, tests[i].y3, tests[i].w3, tests[i].h3);
Rect u = UnionRects(r1, r2);
EXPECT_EQ(r3.x(), u.x());
EXPECT_EQ(r3.y(), u.y());
EXPECT_EQ(r3.width(), u.width());
EXPECT_EQ(r3.height(), u.height());
}
}
TEST(RectTest, Equals) {
ASSERT_TRUE(Rect(0, 0, 0, 0) == Rect(0, 0, 0, 0));
ASSERT_TRUE(Rect(1, 2, 3, 4) == Rect(1, 2, 3, 4));
ASSERT_FALSE(Rect(0, 0, 0, 0) == Rect(0, 0, 0, 1));
ASSERT_FALSE(Rect(0, 0, 0, 0) == Rect(0, 0, 1, 0));
ASSERT_FALSE(Rect(0, 0, 0, 0) == Rect(0, 1, 0, 0));
ASSERT_FALSE(Rect(0, 0, 0, 0) == Rect(1, 0, 0, 0));
}
TEST(RectTest, AdjustToFit) {
static const struct Test {
int x1; // source
int y1;
int w1;
int h1;
int x2; // target
int y2;
int w2;
int h2;
int x3; // rect 3: results of invoking AdjustToFit
int y3;
int w3;
int h3;
} tests[] = {{0, 0, 2, 2, 0, 0, 2, 2, 0, 0, 2, 2},
{2, 2, 3, 3, 0, 0, 4, 4, 1, 1, 3, 3},
{-1, -1, 5, 5, 0, 0, 4, 4, 0, 0, 4, 4},
{2, 2, 4, 4, 0, 0, 3, 3, 0, 0, 3, 3},
{2, 2, 1, 1, 0, 0, 3, 3, 2, 2, 1, 1}};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
Rect r3(tests[i].x3, tests[i].y3, tests[i].w3, tests[i].h3);
Rect u = r1;
u.AdjustToFit(r2);
EXPECT_EQ(r3.x(), u.x());
EXPECT_EQ(r3.y(), u.y());
EXPECT_EQ(r3.width(), u.width());
EXPECT_EQ(r3.height(), u.height());
}
}
TEST(RectTest, RoundFromRectF) {
Rect result;
result = Rect::RoundFromRectF(RectF(-3.8, 2.5, 12.6, 11.8));
// result.right, we have = round(-3.8 + 12.6) = round(8.8) = 9
// result.bottom, we have = round(2.5 + 11.8) = round(14.3) = 14
EXPECT_EQ(Rect(-4, 3, 13, 11), result);
result = Rect::RoundFromRectF(RectF(-3.3, -2.1, 12.6, 11.8));
// result.right, we have = round(-3.3 + 12.6) = round(9.3) = 9
// result.bottom, we have = round(-2.1 + 11.8) = round(9.7) = 10
EXPECT_EQ(Rect(-3, -2, 12, 12), result);
result = Rect::RoundFromRectF(RectF(-3.3, -2.1, 1E12, -1E12));
// result.right, we have = round(-3.3 + 1E12) = clamp(1E12) = int max
// "result.bottom", we have it set to int min, and since "top" is at -2,
// distance to bottom is int min + 2.
EXPECT_EQ(Rect(-3, -2, std::numeric_limits<int>::max(),
std::numeric_limits<int>::min() + 2),
result);
}
TEST(RectTest, PreserveBordersRoundFromRectF) {
Rect result;
result = Rect::RoundFromRectF(RectF(3.49, 1.49, 1.02, 1.02));
// result.right, we have = round(3.49 + 1.02) = round(4.51) = 5
// result.bottom, we have = round(1.49 + 1.02) = round(2.51) = 3
EXPECT_EQ(Rect(3, 1, 2, 2), result);
result = Rect::RoundFromRectF(RectF(4.51, 2.51, 1.02, 1.02));
EXPECT_EQ(Rect(5, 3, 1, 1), result);
}
TEST(RectTest, Subtract) {
Rect result;
// Matching
result = Rect(10, 10, 20, 20);
result.Subtract(Rect(10, 10, 20, 20));
EXPECT_EQ(Rect(0, 0, 0, 0), result);
// Contains
result = Rect(10, 10, 20, 20);
result.Subtract(Rect(5, 5, 30, 30));
EXPECT_EQ(Rect(0, 0, 0, 0), result);
// No intersection
result = Rect(10, 10, 20, 20);
result.Subtract(Rect(30, 30, 30, 30));
EXPECT_EQ(Rect(10, 10, 20, 20), result);
// Not a complete intersection in either direction
result = Rect(10, 10, 20, 20);
result.Subtract(Rect(15, 15, 20, 20));
EXPECT_EQ(Rect(10, 10, 20, 20), result);
// Complete intersection in the x-direction, top edge is fully covered.
result = Rect(10, 10, 20, 20);
result.Subtract(Rect(10, 15, 20, 20));
EXPECT_EQ(Rect(10, 10, 20, 5), result);
// Complete intersection in the x-direction, top edge is fully covered.
result = Rect(10, 10, 20, 20);
result.Subtract(Rect(5, 15, 30, 20));
EXPECT_EQ(Rect(10, 10, 20, 5), result);
// Complete intersection in the x-direction, bottom edge is fully covered.
result = Rect(10, 10, 20, 20);
result.Subtract(Rect(5, 5, 30, 20));
EXPECT_EQ(Rect(10, 25, 20, 5), result);
// Complete intersection in the x-direction, none of the edges is fully
// covered.
result = Rect(10, 10, 20, 20);
result.Subtract(Rect(5, 15, 30, 1));
EXPECT_EQ(Rect(10, 10, 20, 20), result);
// Complete intersection in the y-direction, left edge is fully covered.
result = Rect(10, 10, 20, 20);
result.Subtract(Rect(10, 10, 10, 30));
EXPECT_EQ(Rect(20, 10, 10, 20), result);
// Complete intersection in the y-direction, left edge is fully covered.
result = Rect(10, 10, 20, 20);
result.Subtract(Rect(5, 5, 20, 30));
EXPECT_EQ(Rect(25, 10, 5, 20), result);
// Complete intersection in the y-direction, right edge is fully covered.
result = Rect(10, 10, 20, 20);
result.Subtract(Rect(20, 5, 20, 30));
EXPECT_EQ(Rect(10, 10, 10, 20), result);
// Complete intersection in the y-direction, none of the edges is fully
// covered.
result = Rect(10, 10, 20, 20);
result.Subtract(Rect(15, 5, 1, 30));
EXPECT_EQ(Rect(10, 10, 20, 20), result);
}
TEST(RectTest, IsEmpty) {
EXPECT_TRUE(Rect(0, 0, 0, 0).IsEmpty());
EXPECT_TRUE(Rect(0, 0, 0, 0).size().IsEmpty());
EXPECT_TRUE(Rect(0, 0, 10, 0).IsEmpty());
EXPECT_TRUE(Rect(0, 0, 10, 0).size().IsEmpty());
EXPECT_TRUE(Rect(0, 0, 0, 10).IsEmpty());
EXPECT_TRUE(Rect(0, 0, 0, 10).size().IsEmpty());
EXPECT_FALSE(Rect(0, 0, 10, 10).IsEmpty());
EXPECT_FALSE(Rect(0, 0, 10, 10).size().IsEmpty());
}
TEST(RectTest, SplitVertically) {
Rect left_half, right_half;
// Splitting when origin is (0, 0).
Rect(0, 0, 20, 20).SplitVertically(&left_half, &right_half);
EXPECT_TRUE(left_half == Rect(0, 0, 10, 20));
EXPECT_TRUE(right_half == Rect(10, 0, 10, 20));
// Splitting when origin is arbitrary.
Rect(10, 10, 20, 10).SplitVertically(&left_half, &right_half);
EXPECT_TRUE(left_half == Rect(10, 10, 10, 10));
EXPECT_TRUE(right_half == Rect(20, 10, 10, 10));
// Splitting a rectangle of zero width.
Rect(10, 10, 0, 10).SplitVertically(&left_half, &right_half);
EXPECT_TRUE(left_half == Rect(10, 10, 0, 10));
EXPECT_TRUE(right_half == Rect(10, 10, 0, 10));
// Splitting a rectangle of odd width.
Rect(10, 10, 5, 10).SplitVertically(&left_half, &right_half);
EXPECT_TRUE(left_half == Rect(10, 10, 2, 10));
EXPECT_TRUE(right_half == Rect(12, 10, 3, 10));
}
TEST(RectTest, CenterPoint) {
Point center;
// When origin is (0, 0).
center = Rect(0, 0, 20, 20).CenterPoint();
EXPECT_TRUE(center == Point(10, 10));
// When origin is even.
center = Rect(10, 10, 20, 20).CenterPoint();
EXPECT_TRUE(center == Point(20, 20));
// When origin is odd.
center = Rect(11, 11, 20, 20).CenterPoint();
EXPECT_TRUE(center == Point(21, 21));
// When 0 width or height.
center = Rect(10, 10, 0, 20).CenterPoint();
EXPECT_TRUE(center == Point(10, 20));
center = Rect(10, 10, 20, 0).CenterPoint();
EXPECT_TRUE(center == Point(20, 10));
// When an odd size.
center = Rect(10, 10, 21, 21).CenterPoint();
EXPECT_TRUE(center == Point(20, 20));
// When an odd size and position.
center = Rect(11, 11, 21, 21).CenterPoint();
EXPECT_TRUE(center == Point(21, 21));
}
TEST(RectTest, CenterPointF) {
PointF center;
// When origin is (0, 0).
center = RectF(0, 0, 20, 20).CenterPoint();
EXPECT_TRUE(center == PointF(10, 10));
// When origin is even.
center = RectF(10, 10, 20, 20).CenterPoint();
EXPECT_TRUE(center == PointF(20, 20));
// When origin is odd.
center = RectF(11, 11, 20, 20).CenterPoint();
EXPECT_TRUE(center == PointF(21, 21));
// When 0 width or height.
center = RectF(10, 10, 0, 20).CenterPoint();
EXPECT_TRUE(center == PointF(10, 20));
center = RectF(10, 10, 20, 0).CenterPoint();
EXPECT_TRUE(center == PointF(20, 10));
// When an odd size.
center = RectF(10, 10, 21, 21).CenterPoint();
EXPECT_TRUE(center == PointF(20.5f, 20.5f));
// When an odd size and position.
center = RectF(11, 11, 21, 21).CenterPoint();
EXPECT_TRUE(center == PointF(21.5f, 21.5f));
}
TEST(RectTest, SharesEdgeWith) {
Rect r(2, 3, 4, 5);
// Must be non-overlapping
EXPECT_FALSE(r.SharesEdgeWith(r));
Rect just_above(2, 1, 4, 2);
Rect just_below(2, 8, 4, 2);
Rect just_left(0, 3, 2, 5);
Rect just_right(6, 3, 2, 5);
EXPECT_TRUE(r.SharesEdgeWith(just_above));
EXPECT_TRUE(r.SharesEdgeWith(just_below));
EXPECT_TRUE(r.SharesEdgeWith(just_left));
EXPECT_TRUE(r.SharesEdgeWith(just_right));
// Wrong placement
Rect same_height_no_edge(0, 0, 1, 5);
Rect same_width_no_edge(0, 0, 4, 1);
EXPECT_FALSE(r.SharesEdgeWith(same_height_no_edge));
EXPECT_FALSE(r.SharesEdgeWith(same_width_no_edge));
Rect just_above_no_edge(2, 1, 5, 2); // too wide
Rect just_below_no_edge(2, 8, 3, 2); // too narrow
Rect just_left_no_edge(0, 3, 2, 6); // too tall
Rect just_right_no_edge(6, 3, 2, 4); // too short
EXPECT_FALSE(r.SharesEdgeWith(just_above_no_edge));
EXPECT_FALSE(r.SharesEdgeWith(just_below_no_edge));
EXPECT_FALSE(r.SharesEdgeWith(just_left_no_edge));
EXPECT_FALSE(r.SharesEdgeWith(just_right_no_edge));
}
// Similar to EXPECT_FLOAT_EQ, but lets NaN equal NaN
#define EXPECT_FLOAT_AND_NAN_EQ(a, b) \
{ \
if (a == a || b == b) { \
EXPECT_FLOAT_EQ(a, b); \
} \
}
TEST(RectTest, ScaleRect) {
static const struct Test {
int x1; // source
int y1;
int w1;
int h1;
float scale;
float x2; // target
float y2;
float w2;
float h2;
} tests[] = {
{3, 3, 3, 3, 1.5f, 4.5f, 4.5f, 4.5f, 4.5f},
{3, 3, 3, 3, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
{3, 3, 3, 3, std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::quiet_NaN()},
{3, 3, 3, 3, std::numeric_limits<float>::max(),
std::numeric_limits<float>::max(), std::numeric_limits<float>::max(),
std::numeric_limits<float>::max(), std::numeric_limits<float>::max()}};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
RectF r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
RectF scaled = ScaleRect(r1, tests[i].scale);
EXPECT_FLOAT_AND_NAN_EQ(r2.x(), scaled.x());
EXPECT_FLOAT_AND_NAN_EQ(r2.y(), scaled.y());
EXPECT_FLOAT_AND_NAN_EQ(r2.width(), scaled.width());
EXPECT_FLOAT_AND_NAN_EQ(r2.height(), scaled.height());
}
}
TEST(RectTest, ToEnclosedRect) {
static const struct Test {
float x1; // source
float y1;
float w1;
float h1;
int x2; // target
int y2;
int w2;
int h2;
} tests[] = {
{0.0f, 0.0f, 0.0f, 0.0f, 0, 0, 0, 0},
{-1.5f, -1.5f, 3.0f, 3.0f, -1, -1, 2, 2},
{-1.5f, -1.5f, 3.5f, 3.5f, -1, -1, 3, 3},
{std::numeric_limits<float>::max(), std::numeric_limits<float>::max(),
2.0f, 2.0f, std::numeric_limits<int>::max(),
std::numeric_limits<int>::max(), 0, 0},
{0.0f, 0.0f, std::numeric_limits<float>::max(),
std::numeric_limits<float>::max(), 0, 0,
std::numeric_limits<int>::max(), std::numeric_limits<int>::max()},
{20000.5f, 20000.5f, 0.5f, 0.5f, 20001, 20001, 0, 0},
{std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::quiet_NaN(), 0, 0, 0, 0}};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
RectF r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
Rect enclosed = ToEnclosedRect(r1);
EXPECT_FLOAT_AND_NAN_EQ(r2.x(), enclosed.x());
EXPECT_FLOAT_AND_NAN_EQ(r2.y(), enclosed.y());
EXPECT_FLOAT_AND_NAN_EQ(r2.width(), enclosed.width());
EXPECT_FLOAT_AND_NAN_EQ(r2.height(), enclosed.height());
}
}
TEST(RectTest, ToEnclosingRect) {
static const struct Test {
float x1; // source
float y1;
float w1;
float h1;
int x2; // target
int y2;
int w2;
int h2;
} tests[] = {
{0.0f, 0.0f, 0.0f, 0.0f, 0, 0, 0, 0},
{5.5f, 5.5f, 0.0f, 0.0f, 5, 5, 0, 0},
{-1.5f, -1.5f, 3.0f, 3.0f, -2, -2, 4, 4},
{-1.5f, -1.5f, 3.5f, 3.5f, -2, -2, 4, 4},
{std::numeric_limits<float>::max(), std::numeric_limits<float>::max(),
2.0f, 2.0f, std::numeric_limits<int>::max(),
std::numeric_limits<int>::max(), 0, 0},
{0.0f, 0.0f, std::numeric_limits<float>::max(),
std::numeric_limits<float>::max(), 0, 0,
std::numeric_limits<int>::max(), std::numeric_limits<int>::max()},
{20000.5f, 20000.5f, 0.5f, 0.5f, 20000, 20000, 1, 1},
{std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::quiet_NaN(), 0, 0, 0, 0}};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
RectF r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
Rect enclosed = ToEnclosingRect(r1);
EXPECT_FLOAT_AND_NAN_EQ(r2.x(), enclosed.x());
EXPECT_FLOAT_AND_NAN_EQ(r2.y(), enclosed.y());
EXPECT_FLOAT_AND_NAN_EQ(r2.width(), enclosed.width());
EXPECT_FLOAT_AND_NAN_EQ(r2.height(), enclosed.height());
}
}
TEST(RectTest, ToNearestRect) {
Rect rect;
EXPECT_EQ(rect, ToNearestRect(RectF(rect)));
rect = Rect(-1, -1, 3, 3);
EXPECT_EQ(rect, ToNearestRect(RectF(rect)));
RectF rectf(-1.00001f, -0.999999f, 3.0000001f, 2.999999f);
EXPECT_EQ(rect, ToNearestRect(rectf));
}
TEST(RectTest, ToFlooredRect) {
static const struct Test {
float x1; // source
float y1;
float w1;
float h1;
int x2; // target
int y2;
int w2;
int h2;
} tests[] = {
{0.0f, 0.0f, 0.0f, 0.0f, 0, 0, 0, 0},
{-1.5f, -1.5f, 3.0f, 3.0f, -2, -2, 3, 3},
{-1.5f, -1.5f, 3.5f, 3.5f, -2, -2, 3, 3},
{20000.5f, 20000.5f, 0.5f, 0.5f, 20000, 20000, 0, 0},
};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
RectF r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
Rect floored = ToFlooredRectDeprecated(r1);
EXPECT_FLOAT_EQ(r2.x(), floored.x());
EXPECT_FLOAT_EQ(r2.y(), floored.y());
EXPECT_FLOAT_EQ(r2.width(), floored.width());
EXPECT_FLOAT_EQ(r2.height(), floored.height());
}
}
TEST(RectTest, ScaleToEnclosedRect) {
static const struct Test {
Rect input_rect;
float input_scale;
Rect expected_rect;
} tests[] = {{
Rect(), 5.f, Rect(),
},
{
Rect(1, 1, 1, 1), 5.f, Rect(5, 5, 5, 5),
},
{
Rect(-1, -1, 0, 0), 5.f, Rect(-5, -5, 0, 0),
},
{
Rect(1, -1, 0, 1), 5.f, Rect(5, -5, 0, 5),
},
{
Rect(-1, 1, 1, 0), 5.f, Rect(-5, 5, 5, 0),
},
{
Rect(1, 2, 3, 4), 1.5f, Rect(2, 3, 4, 6),
},
{
Rect(-1, -2, 0, 0), 1.5f, Rect(-1, -3, 0, 0),
}};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
Rect result =
ScaleToEnclosedRect(tests[i].input_rect, tests[i].input_scale);
EXPECT_EQ(tests[i].expected_rect, result);
}
}
TEST(RectTest, ScaleToEnclosingRect) {
static const struct Test {
Rect input_rect;
float input_scale;
Rect expected_rect;
} tests[] = {{
Rect(), 5.f, Rect(),
},
{
Rect(1, 1, 1, 1), 5.f, Rect(5, 5, 5, 5),
},
{
Rect(-1, -1, 0, 0), 5.f, Rect(-5, -5, 0, 0),
},
{
Rect(1, -1, 0, 1), 5.f, Rect(5, -5, 0, 5),
},
{
Rect(-1, 1, 1, 0), 5.f, Rect(-5, 5, 5, 0),
},
{
Rect(1, 2, 3, 4), 1.5f, Rect(1, 3, 5, 6),
},
{
Rect(-1, -2, 0, 0), 1.5f, Rect(-2, -3, 0, 0),
}};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
Rect result =
ScaleToEnclosingRect(tests[i].input_rect, tests[i].input_scale);
EXPECT_EQ(tests[i].expected_rect, result);
}
}
TEST(RectTest, ToRectF) {
// Check that implicit conversion from integer to float compiles.
Rect a(10, 20, 30, 40);
RectF b(10, 20, 30, 40);
RectF intersect = IntersectRects(a, b);
EXPECT_EQ(b, intersect);
EXPECT_EQ(a, b);
EXPECT_EQ(b, a);
}
TEST(RectTest, BoundingRect) {
struct {
Point a;
Point b;
Rect expected;
} int_tests[] = {// If point B dominates A, then A should be the origin.
{Point(4, 6), Point(4, 6), Rect(4, 6, 0, 0)},
{Point(4, 6), Point(8, 6), Rect(4, 6, 4, 0)},
{Point(4, 6), Point(4, 9), Rect(4, 6, 0, 3)},
{Point(4, 6), Point(8, 9), Rect(4, 6, 4, 3)},
// If point A dominates B, then B should be the origin.
{Point(4, 6), Point(4, 6), Rect(4, 6, 0, 0)},
{Point(8, 6), Point(4, 6), Rect(4, 6, 4, 0)},
{Point(4, 9), Point(4, 6), Rect(4, 6, 0, 3)},
{Point(8, 9), Point(4, 6), Rect(4, 6, 4, 3)},
// If neither point dominates, then the origin is a
// combination of the two.
{Point(4, 6), Point(6, 4), Rect(4, 4, 2, 2)},
{Point(-4, -6), Point(-6, -4), Rect(-6, -6, 2, 2)},
{Point(-4, 6), Point(6, -4), Rect(-4, -4, 10, 10)},
};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(int_tests); ++i) {
Rect actual = BoundingRect(int_tests[i].a, int_tests[i].b);
EXPECT_EQ(int_tests[i].expected, actual);
}
struct {
PointF a;
PointF b;
RectF expected;
} float_tests[] = {
// If point B dominates A, then A should be the origin.
{PointF(4.2f, 6.8f), PointF(4.2f, 6.8f), RectF(4.2f, 6.8f, 0, 0)},
{PointF(4.2f, 6.8f), PointF(8.5f, 6.8f), RectF(4.2f, 6.8f, 4.3f, 0)},
{PointF(4.2f, 6.8f), PointF(4.2f, 9.3f), RectF(4.2f, 6.8f, 0, 2.5f)},
{PointF(4.2f, 6.8f), PointF(8.5f, 9.3f), RectF(4.2f, 6.8f, 4.3f, 2.5f)},
// If point A dominates B, then B should be the origin.
{PointF(4.2f, 6.8f), PointF(4.2f, 6.8f), RectF(4.2f, 6.8f, 0, 0)},
{PointF(8.5f, 6.8f), PointF(4.2f, 6.8f), RectF(4.2f, 6.8f, 4.3f, 0)},
{PointF(4.2f, 9.3f), PointF(4.2f, 6.8f), RectF(4.2f, 6.8f, 0, 2.5f)},
{PointF(8.5f, 9.3f), PointF(4.2f, 6.8f), RectF(4.2f, 6.8f, 4.3f, 2.5f)},
// If neither point dominates, then the origin is a combination of the
// two.
{PointF(4.2f, 6.8f), PointF(6.8f, 4.2f), RectF(4.2f, 4.2f, 2.6f, 2.6f)},
{PointF(-4.2f, -6.8f), PointF(-6.8f, -4.2f),
RectF(-6.8f, -6.8f, 2.6f, 2.6f)},
{PointF(-4.2f, 6.8f), PointF(6.8f, -4.2f),
RectF(-4.2f, -4.2f, 11.0f, 11.0f)}};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(float_tests); ++i) {
RectF actual = BoundingRect(float_tests[i].a, float_tests[i].b);
EXPECT_RECTF_EQ(float_tests[i].expected, actual);
}
}
TEST(RectTest, IsExpressibleAsRect) {
EXPECT_TRUE(RectF().IsExpressibleAsRect());
float min = std::numeric_limits<int>::min();
float max = std::numeric_limits<int>::max();
float infinity = std::numeric_limits<float>::infinity();
EXPECT_TRUE(
RectF(min + 200, min + 200, max - 200, max - 200).IsExpressibleAsRect());
EXPECT_FALSE(
RectF(min - 200, min + 200, max + 200, max + 200).IsExpressibleAsRect());
EXPECT_FALSE(
RectF(min + 200, min - 200, max + 200, max + 200).IsExpressibleAsRect());
EXPECT_FALSE(
RectF(min + 200, min + 200, max + 200, max - 200).IsExpressibleAsRect());
EXPECT_FALSE(
RectF(min + 200, min + 200, max - 200, max + 200).IsExpressibleAsRect());
EXPECT_TRUE(RectF(0, 0, max - 200, max - 200).IsExpressibleAsRect());
EXPECT_FALSE(RectF(200, 0, max + 200, max - 200).IsExpressibleAsRect());
EXPECT_FALSE(RectF(0, 200, max - 200, max + 200).IsExpressibleAsRect());
EXPECT_FALSE(RectF(0, 0, max + 200, max - 200).IsExpressibleAsRect());
EXPECT_FALSE(RectF(0, 0, max - 200, max + 200).IsExpressibleAsRect());
EXPECT_FALSE(RectF(infinity, 0, 1, 1).IsExpressibleAsRect());
EXPECT_FALSE(RectF(0, infinity, 1, 1).IsExpressibleAsRect());
EXPECT_FALSE(RectF(0, 0, infinity, 1).IsExpressibleAsRect());
EXPECT_FALSE(RectF(0, 0, 1, infinity).IsExpressibleAsRect());
}
TEST(RectTest, Offset) {
Rect i(1, 2, 3, 4);
EXPECT_EQ(Rect(2, 1, 3, 4), (i + Vector2d(1, -1)));
EXPECT_EQ(Rect(2, 1, 3, 4), (Vector2d(1, -1) + i));
i += Vector2d(1, -1);
EXPECT_EQ(Rect(2, 1, 3, 4), i);
EXPECT_EQ(Rect(1, 2, 3, 4), (i - Vector2d(1, -1)));
i -= Vector2d(1, -1);
EXPECT_EQ(Rect(1, 2, 3, 4), i);
RectF f(1.1f, 2.2f, 3.3f, 4.4f);
EXPECT_EQ(RectF(2.2f, 1.1f, 3.3f, 4.4f), (f + Vector2dF(1.1f, -1.1f)));
EXPECT_EQ(RectF(2.2f, 1.1f, 3.3f, 4.4f), (Vector2dF(1.1f, -1.1f) + f));
f += Vector2dF(1.1f, -1.1f);
EXPECT_EQ(RectF(2.2f, 1.1f, 3.3f, 4.4f), f);
EXPECT_EQ(RectF(1.1f, 2.2f, 3.3f, 4.4f), (f - Vector2dF(1.1f, -1.1f)));
f -= Vector2dF(1.1f, -1.1f);
EXPECT_EQ(RectF(1.1f, 2.2f, 3.3f, 4.4f), f);
}
TEST(RectTest, Corners) {
Rect i(1, 2, 3, 4);
RectF f(1.1f, 2.1f, 3.1f, 4.1f);
EXPECT_EQ(Point(1, 2), i.origin());
EXPECT_EQ(Point(4, 2), i.top_right());
EXPECT_EQ(Point(1, 6), i.bottom_left());
EXPECT_EQ(Point(4, 6), i.bottom_right());
EXPECT_EQ(PointF(1.1f, 2.1f), f.origin());
EXPECT_EQ(PointF(4.2f, 2.1f), f.top_right());
EXPECT_EQ(PointF(1.1f, 6.2f), f.bottom_left());
EXPECT_EQ(PointF(4.2f, 6.2f), f.bottom_right());
}
TEST(RectTest, ManhattanDistanceToPoint) {
Rect i(1, 2, 3, 4);
EXPECT_EQ(0, i.ManhattanDistanceToPoint(Point(1, 2)));
EXPECT_EQ(0, i.ManhattanDistanceToPoint(Point(4, 6)));
EXPECT_EQ(0, i.ManhattanDistanceToPoint(Point(2, 4)));
EXPECT_EQ(3, i.ManhattanDistanceToPoint(Point(0, 0)));
EXPECT_EQ(2, i.ManhattanDistanceToPoint(Point(2, 0)));
EXPECT_EQ(3, i.ManhattanDistanceToPoint(Point(5, 0)));
EXPECT_EQ(1, i.ManhattanDistanceToPoint(Point(5, 4)));
EXPECT_EQ(3, i.ManhattanDistanceToPoint(Point(5, 8)));
EXPECT_EQ(2, i.ManhattanDistanceToPoint(Point(3, 8)));
EXPECT_EQ(2, i.ManhattanDistanceToPoint(Point(0, 7)));
EXPECT_EQ(1, i.ManhattanDistanceToPoint(Point(0, 3)));
RectF f(1.1f, 2.1f, 3.1f, 4.1f);
EXPECT_FLOAT_EQ(0.f, f.ManhattanDistanceToPoint(PointF(1.1f, 2.1f)));
EXPECT_FLOAT_EQ(0.f, f.ManhattanDistanceToPoint(PointF(4.2f, 6.f)));
EXPECT_FLOAT_EQ(0.f, f.ManhattanDistanceToPoint(PointF(2.f, 4.f)));
EXPECT_FLOAT_EQ(3.2f, f.ManhattanDistanceToPoint(PointF(0.f, 0.f)));
EXPECT_FLOAT_EQ(2.1f, f.ManhattanDistanceToPoint(PointF(2.f, 0.f)));
EXPECT_FLOAT_EQ(2.9f, f.ManhattanDistanceToPoint(PointF(5.f, 0.f)));
EXPECT_FLOAT_EQ(.8f, f.ManhattanDistanceToPoint(PointF(5.f, 4.f)));
EXPECT_FLOAT_EQ(2.6f, f.ManhattanDistanceToPoint(PointF(5.f, 8.f)));
EXPECT_FLOAT_EQ(1.8f, f.ManhattanDistanceToPoint(PointF(3.f, 8.f)));
EXPECT_FLOAT_EQ(1.9f, f.ManhattanDistanceToPoint(PointF(0.f, 7.f)));
EXPECT_FLOAT_EQ(1.1f, f.ManhattanDistanceToPoint(PointF(0.f, 3.f)));
}
TEST(RectTest, ManhattanInternalDistance) {
Rect i(0, 0, 400, 400);
EXPECT_EQ(0, i.ManhattanInternalDistance(Rect(-1, 0, 2, 1)));
EXPECT_EQ(1, i.ManhattanInternalDistance(Rect(400, 0, 1, 400)));
EXPECT_EQ(2, i.ManhattanInternalDistance(Rect(-100, -100, 100, 100)));
EXPECT_EQ(2, i.ManhattanInternalDistance(Rect(-101, 100, 100, 100)));
EXPECT_EQ(4, i.ManhattanInternalDistance(Rect(-101, -101, 100, 100)));
EXPECT_EQ(435, i.ManhattanInternalDistance(Rect(630, 603, 100, 100)));
RectF f(0.0f, 0.0f, 400.0f, 400.0f);
static const float kEpsilon = std::numeric_limits<float>::epsilon();
EXPECT_FLOAT_EQ(0.0f,
f.ManhattanInternalDistance(RectF(-1.0f, 0.0f, 2.0f, 1.0f)));
EXPECT_FLOAT_EQ(
kEpsilon, f.ManhattanInternalDistance(RectF(400.0f, 0.0f, 1.0f, 400.0f)));
EXPECT_FLOAT_EQ(2.0f * kEpsilon, f.ManhattanInternalDistance(RectF(
-100.0f, -100.0f, 100.0f, 100.0f)));
EXPECT_FLOAT_EQ(1.0f + kEpsilon, f.ManhattanInternalDistance(
RectF(-101.0f, 100.0f, 100.0f, 100.0f)));
EXPECT_FLOAT_EQ(
2.0f + 2.0f * kEpsilon,
f.ManhattanInternalDistance(RectF(-101.0f, -101.0f, 100.0f, 100.0f)));
EXPECT_FLOAT_EQ(
433.0f + 2.0f * kEpsilon,
f.ManhattanInternalDistance(RectF(630.0f, 603.0f, 100.0f, 100.0f)));
EXPECT_FLOAT_EQ(0.0f,
f.ManhattanInternalDistance(RectF(-1.0f, 0.0f, 1.1f, 1.0f)));
EXPECT_FLOAT_EQ(0.1f + kEpsilon,
f.ManhattanInternalDistance(RectF(-1.5f, 0.0f, 1.4f, 1.0f)));
EXPECT_FLOAT_EQ(kEpsilon,
f.ManhattanInternalDistance(RectF(-1.5f, 0.0f, 1.5f, 1.0f)));
}
} // namespace math
} // namespace cobalt