Math: Add support for Turn angles

This commit is contained in:
SirLynix 2022-09-26 19:09:30 +02:00
parent 6372f9cad8
commit 364d1bafb4
4 changed files with 215 additions and 72 deletions

View File

@ -29,8 +29,7 @@ namespace Nz
constexpr Angle() = default;
constexpr Angle(T angle);
template<typename U> constexpr explicit Angle(const Angle<Unit, U>& Angle);
constexpr Angle(const Angle<AngleUnit::Degree, T>& angle);
constexpr Angle(const Angle<AngleUnit::Radian, T>& angle);
template<AngleUnit FromUnit> constexpr Angle(const Angle<FromUnit, T>& angle);
~Angle() = default;
T GetCos() const;
@ -45,6 +44,8 @@ namespace Nz
constexpr Angle& Set(const Angle& ang);
template<typename U> constexpr Angle& Set(const Angle<Unit, U>& ang);
template<AngleUnit ToUnit> T To() const;
template<AngleUnit ToUnit> Angle<ToUnit, T> ToAngle() const;
constexpr T ToDegrees() const;
constexpr Angle<AngleUnit::Degree, T> ToDegreeAngle() const;
EulerAngles<T> ToEulerAngles() const;
@ -52,6 +53,8 @@ namespace Nz
constexpr T ToRadians() const;
constexpr Angle<AngleUnit::Radian, T> ToRadianAngle() const;
std::string ToString() const;
constexpr T ToTurns() const;
constexpr Angle<AngleUnit::Turn, T> ToTurnAngle() const;
constexpr Angle& operator=(const Angle&) = default;
@ -71,8 +74,10 @@ namespace Nz
constexpr bool operator==(const Angle& other) const;
constexpr bool operator!=(const Angle& other) const;
static constexpr Angle FromDegrees(T ang);
static constexpr Angle FromRadians(T ang);
template<AngleUnit FromUnit> static constexpr Angle From(T value);
static constexpr Angle FromDegrees(T degrees);
static constexpr Angle FromRadians(T radians);
static constexpr Angle FromTurns(T turn);
static constexpr Angle Zero();
T value;
@ -90,6 +95,12 @@ namespace Nz
using RadianAngled = RadianAngle<double>;
using RadianAnglef = RadianAngle<float>;
template<typename T>
using TurnAngle = Angle<AngleUnit::Turn, T>;
using TurnAngled = TurnAngle<double>;
using TurnAnglef = TurnAngle<float>;
template<AngleUnit Unit, typename T> bool Serialize(SerializationContext& context, const Angle<Unit, T>& angle, TypeTag<Angle<Unit, T>>);
template<AngleUnit Unit, typename T> bool Unserialize(SerializationContext& context, Angle<Unit, T>* angle, TypeTag<Angle<Unit, T>>);
}

View File

