common_test.cxx 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. //
  2. // common_test.cxx
  3. // math-test
  4. //
  5. // Created by Sam Jaffe on 5/5/19.
  6. // Copyright © 2019 Sam Jaffe. All rights reserved.
  7. //
  8. #include <cmath>
  9. #include <testing/xcode_gtest_helper.h>
  10. #include "game/math/angle.hpp"
  11. #include "game/math/common.hpp"
  12. #include "game/math/shape.hpp"
  13. #include "test_printers.h"
  14. using namespace math::dim2;
  15. using namespace testing;
  16. namespace math {
  17. bool intersects(dim2::triangle const &, dim2::triangle const &);
  18. }
  19. template <typename T, typename G>
  20. decltype(auto) generate(T min, T max, T step, G && generator) {
  21. std::vector<decltype(generator(min))> out;
  22. for (T val = min; val < max; val += step) {
  23. out.emplace_back(generator(val));
  24. }
  25. return out;
  26. }
  27. template <typename T> decltype(auto) generate(T min, T max, T step) {
  28. auto identity = [](T const & val) { return val; };
  29. return generate(min, max, step, identity);
  30. }
  31. struct LineTest : TestWithParam<std::tuple<point, int>> {};
  32. TEST_P(LineTest, ExistsOnOriginLine) {
  33. point const end = std::get<0>(GetParam());
  34. line const ln{{{0, 0}}, end};
  35. point const pt = end * std::get<1>(GetParam()) / 100.f;
  36. EXPECT_TRUE(math::contains(ln, pt));
  37. }
  38. TEST_P(LineTest, ColinearOutsideDoesNotContain) {
  39. point const end = std::get<0>(GetParam());
  40. line const ln{end, end * 2};
  41. point const pt = end * std::get<1>(GetParam()) / 100.f;
  42. EXPECT_FALSE(math::contains(ln, pt));
  43. }
  44. std::vector<point> const line_ends{{{1, 1}}, {{0, 1}}, {{1, 0}},
  45. {{-1, -1}}, {{0, -1}}, {{-1, 0}}};
  46. INSTANTIATE_TEST_SUITE_P(Contains, LineTest,
  47. Combine(ValuesIn(line_ends),
  48. ValuesIn(generate(0, 100, 10))));
  49. struct UnitCircleTest : TestWithParam<point> {};
  50. TEST_P(UnitCircleTest, ExistsInCircle) {
  51. circle unit{{{0, 0}}, 1};
  52. EXPECT_TRUE(math::contains(unit, GetParam()));
  53. }
  54. TEST_P(UnitCircleTest, OutsideSmallerCircle) {
  55. circle subunit{{{0, 0}}, 0.9999};
  56. EXPECT_FALSE(math::contains(subunit, GetParam()));
  57. }
  58. point unit_circle_angle(math::degree degs) {
  59. return point(math::sin(degs), math::cos(degs));
  60. }
  61. INSTANTIATE_TEST_SUITE_P(Contains, UnitCircleTest,
  62. ValuesIn(generate(0.0, 360.0, 5.0,
  63. unit_circle_angle)));
  64. struct UnitSquareTest : TestWithParam<std::tuple<float, float>> {};
  65. TEST_P(UnitSquareTest, PointInSquare) {
  66. square unit{{{0, 0}}, 1};
  67. point pt{{std::get<0>(GetParam()), std::get<1>(GetParam())}};
  68. EXPECT_TRUE(math::contains(unit, pt));
  69. }
  70. TEST_F(UnitSquareTest, PointOutsideSquare) {
  71. square unit{{{0, 0}}, 1};
  72. EXPECT_FALSE(math::contains(unit, {{0.f, 1.1f}}));
  73. EXPECT_FALSE(math::contains(unit, {{0.f, -0.1f}}));
  74. EXPECT_FALSE(math::contains(unit, {{-0.1f, 0.0f}}));
  75. EXPECT_FALSE(math::contains(unit, {{1.1f, 0.0f}}));
  76. }
  77. INSTANTIATE_TEST_SUITE_P(Contains, UnitSquareTest,
  78. Combine(ValuesIn(generate(0.f, 1.f, 0.25f)),
  79. ValuesIn(generate(0.f, 1.f, 0.25f))));
  80. struct LineQuadTest : TestWithParam<point> {};
  81. TEST_P(LineQuadTest, OriginLineIntersectsUnitSquare) {
  82. square const unit{{{0, 0}}, 1};
  83. line const ln{{{0, 0}}, GetParam()};
  84. EXPECT_TRUE(math::intersects(ln, unit));
  85. EXPECT_TRUE(math::intersects(unit, ln));
  86. }
  87. TEST_P(LineQuadTest, CrossingLineIntersectsUnitSquare) {
  88. square const unit{{{0, 0}}, 1};
  89. line const ln{{{0.5, 0.5}}, GetParam()};
  90. EXPECT_TRUE(math::intersects(ln, unit));
  91. EXPECT_TRUE(math::intersects(unit, ln));
  92. }
  93. TEST_P(LineQuadTest, CrossingLineIntersectsSquare) {
  94. square const unit{{{0, 0}}, 0.9};
  95. line const ln{{{0.5, 0.5}}, GetParam()};
  96. EXPECT_TRUE(math::intersects(ln, unit));
  97. EXPECT_TRUE(math::intersects(unit, ln));
  98. }
  99. TEST_F(LineQuadTest, JustPointIntersection) {
  100. square const unit{{{0, 0}}, 1};
  101. line const ln{{{1, 1}}, {{2, 1}}};
  102. EXPECT_TRUE(math::intersects(ln, unit));
  103. EXPECT_TRUE(math::intersects(unit, ln));
  104. }
  105. TEST_F(LineQuadTest, EntirelyEncasedIntersection) {
  106. square const unit{{{0, 0}}, 1};
  107. line const ln{{{0.5, 0.5}}, {{0.75, 0.75}}};
  108. EXPECT_TRUE(math::intersects(ln, unit));
  109. EXPECT_TRUE(math::intersects(unit, ln));
  110. }
  111. TEST_F(LineQuadTest, OutsideByAnInch) {
  112. square const unit{{{0, 0}}, 1};
  113. line const ln{{{1.001, 1}}, {{2, 1}}};
  114. math::intersects(ln, unit);
  115. EXPECT_FALSE(math::intersects(ln, unit));
  116. EXPECT_FALSE(math::intersects(unit, ln));
  117. }
  118. INSTANTIATE_TEST_SUITE_P(Intersects, LineQuadTest, ValuesIn(line_ends));
  119. TEST(CircleTest, CircleIntersectsSelf) {
  120. circle const c1{{{0, 0}}, 1};
  121. circle const c2{{{0, 0}}, 1};
  122. EXPECT_TRUE(math::intersects(c1, c2));
  123. }
  124. TEST(CircleTest, CircleIntersectsAtOnePoint) {
  125. circle const c1{{{0, 0}}, 1};
  126. circle const c2{{{0, 2}}, 1};
  127. EXPECT_TRUE(math::intersects(c1, c2));
  128. }
  129. TEST(CircleTest, CircleIntersectsWithChord) {
  130. circle const c1{{{0, 0}}, 0.5};
  131. circle const c2{{{0, 1}}, 0.5};
  132. EXPECT_TRUE(math::intersects(c1, c2));
  133. }
  134. TEST(CircleTest, CircleContainedWithin) {
  135. circle const c1{{{0, 0}}, 2};
  136. circle const c2{{{0, 1}}, 0.5};
  137. EXPECT_TRUE(math::intersects(c1, c2));
  138. }
  139. TEST(CircleTest, CircleOutsideDoesNotIntersect) {
  140. circle const c1{{{0, 0}}, 1};
  141. circle const c2{{{1.5, 1.5}}, 1};
  142. EXPECT_FALSE(math::intersects(c1, c2));
  143. }
  144. TEST(CircleTest, LineIntersectsFromWithin) {
  145. circle const c1{{{0, 0}}, 1};
  146. line const ln{{{0.5, 0.5}}, {{1, 1}}};
  147. EXPECT_TRUE(math::intersects(c1, ln));
  148. }
  149. TEST(CircleTest, LineIntersectsOnEdge) {
  150. circle const c1{{{0, 0}}, 1};
  151. line const ln{{{-1, 1}}, {{1, 1}}};
  152. EXPECT_TRUE(math::intersects(c1, ln));
  153. }
  154. TEST(CircleTest, LineIntersectsWhenContained) {
  155. circle const c1{{{0, 0}}, 1};
  156. line const ln{{{0.5, 0.5}}, {{-0.5, -0.5}}};
  157. EXPECT_TRUE(math::intersects(c1, ln));
  158. }
  159. TEST(CircleTest, ChordLineIntersects) {
  160. circle const c1{{{0, 0}}, 1};
  161. line const ln{{{1.5, -0.5}}, {{-0.5, 1.5}}};
  162. EXPECT_TRUE(math::intersects(c1, ln));
  163. }
  164. TEST(CircleTest, OutsideLineDoesntIntersect) {
  165. circle const c1{{{0, 0}}, 1};
  166. line const ln{{{1, 1}}, {{0, 1.5}}};
  167. EXPECT_FALSE(math::intersects(c1, ln));
  168. }
  169. TEST(CircleTest, OverlappingQuad) {
  170. circle const c1{{{0, 0}}, 1};
  171. square const sq{{{0.5, 0.5}}, 0.5};
  172. EXPECT_TRUE(math::intersects(c1, sq));
  173. }
  174. TEST(CircleTest, IntersectsAtEdge) {
  175. circle const c1{{{0, 0}}, 1};
  176. square const sq{{{-1, 1}}, 2};
  177. EXPECT_TRUE(math::intersects(c1, sq));
  178. }
  179. TEST(CircleTest, IntersectsAtCorner) {
  180. circle const c1{{{0, 0}}, 1};
  181. square const sq{{{0, 1}}, 1};
  182. EXPECT_TRUE(math::intersects(c1, sq));
  183. }
  184. TEST(CircleTest, ContainingQuad) {
  185. circle const c1{{{0, 0}}, 1};
  186. square const sq{{{0, 0}}, 0.5};
  187. EXPECT_TRUE(math::intersects(c1, sq));
  188. }
  189. TEST(CircleTest, ContainedInQuad) {
  190. circle const c1{{{0, 0}}, 0.5};
  191. square const sq{{{-1, -1}}, 2};
  192. EXPECT_TRUE(math::intersects(c1, sq));
  193. }
  194. TEST(CircleTest, NonIntersectingTangent) {
  195. circle const c1{{{0, 0}}, 1};
  196. square const sq{{{1, 1}}, 1};
  197. EXPECT_FALSE(math::intersects(c1, sq));
  198. }
  199. TEST(CircleTest, NonIntersecting) {
  200. circle const c1{{{0, 0}}, 1};
  201. square const sq{{{1.5, 0.5}}, 0.5};
  202. EXPECT_FALSE(math::intersects(c1, sq));
  203. }
  204. TEST(TriangleTest, TriangleIntersectsSelf) {
  205. triangle tri{{{0, 0}}, {{0, 1}}, {{1, 0}}};
  206. EXPECT_TRUE(math::intersects(tri, tri));
  207. }
  208. TEST(TriangleTest, DoesNotIntersectOffset) {
  209. triangle lhs{{{0, 0}}, {{0, 1}}, {{1, 0}}};
  210. auto tri = [](math::dim2::point const & pt) { return triangle{pt, pt, pt}; };
  211. math::dim2::point const clock_ab{{1, 1}};
  212. math::dim2::point const clock_bc{{-1, -1}};
  213. math::dim2::point const clock_ca{{-1, 2}};
  214. // Each of these expectations fails one of the check-edges calls
  215. // in the intersection algorithm, which proves that each point of
  216. // the triangle is not within (or on) the bounds of the other triangle
  217. // Each of these tests indicates one edge of the triangle 'lhs', and
  218. // tests for whether it orients around any of the points of the other
  219. // 'triangle'.
  220. // Tests are done for each edge vs. the the opposite triangle,
  221. // starting with edges for the lhs, and then the edges on the rhs.
  222. EXPECT_FALSE(math::intersects(lhs, tri(clock_ab)));
  223. EXPECT_FALSE(math::intersects(lhs, tri(clock_bc)));
  224. EXPECT_FALSE(math::intersects(lhs, tri(clock_ca)));
  225. EXPECT_FALSE(math::intersects(tri(clock_ab), lhs));
  226. EXPECT_FALSE(math::intersects(tri(clock_bc), lhs));
  227. EXPECT_FALSE(math::intersects(tri(clock_ca), lhs));
  228. }
  229. TEST(QuadTest, IntersectsContain) {
  230. quad const lhs = square{{{-0.5f, -0.5f}}, 1};
  231. quad const rhs = square{{{-.25f, -.25f}}, 0.5};
  232. EXPECT_TRUE(math::intersects(lhs, rhs));
  233. }
  234. TEST(QuadTest, NoIntersectionAtEdge) {
  235. quad const lhs = square{{{-0.5f, -0.5f}}, 1};
  236. quad const rhs = square{{{0.0f, 0.5f}}, 1};
  237. EXPECT_FALSE(math::intersects(lhs, rhs));
  238. }
  239. TEST(QuadTest, NoIntersectionAtCorner) {
  240. quad const lhs = square{{{-0.5f, -0.5f}}, 1};
  241. quad const rhs = square{{{0.5f, 0.5f}}, 1};
  242. EXPECT_FALSE(math::intersects(lhs, rhs));
  243. }
  244. TEST(QuadTest, NoIntersection) {
  245. quad const lhs = square{{{-1.0f, -0.5f}}, 1};
  246. quad const rhs = square{{{0.5f, 0.5f}}, 1};
  247. EXPECT_FALSE(math::intersects(lhs, rhs));
  248. }
  249. TEST(RotateTest, RotatingSquareAroundOrigin) {
  250. math::degree degrees{90.0};
  251. // A square with a side-length of 2 and a center at the origin
  252. math::vec2 const origin{0.f, 0.f};
  253. quad const object = square{{{-1, -1}}, 2};
  254. quad const rotated = math::rotate(origin, object, degrees);
  255. EXPECT_THAT(rotated.ll, Eq(math::vec2(1.f, -1.f)));
  256. EXPECT_THAT(rotated.lr, Eq(math::vec2(1.f, 1.f)));
  257. EXPECT_THAT(rotated.ur, Eq(math::vec2(-1.f, 1.f)));
  258. EXPECT_THAT(rotated.ul, Eq(math::vec2(-1.f, -1.f)));
  259. EXPECT_THAT(math::rotate(origin, rotated, -degrees), Eq(object));
  260. }
  261. TEST(RotateTest, RotatingSquareAroundOwnPoint) {
  262. math::degree degrees{90.0};
  263. // A square with a side-length of 2 and a center at the origin
  264. math::vec2 const axis{-1.f, -1.f};
  265. quad const object = square{{{-1, -1}}, 2};
  266. quad const rotated = math::rotate(axis, object, degrees);
  267. EXPECT_THAT(rotated.ll, Eq(math::vec2(-1.f, -1.f)));
  268. EXPECT_THAT(rotated.lr, Eq(math::vec2(-1.f, 1.f)));
  269. EXPECT_THAT(rotated.ur, Eq(math::vec2(-3.f, 1.f)));
  270. EXPECT_THAT(rotated.ul, Eq(math::vec2(-3.f, -1.f)));
  271. EXPECT_THAT(math::rotate(axis, rotated, -degrees), Eq(object));
  272. }