mirror of
https://github.com/SFML/SFML.git
synced 2024-11-28 22:31:09 +08:00
Add extension methods to Vector3<T>
Includes relevant tests and updates TestUtilities to feature ApproxVec2 & ApproxVec3
This commit is contained in:
parent
65e357e901
commit
b2ab6d6ab3
@ -431,7 +431,7 @@ template <typename T>
|
||||
///
|
||||
/// float s = v.dot(w);
|
||||
///
|
||||
/// bool different = (v2 != v3);
|
||||
/// bool different = (v != u);
|
||||
/// \endcode
|
||||
///
|
||||
/// Note: for 3-dimensional vectors, see sf::Vector3.
|
||||
|
@ -25,6 +25,9 @@
|
||||
#ifndef SFML_VECTOR3_HPP
|
||||
#define SFML_VECTOR3_HPP
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <type_traits>
|
||||
|
||||
namespace sf
|
||||
{
|
||||
@ -70,6 +73,65 @@ public:
|
||||
template <typename U>
|
||||
constexpr explicit Vector3(const Vector3<U>& vector);
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Length of the vector <i><b>(floating-point)</b></i>.
|
||||
///
|
||||
/// If you are not interested in the actual length, but only in comparisons, consider using lengthSq().
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
T length() const;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Square of vector's length.
|
||||
///
|
||||
/// Suitable for comparisons, more efficient than length().
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
constexpr T lengthSq() const;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Vector with same direction but length 1 <i><b>(floating-point)</b></i>.
|
||||
///
|
||||
/// \pre \c *this is no zero vector.
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
[[nodiscard]] Vector3 normalized() const;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Dot product of two 3D vectors.
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
constexpr T dot(const Vector3& rhs) const;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Cross product of two 3D vectors.
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
constexpr Vector3 cross(const Vector3& rhs) const;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Component-wise multiplication of \c *this and \c rhs.
|
||||
///
|
||||
/// Computes <tt>(lhs.x*rhs.x, lhs.y*rhs.y, lhs.z*rhs.z)</tt>.
|
||||
///
|
||||
/// Scaling is the most common use case for component-wise multiplication/division.
|
||||
/// This operation is also known as the Hadamard or Schur product.
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
constexpr Vector3 cwiseMul(const Vector3& rhs) const;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Component-wise division of \c *this and \c rhs.
|
||||
///
|
||||
/// Computes <tt>(lhs.x/rhs.x, lhs.y/rhs.y, lhs.z/rhs.z)</tt>.
|
||||
///
|
||||
/// Scaling is the most common use case for component-wise multiplication/division.
|
||||
///
|
||||
/// \pre Neither component of \c rhs is zero.
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
constexpr Vector3 cwiseDiv(const Vector3& rhs) const;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Member data
|
||||
////////////////////////////////////////////////////////////
|
||||
@ -273,28 +335,33 @@ using Vector3f = Vector3<float>;
|
||||
/// The template parameter T is the type of the coordinates. It
|
||||
/// can be any type that supports arithmetic operations (+, -, /, *)
|
||||
/// and comparisons (==, !=), for example int or float.
|
||||
/// Note that some operations are only meaningful for vectors where T is
|
||||
/// a floating point type (e.g. float or double), often because
|
||||
/// results cannot be represented accurately with integers.
|
||||
/// The method documentation mentions "(floating-point)" in those cases.
|
||||
///
|
||||
/// You generally don't have to care about the templated form (sf::Vector3<T>),
|
||||
/// the most common specializations have special type aliases:
|
||||
/// \li sf::Vector3<float> is sf::Vector3f
|
||||
/// \li sf::Vector3<int> is sf::Vector3i
|
||||
///
|
||||
/// The sf::Vector3 class has a small and simple interface, its x and y members
|
||||
/// can be accessed directly (there are no accessors like setX(), getX()) and it
|
||||
/// contains no mathematical function like dot product, cross product, length, etc.
|
||||
/// The sf::Vector3 class has a small and simple interface, its x, y and z members
|
||||
/// can be accessed directly (there are no accessors like setX(), getX()).
|
||||
///
|
||||
/// Usage example:
|
||||
/// \code
|
||||
/// sf::Vector3f v1(16.5f, 24.f, -8.2f);
|
||||
/// v1.x = 18.2f;
|
||||
/// float y = v1.y;
|
||||
/// float z = v1.z;
|
||||
/// sf::Vector3f v(16.5f, 24.f, -3.2f);
|
||||
/// v.x = 18.2f;
|
||||
/// float y = v.y;
|
||||
///
|
||||
/// sf::Vector3f v2 = v1 * 5.f;
|
||||
/// sf::Vector3f v3;
|
||||
/// v3 = v1 + v2;
|
||||
/// sf::Vector3f w = v * 5.f;
|
||||
/// sf::Vector3f u;
|
||||
/// u = v + w;
|
||||
///
|
||||
/// bool different = (v2 != v3);
|
||||
/// float s = v.dot(w);
|
||||
/// sf::Vector3f t = v.cross(w);
|
||||
///
|
||||
/// bool different = (v != u);
|
||||
/// \endcode
|
||||
///
|
||||
/// Note: for 2-dimensional vectors, see sf::Vector2.
|
||||
|
@ -56,6 +56,74 @@ z(static_cast<T>(vector.z))
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
T Vector3<T>::length() const
|
||||
{
|
||||
static_assert(std::is_floating_point_v<T>, "Vector3::length() is only supported for floating point types");
|
||||
|
||||
return std::hypot(x, y, z);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
constexpr T Vector3<T>::lengthSq() const
|
||||
{
|
||||
return dot(*this);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
Vector3<T> Vector3<T>::normalized() const
|
||||
{
|
||||
static_assert(std::is_floating_point_v<T>, "Vector3::normalized() is only supported for floating point types");
|
||||
|
||||
assert(*this != Vector3<T>());
|
||||
return (*this) / length();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
constexpr T Vector3<T>::dot(const Vector3<T>& rhs) const
|
||||
{
|
||||
return x * rhs.x + y * rhs.y + z * rhs.z;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
constexpr Vector3<T> Vector3<T>::cross(const Vector3<T>& rhs) const
|
||||
{
|
||||
return Vector3<T>(
|
||||
(y * rhs.z) - (z * rhs.y),
|
||||
(z * rhs.x) - (x * rhs.z),
|
||||
(x * rhs.y) - (y * rhs.x)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
constexpr Vector3<T> Vector3<T>::cwiseMul(const Vector3<T>& rhs) const
|
||||
{
|
||||
return Vector3<T>(x * rhs.x, y * rhs.y, z * rhs.z);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
constexpr Vector3<T> Vector3<T>::cwiseDiv(const Vector3<T>& rhs) const
|
||||
{
|
||||
assert(rhs.x != 0);
|
||||
assert(rhs.y != 0);
|
||||
assert(rhs.z != 0);
|
||||
return Vector3<T>(x / rhs.x, y / rhs.y, z / rhs.z);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
constexpr Vector3<T> operator -(const Vector3<T>& left)
|
||||
|
@ -195,13 +195,13 @@ TEST_CASE("sf::Vector2 class template - [system]")
|
||||
|
||||
CHECK(v.length() == Approx(3.84187));
|
||||
CHECK(v.lengthSq() == Approx(14.7599650969));
|
||||
CHECK(v.normalized() == ApproxVec(0.624695f, 0.780869f));
|
||||
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() == ApproxVec(-0.303204f, -0.952926f));
|
||||
CHECK(w.normalized() == ApproxVec2(-0.303204f, -0.952926f));
|
||||
}
|
||||
|
||||
SUBCASE("Rotations and angles")
|
||||
@ -222,15 +222,15 @@ TEST_CASE("sf::Vector2 class template - [system]")
|
||||
CHECK(w.angleTo(v) == ApproxDeg(158.9902f));
|
||||
|
||||
const 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.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) == ApproxVec(-3.0f, 2.4f));
|
||||
CHECK(v.rotatedBy(27.14_deg) == ApproxVec(0.767248f, 3.76448f));
|
||||
CHECK(v.rotatedBy(-36.11_deg) == ApproxVec(3.70694f, 1.00925f));
|
||||
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")
|
||||
@ -244,10 +244,10 @@ TEST_CASE("sf::Vector2 class template - [system]")
|
||||
CHECK(v.cross(w) == Approx(-3.18));
|
||||
CHECK(w.cross(v) == Approx(+3.18));
|
||||
|
||||
CHECK(v.cwiseMul(w) == ApproxVec(-1.68f, -6.6f));
|
||||
CHECK(w.cwiseMul(v) == ApproxVec(-1.68f, -6.6f));
|
||||
CHECK(v.cwiseDiv(w) == ApproxVec(-3.428571f, -1.363636f));
|
||||
CHECK(w.cwiseDiv(v) == ApproxVec(-0.291666f, -0.733333f));
|
||||
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")
|
||||
@ -255,14 +255,14 @@ TEST_CASE("sf::Vector2 class template - [system]")
|
||||
const sf::Vector2f v(2.4f, 3.0f);
|
||||
const sf::Vector2f w(-0.7f, -2.2f);
|
||||
|
||||
CHECK(v.projectedOnto(w) == ApproxVec(1.087430f, 3.417636f));
|
||||
CHECK(v.projectedOnto(w) == ApproxVec(-1.55347f * w));
|
||||
CHECK(v.projectedOnto(w) == ApproxVec2(1.087430f, 3.417636f));
|
||||
CHECK(v.projectedOnto(w) == ApproxVec2(-1.55347f * w));
|
||||
|
||||
CHECK(w.projectedOnto(v) == ApproxVec(-1.346342f, -1.682927f));
|
||||
CHECK(w.projectedOnto(v) == ApproxVec(-0.560976f * v));
|
||||
CHECK(w.projectedOnto(v) == ApproxVec2(-1.346342f, -1.682927f));
|
||||
CHECK(w.projectedOnto(v) == ApproxVec2(-0.560976f * v));
|
||||
|
||||
CHECK(v.projectedOnto(sf::Vector2f::UnitX) == ApproxVec(2.4f, 0.0f));
|
||||
CHECK(v.projectedOnto(sf::Vector2f::UnitY) == ApproxVec(0.0f, 3.0f));
|
||||
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")
|
||||
|
@ -4,7 +4,10 @@
|
||||
|
||||
#include <doctest.h>
|
||||
|
||||
// Use sf::Vector3i for tests. Test coverage is given, as there are no template specializations.
|
||||
using doctest::Approx;
|
||||
|
||||
// Use sf::Vector3i for tests (except for float vector algebra).
|
||||
// Test coverage is given, as there are no template specializations.
|
||||
|
||||
TEST_CASE("sf::Vector3 class template - [system]")
|
||||
{
|
||||
@ -199,6 +202,32 @@ TEST_CASE("sf::Vector3 class template - [system]")
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("Length and normalization")
|
||||
{
|
||||
const sf::Vector3f v(2.4f, 3.0f, 5.2f);
|
||||
|
||||
CHECK(v.length() == Approx(6.46529));
|
||||
CHECK(v.lengthSq() == Approx(41.79997));
|
||||
CHECK(v.normalized() == ApproxVec3(0.37121f, 0.46401f, 0.80429f));
|
||||
}
|
||||
|
||||
SUBCASE("Products and quotients")
|
||||
{
|
||||
const sf::Vector3f v(2.4f, 3.0f, 5.2f);
|
||||
const sf::Vector3f w(-0.7f, -2.2f, -4.8f);
|
||||
|
||||
CHECK(v.dot(w) == Approx(-33.24));
|
||||
CHECK(w.dot(v) == Approx(-33.24));
|
||||
|
||||
CHECK(v.cross(w) == ApproxVec3(-2.96f, 7.88f, -3.18f));
|
||||
CHECK(w.cross(v) == ApproxVec3(2.96f, -7.88f, 3.18f));
|
||||
|
||||
CHECK(v.cwiseMul(w) == ApproxVec3(-1.68f, -6.6f, -24.96f));
|
||||
CHECK(w.cwiseMul(v) == ApproxVec3(-1.68f, -6.6f, -24.96f));
|
||||
CHECK(v.cwiseDiv(w) == ApproxVec3(-3.428571f, -1.363636f, -1.0833333f));
|
||||
CHECK(w.cwiseDiv(v) == ApproxVec3(-0.291666f, -0.733333f, -0.9230769f));
|
||||
}
|
||||
|
||||
SUBCASE("Constexpr support")
|
||||
{
|
||||
constexpr sf::Vector3i vector(1, 2, 3);
|
||||
|
@ -35,7 +35,12 @@ namespace sf
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const sf::Vector2f& lhs, const ApproxVec& rhs)
|
||||
bool operator==(const sf::Vector2f& lhs, const ApproxVec2& rhs)
|
||||
{
|
||||
return (lhs - rhs.vector).length() == doctest::Approx(0.0);
|
||||
}
|
||||
|
||||
bool operator==(const sf::Vector3f& lhs, const ApproxVec3& rhs)
|
||||
{
|
||||
return static_cast<double>((lhs - rhs.vector).length()) == doctest::Approx(0.0);
|
||||
}
|
||||
@ -45,7 +50,13 @@ bool operator==(const sf::Angle& lhs, const ApproxDeg& rhs)
|
||||
return static_cast<double>(lhs.asDegrees()) == doctest::Approx(static_cast<double>(rhs.degrees));
|
||||
}
|
||||
|
||||
std::ostream& operator <<(std::ostream& os, const ApproxVec& approx)
|
||||
std::ostream& operator <<(std::ostream& os, const ApproxVec2& approx)
|
||||
{
|
||||
os << approx.vector;
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator <<(std::ostream& os, const ApproxVec3& approx)
|
||||
{
|
||||
os << approx.vector;
|
||||
return os;
|
||||
|
@ -40,17 +40,28 @@ namespace sf
|
||||
}
|
||||
|
||||
// Utilities for approximate equality
|
||||
struct ApproxVec
|
||||
struct ApproxVec2
|
||||
{
|
||||
ApproxVec(float x, float y)
|
||||
ApproxVec2(float x, float y)
|
||||
: vector(x, y) {}
|
||||
|
||||
explicit ApproxVec(const sf::Vector2f& v)
|
||||
explicit ApproxVec2(const sf::Vector2f& v)
|
||||
: vector(v) {}
|
||||
|
||||
sf::Vector2f vector;
|
||||
};
|
||||
|
||||
struct ApproxVec3
|
||||
{
|
||||
ApproxVec3(float x, float y, float z)
|
||||
: vector(x, y, z) {}
|
||||
|
||||
explicit ApproxVec3(const sf::Vector3f& v)
|
||||
: vector(v) {}
|
||||
|
||||
sf::Vector3f vector;
|
||||
};
|
||||
|
||||
// Utilities for approximate equality
|
||||
struct ApproxDeg
|
||||
{
|
||||
@ -60,10 +71,12 @@ struct ApproxDeg
|
||||
float degrees;
|
||||
};
|
||||
|
||||
bool operator==(const sf::Vector2f& lhs, const ApproxVec& rhs);
|
||||
bool operator==(const sf::Vector2f& lhs, const ApproxVec2& rhs);
|
||||
bool operator==(const sf::Vector3f& lhs, const ApproxVec3& 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 ApproxVec2& approx);
|
||||
std::ostream& operator <<(std::ostream& os, const ApproxVec3& approx);
|
||||
std::ostream& operator <<(std::ostream& os, const ApproxDeg& approx);
|
||||
|
||||
namespace sf::Testing
|
||||
|
Loading…
Reference in New Issue
Block a user