SFML/test/System/Vector2.cpp
Chris Thrasher fd3526f742 Use <> for test utilities includes
SFML convention is to only use "" includes when the header is in
the same directory as the file including it. Because these test
util headers are in a separate directory, it makes more sense to
include them via <>.
2022-06-27 00:22:16 +02:00

337 lines
11 KiB
C++

#include <SFML/System/Vector2.hpp>
#include <SystemUtil.hpp>
#include <type_traits>
#include <cmath>
#include <doctest.h>
using namespace sf::Literals;
// 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<int>(sourceVector.x));
CHECK(vector.y == static_cast<int>(sourceVector.y));
}
SUBCASE("Length and angle constructor")
{
CHECK(sf::Vector2f(0, 0_deg) == sf::Vector2f(0, 0));
CHECK(sf::Vector2f(0, 45_deg) == sf::Vector2f(0, 0));
CHECK(sf::Vector2f(0, 90_deg) == sf::Vector2f(0, 0));
CHECK(sf::Vector2f(0, 135_deg) == sf::Vector2f(0, 0));
CHECK(sf::Vector2f(0, 180_deg) == sf::Vector2f(0, 0));
CHECK(sf::Vector2f(0, 270_deg) == sf::Vector2f(0, 0));
CHECK(sf::Vector2f(0, 360_deg) == sf::Vector2f(0, 0));
CHECK(sf::Vector2f(0, -90_deg) == sf::Vector2f(0, 0));
CHECK(sf::Vector2f(0, -180_deg) == sf::Vector2f(0, 0));
CHECK(sf::Vector2f(0, -270_deg) == sf::Vector2f(0, 0));
CHECK(sf::Vector2f(0, -360_deg) == sf::Vector2f(0, 0));
CHECK(sf::Vector2f(1, 0_deg) == sf::Vector2f(1, 0));
CHECK(sf::Vector2f(1, 45_deg) == Approx(sf::Vector2f(std::sqrt(2.f) / 2.f, std::sqrt(2.f) / 2.f)));
CHECK(sf::Vector2f(1, 90_deg) == Approx(sf::Vector2f(0, 1)));
CHECK(sf::Vector2f(1, 135_deg) == Approx(sf::Vector2f(-std::sqrt(2.f) / 2.f, std::sqrt(2.f) / 2.f)));
CHECK(sf::Vector2f(1, 180_deg) == Approx(sf::Vector2f(-1, 0)));
CHECK(sf::Vector2f(1, 270_deg) == Approx(sf::Vector2f(0, -1)));
CHECK(sf::Vector2f(1, 360_deg) == Approx(sf::Vector2f(1, 0)));
CHECK(sf::Vector2f(1, -90_deg) == Approx(sf::Vector2f(0, -1)));
CHECK(sf::Vector2f(1, -180_deg) == Approx(sf::Vector2f(-1, 0)));
CHECK(sf::Vector2f(1, -270_deg) == Approx(sf::Vector2f(0, 1)));
CHECK(sf::Vector2f(1, -360_deg) == Approx(sf::Vector2f(1, 0)));
CHECK(sf::Vector2f(-1, 0_deg) == sf::Vector2f(-1, 0));
CHECK(sf::Vector2f(-1, 45_deg) == Approx(sf::Vector2f(-std::sqrt(2.f) / 2.f, -std::sqrt(2.f) / 2.f)));
CHECK(sf::Vector2f(-1, 90_deg) == Approx(sf::Vector2f(0, -1)));
CHECK(sf::Vector2f(-1, 135_deg) == Approx(sf::Vector2f(std::sqrt(2.f) / 2.f, -std::sqrt(2.f) / 2.f)));
CHECK(sf::Vector2f(-1, 180_deg) == Approx(sf::Vector2f(1, 0)));
CHECK(sf::Vector2f(-1, 270_deg) == Approx(sf::Vector2f(0, 1)));
CHECK(sf::Vector2f(-1, 360_deg) == Approx(sf::Vector2f(-1, 0)));
CHECK(sf::Vector2f(-1, -90_deg) == Approx(sf::Vector2f(0, 1)));
CHECK(sf::Vector2f(-1, -180_deg) == Approx(sf::Vector2f(1, 0)));
CHECK(sf::Vector2f(-1, -270_deg) == Approx(sf::Vector2f(0, -1)));
CHECK(sf::Vector2f(-1, -360_deg) == Approx(sf::Vector2f(-1, 0)));
CHECK(sf::Vector2f(4.2f, 0_deg) == sf::Vector2f(4.2f, 0));
CHECK(sf::Vector2f(4.2f, 45_deg) == Approx(sf::Vector2f(4.2f * std::sqrt(2.f) / 2.f, 4.2f * std::sqrt(2.f) / 2.f)));
CHECK(sf::Vector2f(4.2f, 90_deg) == Approx(sf::Vector2f(0, 4.2f)));
CHECK(sf::Vector2f(4.2f, 135_deg) == Approx(sf::Vector2f(-4.2f * std::sqrt(2.f) / 2.f, 4.2f * std::sqrt(2.f) / 2.f)));
CHECK(sf::Vector2f(4.2f, 180_deg) == Approx(sf::Vector2f(-4.2f, 0)));
CHECK(sf::Vector2f(4.2f, 270_deg) == Approx(sf::Vector2f(0, -4.2f)));
CHECK(sf::Vector2f(4.2f, 360_deg) == Approx(sf::Vector2f(4.2f, 0)));
CHECK(sf::Vector2f(4.2f, -90_deg) == Approx(sf::Vector2f(0, -4.2f)));
CHECK(sf::Vector2f(4.2f, -180_deg) == Approx(sf::Vector2f(-4.2f, 0)));
CHECK(sf::Vector2f(4.2f, -270_deg) == Approx(sf::Vector2f(0, 4.2f)));
CHECK(sf::Vector2f(4.2f, -360_deg) == Approx(sf::Vector2f(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<decltype(x), decltype(vector.x)>);
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<decltype(x), decltype(vector.x)>);
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.84187f));
CHECK(v.lengthSq() == Approx(14.7599650969f));
CHECK(v.normalized() == Approx(sf::Vector2f(0.624695f, 0.780869f)));
const sf::Vector2f w(-0.7f, -2.2f);
CHECK(w.length() == Approx(2.30868f));
CHECK(w.lengthSq() == Approx(5.3300033f));
CHECK(w.normalized() == Approx(sf::Vector2f(-0.303204f, -0.952926f)));
}
SUBCASE("Rotations and angles")
{
const sf::Vector2f v(2.4f, 3.0f);
CHECK(v.angle() == Approx(51.3402_deg));
CHECK(sf::Vector2f::UnitX.angleTo(v) == Approx(51.3402_deg));
CHECK(sf::Vector2f::UnitY.angleTo(v) == Approx(-38.6598_deg));
const sf::Vector2f w(-0.7f, -2.2f);
CHECK(w.angle() == Approx(-107.65_deg));
CHECK(sf::Vector2f::UnitX.angleTo(w) == Approx(-107.65_deg));
CHECK(sf::Vector2f::UnitY.angleTo(w) == Approx(162.35_deg));
CHECK(v.angleTo(w) == Approx(-158.9902_deg));
CHECK(w.angleTo(v) == Approx(158.9902_deg));
const float ratio = w.length() / v.length();
CHECK(v.rotatedBy(-158.9902_deg) * ratio == Approx(w));
CHECK(w.rotatedBy(158.9902_deg) / ratio == Approx(v));
CHECK(v.perpendicular() == sf::Vector2f(-3.0f, 2.4f));
CHECK(v.perpendicular().perpendicular().perpendicular().perpendicular() == v);
CHECK(v.rotatedBy(90_deg) == Approx(sf::Vector2f(-3.0f, 2.4f)));
CHECK(v.rotatedBy(27.14_deg) == Approx(sf::Vector2f(0.767248f, 3.76448f)));
CHECK(v.rotatedBy(-36.11_deg) == Approx(sf::Vector2f(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.28f));
CHECK(w.dot(v) == Approx(-8.28f));
CHECK(v.cross(w) == Approx(-3.18f));
CHECK(w.cross(v) == Approx(+3.18f));
CHECK(v.cwiseMul(w) == Approx(sf::Vector2f(-1.68f, -6.6f)));
CHECK(w.cwiseMul(v) == Approx(sf::Vector2f(-1.68f, -6.6f)));
CHECK(v.cwiseDiv(w) == Approx(sf::Vector2f(-3.428571f, -1.363636f)));
CHECK(w.cwiseDiv(v) == Approx(sf::Vector2f(-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) == Approx(sf::Vector2f(1.087430f, 3.417636f)));
CHECK(v.projectedOnto(w) == Approx(sf::Vector2f(-1.55347f * w)));
CHECK(w.projectedOnto(v) == Approx(sf::Vector2f(-1.346342f, -1.682927f)));
CHECK(w.projectedOnto(v) == Approx(sf::Vector2f(-0.560976f * v)));
CHECK(v.projectedOnto(sf::Vector2f::UnitX) == Approx(sf::Vector2f(2.4f, 0.0f)));
CHECK(v.projectedOnto(sf::Vector2f::UnitY) == Approx(sf::Vector2f(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));
}
}