@ -17,6 +17,71 @@ namespace Nz
{
namespace Detail
{
template<AngleUnit From, AngleUnit To> struct AngleConversion;
template<AngleUnit Unit>
struct AngleConversion<Unit, Unit>
{
template<typename T> static constexpr T Convert(T angle)
{
return angle;
}
};
template<>
struct AngleConversion<AngleUnit::Degree, AngleUnit::Radian>
{
template<typename T> static constexpr T Convert(T angle)
{
return DegreeToRadian(angle);
}
};
template<>
struct AngleConversion<AngleUnit::Degree, AngleUnit::Turn>
{
template<typename T> static constexpr T Convert(T angle)
{
return angle / T(360);
}
};
template<>
struct AngleConversion<AngleUnit::Radian, AngleUnit::Degree>
{
template<typename T> static constexpr T Convert(T angle)
{
return RadianToDegree(angle);
}
};
template<>
struct AngleConversion<AngleUnit::Radian, AngleUnit::Turn>
{
template<typename T> static constexpr T Convert(T angle)
{
return angle / (T(2) * Pi<T>);
}
};
template<>
struct AngleConversion<AngleUnit::Turn, AngleUnit::Degree>
{
template<typename T> static constexpr T Convert(T angle)
{
return angle * T(360);
}
};
template<>
struct AngleConversion<AngleUnit::Turn, AngleUnit::Radian>
{
template<typename T> static constexpr T Convert(T angle)
{
return angle * T(2) * Pi<T>;
}
};
template<AngleUnit Unit> struct AngleUtils;
template<>
@ -29,27 +94,7 @@ namespace Nz
template<typename T> static constexpr T GetLimit()
{
return 180;
}
template<typename T> static constexpr T FromDegrees(T degrees)
{
return degrees;
}
template<typename T> static constexpr T FromRadians(T radians)
{
return RadianToDegree(radians);
}
template<typename T> static constexpr T ToDegrees(T degrees)
{
return degrees;
}
template<typename T> static constexpr T ToRadians(T degrees)
{
return DegreeToRadian(degrees);
return 360;
}
template<typename T> static std::ostream& ToString(std::ostream& out, T value)
@ -68,27 +113,7 @@ namespace Nz
template<typename T> static constexpr T GetLimit()
{
return Pi<T>;
}
template<typename T> static constexpr T FromDegrees(T degrees)
{
return DegreeToRadian(degrees);
}
template<typename T> static constexpr T FromRadians(T radians)
{
return radians;
}
template<typename T> static constexpr T ToDegrees(T radians)
{
return RadianToDegree(radians);
}
template<typename T> static constexpr T ToRadians(T radians)
{
return radians;
return T(2) * Pi<T>;
}
template<typename T> static std::ostream& ToString(std::ostream& out, T value)
@ -97,6 +122,25 @@ namespace Nz
}
};
template<>
struct AngleUtils<AngleUnit::Turn>
{
template<typename T> static constexpr T GetEpsilon()
{
return T(1e-5);
}
template<typename T> static constexpr T GetLimit()
{
return 1;
}
template<typename T> static std::ostream& ToString(std::ostream& out, T value)
{
return out << "Angle(" << value << "turn)";
}
};
#ifdef NAZARA_PLATFORM_LINUX
template<typename T>
void SinCos(std::enable_if_t<!std::is_same<T, float>::value && !std::is_same<T, long double>::value, double> x, T* sin, T* cos)
@ -147,24 +191,14 @@ namespace Nz
}
/*!
* \brief Constructs an Angle object from a angle in degrees, converting if required
* \brief Constructs an Angle object from a angle in a specific unit, converting if required
*
* \param value Angle object to copy
*/
template<AngleUnit Unit, typename T>
constexpr Angle<Unit, T>::Angle(const Angle<AngleUnit::Degree, T>& angle) :
value(Detail::AngleUtils<Unit>::FromDegrees(angle.value))
{
}
/*!
* \brief Constructs an Angle object from a angle in radians, converting if required
*
* \param value Angle object to copy
*/
template<AngleUnit Unit, typename T>
constexpr Angle<Unit, T>::Angle(const Angle<AngleUnit::Radian, T>& angle) :
value(Detail::AngleUtils<Unit>::FromRadians(angle.value))
template<AngleUnit FromUnit>
constexpr Angle<Unit, T>::Angle(const Angle<FromUnit, T>& angle) :
value(Detail::AngleConversion<FromUnit, Unit>::Convert(angle.value))
{
}
@ -237,16 +271,16 @@ namespace Nz
* If angle exceeds local limits positively or negatively, bring it back between them.
* For degree angles, local limits are [-180, 180]
* For radian angles, local limits are [-Pi, Pi]
* For turn angles, local limits are [-1, 1]
*/
template<AngleUnit Unit, typename T>
constexpr Angle<Unit, T>& Angle<Unit, T>::Normalize()
{
constexpr T limit = Detail::AngleUtils<Unit>::template GetLimit<T>();
constexpr T twoLimit = limit * T(2);
value = std::fmod(value, twoLimit);
value = std::fmod(value, limit);
if (value < T(0))
value += twoLimit;
value += limit;
return *this;
}
@ -278,6 +312,28 @@ namespace Nz
return *this;
}
/*!
* \brief Returns the ToUnit angle that is equivalent to this one
* \return Equivalent ToUnit angle value
*/
template<AngleUnit Unit, typename T>
template<AngleUnit ToUnit>
T Angle<Unit, T>::To() const
{
return Detail::AngleConversion<Unit, ToUnit>::Convert(value);
}
/*!
* \brief Returns the ToUnit angle that is equivalent to this one
* \return Equivalent ToUnit angle
*/
template<AngleUnit Unit, typename T>
template<AngleUnit ToUnit>
Angle<ToUnit, T> Angle<Unit, T>::ToAngle() const
{
return Angle<ToUnit, T>(To<ToUnit>());
}
/*!
* \brief Returns the degree angle that is equivalent to this one
* \return Equivalent degree angle value
@ -285,7 +341,7 @@ namespace Nz
template<AngleUnit Unit, typename T>
constexpr T Angle<Unit, T>::ToDegrees() const
{
return Detail::AngleUtils<Unit>::ToDegrees(value);
return To<AngleUnit::Degree>();
}
/*!
@ -295,7 +351,7 @@ namespace Nz
template<AngleUnit Unit, typename T>
constexpr Angle<AngleUnit::Degree, T> Angle<Unit, T>::ToDegreeAngle() const
{
return DegreeAngle<T>(ToDegrees());
return ToAngle<AngleUnit::Degree>();
}
/*!
@ -333,7 +389,7 @@ namespace Nz
template<AngleUnit Unit, typename T>
constexpr T Angle<Unit, T>::ToRadians() const
{
return Detail::AngleUtils<Unit>::ToRadians(value);
return To<AngleUnit::Radian>();
}
/*!
@ -343,7 +399,7 @@ namespace Nz
template<AngleUnit Unit, typename T>
constexpr Angle<AngleUnit::Radian, T> Angle<Unit, T>::ToRadianAngle() const
{
return RadianAngle<T>(ToRadians());
return ToAngle<AngleUnit::Radian>();
}
/*!
@ -359,6 +415,26 @@ namespace Nz
return oss.str();
}
/*!
* \brief Returns the turn angle that is equivalent to this angle
* \return Equivalent turn angle value
*/
template<AngleUnit Unit, typename T>
constexpr T Angle<Unit, T>::ToTurns() const
{
return To<AngleUnit::Turn>(value);
}
/*!
* \brief Returns the turn angle that is equivalent to this angle
* \return Equivalent turn angle
*/
template<AngleUnit Unit, typename T>
constexpr Angle<AngleUnit::Turn, T> Angle<Unit, T>::ToTurnAngle() const
{
return ToAngle<AngleUnit::Turn>();
}
/*!
* \brief Returns the degree angle that is equivalent to this one
* \return Equivalent degree angle
@ -526,6 +602,19 @@ namespace Nz
return !NumberEquals(value, other.value, Detail::AngleUtils<Unit>::template GetEpsilon<T>());
}
/*!
* \brief Builds an Angle instance using a FromUnit angle, converting if needed
* \return An angle describing the FromUnit angle as Unit
*
* \param ang Degree angle
*/
template<AngleUnit Unit, typename T>
template<AngleUnit FromUnit>
constexpr Angle<Unit, T> Angle<Unit, T>::From(T value)
{
return Angle(Detail::AngleConversion<FromUnit, Unit>::Convert(value));
}
/*!
* \brief Builds an Angle instance using a degree angle, converting if needed
* \return An angle describing the degree angle as Unit
@ -533,9 +622,9 @@ namespace Nz
* \param ang Degree angle
*/
template<AngleUnit Unit, typename T>
constexpr Angle<Unit, T> Angle<Unit, T>::FromDegrees(T ang)
constexpr Angle<Unit, T> Angle<Unit, T>::FromDegrees(T degrees)
{
return Angle(Detail::AngleUtils<Unit>::FromDegrees(ang));
return From<AngleUnit::Degree>(degrees);
}
/*!
@ -545,9 +634,21 @@ namespace Nz
* \param ang Radian angle
*/
template<AngleUnit Unit, typename T>
constexpr Angle<Unit, T> Angle<Unit, T>::FromRadians(T ang)
constexpr Angle<Unit, T> Angle<Unit, T>::FromRadians(T radians)
{
return Angle(Detail::AngleUtils<Unit>::FromRadians(ang));
return From<AngleUnit::Radian>(radians);
}
/*!
* \brief Builds an Angle instance using a radian angle, converting if needed
* \return An angle describing the radian angle as Unit
*
* \param ang Radian angle
*/
template<AngleUnit Unit, typename T>
constexpr Angle<Unit, T> Angle<Unit, T>::FromTurns(T turns)
{
return From<AngleUnit::Turn>(turns);
}
/*!

View File

@ -14,7 +14,8 @@ namespace Nz
enum class AngleUnit
{
Degree,
Radian
Radian,
Turn
};
enum class BoxCorner

View File

@ -33,6 +33,19 @@ SCENARIO("Angle", "[MATH][ANGLE]")
}
}
WHEN("We convert it to turns")
{
Nz::TurnAnglef turnAngle(angle);
THEN("It should be equal to pi/2")
{
Nz::TurnAnglef expectedResult(1.f / 4.f);
CHECK(turnAngle == expectedResult);
CHECK(angle.ToTurnAngle() == expectedResult);
}
}
WHEN("We compute its sinus/cosinus separatly")
{
THEN("It should be equal to 1 and 0")
@ -204,4 +217,21 @@ SCENARIO("Angle", "[MATH][ANGLE]")
}
}
}
GIVEN("A turn angle of 1.5f")
{
Nz::TurnAnglef angle(1.5f);
WHEN("We normalize it")
{
angle.Normalize();
THEN("It should be equal to a normalized version of itself")
{
Nz::TurnAnglef expectedResult(0.5f);
CHECK(angle == expectedResult);
}
}
}
}