Math: Add Angle class
This commit is contained in:
parent
960af3afa3
commit
f59810b68e
|
|
@ -0,0 +1,89 @@
|
|||
// Copyright (C) 2017 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Mathematics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef NAZARA_ANGLE_HPP
|
||||
#define NAZARA_ANGLE_HPP
|
||||
|
||||
#include <Nazara/Core/String.hpp>
|
||||
#include <Nazara/Math/Algorithm.hpp>
|
||||
#include <Nazara/Math/Enums.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
struct SerializationContext;
|
||||
|
||||
template<AngleUnit Unit, typename T>
|
||||
class Angle
|
||||
{
|
||||
public:
|
||||
Angle() = default;
|
||||
Angle(T Angle);
|
||||
template<AngleUnit U = Unit, typename C = std::enable_if_t<Unit == AngleUnit::Degree>> explicit Angle(const Angle<AngleUnit::Radian, T>& Angle) { Set(Angle); }
|
||||
template<AngleUnit U = Unit, typename C = std::enable_if_t<Unit == AngleUnit::Radian>> explicit Angle(const Angle<AngleUnit::Degree, T>& Angle) { Set(Angle); }
|
||||
template<typename U> explicit Angle(const Angle<Unit, U>& Angle);
|
||||
Angle(const Angle&) = default;
|
||||
~Angle() = default;
|
||||
|
||||
Angle& MakeZero();
|
||||
|
||||
void Normalize();
|
||||
|
||||
template<AngleUnit U = Unit, typename = std::enable_if_t<U == AngleUnit::Degree>> Angle& Set(const Angle<AngleUnit::Radian, T>& Angle);
|
||||
template<AngleUnit U = Unit, typename = std::enable_if_t<U == AngleUnit::Radian>> Angle& Set(const Angle<AngleUnit::Degree, T>& Angle);
|
||||
Angle& Set(const Angle& Angle);
|
||||
template<typename U> Angle& Set(const Angle<Unit, U>& Angle);
|
||||
|
||||
Angle<AngleUnit::Degree, T> ToDegrees() const;
|
||||
Angle<AngleUnit::Radian, T> ToRadians() const;
|
||||
String ToString() const;
|
||||
|
||||
Angle& operator=(const Angle&) = default;
|
||||
|
||||
Angle operator+(const Angle& Angle) const;
|
||||
Angle operator-(const Angle& Angle) const;
|
||||
|
||||
Angle& operator+=(const Angle& Angle);
|
||||
Angle& operator-=(const Angle& Angle);
|
||||
Angle& operator*=(T scalar);
|
||||
Angle& operator/=(T divider);
|
||||
|
||||
bool operator==(const Angle& Angle) const;
|
||||
bool operator!=(const Angle& Angle) const;
|
||||
|
||||
static Angle Zero();
|
||||
|
||||
T angle;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using DegreeAngle = Angle<AngleUnit::Degree, T>;
|
||||
|
||||
using DegreeAngled = DegreeAngle<double>;
|
||||
using DegreeAnglef = DegreeAngle<float>;
|
||||
|
||||
template<typename T>
|
||||
using RadianAngle = Angle<AngleUnit::Radian, T>;
|
||||
|
||||
using RadianAngled = RadianAngle<double>;
|
||||
using RadianAnglef = RadianAngle<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>>);
|
||||
}
|
||||
|
||||
template<Nz::AngleUnit Unit, typename T>
|
||||
Nz::Angle<Unit, T> operator*(T scale, const Nz::Angle<Unit, T>& angle);
|
||||
|
||||
template<Nz::AngleUnit Unit, typename T>
|
||||
Nz::Angle<Unit, T> operator/(T divider, const Nz::Angle<Unit, T>& angle);
|
||||
|
||||
template<Nz::AngleUnit Unit, typename T>
|
||||
std::ostream& operator<<(std::ostream& out, const Nz::Angle<Unit, T>& angle);
|
||||
|
||||
#include <Nazara/Math/Angle.inl>
|
||||
|
||||
#endif // NAZARA_ANGLE_HPP
|
||||
|
|
@ -0,0 +1,406 @@
|
|||
// Copyright (C) 2017 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Mathematics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Math/Angle.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <Nazara/Core/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
namespace Detail
|
||||
{
|
||||
template<AngleUnit Unit> struct AngleUtils;
|
||||
|
||||
template<>
|
||||
struct AngleUtils<AngleUnit::Degree>
|
||||
{
|
||||
template<typename T> static constexpr T GetLimit()
|
||||
{
|
||||
return 180;
|
||||
}
|
||||
|
||||
template<typename T> static T FromDegrees(T degrees)
|
||||
{
|
||||
return degrees;
|
||||
}
|
||||
|
||||
template<typename T> static T FromRadians(T radians)
|
||||
{
|
||||
return RadianToDegree(radians);
|
||||
}
|
||||
|
||||
template<typename T> static T ToDegrees(T degrees)
|
||||
{
|
||||
return degrees;
|
||||
}
|
||||
|
||||
template<typename T> static T ToRadians(T degrees)
|
||||
{
|
||||
return DegreeToRadian(degrees);
|
||||
}
|
||||
|
||||
template<typename T> static String ToString(T value)
|
||||
{
|
||||
return "Angle(" + String::Number(value) + "deg)";
|
||||
}
|
||||
|
||||
template<typename T> static std::ostream& ToString(std::ostream& out, T value)
|
||||
{
|
||||
return out << "Angle(" << value << "deg)";
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct AngleUtils<AngleUnit::Radian>
|
||||
{
|
||||
template<typename T> static constexpr T GetLimit()
|
||||
{
|
||||
return M_PI;
|
||||
}
|
||||
|
||||
template<typename T> static T FromDegrees(T degrees)
|
||||
{
|
||||
return DegreeToRadian(degrees);
|
||||
}
|
||||
|
||||
template<typename T> static T FromRadians(T radians)
|
||||
{
|
||||
return radians;
|
||||
}
|
||||
|
||||
template<typename T> static T ToDegrees(T radians)
|
||||
{
|
||||
return RadianToDegree(radians);
|
||||
}
|
||||
|
||||
template<typename T> static T ToRadians(T radians)
|
||||
{
|
||||
return radians;
|
||||
}
|
||||
|
||||
template<typename T> static String ToString(T value)
|
||||
{
|
||||
return "Angle(" + String::Number(value) + "rad)";
|
||||
}
|
||||
|
||||
template<typename T> static std::ostream& ToString(std::ostream& out, T value)
|
||||
{
|
||||
return out << "Angle(" << value << "rad)";
|
||||
}
|
||||
};
|
||||
}
|
||||
/*!
|
||||
* \ingroup math
|
||||
* \class Nz::Angle
|
||||
* \brief Math class that represents an angle
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief Constructs an Angle object with an angle value
|
||||
*
|
||||
* \param Angle value of the angle
|
||||
*/
|
||||
template<AngleUnit Unit, typename T>
|
||||
Angle<Unit, T>::Angle(T Angle) :
|
||||
angle(Angle)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Changes the angle value to zero
|
||||
*/
|
||||
template<AngleUnit Unit, typename T>
|
||||
Angle<Unit, T>& Angle<Unit, T>::MakeZero()
|
||||
{
|
||||
angle = T(0);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Normalizes the angle value
|
||||
*
|
||||
* 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 [-M_PI, M_PI]
|
||||
*/
|
||||
template<AngleUnit Unit, typename T>
|
||||
void Angle<Unit, T>::Normalize()
|
||||
{
|
||||
constexpr T limit = Detail::AngleUtils<Unit>::GetLimit<T>();
|
||||
constexpr T twoLimit = limit * T(2);
|
||||
|
||||
angle = std::fmod(angle, twoLimit);
|
||||
if (angle < T(0))
|
||||
angle += twoLimit;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Changes the angle value by converting a radian angle
|
||||
*
|
||||
* \param Angle Radian angle which will be converted
|
||||
*/
|
||||
template<AngleUnit Unit, typename T>
|
||||
template<AngleUnit U, typename>
|
||||
Angle<Unit, T>& Angle<Unit, T>::Set(const Angle<AngleUnit::Radian, T>& Angle)
|
||||
{
|
||||
angle = RadianToDegree(Angle.angle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Changes the angle value by converting a degree angle
|
||||
*
|
||||
* \param Angle Degree angle which will be converted
|
||||
*/
|
||||
template<AngleUnit Unit, typename T>
|
||||
template<AngleUnit U, typename>
|
||||
Angle<Unit, T>& Angle<Unit, T>::Set(const Angle<AngleUnit::Degree, T>& Angle)
|
||||
{
|
||||
angle = DegreeToRadian(Angle.angle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Copies the angle value of an angle
|
||||
*
|
||||
* \param Angle Angle which will be copied
|
||||
*/
|
||||
template<AngleUnit Unit, typename T>
|
||||
Angle<Unit, T>& Angle<Unit, T>::Set(const Angle& Angle)
|
||||
{
|
||||
angle = Angle.angle;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Changes the angle value to the same as an Angle of a different type
|
||||
*
|
||||
* \param Angle Angle which will be casted
|
||||
*
|
||||
* \remark Conversion from U to T occurs using static_cast
|
||||
*/
|
||||
template<AngleUnit Unit, typename T>
|
||||
template<typename U>
|
||||
Angle<Unit, T>& Angle<Unit, T>::Set(const Angle<Unit, U>& Angle)
|
||||
{
|
||||
angle = static_cast<T>(Angle.angle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the degree angle that is equivalent to this one
|
||||
* \return Equivalent degree angle
|
||||
*/
|
||||
template<AngleUnit Unit, typename T>
|
||||
Angle<AngleUnit::Degree, T> Angle<Unit, T>::ToDegrees() const
|
||||
{
|
||||
return DegreeAngle<T>(Detail::AngleUtils<Unit>::ToDegrees(angle));
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the radian angle that is equivalent to this angle
|
||||
* \return Equivalent radian angle
|
||||
*/
|
||||
template<AngleUnit Unit, typename T>
|
||||
Angle<AngleUnit::Radian, T> Angle<Unit, T>::ToRadians() const
|
||||
{
|
||||
return RadianAngle<T>(Detail::AngleUtils<Unit>::ToRadians(angle));
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Converts the angle to a string representation
|
||||
* \return String representation of the angle
|
||||
*/
|
||||
template<AngleUnit Unit, typename T>
|
||||
String Angle<Unit, T>::ToString() const
|
||||
{
|
||||
return Detail::AngleUtils<Unit>::ToString(angle);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Addition operator
|
||||
* \return Adds two angles together
|
||||
*
|
||||
* \param angle Angle to add
|
||||
*/
|
||||
template<AngleUnit Unit, typename T>
|
||||
Angle<Unit, T> Angle<Unit, T>::operator+(const Angle& Angle) const
|
||||
{
|
||||
return Angle(angle + Angle.angle);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Subtraction operator
|
||||
* \return Subtracts two angles together
|
||||
*
|
||||
* \param angle Angle to subtract
|
||||
*/
|
||||
template<AngleUnit Unit, typename T>
|
||||
Angle<Unit, T> Angle<Unit, T>::operator-(const Angle& Angle) const
|
||||
{
|
||||
return Angle(angle - Angle.angle);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Adds an angle by another
|
||||
* \return A reference to the angle
|
||||
*
|
||||
* \param angle Angle to add
|
||||
*/
|
||||
template<AngleUnit Unit, typename T>
|
||||
Angle<Unit, T>& Angle<Unit, T>::operator+=(const Angle& Angle)
|
||||
{
|
||||
angle += Angle.angle;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Subtract an angle by another
|
||||
* \return A reference to the angle
|
||||
*
|
||||
* \param angle Angle to subtract
|
||||
*/
|
||||
template<AngleUnit Unit, typename T>
|
||||
Angle<Unit, T>& Angle<Unit, T>::operator-=(const Angle& Angle)
|
||||
{
|
||||
angle -= Angle.angle;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Scales the angle by a scalar
|
||||
* \return A reference to the angle
|
||||
*
|
||||
* \param scalar Multiplier
|
||||
*/
|
||||
template<AngleUnit Unit, typename T>
|
||||
Angle<Unit, T>& Angle<Unit, T>::operator*=(T scalar)
|
||||
{
|
||||
angle *= scalar;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Divides the angle by a scalar
|
||||
* \return A reference to the angle
|
||||
*
|
||||
* \param divider Divider
|
||||
*/
|
||||
template<AngleUnit Unit, typename T>
|
||||
Angle<Unit, T>& Angle<Unit, T>::operator/=(T divider)
|
||||
{
|
||||
angle /= divider;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Compares the angle to another for equality
|
||||
* \return True if both angles are equal
|
||||
*
|
||||
* \param Angle The other angle to compare to
|
||||
*/
|
||||
template<AngleUnit Unit, typename T>
|
||||
bool Angle<Unit, T>::operator==(const Angle& Angle) const
|
||||
{
|
||||
return NumberEquals(angle, Angle.angle);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Compares the angle to another for inequality
|
||||
* \return True if both angles are equal
|
||||
*
|
||||
* \param Angle The other angle to compare to
|
||||
*/
|
||||
template<AngleUnit Unit, typename T>
|
||||
bool Angle<Unit, T>::operator!=(const Angle& Angle) const
|
||||
{
|
||||
return !NumberEquals(angle, Angle.angle);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns an angle with an angle of zero
|
||||
* \return Zero angle
|
||||
*/
|
||||
template<AngleUnit Unit, typename T>
|
||||
Angle<Unit, T> Angle<Unit, T>::Zero()
|
||||
{
|
||||
Angle angle;
|
||||
angle.MakeZero();
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Serializes an Angle
|
||||
* \return true if successfully serialized
|
||||
*
|
||||
* \param context Serialization context
|
||||
* \param angle Input Angle
|
||||
*/
|
||||
template<AngleUnit Unit, typename T>
|
||||
bool Serialize(SerializationContext& context, const Angle<Unit, T>& angle, TypeTag<Angle<Unit, T>>)
|
||||
{
|
||||
if (!Serialize(context, angle.angle))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Unserializes an Angle
|
||||
* \return true if successfully unserialized
|
||||
*
|
||||
* \param context Serialization context
|
||||
* \param angle Output Angle
|
||||
*/
|
||||
template<AngleUnit Unit, typename T>
|
||||
bool Unserialize(SerializationContext& context, Angle<Unit, T>* angle, TypeTag<Angle<Unit, T>>)
|
||||
{
|
||||
if (!Unserialize(context, &angle->angle))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Multiplication operator
|
||||
* \return An angle corresponding to scale * angle
|
||||
*
|
||||
* \param scale Multiplier
|
||||
* \param angle Angle
|
||||
*/
|
||||
template<Nz::AngleUnit Unit, typename T>
|
||||
Nz::Angle<Unit, T> operator*(T scale, const Nz::Angle<Unit, T>& angle)
|
||||
{
|
||||
return Nz::Angle<Unit, T>(scale * angle.angle);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Division operator
|
||||
* \return An angle corresponding to scale / angle
|
||||
*
|
||||
* \param scale Divisor
|
||||
* \param angle Angle
|
||||
*/
|
||||
template<Nz::AngleUnit Unit, typename T>
|
||||
Nz::Angle<Unit, T> operator/(T scale, const Nz::Angle<Unit, T>& angle)
|
||||
{
|
||||
return Nz::Angle<Unit, T>(scale / angle.angle);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Output operator
|
||||
* \return The stream
|
||||
*
|
||||
* \param out The stream
|
||||
* \param box The box to output
|
||||
*/
|
||||
template<Nz::AngleUnit Unit, typename T>
|
||||
std::ostream& operator<<(std::ostream& out, const Nz::Angle<Unit, T>& angle)
|
||||
{
|
||||
return Nz::Detail::AngleUtils<Unit>::ToString(out, angle.angle);
|
||||
}
|
||||
|
||||
#include <Nazara/Core/DebugOff.hpp>
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
#include <Nazara/Math/Angle.hpp>
|
||||
#include <Catch/catch.hpp>
|
||||
|
||||
SCENARIO("Angle", "[MATH][ANGLE]")
|
||||
{
|
||||
GIVEN("A degree angle of 90deg")
|
||||
{
|
||||
Nz::DegreeAnglef angle(90.f);
|
||||
|
||||
WHEN("We convert it to degrees")
|
||||
{
|
||||
Nz::DegreeAnglef copyAngle = angle.ToDegrees();
|
||||
|
||||
THEN("It should compare to itself")
|
||||
{
|
||||
CHECK(angle == copyAngle);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We convert it to radians")
|
||||
{
|
||||
Nz::RadianAnglef radAngle(angle);
|
||||
|
||||
THEN("It should be equal to pi/2")
|
||||
{
|
||||
Nz::RadianAnglef expectedResult(float(M_PI_2));
|
||||
|
||||
CHECK(radAngle == expectedResult);
|
||||
CHECK(angle.ToRadians() == expectedResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A degree angle of 480deg")
|
||||
{
|
||||
Nz::DegreeAnglef angle(480.f);
|
||||
|
||||
WHEN("We normalize it")
|
||||
{
|
||||
angle.Normalize();
|
||||
|
||||
THEN("It should be equal to a normalized version of itself")
|
||||
{
|
||||
Nz::DegreeAnglef expectedResult(120.f);
|
||||
|
||||
CHECK(angle == expectedResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A degree angle of -270deg")
|
||||
{
|
||||
Nz::DegreeAnglef angle(-270.f);
|
||||
|
||||
WHEN("We normalize it")
|
||||
{
|
||||
angle.Normalize();
|
||||
|
||||
THEN("It should be equal to a normalized version of itself")
|
||||
{
|
||||
Nz::DegreeAnglef expectedResult(90.f);
|
||||
|
||||
CHECK(angle == expectedResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A radian angle of -M_PI")
|
||||
{
|
||||
Nz::RadianAnglef angle(float(-M_PI));
|
||||
|
||||
WHEN("We convert it to radians")
|
||||
{
|
||||
Nz::RadianAnglef copyAngle = angle.ToRadians();
|
||||
|
||||
THEN("It should compare to itself")
|
||||
{
|
||||
CHECK(angle == copyAngle);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We convert it to degrees")
|
||||
{
|
||||
Nz::DegreeAnglef degAngle(angle);
|
||||
|
||||
THEN("It should be equal to pi/2")
|
||||
{
|
||||
Nz::DegreeAnglef expectedResult(-180.f);
|
||||
|
||||
CHECK(degAngle == expectedResult);
|
||||
CHECK(angle.ToDegrees() == expectedResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
GIVEN("A radian angle of 7pi")
|
||||
{
|
||||
Nz::RadianAnglef angle(float(7 * M_PI));
|
||||
|
||||
WHEN("We normalize it")
|
||||
{
|
||||
angle.Normalize();
|
||||
|
||||
THEN("It should be equal to a normalized version of itself")
|
||||
{
|
||||
Nz::RadianAnglef expectedResult(float(M_PI));
|
||||
|
||||
CHECK(angle == expectedResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A radian angle of -4pi")
|
||||
{
|
||||
Nz::RadianAnglef angle(float(-4 * M_PI));
|
||||
|
||||
WHEN("We normalize it")
|
||||
{
|
||||
angle.Normalize();
|
||||
|
||||
THEN("It should be equal to a normalized version of itself")
|
||||
{
|
||||
Nz::RadianAnglef expectedResult(0.f);
|
||||
|
||||
CHECK(angle == expectedResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue