| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328 |
- //
- // common_test.cxx
- // math-test
- //
- // Created by Sam Jaffe on 5/5/19.
- // Copyright © 2019 Sam Jaffe. All rights reserved.
- //
- #include <cmath>
- #include <testing/xcode_gtest_helper.h>
- #include "game/math/angle.hpp"
- #include "game/math/common.hpp"
- #include "game/math/shape.hpp"
- #include "test_printers.h"
- using namespace math::dim2;
- using namespace testing;
- namespace math {
- bool intersects(dim2::triangle const &, dim2::triangle const &);
- }
- template <typename T, typename G>
- decltype(auto) generate(T min, T max, T step, G && generator) {
- std::vector<decltype(generator(min))> out;
- for (T val = min; val < max; val += step) {
- out.emplace_back(generator(val));
- }
- return out;
- }
- template <typename T> decltype(auto) generate(T min, T max, T step) {
- auto identity = [](T const & val) { return val; };
- return generate(min, max, step, identity);
- }
- struct LineTest : TestWithParam<std::tuple<point, int>> {};
- TEST_P(LineTest, ExistsOnOriginLine) {
- point const end = std::get<0>(GetParam());
- line const ln{{{0, 0}}, end};
- point const pt = end * std::get<1>(GetParam()) / 100.f;
- EXPECT_TRUE(math::contains(ln, pt));
- }
- TEST_P(LineTest, ColinearOutsideDoesNotContain) {
- point const end = std::get<0>(GetParam());
- line const ln{end, end * 2};
- point const pt = end * std::get<1>(GetParam()) / 100.f;
- EXPECT_FALSE(math::contains(ln, pt));
- }
- std::vector<point> const line_ends{{{1, 1}}, {{0, 1}}, {{1, 0}},
- {{-1, -1}}, {{0, -1}}, {{-1, 0}}};
- INSTANTIATE_TEST_SUITE_P(Contains, LineTest,
- Combine(ValuesIn(line_ends),
- ValuesIn(generate(0, 100, 10))));
- struct UnitCircleTest : TestWithParam<point> {};
- TEST_P(UnitCircleTest, ExistsInCircle) {
- circle unit{{{0, 0}}, 1};
- EXPECT_TRUE(math::contains(unit, GetParam()));
- }
- TEST_P(UnitCircleTest, OutsideSmallerCircle) {
- circle subunit{{{0, 0}}, 0.9999};
- EXPECT_FALSE(math::contains(subunit, GetParam()));
- }
- point unit_circle_angle(math::degree degs) {
- return point(math::sin(degs), math::cos(degs));
- }
- INSTANTIATE_TEST_SUITE_P(Contains, UnitCircleTest,
- ValuesIn(generate(0.0, 360.0, 5.0,
- unit_circle_angle)));
- struct UnitSquareTest : TestWithParam<std::tuple<float, float>> {};
- TEST_P(UnitSquareTest, PointInSquare) {
- square unit{{{0, 0}}, 1};
- point pt{{std::get<0>(GetParam()), std::get<1>(GetParam())}};
- EXPECT_TRUE(math::contains(unit, pt));
- }
- TEST_F(UnitSquareTest, PointOutsideSquare) {
- square unit{{{0, 0}}, 1};
- EXPECT_FALSE(math::contains(unit, {{0.f, 1.1f}}));
- EXPECT_FALSE(math::contains(unit, {{0.f, -0.1f}}));
- EXPECT_FALSE(math::contains(unit, {{-0.1f, 0.0f}}));
- EXPECT_FALSE(math::contains(unit, {{1.1f, 0.0f}}));
- }
- INSTANTIATE_TEST_SUITE_P(Contains, UnitSquareTest,
- Combine(ValuesIn(generate(0.f, 1.f, 0.25f)),
- ValuesIn(generate(0.f, 1.f, 0.25f))));
- struct LineQuadTest : TestWithParam<point> {};
- TEST_P(LineQuadTest, OriginLineIntersectsUnitSquare) {
- square const unit{{{0, 0}}, 1};
- line const ln{{{0, 0}}, GetParam()};
- EXPECT_TRUE(math::intersects(ln, unit));
- EXPECT_TRUE(math::intersects(unit, ln));
- }
- TEST_P(LineQuadTest, CrossingLineIntersectsUnitSquare) {
- square const unit{{{0, 0}}, 1};
- line const ln{{{0.5, 0.5}}, GetParam()};
- EXPECT_TRUE(math::intersects(ln, unit));
- EXPECT_TRUE(math::intersects(unit, ln));
- }
- TEST_P(LineQuadTest, CrossingLineIntersectsSquare) {
- square const unit{{{0, 0}}, 0.9};
- line const ln{{{0.5, 0.5}}, GetParam()};
- EXPECT_TRUE(math::intersects(ln, unit));
- EXPECT_TRUE(math::intersects(unit, ln));
- }
- TEST_F(LineQuadTest, JustPointIntersection) {
- square const unit{{{0, 0}}, 1};
- line const ln{{{1, 1}}, {{2, 1}}};
- EXPECT_TRUE(math::intersects(ln, unit));
- EXPECT_TRUE(math::intersects(unit, ln));
- }
- TEST_F(LineQuadTest, EntirelyEncasedIntersection) {
- square const unit{{{0, 0}}, 1};
- line const ln{{{0.5, 0.5}}, {{0.75, 0.75}}};
- EXPECT_TRUE(math::intersects(ln, unit));
- EXPECT_TRUE(math::intersects(unit, ln));
- }
- TEST_F(LineQuadTest, OutsideByAnInch) {
- square const unit{{{0, 0}}, 1};
- line const ln{{{1.001, 1}}, {{2, 1}}};
- math::intersects(ln, unit);
- EXPECT_FALSE(math::intersects(ln, unit));
- EXPECT_FALSE(math::intersects(unit, ln));
- }
- INSTANTIATE_TEST_SUITE_P(Intersects, LineQuadTest, ValuesIn(line_ends));
- TEST(CircleTest, CircleIntersectsSelf) {
- circle const c1{{{0, 0}}, 1};
- circle const c2{{{0, 0}}, 1};
- EXPECT_TRUE(math::intersects(c1, c2));
- }
- TEST(CircleTest, CircleIntersectsAtOnePoint) {
- circle const c1{{{0, 0}}, 1};
- circle const c2{{{0, 2}}, 1};
- EXPECT_TRUE(math::intersects(c1, c2));
- }
- TEST(CircleTest, CircleIntersectsWithChord) {
- circle const c1{{{0, 0}}, 0.5};
- circle const c2{{{0, 1}}, 0.5};
- EXPECT_TRUE(math::intersects(c1, c2));
- }
- TEST(CircleTest, CircleContainedWithin) {
- circle const c1{{{0, 0}}, 2};
- circle const c2{{{0, 1}}, 0.5};
- EXPECT_TRUE(math::intersects(c1, c2));
- }
- TEST(CircleTest, CircleOutsideDoesNotIntersect) {
- circle const c1{{{0, 0}}, 1};
- circle const c2{{{1.5, 1.5}}, 1};
- EXPECT_FALSE(math::intersects(c1, c2));
- }
- TEST(CircleTest, LineIntersectsFromWithin) {
- circle const c1{{{0, 0}}, 1};
- line const ln{{{0.5, 0.5}}, {{1, 1}}};
- EXPECT_TRUE(math::intersects(c1, ln));
- }
- TEST(CircleTest, LineIntersectsOnEdge) {
- circle const c1{{{0, 0}}, 1};
- line const ln{{{-1, 1}}, {{1, 1}}};
- EXPECT_TRUE(math::intersects(c1, ln));
- }
- TEST(CircleTest, LineIntersectsWhenContained) {
- circle const c1{{{0, 0}}, 1};
- line const ln{{{0.5, 0.5}}, {{-0.5, -0.5}}};
- EXPECT_TRUE(math::intersects(c1, ln));
- }
- TEST(CircleTest, ChordLineIntersects) {
- circle const c1{{{0, 0}}, 1};
- line const ln{{{1.5, -0.5}}, {{-0.5, 1.5}}};
- EXPECT_TRUE(math::intersects(c1, ln));
- }
- TEST(CircleTest, OutsideLineDoesntIntersect) {
- circle const c1{{{0, 0}}, 1};
- line const ln{{{1, 1}}, {{0, 1.5}}};
- EXPECT_FALSE(math::intersects(c1, ln));
- }
- TEST(CircleTest, OverlappingQuad) {
- circle const c1{{{0, 0}}, 1};
- square const sq{{{0.5, 0.5}}, 0.5};
- EXPECT_TRUE(math::intersects(c1, sq));
- }
- TEST(CircleTest, IntersectsAtEdge) {
- circle const c1{{{0, 0}}, 1};
- square const sq{{{-1, 1}}, 2};
- EXPECT_TRUE(math::intersects(c1, sq));
- }
- TEST(CircleTest, IntersectsAtCorner) {
- circle const c1{{{0, 0}}, 1};
- square const sq{{{0, 1}}, 1};
- EXPECT_TRUE(math::intersects(c1, sq));
- }
- TEST(CircleTest, ContainingQuad) {
- circle const c1{{{0, 0}}, 1};
- square const sq{{{0, 0}}, 0.5};
- EXPECT_TRUE(math::intersects(c1, sq));
- }
- TEST(CircleTest, ContainedInQuad) {
- circle const c1{{{0, 0}}, 0.5};
- square const sq{{{-1, -1}}, 2};
- EXPECT_TRUE(math::intersects(c1, sq));
- }
- TEST(CircleTest, NonIntersectingTangent) {
- circle const c1{{{0, 0}}, 1};
- square const sq{{{1, 1}}, 1};
- EXPECT_FALSE(math::intersects(c1, sq));
- }
- TEST(CircleTest, NonIntersecting) {
- circle const c1{{{0, 0}}, 1};
- square const sq{{{1.5, 0.5}}, 0.5};
- EXPECT_FALSE(math::intersects(c1, sq));
- }
- TEST(TriangleTest, TriangleIntersectsSelf) {
- triangle tri{{{0, 0}}, {{0, 1}}, {{1, 0}}};
- EXPECT_TRUE(math::intersects(tri, tri));
- }
- TEST(TriangleTest, DoesNotIntersectOffset) {
- triangle lhs{{{0, 0}}, {{0, 1}}, {{1, 0}}};
- auto tri = [](math::dim2::point const & pt) { return triangle{pt, pt, pt}; };
- math::dim2::point const clock_ab{{1, 1}};
- math::dim2::point const clock_bc{{-1, -1}};
- math::dim2::point const clock_ca{{-1, 2}};
- // Each of these expectations fails one of the check-edges calls
- // in the intersection algorithm, which proves that each point of
- // the triangle is not within (or on) the bounds of the other triangle
- // Each of these tests indicates one edge of the triangle 'lhs', and
- // tests for whether it orients around any of the points of the other
- // 'triangle'.
- // Tests are done for each edge vs. the the opposite triangle,
- // starting with edges for the lhs, and then the edges on the rhs.
- EXPECT_FALSE(math::intersects(lhs, tri(clock_ab)));
- EXPECT_FALSE(math::intersects(lhs, tri(clock_bc)));
- EXPECT_FALSE(math::intersects(lhs, tri(clock_ca)));
- EXPECT_FALSE(math::intersects(tri(clock_ab), lhs));
- EXPECT_FALSE(math::intersects(tri(clock_bc), lhs));
- EXPECT_FALSE(math::intersects(tri(clock_ca), lhs));
- }
- TEST(QuadTest, IntersectsContain) {
- quad const lhs = square{{{-0.5f, -0.5f}}, 1};
- quad const rhs = square{{{-.25f, -.25f}}, 0.5};
- EXPECT_TRUE(math::intersects(lhs, rhs));
- }
- TEST(QuadTest, NoIntersectionAtEdge) {
- quad const lhs = square{{{-0.5f, -0.5f}}, 1};
- quad const rhs = square{{{0.0f, 0.5f}}, 1};
- EXPECT_FALSE(math::intersects(lhs, rhs));
- }
- TEST(QuadTest, NoIntersectionAtCorner) {
- quad const lhs = square{{{-0.5f, -0.5f}}, 1};
- quad const rhs = square{{{0.5f, 0.5f}}, 1};
- EXPECT_FALSE(math::intersects(lhs, rhs));
- }
- TEST(QuadTest, NoIntersection) {
- quad const lhs = square{{{-1.0f, -0.5f}}, 1};
- quad const rhs = square{{{0.5f, 0.5f}}, 1};
- EXPECT_FALSE(math::intersects(lhs, rhs));
- }
- TEST(RotateTest, RotatingSquareAroundOrigin) {
- math::degree degrees{90.0};
- // A square with a side-length of 2 and a center at the origin
- math::vec2 const origin{0.f, 0.f};
- quad const object = square{{{-1, -1}}, 2};
- quad const rotated = math::rotate(origin, object, degrees);
- EXPECT_THAT(rotated.ll, Eq(math::vec2(1.f, -1.f)));
- EXPECT_THAT(rotated.lr, Eq(math::vec2(1.f, 1.f)));
- EXPECT_THAT(rotated.ur, Eq(math::vec2(-1.f, 1.f)));
- EXPECT_THAT(rotated.ul, Eq(math::vec2(-1.f, -1.f)));
- EXPECT_THAT(math::rotate(origin, rotated, -degrees), Eq(object));
- }
- TEST(RotateTest, RotatingSquareAroundOwnPoint) {
- math::degree degrees{90.0};
- // A square with a side-length of 2 and a center at the origin
- math::vec2 const axis{-1.f, -1.f};
- quad const object = square{{{-1, -1}}, 2};
- quad const rotated = math::rotate(axis, object, degrees);
- EXPECT_THAT(rotated.ll, Eq(math::vec2(-1.f, -1.f)));
- EXPECT_THAT(rotated.lr, Eq(math::vec2(-1.f, 1.f)));
- EXPECT_THAT(rotated.ur, Eq(math::vec2(-3.f, 1.f)));
- EXPECT_THAT(rotated.ul, Eq(math::vec2(-3.f, -1.f)));
- EXPECT_THAT(math::rotate(axis, rotated, -degrees), Eq(object));
- }
|