Math/Angle: Add sine, cosine, tangent methods

This commit is contained in:
Jérôme Leclercq 2018-09-05 15:14:31 +02:00
parent 3cc70daf3e
commit 9e0b61f30d
3 changed files with 125 additions and 6 deletions

View File

@ -11,6 +11,7 @@
#include <Nazara/Math/Algorithm.hpp> #include <Nazara/Math/Algorithm.hpp>
#include <Nazara/Math/Enums.hpp> #include <Nazara/Math/Enums.hpp>
#include <type_traits> #include <type_traits>
#include <utility>
namespace Nz namespace Nz
{ {
@ -28,6 +29,11 @@ namespace Nz
Angle(const Angle&) = default; Angle(const Angle&) = default;
~Angle() = default; ~Angle() = default;
T GetCos() const;
T GetSin() const;
std::pair<T, T> GetSinCos() const;
T GetTan() const;
Angle& MakeZero(); Angle& MakeZero();
void Normalize(); void Normalize();

View File

@ -16,6 +16,11 @@ namespace Nz
template<> template<>
struct AngleUtils<AngleUnit::Degree> struct AngleUtils<AngleUnit::Degree>
{ {
template<typename T> static constexpr T GetEpsilon()
{
return T(1e-4);
}
template<typename T> static constexpr T GetLimit() template<typename T> static constexpr T GetLimit()
{ {
return 180; return 180;
@ -55,9 +60,14 @@ namespace Nz
template<> template<>
struct AngleUtils<AngleUnit::Radian> struct AngleUtils<AngleUnit::Radian>
{ {
template<typename T> static constexpr T GetEpsilon()
{
return T(1e-5);
}
template<typename T> static constexpr T GetLimit() template<typename T> static constexpr T GetLimit()
{ {
return M_PI; return T(M_PI);
} }
template<typename T> static T FromDegrees(T degrees) template<typename T> static T FromDegrees(T degrees)
@ -90,6 +100,14 @@ namespace Nz
return out << "Angle(" << value << "rad)"; return out << "Angle(" << value << "rad)";
} }
}; };
// Naive implementation, hopefully optimized by the compiler
template<typename T>
void SinCos(T x, T* sin, T* cos)
{
*sin = std::sin(x);
*cos = std::cos(x);
}
} }
/*! /*!
* \ingroup math * \ingroup math
@ -108,6 +126,59 @@ namespace Nz
{ {
} }
/*!
* \brief Computes the cosine of the angle
* \return Cosine of angle
*
* \see GetSinCos
*/
template<AngleUnit Unit, typename T>
T Angle<Unit, T>::GetCos() const
{
return std::cos(ToRadians().angle);
}
/*!
* \brief Computes the sine of the angle
* \return Sine of angle
*
* \see GetSinCos
*/
template<AngleUnit Unit, typename T>
T Angle<Unit, T>::GetSin() const
{
return std::sin(ToRadians().angle);
}
/*!
* \brief Computes both sines and cosines of the angle
* \return Sine and cosine of the angle
*
* \remark This is potentially faster than calling both GetSin and GetCos separately as it can computes both values at the same time.
*
* \see GetCos, GetSin
*/
template<AngleUnit Unit, typename T>
std::pair<T, T> Angle<Unit, T>::GetSinCos() const
{
T sin, cos;
Detail::SinCos<T>(ToRadians().angle, &sin, &cos);
return std::make_pair(sin, cos);
}
/*!
* \brief Computes the tangent of the angle
* \return Tangent value of the angle
*
* \see GetCos, GetSin
*/
template<AngleUnit Unit, typename T>
T Angle<Unit, T>::GetTan() const
{
return std::tan(ToRadians().angle);
}
/*! /*!
* \brief Changes the angle value to zero * \brief Changes the angle value to zero
*/ */
@ -115,6 +186,7 @@ namespace Nz
Angle<Unit, T>& Angle<Unit, T>::MakeZero() Angle<Unit, T>& Angle<Unit, T>::MakeZero()
{ {
angle = T(0); angle = T(0);
return *this;
} }
/*! /*!
@ -303,7 +375,7 @@ namespace Nz
template<AngleUnit Unit, typename T> template<AngleUnit Unit, typename T>
bool Angle<Unit, T>::operator==(const Angle& Angle) const bool Angle<Unit, T>::operator==(const Angle& Angle) const
{ {
return NumberEquals(angle, Angle.angle); return NumberEquals(angle, Angle.angle, Detail::AngleUtils<Unit>::GetEpsilon<T>());
} }
/*! /*!
@ -315,7 +387,7 @@ namespace Nz
template<AngleUnit Unit, typename T> template<AngleUnit Unit, typename T>
bool Angle<Unit, T>::operator!=(const Angle& Angle) const bool Angle<Unit, T>::operator!=(const Angle& Angle) const
{ {
return !NumberEquals(angle, Angle.angle); return !NumberEquals(angle, Angle.angle, Detail::AngleUtils<Unit>::GetEpsilon<T>());
} }
/*! /*!

View File

@ -29,6 +29,26 @@ SCENARIO("Angle", "[MATH][ANGLE]")
CHECK(angle.ToRadians() == expectedResult); CHECK(angle.ToRadians() == expectedResult);
} }
} }
WHEN("We compute its sinus/cosinus separatly")
{
THEN("It should be equal to 1 and 0")
{
CHECK(angle.GetSin() == Approx(1.f).margin(0.0001f));
CHECK(angle.GetCos() == Approx(0.f).margin(0.0001f));
}
}
AND_WHEN("We compute it at the same time")
{
auto sincos = angle.GetSinCos();
THEN("It should also be equal to 1 and 0")
{
CHECK(sincos.first == Approx(1.f).margin(0.0001f));
CHECK(sincos.second == Approx(0.f).margin(0.0001f));
}
}
} }
GIVEN("A degree angle of 480deg") GIVEN("A degree angle of 480deg")
@ -48,9 +68,9 @@ SCENARIO("Angle", "[MATH][ANGLE]")
} }
} }
GIVEN("A degree angle of -270deg") GIVEN("A degree angle of -300deg")
{ {
Nz::DegreeAnglef angle(-270.f); Nz::DegreeAnglef angle(-300.f);
WHEN("We normalize it") WHEN("We normalize it")
{ {
@ -58,7 +78,7 @@ SCENARIO("Angle", "[MATH][ANGLE]")
THEN("It should be equal to a normalized version of itself") THEN("It should be equal to a normalized version of itself")
{ {
Nz::DegreeAnglef expectedResult(90.f); Nz::DegreeAnglef expectedResult(60.f);
CHECK(angle == expectedResult); CHECK(angle == expectedResult);
} }
@ -91,7 +111,28 @@ SCENARIO("Angle", "[MATH][ANGLE]")
CHECK(angle.ToDegrees() == expectedResult); CHECK(angle.ToDegrees() == expectedResult);
} }
} }
WHEN("We compute its sinus/cosinus separatly")
{
THEN("It should be equal to 0 and -1")
{
CHECK(angle.GetSin() == Approx(0.f).margin(0.0001f));
CHECK(angle.GetCos() == Approx(-1.f).margin(0.0001f));
} }
}
AND_WHEN("We compute it at the same time")
{
auto sincos = angle.GetSinCos();
THEN("It should also be equal to 0 and -1")
{
CHECK(sincos.first == Approx(0.f).margin(0.0001f));
CHECK(sincos.second == Approx(-1.f).margin(0.0001f));
}
}
}
GIVEN("A radian angle of 7pi") GIVEN("A radian angle of 7pi")
{ {
Nz::RadianAnglef angle(float(7 * M_PI)); Nz::RadianAnglef angle(float(7 * M_PI));