From 2eef193c0ef3e1e1f156bc2cc0575dc7726201e8 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Thu, 17 Feb 2022 17:22:00 +0100 Subject: [PATCH] Unit tests for Vector2 extensions Also adds ApproxVec and ApproxDeg types for more readable comparison of approximate vectors/angles. --- test/System/Vector2.cpp | 101 ++++++++++++++++++++++++++++-- test/TestUtilities/SystemUtil.cpp | 23 +++++++ test/TestUtilities/SystemUtil.hpp | 27 ++++++++ 3 files changed, 146 insertions(+), 5 deletions(-) diff --git a/test/System/Vector2.cpp b/test/System/Vector2.cpp index dd1ebe81..cd27833e 100644 --- a/test/System/Vector2.cpp +++ b/test/System/Vector2.cpp @@ -4,7 +4,12 @@ #include -// Use sf::Vector2i for tests. Test coverage is given, as there are no template specializations. +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]") { @@ -184,11 +189,97 @@ TEST_CASE("sf::Vector2 class template - [system]") } } + SUBCASE("Length and normalization") + { + sf::Vector2f v(2.4f, 3.0f); + + CHECK(v.length() == Approx(3.84187)); + CHECK(v.lengthSq() == Approx(14.7599650969)); + CHECK(v.normalized() == ApproxVec(0.624695, 0.780869)); + + sf::Vector2f w(-0.7f, -2.2f); + + CHECK(w.length() == Approx(2.30868)); + CHECK(w.lengthSq() == Approx(5.3300033)); + CHECK(w.normalized() == ApproxVec(-0.303204, -0.952926)); + } + + SUBCASE("Rotations and angles") + { + sf::Vector2f v(2.4f, 3.0f); + + CHECK(v.angle() == ApproxDeg(51.3402)); + CHECK(sf::Vector2f::UnitX.angleTo(v) == ApproxDeg(51.3402)); + CHECK(sf::Vector2f::UnitY.angleTo(v) == ApproxDeg(-38.6598)); + + sf::Vector2f w(-0.7f, -2.2f); + + CHECK(w.angle() == ApproxDeg(-107.65)); + CHECK(sf::Vector2f::UnitX.angleTo(w) == ApproxDeg(-107.65)); + CHECK(sf::Vector2f::UnitY.angleTo(w) == ApproxDeg(162.35)); + + CHECK(v.angleTo(w) == ApproxDeg(-158.9902)); + CHECK(w.angleTo(v) == ApproxDeg(158.9902)); + + float ratio = w.length() / v.length(); + CHECK(v.rotatedBy(-158.9902_deg) * ratio == ApproxVec(w)); + CHECK(w.rotatedBy(158.9902_deg) / ratio == ApproxVec(v)); + + CHECK(v.perpendicular() == sf::Vector2f(-3.0f, 2.4f)); + CHECK(v.perpendicular().perpendicular().perpendicular().perpendicular() == v); + + CHECK(v.rotatedBy(90_deg) == ApproxVec(-3.0, 2.4)); + CHECK(v.rotatedBy(27.14_deg) == ApproxVec(0.767248, 3.76448)); + CHECK(v.rotatedBy(-36.11_deg) == ApproxVec(3.70694, 1.00925)); + } + + SUBCASE("Products and quotients") + { + sf::Vector2f v(2.4f, 3.0f); + 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) == ApproxVec(-1.68, -6.6)); + CHECK(w.cwiseMul(v) == ApproxVec(-1.68, -6.6)); + CHECK(v.cwiseDiv(w) == ApproxVec(-3.428571, -1.363636)); + CHECK(w.cwiseDiv(v) == ApproxVec(-0.291666, -0.733333)); + } + + SUBCASE("Projection") + { + sf::Vector2f v(2.4f, 3.0f); + sf::Vector2f w(-0.7f, -2.2f); + + CHECK(v.projectedOnto(w) == ApproxVec(1.087430, 3.417636)); + CHECK(v.projectedOnto(w) == ApproxVec(-1.55347f * w)); + + CHECK(w.projectedOnto(v) == ApproxVec(-1.346342, -1.682927)); + CHECK(w.projectedOnto(v) == ApproxVec(-0.560976f * v)); + + CHECK(v.projectedOnto(sf::Vector2f::UnitX) == ApproxVec(2.4, 0.0)); + CHECK(v.projectedOnto(sf::Vector2f::UnitY) == ApproxVec(0.0, 3.0)); + } + SUBCASE("Constexpr support") { - constexpr sf::Vector2i vector(1, 2); - static_assert(vector.x == 1); - static_assert(vector.y == 2); - static_assert(vector + sf::Vector2i(2, 1) == sf::Vector2i(3, 3)); + 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)); } } diff --git a/test/TestUtilities/SystemUtil.cpp b/test/TestUtilities/SystemUtil.cpp index da511d6c..d0eaa959 100644 --- a/test/TestUtilities/SystemUtil.cpp +++ b/test/TestUtilities/SystemUtil.cpp @@ -4,6 +4,7 @@ #include #include +#include // for Approx #include #include #include @@ -34,6 +35,28 @@ namespace sf } } +bool operator==(const sf::Vector2f& lhs, const ApproxVec& rhs) +{ + return (lhs - rhs.vector).length() == doctest::Approx(0.0); +} + +bool operator==(const sf::Angle& lhs, const ApproxDeg& rhs) +{ + return lhs.asDegrees() == doctest::Approx(rhs.degrees); +} + +std::ostream& operator <<(std::ostream& os, const ApproxVec& approx) +{ + os << approx.vector; + return os; +} + +std::ostream& operator <<(std::ostream& os, const ApproxDeg& approx) +{ + os << sf::degrees(approx.degrees); + return os; +} + namespace sf::Testing { static std::string getTemporaryFilePath() diff --git a/test/TestUtilities/SystemUtil.hpp b/test/TestUtilities/SystemUtil.hpp index b6139595..f3b5c807 100644 --- a/test/TestUtilities/SystemUtil.hpp +++ b/test/TestUtilities/SystemUtil.hpp @@ -39,6 +39,33 @@ namespace sf } } +// Utilities for approximate equality +struct ApproxVec +{ + ApproxVec(double x, double y) + : vector(static_cast(x), static_cast(y)) {} + + explicit ApproxVec(const sf::Vector2f& v) + : vector(v) {} + + sf::Vector2f vector; +}; + +// Utilities for approximate equality +struct ApproxDeg +{ + ApproxDeg(double degrees) + : degrees(static_cast(degrees)) {} + + float degrees; +}; + +bool operator==(const sf::Vector2f& lhs, const ApproxVec& rhs); +bool operator==(const sf::Angle& lhs, const ApproxDeg& rhs); + +std::ostream& operator <<(std::ostream& os, const ApproxVec& approx); +std::ostream& operator <<(std::ostream& os, const ApproxDeg& approx); + namespace sf::Testing { class TemporaryFile