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() = default;
constexpr Angle(T angle); constexpr Angle(T angle);
template<typename U> constexpr explicit Angle(const Angle<Unit, U>& Angle); template<typename U> constexpr explicit Angle(const Angle<Unit, U>& Angle);
constexpr Angle(const Angle<AngleUnit::Degree, T>& angle); template<AngleUnit FromUnit> constexpr Angle(const Angle<FromUnit, T>& angle);
constexpr Angle(const Angle<AngleUnit::Radian, T>& angle);
~Angle() = default; ~Angle() = default;
T GetCos() const; T GetCos() const;
@ -45,6 +44,8 @@ namespace Nz
constexpr Angle& Set(const Angle& ang); constexpr Angle& Set(const Angle& ang);
template<typename U> constexpr Angle& Set(const Angle<Unit, U>& 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 T ToDegrees() const;
constexpr Angle<AngleUnit::Degree, T> ToDegreeAngle() const; constexpr Angle<AngleUnit::Degree, T> ToDegreeAngle() const;
EulerAngles<T> ToEulerAngles() const; EulerAngles<T> ToEulerAngles() const;
@ -52,6 +53,8 @@ namespace Nz
constexpr T ToRadians() const; constexpr T ToRadians() const;
constexpr Angle<AngleUnit::Radian, T> ToRadianAngle() const; constexpr Angle<AngleUnit::Radian, T> ToRadianAngle() const;
std::string ToString() const; std::string ToString() const;
constexpr T ToTurns() const;
constexpr Angle<AngleUnit::Turn, T> ToTurnAngle() const;
constexpr Angle& operator=(const Angle&) = default; 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;
constexpr bool operator!=(const Angle& other) const; constexpr bool operator!=(const Angle& other) const;
static constexpr Angle FromDegrees(T ang); template<AngleUnit FromUnit> static constexpr Angle From(T value);
static constexpr Angle FromRadians(T ang); static constexpr Angle FromDegrees(T degrees);
static constexpr Angle FromRadians(T radians);
static constexpr Angle FromTurns(T turn);
static constexpr Angle Zero(); static constexpr Angle Zero();
T value; T value;
@ -90,6 +95,12 @@ namespace Nz
using RadianAngled = RadianAngle<double>; using RadianAngled = RadianAngle<double>;
using RadianAnglef = RadianAngle<float>; 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 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>>); 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 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<AngleUnit Unit> struct AngleUtils;
template<> template<>
@ -29,27 +94,7 @@ namespace Nz
template<typename T> static constexpr T GetLimit() template<typename T> static constexpr T GetLimit()
{ {
return 180; return 360;
}
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);
} }
template<typename T> static std::ostream& ToString(std::ostream& out, T value) 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() template<typename T> static constexpr T GetLimit()
{ {
return Pi<T>; return T(2) * 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;
} }
template<typename T> static std::ostream& ToString(std::ostream& out, T value) 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 #ifdef NAZARA_PLATFORM_LINUX
template<typename T> 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) 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 * \param value Angle object to copy
*/ */
template<AngleUnit Unit, typename T> template<AngleUnit Unit, typename T>
constexpr Angle<Unit, T>::Angle(const Angle<AngleUnit::Degree, T>& angle) : template<AngleUnit FromUnit>
value(Detail::AngleUtils<Unit>::FromDegrees(angle.value)) constexpr Angle<Unit, T>::Angle(const Angle<FromUnit, T>& angle) :
{ value(Detail::AngleConversion<FromUnit, Unit>::Convert(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))
{ {
} }
@ -237,16 +271,16 @@ namespace Nz
* If angle exceeds local limits positively or negatively, bring it back between them. * If angle exceeds local limits positively or negatively, bring it back between them.
* For degree angles, local limits are [-180, 180] * For degree angles, local limits are [-180, 180]
* For radian angles, local limits are [-Pi, Pi] * For radian angles, local limits are [-Pi, Pi]
* For turn angles, local limits are [-1, 1]
*/ */
template<AngleUnit Unit, typename T> template<AngleUnit Unit, typename T>
constexpr Angle<Unit, T>& Angle<Unit, T>::Normalize() constexpr Angle<Unit, T>& Angle<Unit, T>::Normalize()
{ {
constexpr T limit = Detail::AngleUtils<Unit>::template GetLimit<T>(); 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)) if (value < T(0))
value += twoLimit; value += limit;
return *this; return *this;
} }
@ -278,6 +312,28 @@ namespace Nz
return *this; 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 * \brief Returns the degree angle that is equivalent to this one
* \return Equivalent degree angle value * \return Equivalent degree angle value
@ -285,7 +341,7 @@ namespace Nz
template<AngleUnit Unit, typename T> template<AngleUnit Unit, typename T>
constexpr T Angle<Unit, T>::ToDegrees() const 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> template<AngleUnit Unit, typename T>
constexpr Angle<AngleUnit::Degree, T> Angle<Unit, T>::ToDegreeAngle() const 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> template<AngleUnit Unit, typename T>
constexpr T Angle<Unit, T>::ToRadians() const 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> template<AngleUnit Unit, typename T>
constexpr Angle<AngleUnit::Radian, T> Angle<Unit, T>::ToRadianAngle() const 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(); 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 * \brief Returns the degree angle that is equivalent to this one
* \return Equivalent degree angle * \return Equivalent degree angle
@ -526,6 +602,19 @@ namespace Nz
return !NumberEquals(value, other.value, Detail::AngleUtils<Unit>::template GetEpsilon<T>()); 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 * \brief Builds an Angle instance using a degree angle, converting if needed
* \return An angle describing the degree angle as Unit * \return An angle describing the degree angle as Unit
@ -533,9 +622,9 @@ namespace Nz
* \param ang Degree angle * \param ang Degree angle
*/ */
template<AngleUnit Unit, typename T> 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 * \param ang Radian angle
*/ */
template<AngleUnit Unit, typename T> 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 enum class AngleUnit
{ {
Degree, Degree,
Radian Radian,
Turn
}; };
enum class BoxCorner 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") WHEN("We compute its sinus/cosinus separatly")
{ {
THEN("It should be equal to 1 and 0") 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);
}
}
}
} }