// Copyright (C) 2024 Jérôme "SirLynix" 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 AngleConversion; template struct AngleConversion { template static constexpr T Convert(T angle) { return angle; } }; template<> struct AngleConversion { template static constexpr T Convert(T angle) { return DegreeToRadian(angle); } }; template<> struct AngleConversion { template static constexpr T Convert(T angle) { return angle / T(360); } }; template<> struct AngleConversion { template static constexpr T Convert(T angle) { return RadianToDegree(angle); } }; template<> struct AngleConversion { template static constexpr T Convert(T angle) { return angle / (T(2) * Pi); } }; template<> struct AngleConversion { template static constexpr T Convert(T angle) { return angle * T(360); } }; template<> struct AngleConversion { template static constexpr T Convert(T angle) { return angle * T(2) * Pi; } }; template struct AngleUtils; template<> struct AngleUtils { template static constexpr T GetEpsilon() { return T(1e-4); } template static constexpr T GetLimit() { return 360; } 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 T(2) * Pi; } template static std::ostream& ToString(std::ostream& out, T value) { return out << "Angle(" << value << "rad)"; } }; template<> struct AngleUtils { template static constexpr T GetEpsilon() { return T(1e-5); } template static constexpr T GetLimit() { return 1; } template static std::ostream& ToString(std::ostream& out, T value) { return out << "Angle(" << value << "turn)"; } }; #ifdef NAZARA_PLATFORM_LINUX template void SinCos(T x, T* sin, T* cos) { double s, c; ::sincos(x, &s, &c); *sin = static_cast(s); *cos = static_cast(c); } template<> inline void SinCos(float x, float* s, float* c) { ::sincosf(x, s, c); } template<> inline void SinCos(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 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) { } template template constexpr Angle::Angle(const Angle& angle) : value(static_cast(angle.value)) { } /*! * \brief Constructs an Angle object from a angle in a specific unit, converting if required * * \param value Angle object to copy */ template template constexpr Angle::Angle(const Angle& angle) : value(Detail::AngleConversion::Convert(angle.value)) { } template constexpr bool Angle::ApproxEqual(const Angle& angle) const { return ApproxEqual(angle, Detail::AngleUtils::template GetEpsilon()); } template constexpr bool Angle::ApproxEqual(const Angle& angle, T maxDifference) const { return NumberEquals(value, angle.value, maxDifference); } /*! * \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 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] * For turn angles, local limits are [-1, 1] */ template constexpr Angle& Angle::Normalize() { constexpr T limit = Detail::AngleUtils::template GetLimit(); constexpr T halfLimit = limit / T(2); value = Nz::Mod(value + halfLimit, limit); if (value < T(0)) value += limit; value -= halfLimit; return *this; } /*! * \brief Returns the ToUnit angle that is equivalent to this one * \return Equivalent ToUnit angle value */ template template T Angle::To() const { return Detail::AngleConversion::Convert(value); } /*! * \brief Returns the ToUnit angle that is equivalent to this one * \return Equivalent ToUnit angle */ template template Angle Angle::ToAngle() const { return Angle(To()); } /*! * \brief Returns the degree angle that is equivalent to this one * \return Equivalent degree angle value */ template constexpr T Angle::ToDegrees() const { return To(); } /*! * \brief Returns the degree angle that is equivalent to this one * \return Equivalent degree angle */ template constexpr Angle Angle::ToDegreeAngle() const { return ToAngle(); } /*! * \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 To(); } /*! * \brief Returns the radian angle that is equivalent to this angle * \return Equivalent radian angle */ template constexpr Angle Angle::ToRadianAngle() const { return ToAngle(); } /*! * \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 turn angle that is equivalent to this angle * \return Equivalent turn angle value */ template constexpr T Angle::ToTurns() const { return To(value); } /*! * \brief Returns the turn angle that is equivalent to this angle * \return Equivalent turn angle */ template constexpr Angle Angle::ToTurnAngle() const { return ToAngle(); } /*! * \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 Helps to represent the sign of the angle * \return A constant reference to this angle */ template constexpr Angle Angle::operator+() const { return *this; } /*! * \brief Negates the angle * \return An angle with a negated value */ template constexpr Angle Angle::operator-() const { return Angle(-value); } /*! * \brief Addition operator * \return Adds two angles together * * \param other Angle to add */ template constexpr Angle Angle::operator+(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-(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+=(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-=(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==(Angle other) const { return value == other.value; } /*! * \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!=(Angle other) const { return value != other.value; } template constexpr bool Angle::operator<(Angle other) const { return value < other.value; } template constexpr bool Angle::operator<=(Angle other) const { return value <= other.value; } template constexpr bool Angle::operator>(Angle other) const { return value > other.value; } template constexpr bool Angle::operator>=(Angle other) const { return value >= other.value; } template constexpr bool Angle::ApproxEqual(const Angle& lhs, const Angle& rhs) { return lhs.ApproxEqual(rhs); } template constexpr bool Angle::ApproxEqual(const Angle& lhs, const Angle& rhs, T maxDifference) { return lhs.ApproxEqual(rhs, maxDifference); } template constexpr Angle Angle::Clamp(Angle angle, Angle min, Angle max) { return Angle(std::clamp(angle.value, min.value, max.value)); } /*! * \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 template constexpr Angle Angle::From(T value) { return Angle(Detail::AngleConversion::Convert(value)); } /*! * \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 degrees) { return From(degrees); } /*! * \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 radians) { return From(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 constexpr Angle Angle::FromTurns(T turns) { return From(turns); } /*! * \brief Returns an angle with an angle of zero * \return Zero angle */ template constexpr Angle Angle::Zero() { return Angle(0); } /*! * \brief Multiplication operator * \return An angle corresponding to scale * angle * * \param scale Multiplier * \param angle Angle */ template Angle operator*(T scale, Angle angle) { return Angle(scale * angle.value); } /*! * \brief Division operator * \return An angle corresponding to scale / angle * * \param scale Divisor * \param angle Angle */ template Angle operator/(T scale, Angle angle) { return 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, Angle angle) { return Detail::AngleUtils::ToString(out, angle.value); } /*! * \ingroup math * \brief Clamps an angle value between min and max and returns the expected value * \return If value is not in the interval of min..max, value obtained is the nearest limit of this interval * * \param value Value to clamp * \param min Minimum of the interval * \param max Maximum of the interval */ template constexpr Angle Clamp(Angle value, T min, T max) { return std::max(std::min(value.value, max), min); } /*! * \brief Serializes an Angle * \return true if successfully serialized * * \param context Serialization context * \param angle Input Angle */ template bool Serialize(SerializationContext& context, 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; } } #include