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 <gmock/gmock.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_CASE_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(make_vector(math::sin(degs), math::cos(degs)));
  60. }
  61. INSTANTIATE_TEST_CASE_P(Contains, UnitCircleTest,
  62. ValuesIn(generate(0.0, 360.0, 5.0, unit_circle_angle)));
  63. struct UnitSquareTest : TestWithParam<std::tuple<float, float>> {};
  64. TEST_P(UnitSquareTest, PointInSquare) {
  65. square unit{{{0, 0}}, 1};
  66. point pt{{std::get<0>(GetParam()), std::get<1>(GetParam())}};
  67. EXPECT_TRUE(math::contains(unit, pt));
  68. }
  69. TEST_F(UnitSquareTest, PointOutsideSquare) {
  70. square unit{{{0, 0}}, 1};
  71. EXPECT_FALSE(math::contains(unit, {{0.f, 1.1f}}));
  72. EXPECT_FALSE(math::contains(unit, {{0.f, -0.1f}}));
  73. EXPECT_FALSE(math::contains(unit, {{-0.1f, 0.0f}}));
  74. EXPECT_FALSE(math::contains(unit, {{1.1f, 0.0f}}));
  75. }
  76. INSTANTIATE_TEST_CASE_P(Contains, UnitSquareTest,
  77. Combine(ValuesIn(generate(0.f, 1.f, 0.25f)),
  78. ValuesIn(generate(0.f, 1.f, 0.25f))));
  79. struct LineQuadTest : TestWithParam<point> {};
  80. TEST_P(LineQuadTest, OriginLineIntersectsUnitSquare) {
  81. square const unit{{{0, 0}}, 1};
  82. line const ln{{{0, 0}}, GetParam()};
  83. EXPECT_TRUE(math::intersects(ln, unit));
  84. EXPECT_TRUE(math::intersects(unit, ln));
  85. }
  86. TEST_P(LineQuadTest, CrossingLineIntersectsUnitSquare) {
  87. square const unit{{{0, 0}}, 1};
  88. line const ln{{{0.5, 0.5}}, GetParam()};
  89. EXPECT_TRUE(math::intersects(ln, unit));
  90. EXPECT_TRUE(math::intersects(unit, ln));
  91. }
  92. TEST_P(LineQuadTest, CrossingLineIntersectsSquare) {
  93. square const unit{{{0, 0}}, 0.9};
  94. line const ln{{{0.5, 0.5}}, GetParam()};
  95. EXPECT_TRUE(math::intersects(ln, unit));
  96. EXPECT_TRUE(math::intersects(unit, ln));
  97. }
  98. TEST_F(LineQuadTest, JustPointIntersection) {
  99. square const unit{{{0, 0}}, 1};
  100. line const ln{{{1, 1}}, {{2, 1}}};
  101. EXPECT_TRUE(math::intersects(ln, unit));
  102. EXPECT_TRUE(math::intersects(unit, ln));
  103. }
  104. TEST_F(LineQuadTest, EntirelyEncasedIntersection) {
  105. square const unit{{{0, 0}}, 1};
  106. line const ln{{{0.5, 0.5}}, {{0.75, 0.75}}};
  107. EXPECT_TRUE(math::intersects(ln, unit));
  108. EXPECT_TRUE(math::intersects(unit, ln));
  109. }
  110. TEST_F(LineQuadTest, OutsideByAnInch) {
  111. square const unit{{{0, 0}}, 1};
  112. line const ln{{{1.001, 1}}, {{2, 1}}};
  113. math::intersects(ln, unit);
  114. EXPECT_FALSE(math::intersects(ln, unit));
  115. EXPECT_FALSE(math::intersects(unit, ln));
  116. }
  117. INSTANTIATE_TEST_CASE_P(Intersects, LineQuadTest, ValuesIn(line_ends));
  118. TEST(CircleTest, CircleIntersectsSelf) {
  119. circle const c1{{{0, 0}}, 1};
  120. circle const c2{{{0, 0}}, 1};
  121. EXPECT_TRUE(math::intersects(c1, c2));
  122. }
  123. TEST(CircleTest, CircleIntersectsAtOnePoint) {
  124. circle const c1{{{0, 0}}, 1};
  125. circle const c2{{{0, 2}}, 1};
  126. EXPECT_TRUE(math::intersects(c1, c2));
  127. }
  128. TEST(CircleTest, CircleIntersectsWithChord) {
  129. circle const c1{{{0, 0}}, 0.5};
  130. circle const c2{{{0, 1}}, 0.5};
  131. EXPECT_TRUE(math::intersects(c1, c2));
  132. }
  133. TEST(CircleTest, CircleContainedWithin) {
  134. circle const c1{{{0, 0}}, 2};
  135. circle const c2{{{0, 1}}, 0.5};
  136. EXPECT_TRUE(math::intersects(c1, c2));
  137. }
  138. TEST(CircleTest, CircleOutsideDoesNotIntersect) {
  139. circle const c1{{{0, 0}}, 1};
  140. circle const c2{{{1.5, 1.5}}, 1};
  141. EXPECT_FALSE(math::intersects(c1, c2));
  142. }
  143. TEST(CircleTest, LineIntersectsFromWithin) {
  144. circle const c1{{{0, 0}}, 1};
  145. line const ln{{{0.5, 0.5}}, {{1, 1}}};
  146. EXPECT_TRUE(math::intersects(c1, ln));
  147. }
  148. TEST(CircleTest, LineIntersectsOnEdge) {
  149. circle const c1{{{0, 0}}, 1};
  150. line const ln{{{-1, 1}}, {{1, 1}}};
  151. EXPECT_TRUE(math::intersects(c1, ln));
  152. }
  153. TEST(CircleTest, LineIntersectsWhenContained) {
  154. circle const c1{{{0, 0}}, 1};
  155. line const ln{{{0.5, 0.5}}, {{-0.5, -0.5}}};
  156. EXPECT_TRUE(math::intersects(c1, ln));
  157. }
  158. TEST(CircleTest, ChordLineIntersects) {
  159. circle const c1{{{0, 0}}, 1};
  160. line const ln{{{1.5, -0.5}}, {{-0.5, 1.5}}};
  161. EXPECT_TRUE(math::intersects(c1, ln));
  162. }
  163. TEST(CircleTest, OutsideLineDoesntIntersect) {
  164. circle const c1{{{0, 0}}, 1};
  165. line const ln{{{1, 1}}, {{0, 1.5}}};
  166. EXPECT_FALSE(math::intersects(c1, ln));
  167. }
  168. TEST(CircleTest, OverlappingQuad) {
  169. circle const c1{{{0, 0}}, 1};
  170. square const sq{{{0.5, 0.5}}, 0.5};
  171. EXPECT_TRUE(math::intersects(c1, sq));
  172. }
  173. TEST(CircleTest, IntersectsAtEdge) {
  174. circle const c1{{{0, 0}}, 1};
  175. square const sq{{{-1, 1}}, 2};
  176. EXPECT_TRUE(math::intersects(c1, sq));
  177. }
  178. TEST(CircleTest, IntersectsAtCorner) {
  179. circle const c1{{{0, 0}}, 1};
  180. square const sq{{{0, 1}}, 1};
  181. EXPECT_TRUE(math::intersects(c1, sq));
  182. }
  183. TEST(CircleTest, ContainingQuad) {
  184. circle const c1{{{0, 0}}, 1};
  185. square const sq{{{0, 0}}, 0.5};
  186. EXPECT_TRUE(math::intersects(c1, sq));
  187. }
  188. TEST(CircleTest, ContainedInQuad) {
  189. circle const c1{{{0, 0}}, 0.5};
  190. square const sq{{{-1, -1}}, 2};
  191. EXPECT_TRUE(math::intersects(c1, sq));
  192. }
  193. TEST(CircleTest, NonIntersectingTangent) {
  194. circle const c1{{{0, 0}}, 1};
  195. square const sq{{{1, 1}}, 1};
  196. EXPECT_FALSE(math::intersects(c1, sq));
  197. }
  198. TEST(CircleTest, NonIntersecting) {
  199. circle const c1{{{0, 0}}, 1};
  200. square const sq{{{1.5, 0.5}}, 0.5};
  201. EXPECT_FALSE(math::intersects(c1, sq));
  202. }
  203. TEST(TriangleTest, TriangleIntersectsSelf) {
  204. triangle tri{{{0, 0}}, {{0, 1}}, {{1, 0}}};
  205. EXPECT_TRUE(math::intersects(tri, tri));
  206. }
  207. TEST(TriangleTest, DoesNotIntersectOffset) {
  208. triangle lhs{{{0, 0}}, {{0, 1}}, {{1, 0}}};
  209. auto tri = [](math::dim2::point const & pt) {
  210. return triangle{pt, pt, pt};
  211. };
  212. math::dim2::point const clock_ab{{1, 1}};
  213. math::dim2::point const clock_bc{{-1, -1}};
  214. math::dim2::point const clock_ca{{-1, 2}};
  215. // Each of these expectations fails one of the check-edges calls
  216. // in the intersection algorithm, which proves that each point of
  217. // the triangle is not within (or on) the bounds of the other triangle
  218. // Each of these tests indicates one edge of the triangle 'lhs', and
  219. // tests for whether it orients around any of the points of the other
  220. // 'triangle'.
  221. // Tests are done for each edge vs. the the opposite triangle,
  222. // starting with edges for the lhs, and then the edges on the rhs.
  223. EXPECT_FALSE(math::intersects(lhs, tri(clock_ab)));
  224. EXPECT_FALSE(math::intersects(lhs, tri(clock_bc)));
  225. EXPECT_FALSE(math::intersects(lhs, tri(clock_ca)));
  226. EXPECT_FALSE(math::intersects(tri(clock_ab), lhs));
  227. EXPECT_FALSE(math::intersects(tri(clock_bc), lhs));
  228. EXPECT_FALSE(math::intersects(tri(clock_ca), lhs));
  229. }
  230. TEST(QuadTest, IntersectsContain) {
  231. quad const lhs = square{{{-0.5f, -0.5f}}, 1};
  232. quad const rhs = square{{{-.25f, -.25f}}, 0.5};
  233. EXPECT_TRUE(math::intersects(lhs, rhs));
  234. }
  235. TEST(QuadTest, NoIntersectionAtEdge) {
  236. quad const lhs = square{{{-0.5f, -0.5f}}, 1};
  237. quad const rhs = square{{{ 0.0f, 0.5f}}, 1};
  238. EXPECT_FALSE(math::intersects(lhs, rhs));
  239. }
  240. TEST(QuadTest, NoIntersectionAtCorner) {
  241. quad const lhs = square{{{-0.5f, -0.5f}}, 1};
  242. quad const rhs = square{{{ 0.5f, 0.5f}}, 1};
  243. EXPECT_FALSE(math::intersects(lhs, rhs));
  244. }
  245. TEST(QuadTest, NoIntersection) {
  246. quad const lhs = square{{{-1.0f, -0.5f}}, 1};
  247. quad const rhs = square{{{ 0.5f, 0.5f}}, 1};
  248. EXPECT_FALSE(math::intersects(lhs, rhs));
  249. }
  250. TEST(RotateTest, RotatingSquareAroundOrigin) {
  251. math::degree degrees{90.0};
  252. // A square with a side-length of 2 and a center at the origin
  253. math::vec2 const origin = make_vector(0.f, 0.f);
  254. quad const object = square{{{-1, -1}}, 2};
  255. quad const rotated = math::rotate(origin, object, degrees);
  256. EXPECT_THAT(rotated.ll, Eq(make_vector(1.f, -1.f)));
  257. EXPECT_THAT(rotated.lr, Eq(make_vector(1.f, 1.f)));
  258. EXPECT_THAT(rotated.ur, Eq(make_vector(-1.f, 1.f)));
  259. EXPECT_THAT(rotated.ul, Eq(make_vector(-1.f, -1.f)));
  260. EXPECT_THAT(math::rotate(origin, rotated, -degrees), Eq(object));
  261. }
  262. TEST(RotateTest, RotatingSquareAroundOwnPoint) {
  263. math::degree degrees{90.0};
  264. // A square with a side-length of 2 and a center at the origin
  265. math::vec2 const axis = make_vector(-1.f, -1.f);
  266. quad const object = square{{{-1, -1}}, 2};
  267. quad const rotated = math::rotate(axis, object, degrees);
  268. EXPECT_THAT(rotated.ll, Eq(make_vector(-1.f, -1.f)));
  269. EXPECT_THAT(rotated.lr, Eq(make_vector(-1.f, 1.f)));
  270. EXPECT_THAT(rotated.ur, Eq(make_vector(-3.f, 1.f)));
  271. EXPECT_THAT(rotated.ul, Eq(make_vector(-3.f, -1.f)));
  272. EXPECT_THAT(math::rotate(axis, rotated, -degrees), Eq(object));
  273. }