// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) // This file is part of the "Nazara Engine - Math module" // For conditions of distribution and use, see copyright notice in Config.hpp #include #include #include #include #ifdef NAZARA_PLATFORM_POSIX #include //< sincos #endif #include namespace Nz { namespace Detail { template struct AngleUtils; template<> struct AngleUtils { template static constexpr T GetEpsilon() { return T(1e-4); } template static constexpr T GetLimit() { return 180; } template static constexpr T FromDegrees(T degrees) { return degrees; } template static constexpr T FromRadians(T radians) { return RadianToDegree(radians); } template static constexpr T ToDegrees(T degrees) { return degrees; } template static constexpr T ToRadians(T degrees) { return DegreeToRadian(degrees); } template static std::ostream& ToString(std::ostream& out, T value) { return out << "Angle(" << value << "deg)"; } }; template<> struct AngleUtils { template static constexpr T GetEpsilon() { return T(1e-5); } template static constexpr T GetLimit() { return Pi; } template static constexpr T FromDegrees(T degrees) { return DegreeToRadian(degrees); } template static constexpr T FromRadians(T radians) { return radians; } template static constexpr T ToDegrees(T radians) { return RadianToDegree(radians); } template static constexpr T ToRadians(T radians) { return radians; } template static std::ostream& ToString(std::ostream& out, T value) { return out << "Angle(" << value << "rad)"; } }; #ifdef NAZARA_PLATFORM_LINUX template void SinCos(std::enable_if_t::value && !std::is_same::value, double> x, T* sin, T* cos) { double s, c; ::sincos(x, &s, &c); *sin = static_cast(s); *cos = static_cast(c); } template void SinCos(std::enable_if_t::value, float> x, float* s, float* c) { ::sincosf(x, s, c); } template void SinCos(std::enable_if_t::value, long double> x, long double* s, long double* c) { ::sincosl(x, s, c); } #else // Naive implementation, hopefully optimized by the compiler template void SinCos(T x, T* sin, T* cos) { *sin = std::sin(x); *cos = std::cos(x); } #endif } /*! * \ingroup math * \class Nz::Angle * \brief Math class that represents an angle */ /*! * \brief Constructs an Angle object with an angle value * * \param value value of the angle */ template constexpr Angle::Angle(T angle) : value(angle) { } /*! * \brief Constructs an Angle object from a angle in degrees, converting if required * * \param value Angle object to copy */ template constexpr Angle::Angle(const Angle& angle) : value(Detail::AngleUtils::FromDegrees(angle.value)) { } /*! * \brief Constructs an Angle object from a angle in radians, converting if required * * \param value Angle object to copy */ template constexpr Angle::Angle(const Angle& angle) : value(Detail::AngleUtils::FromRadians(angle.value)) { } /*! * \brief Computes the cosine of the angle * \return Cosine of angle * * \see GetSinCos */ template T Angle::GetCos() const { return std::cos(ToRadians()); } /*! * \brief Computes the sine of the angle * \return Sine of angle * * \see GetSinCos */ template T Angle::GetSin() const { return std::sin(ToRadians()); } /*! * \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 std::pair Angle::GetSinCos() const { T sin, cos; Detail::SinCos(ToRadians(), &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 T Angle::GetTan() const { return std::tan(ToRadians()); } /*! * \brief Changes the angle value to zero */ template constexpr Angle& Angle::MakeZero() { value = T(0); return *this; } /*! * \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 [-Pi, Pi] */ template constexpr Angle& Angle::Normalize() { constexpr T limit = Detail::AngleUtils::template GetLimit(); constexpr T twoLimit = limit * T(2); value = std::fmod(value, twoLimit); if (value < T(0)) value += twoLimit; return *this; } /*! * \brief Copies the angle value of an angle * * \param Angle Angle which will be copied */ template constexpr Angle& Angle::Set(const Angle& ang) { value = ang.value; 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 template constexpr Angle& Angle::Set(const Angle& ang) { value = static_cast(ang.value); return *this; } /*! * \brief Returns the degree angle that is equivalent to this one * \return Equivalent degree angle value */ template constexpr T Angle::ToDegrees() const { return Detail::AngleUtils::ToDegrees(value); } /*! * \brief Returns the degree angle that is equivalent to this one * \return Equivalent degree angle */ template constexpr Angle Angle::ToDegreeAngle() const { return DegreeAngle(ToDegrees()); } /*! * \brief Converts the angle to an Euler Angles representation * \return A 2D rotation expressed in Euler angles * * This will assume two-dimensional usage, and will set the angle value (as degrees) as the roll value of the Euler Angles, leaving pitch and yaw to zero */ template EulerAngles Angle::ToEulerAngles() const { return EulerAngles(0, 0, ToDegrees()); } /*! * \brief Converts the angle to a Quaternion representation * \return A 2D rotation expressed with Quaternion * * This will assume two-dimensional usage, as if the angle was first converted to Euler Angles and then to a Quaternion * * \see ToEulerAngles */ template Quaternion Angle::ToQuaternion() const { auto halfAngle = Angle(*this) / 2.f; auto sincos = halfAngle.GetSinCos(); return Quaternion(sincos.second, 0, 0, sincos.first); } /*! * \brief Returns the radian angle that is equivalent to this angle * \return Equivalent radian angle value */ template constexpr T Angle::ToRadians() const { return Detail::AngleUtils::ToRadians(value); } /*! * \brief Returns the radian angle that is equivalent to this angle * \return Equivalent radian angle */ template constexpr Angle Angle::ToRadianAngle() const { return RadianAngle(ToRadians()); } /*! * \brief Converts the angle to a string representation * \return String representation of the angle */ template std::string Angle::ToString() const { std::ostringstream oss; Detail::AngleUtils::ToString(oss, value); return oss.str(); } /*! * \brief Returns the degree angle that is equivalent to this one * \return Equivalent degree angle */ /*template template Angle::operator Angle() const { return ToDegreeAngle(); }*/ /*! * \brief Converts the angle to a string representation * \return String representation of the angle */ /*template template Angle::operator Angle() const { return ToRadianAngle(); }*/ /*! * \brief Addition operator * \return Adds two angles together * * \param other Angle to add */ template constexpr Angle Angle::operator+(const Angle& other) const { return Angle(value + other.value); } /*! * \brief Subtraction operator * \return Subtracts two angles together * * \param other Angle to subtract */ template constexpr Angle Angle::operator-(const Angle& other) const { return Angle(value - other.value); } /*! * \brief Multiplication operator * \return A copy of the angle, scaled by the multiplier * * \param scalar Multiplier */ template constexpr Angle Angle::operator*(T scalar) const { return Angle(value * scalar); } /*! * \brief Divides the angle by a scalar * \return A copy of the angle, divided by the divider * * \param divider Divider */ template constexpr Angle Angle::operator/(T divider) const { return Angle(value / divider); } /*! * \brief Adds an angle by another * \return A reference to the angle * * \param other Angle to add */ template constexpr Angle& Angle::operator+=(const Angle& other) { value += other.value; return *this; } /*! * \brief Subtract an angle by another * \return A reference to the angle * * \param other Angle to subtract */ template constexpr Angle& Angle::operator-=(const Angle& other) { value -= other.value; return *this; } /*! * \brief Scales the angle by a scalar * \return A reference to the angle * * \param scalar Multiplier */ template constexpr Angle& Angle::operator*=(T scalar) { value *= scalar; return *this; } /*! * \brief Divides the angle by a scalar * \return A reference to the angle * * \param divider Divider */ template constexpr Angle& Angle::operator/=(T divider) { value /= divider; return *this; } /*! * \brief Compares the angle to another for equality * \return True if both angles are equal * * \param other The other angle to compare to */ template constexpr bool Angle::operator==(const Angle& other) const { return NumberEquals(value, other.value, Detail::AngleUtils::template GetEpsilon()); } /*! * \brief Compares the angle to another for inequality * \return True if both angles are equal * * \param other The other angle to compare to */ template constexpr bool Angle::operator!=(const Angle& other) const { return !NumberEquals(value, other.value, Detail::AngleUtils::template GetEpsilon()); } /*! * \brief Builds an Angle instance using a degree angle, converting if needed * \return An angle describing the degree angle as Unit * * \param ang Degree angle */ template constexpr Angle Angle::FromDegrees(T ang) { return Angle(Detail::AngleUtils::FromDegrees(ang)); } /*! * \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 constexpr Angle Angle::FromRadians(T ang) { return Angle(Detail::AngleUtils::FromRadians(ang)); } /*! * \brief Returns an angle with an angle of zero * \return Zero angle */ template constexpr Angle Angle::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 bool Serialize(SerializationContext& context, const Angle& angle, TypeTag>) { if (!Serialize(context, angle.value)) return false; return true; } /*! * \brief Unserializes an Angle * \return true if successfully unserialized * * \param context Serialization context * \param angle Output Angle */ template bool Unserialize(SerializationContext& context, Angle* angle, TypeTag>) { if (!Unserialize(context, &angle->value)) return false; return true; } } /*! * \brief Multiplication operator * \return An angle corresponding to scale * angle * * \param scale Multiplier * \param angle Angle */ template Nz::Angle operator*(T scale, const Nz::Angle& angle) { return Nz::Angle(scale * angle.value); } /*! * \brief Division operator * \return An angle corresponding to scale / angle * * \param scale Divisor * \param angle Angle */ template Nz::Angle operator/(T scale, const Nz::Angle& angle) { return Nz::Angle(scale / angle.value); } /*! * \brief Output operator * \return The stream * * \param out The stream * \param box The box to output */ template std::ostream& operator<<(std::ostream& out, const Nz::Angle& angle) { return Nz::Detail::AngleUtils::ToString(out, angle.value); } #include