#include #include "SystemUtil.hpp" #include #include using namespace sf::Literals; using doctest::Approx; // Use sf::Vector2i for tests (except for float vector algebra). // Test coverage is given, as there are no template specializations. TEST_CASE("sf::Vector2 class template - [system]") { SUBCASE("Construction") { SUBCASE("Default constructor") { sf::Vector2i vector; CHECK(vector.x == 0); CHECK(vector.y == 0); } SUBCASE("(x, y) coordinate constructor") { sf::Vector2i vector(1, 2); CHECK(vector.x == 1); CHECK(vector.y == 2); } SUBCASE("Conversion constructor") { const sf::Vector2f sourceVector(1.0f, 2.0f); const sf::Vector2i vector(sourceVector); CHECK(vector.x == static_cast(sourceVector.x)); CHECK(vector.y == static_cast(sourceVector.y)); } SUBCASE("Length and angle constructor") { CHECK(sf::Vector2f(0, sf::degrees(0)) == sf::Vector2f(0, 0)); CHECK(sf::Vector2f(0, sf::degrees(45)) == sf::Vector2f(0, 0)); CHECK(sf::Vector2f(0, sf::degrees(90)) == sf::Vector2f(0, 0)); CHECK(sf::Vector2f(0, sf::degrees(135)) == sf::Vector2f(0, 0)); CHECK(sf::Vector2f(0, sf::degrees(180)) == sf::Vector2f(0, 0)); CHECK(sf::Vector2f(0, sf::degrees(270)) == sf::Vector2f(0, 0)); CHECK(sf::Vector2f(0, sf::degrees(360)) == sf::Vector2f(0, 0)); CHECK(sf::Vector2f(0, sf::degrees(-90)) == sf::Vector2f(0, 0)); CHECK(sf::Vector2f(0, sf::degrees(-180)) == sf::Vector2f(0, 0)); CHECK(sf::Vector2f(0, sf::degrees(-270)) == sf::Vector2f(0, 0)); CHECK(sf::Vector2f(0, sf::degrees(-360)) == sf::Vector2f(0, 0)); CHECK(sf::Vector2f(1, sf::degrees(0)) == sf::Vector2f(1, 0)); CHECK(sf::Vector2f(1, sf::degrees(45)) == ApproxVec2(std::sqrt(2.f) / 2.f, std::sqrt(2.f) / 2.f)); CHECK(sf::Vector2f(1, sf::degrees(90)) == ApproxVec2(0, 1)); CHECK(sf::Vector2f(1, sf::degrees(135)) == ApproxVec2(-std::sqrt(2.f) / 2.f, std::sqrt(2.f) / 2.f)); CHECK(sf::Vector2f(1, sf::degrees(180)) == ApproxVec2(-1, 0)); CHECK(sf::Vector2f(1, sf::degrees(270)) == ApproxVec2(0, -1)); CHECK(sf::Vector2f(1, sf::degrees(360)) == ApproxVec2(1, 0)); CHECK(sf::Vector2f(1, sf::degrees(-90)) == ApproxVec2(0, -1)); CHECK(sf::Vector2f(1, sf::degrees(-180)) == ApproxVec2(-1, 0)); CHECK(sf::Vector2f(1, sf::degrees(-270)) == ApproxVec2(0, 1)); CHECK(sf::Vector2f(1, sf::degrees(-360)) == ApproxVec2(1, 0)); CHECK(sf::Vector2f(-1, sf::degrees(0)) == sf::Vector2f(-1, 0)); CHECK(sf::Vector2f(-1, sf::degrees(45)) == ApproxVec2(-std::sqrt(2.f) / 2.f, -std::sqrt(2.f) / 2.f)); CHECK(sf::Vector2f(-1, sf::degrees(90)) == ApproxVec2(0, -1)); CHECK(sf::Vector2f(-1, sf::degrees(135)) == ApproxVec2(std::sqrt(2.f) / 2.f, -std::sqrt(2.f) / 2.f)); CHECK(sf::Vector2f(-1, sf::degrees(180)) == ApproxVec2(1, 0)); CHECK(sf::Vector2f(-1, sf::degrees(270)) == ApproxVec2(0, 1)); CHECK(sf::Vector2f(-1, sf::degrees(360)) == ApproxVec2(-1, 0)); CHECK(sf::Vector2f(-1, sf::degrees(-90)) == ApproxVec2(0, 1)); CHECK(sf::Vector2f(-1, sf::degrees(-180)) == ApproxVec2(1, 0)); CHECK(sf::Vector2f(-1, sf::degrees(-270)) == ApproxVec2(0, -1)); CHECK(sf::Vector2f(-1, sf::degrees(-360)) == ApproxVec2(-1, 0)); CHECK(sf::Vector2f(4.2f, sf::degrees(0)) == sf::Vector2f(4.2f, 0)); CHECK(sf::Vector2f(4.2f, sf::degrees(45)) == ApproxVec2(4.2f * std::sqrt(2.f) / 2.f, 4.2f * std::sqrt(2.f) / 2.f)); CHECK(sf::Vector2f(4.2f, sf::degrees(90)) == ApproxVec2(0, 4.2f)); CHECK(sf::Vector2f(4.2f, sf::degrees(135)) == ApproxVec2(-4.2f * std::sqrt(2.f) / 2.f, 4.2f * std::sqrt(2.f) / 2.f)); CHECK(sf::Vector2f(4.2f, sf::degrees(180)) == ApproxVec2(-4.2f, 0)); CHECK(sf::Vector2f(4.2f, sf::degrees(270)) == ApproxVec2(0, -4.2f)); CHECK(sf::Vector2f(4.2f, sf::degrees(360)) == ApproxVec2(4.2f, 0)); CHECK(sf::Vector2f(4.2f, sf::degrees(-90)) == ApproxVec2(0, -4.2f)); CHECK(sf::Vector2f(4.2f, sf::degrees(-180)) == ApproxVec2(-4.2f, 0)); CHECK(sf::Vector2f(4.2f, sf::degrees(-270)) == ApproxVec2(0, 4.2f)); CHECK(sf::Vector2f(4.2f, sf::degrees(-360)) == ApproxVec2(4.2f, 0)); } } SUBCASE("Unary operations") { SUBCASE("-vector") { const sf::Vector2i vector(1, 2); const sf::Vector2i negatedVector = -vector; CHECK(negatedVector.x == -1); CHECK(negatedVector.y == -2); } } SUBCASE("Arithmetic operations between two vectors") { sf::Vector2i firstVector(2, 5); const sf::Vector2i secondVector(8, 3); SUBCASE("vector += vector") { firstVector += secondVector; CHECK(firstVector.x == 10); CHECK(firstVector.y == 8); } SUBCASE("vector -= vector") { firstVector -= secondVector; CHECK(firstVector.x == -6); CHECK(firstVector.y == 2); } SUBCASE("vector + vector") { const sf::Vector2i result = firstVector + secondVector; CHECK(result.x == 10); CHECK(result.y == 8); } SUBCASE("vector - vector") { const sf::Vector2i result = firstVector - secondVector; CHECK(result.x == -6); CHECK(result.y == 2); } } SUBCASE("Arithmetic operations between vector and scalar value") { sf::Vector2i vector(26, 12); int scalar = 2; SUBCASE("vector * scalar") { const sf::Vector2i result = vector * scalar; CHECK(result.x == 52); CHECK(result.y == 24); } SUBCASE("scalar * vector") { const sf::Vector2i result = scalar * vector; CHECK(result.x == 52); CHECK(result.y == 24); } SUBCASE("vector *= scalar") { vector *= scalar; CHECK(vector.x == 52); CHECK(vector.y == 24); } SUBCASE("vector / scalar") { const sf::Vector2i result = vector / scalar; CHECK(result.x == 13); CHECK(result.y == 6); } SUBCASE("vector /= scalar") { vector /= scalar; CHECK(vector.x == 13); CHECK(vector.y == 6); } } SUBCASE("Comparison operations (two equal and one different vector)") { const sf::Vector2i firstEqualVector(1, 5); const sf::Vector2i secondEqualVector(1, 5); const sf::Vector2i differentVector(6, 9); SUBCASE("vector == vector") { CHECK(firstEqualVector == secondEqualVector); CHECK_FALSE(firstEqualVector == differentVector); } SUBCASE("vector != vector") { CHECK(firstEqualVector != differentVector); CHECK_FALSE(firstEqualVector != secondEqualVector); } } SUBCASE("Structured bindings") { sf::Vector2i vector(1, 2); SUBCASE("destructure by value") { auto [x, y] = vector; CHECK(x == 1); CHECK(y == 2); static_assert(std::is_same_v); x = 3; CHECK(x == 3); CHECK(vector.x == 1); } SUBCASE("destructure by ref") { auto& [x, y] = vector; CHECK(x == 1); CHECK(y == 2); static_assert(std::is_same_v); x = 3; CHECK(x == 3); CHECK(vector.x == 3); } } SUBCASE("Length and normalization") { const sf::Vector2f v(2.4f, 3.0f); CHECK(v.length() == Approx(3.84187)); CHECK(v.lengthSq() == Approx(14.7599650969)); CHECK(v.normalized() == ApproxVec2(0.624695f, 0.780869f)); const sf::Vector2f w(-0.7f, -2.2f); CHECK(w.length() == Approx(2.30868)); CHECK(w.lengthSq() == Approx(5.3300033)); CHECK(w.normalized() == ApproxVec2(-0.303204f, -0.952926f)); } SUBCASE("Rotations and angles") { const sf::Vector2f v(2.4f, 3.0f); CHECK(v.angle() == ApproxDeg(51.3402f)); CHECK(sf::Vector2f::UnitX.angleTo(v) == ApproxDeg(51.3402f)); CHECK(sf::Vector2f::UnitY.angleTo(v) == ApproxDeg(-38.6598f)); const sf::Vector2f w(-0.7f, -2.2f); CHECK(w.angle() == ApproxDeg(-107.65f)); CHECK(sf::Vector2f::UnitX.angleTo(w) == ApproxDeg(-107.65f)); CHECK(sf::Vector2f::UnitY.angleTo(w) == ApproxDeg(162.35f)); CHECK(v.angleTo(w) == ApproxDeg(-158.9902f)); CHECK(w.angleTo(v) == ApproxDeg(158.9902f)); const float ratio = w.length() / v.length(); CHECK(v.rotatedBy(-158.9902_deg) * ratio == ApproxVec2(w)); CHECK(w.rotatedBy(158.9902_deg) / ratio == ApproxVec2(v)); CHECK(v.perpendicular() == sf::Vector2f(-3.0f, 2.4f)); CHECK(v.perpendicular().perpendicular().perpendicular().perpendicular() == v); CHECK(v.rotatedBy(90_deg) == ApproxVec2(-3.0f, 2.4f)); CHECK(v.rotatedBy(27.14_deg) == ApproxVec2(0.767248f, 3.76448f)); CHECK(v.rotatedBy(-36.11_deg) == ApproxVec2(3.70694f, 1.00925f)); } SUBCASE("Products and quotients") { const sf::Vector2f v(2.4f, 3.0f); const sf::Vector2f w(-0.7f, -2.2f); CHECK(v.dot(w) == Approx(-8.28)); CHECK(w.dot(v) == Approx(-8.28)); CHECK(v.cross(w) == Approx(-3.18)); CHECK(w.cross(v) == Approx(+3.18)); CHECK(v.cwiseMul(w) == ApproxVec2(-1.68f, -6.6f)); CHECK(w.cwiseMul(v) == ApproxVec2(-1.68f, -6.6f)); CHECK(v.cwiseDiv(w) == ApproxVec2(-3.428571f, -1.363636f)); CHECK(w.cwiseDiv(v) == ApproxVec2(-0.291666f, -0.733333f)); } SUBCASE("Projection") { const sf::Vector2f v(2.4f, 3.0f); const sf::Vector2f w(-0.7f, -2.2f); CHECK(v.projectedOnto(w) == ApproxVec2(1.087430f, 3.417636f)); CHECK(v.projectedOnto(w) == ApproxVec2(-1.55347f * w)); CHECK(w.projectedOnto(v) == ApproxVec2(-1.346342f, -1.682927f)); CHECK(w.projectedOnto(v) == ApproxVec2(-0.560976f * v)); CHECK(v.projectedOnto(sf::Vector2f::UnitX) == ApproxVec2(2.4f, 0.0f)); CHECK(v.projectedOnto(sf::Vector2f::UnitY) == ApproxVec2(0.0f, 3.0f)); } SUBCASE("Constexpr support") { constexpr sf::Vector2i v(1, 2); constexpr sf::Vector2i w(2, -3); static_assert(v.x == 1); static_assert(v.y == 2); static_assert(v + w == sf::Vector2i(3, -1)); static_assert(v.lengthSq() == 5); static_assert(v.perpendicular() == sf::Vector2i(-2, 1)); static_assert(v.dot(w) == -4); static_assert(v.cross(w) == -7); static_assert(v.cwiseMul(w) == sf::Vector2i(2, -6)); static_assert(w.cwiseDiv(v) == sf::Vector2i(2, -1)); } }