From 58beb363ea523049955c207d15d4406a4ea8c6e5 Mon Sep 17 00:00:00 2001 From: Pascal Serrarens Date: Fri, 27 Dec 2024 23:10:53 +0100 Subject: [PATCH] Completed AngleOf documentation --- Angle.cpp | 209 +++++++++++++++++++++------------------ Angle.h | 188 +++++++++++++++++++++++++++++------ FloatSingle.cpp | 2 + test/Angle16_test.cc | 10 +- test/Angle8_test.cc | 10 +- test/AngleSingle_test.cc | 4 +- test/FloatSingle_test.cc | 4 +- 7 files changed, 288 insertions(+), 139 deletions(-) diff --git a/Angle.cpp b/Angle.cpp index 6f27334..c88afdc 100644 --- a/Angle.cpp +++ b/Angle.cpp @@ -25,36 +25,34 @@ float Angle::Normalize(float angle) { template AngleOf::AngleOf() : value(0) {} -// template AngleOf::AngleOf(T angle) : value(angle) {} +template const AngleOf AngleOf::zero = AngleOf(); +// template +// const AngleOf AngleOf::deg90 = AngleOf::Degrees(90); +// template +// const AngleOf AngleOf::deg180 = AngleOf::Degrees(180); //===== AngleSingle, AngleOf -template <> AngleOf AngleOf::Degrees(float angle) { - if (isfinite(angle)) { - while (angle < -180) - angle += 360; - while (angle >= 180) - angle -= 360; +template <> AngleOf AngleOf::Degrees(float degrees) { + if (isfinite(degrees)) { + while (degrees < -180) + degrees += 360; + while (degrees >= 180) + degrees -= 360; } - return AngleOf(angle); + return Binary(degrees); } -template <> AngleOf AngleOf::Radians(float angle) { - if (isfinite(angle)) { - while (angle <= -pi) - angle += 2 * pi; - while (angle > pi) - angle -= 2 * pi; +template <> AngleOf AngleOf::Radians(float radians) { + if (isfinite(radians)) { + while (radians <= -pi) + radians += 2 * pi; + while (radians > pi) + radians -= 2 * pi; } - return AngleOf(angle * Rad2Deg); -} - -template AngleOf AngleOf::Binary(T x) { - AngleOf angle = AngleOf(); - angle.value = x; - return angle; + return Binary(radians * Rad2Deg); } template <> float AngleOf::InDegrees() const { return this->value; } @@ -65,19 +63,21 @@ template <> float AngleOf::InRadians() const { //===== Angle16, AngleOf -template <> AngleOf AngleOf::Degrees(float angle) { +template <> +AngleOf AngleOf::Degrees(float degrees) { // map float [-180..180) to integer [-32768..32767] - signed short value = (signed short)roundf(angle / 360.0F * 65536.0F); - return AngleOf(value); + signed short value = (signed short)roundf(degrees / 360.0F * 65536.0F); + return Binary(value); } -template <> AngleOf AngleOf::Radians(float angle) { - if (!isfinite(angle)) - return AngleOf(0); +template <> +AngleOf AngleOf::Radians(float radians) { + if (!isfinite(radians)) + return AngleOf::zero; // map float [-PI..PI) to integer [-32768..32767] - signed short value = (signed short)roundf(angle / pi * 32768.0F); - return AngleOf(value); + signed short value = (signed short)roundf(radians / pi * 32768.0F); + return Binary(value); } template <> float AngleOf::InDegrees() const { @@ -92,19 +92,19 @@ template <> float AngleOf::InRadians() const { //===== Angle8, AngleOf -template <> AngleOf AngleOf::Degrees(float angle) { +template <> AngleOf AngleOf::Degrees(float degrees) { // map float [-180..180) to integer [-128..127) - signed char value = (signed char)roundf(angle / 360.0F * 256.0F); - return AngleOf(value); + signed char value = (signed char)roundf(degrees / 360.0F * 256.0F); + return Binary(value); } -template <> AngleOf AngleOf::Radians(float angle) { - if (!isfinite(angle)) - return AngleOf(0); +template <> AngleOf AngleOf::Radians(float radians) { + if (!isfinite(radians)) + return AngleOf::zero; // map float [-pi..pi) to integer [-128..127) - signed char value = (signed char)roundf(angle / pi * 128.0f); - return AngleOf(value); + signed char value = (signed char)roundf(radians / pi * 128.0f); + return Binary(value); } template <> float AngleOf::InDegrees() const { @@ -119,90 +119,109 @@ template <> float AngleOf::InRadians() const { //===== Generic -// template -// const AngleOf AngleOf::zero = AngleOf(); -// template -// const AngleOf AngleOf::deg90 = AngleOf::Degrees(90); -// template -// const AngleOf AngleOf::deg180 = AngleOf::Degrees(180); - -template bool AngleOf::operator==(const AngleOf a) const { - return this->value == a.value; +template AngleOf AngleOf::Binary(T rawValue) { + AngleOf angle = AngleOf(); + angle.SetBinary(rawValue); + return angle; } -template bool AngleOf::operator>(AngleOf a) const { - return this->value > a.value; -} - -template bool AngleOf::operator>=(AngleOf a) const { - return this->value >= a.value; -} - -template bool AngleOf::operator<(AngleOf a) const { - return this->value < a.value; -} - -template bool AngleOf::operator<=(AngleOf a) const { - return this->value <= a.value; +template T AngleOf::GetBinary() const { return this->value; } +template void AngleOf::SetBinary(T rawValue) { + this->value = rawValue; } template -signed int Passer::LinearAlgebra::AngleOf::Sign(AngleOf a) { - if (a.value < 0) +bool AngleOf::operator==(const AngleOf angle) const { + return this->value == angle.value; +} + +template bool AngleOf::operator>(AngleOf angle) const { + return this->value > angle.value; +} + +template bool AngleOf::operator>=(AngleOf angle) const { + return this->value >= angle.value; +} + +template bool AngleOf::operator<(AngleOf angle) const { + return this->value < angle.value; +} + +template bool AngleOf::operator<=(AngleOf angle) const { + return this->value <= angle.value; +} + +template +signed int Passer::LinearAlgebra::AngleOf::Sign(AngleOf angle) { + if (angle.value < 0) return -1; - if (a.value > 0) + if (angle.value > 0) return 1; return 0; } template -AngleOf Passer::LinearAlgebra::AngleOf::Abs(AngleOf a) { - if (Sign(a) < 0) - return -a; +AngleOf Passer::LinearAlgebra::AngleOf::Abs(AngleOf angle) { + if (Sign(angle) < 0) + return -angle; else - return a; + return angle; } template AngleOf AngleOf::operator-() const { - AngleOf angle = AngleOf(-this->value); + AngleOf angle = Binary(-this->value); return angle; } template <> -AngleOf AngleOf::operator-(const AngleOf &a) const { - AngleOf angle = AngleOf(this->value - a.value); - angle = Normalize(angle); - return angle; +AngleOf AngleOf::operator-(const AngleOf &angle) const { + AngleOf r = Binary(this->value - angle.value); + r = Normalize(r); + return r; } template -AngleOf AngleOf::operator-(const AngleOf &a) const { - AngleOf angle = AngleOf(this->value - a.value); - return angle; +AngleOf AngleOf::operator-(const AngleOf &angle) const { + AngleOf r = Binary(this->value - angle.value); + return r; } template <> -AngleOf AngleOf::operator+(const AngleOf &a) const { - AngleOf angle = AngleOf(this->value + a.value); - angle = Normalize(angle); - return angle; +AngleOf AngleOf::operator+(const AngleOf &angle) const { + AngleOf r = Binary(this->value + angle.value); + r = Normalize(r); + return r; } template -AngleOf AngleOf::operator+(const AngleOf &a) const { - AngleOf angle = AngleOf(this->value + a.value); - return angle; +AngleOf AngleOf::operator+(const AngleOf &angle) const { + AngleOf r = Binary(this->value + angle.value); + return r; } -template <> AngleOf AngleOf::operator+=(const AngleOf &a) { - this->value += a.value; +template <> +AngleOf AngleOf::operator+=(const AngleOf &angle) { + this->value += angle.value; this->Normalize(); return *this; } -template AngleOf AngleOf::operator+=(const AngleOf &a) { - this->value += a.value; +template +AngleOf AngleOf::operator+=(const AngleOf &angle) { + this->value += angle.value; return *this; } +// This defintion is not matching the declaration in the header file somehow +// template +// AngleOf operator*(const AngleOf &angle, float factor) { +// return AngleOf::Degrees((float)angle.InDegrees() * factor); +// } + +// This defintion is not matching the declaration in the header file somehow +// template +// AngleOf operator*(float factor, const AngleOf &angle) { +// return AngleOf::Degrees((float)factor * angle.InDegrees()); +// } + template void AngleOf::Normalize() { float angleValue = this->InDegrees(); if (!isfinite(angleValue)) @@ -246,14 +265,14 @@ AngleOf AngleOf::MoveTowards(AngleOf fromAngle, AngleOf toAngle, return fromAngle + d; } -template float AngleOf::Cos(AngleOf a) { - return cosf(a.InRadians()); +template float AngleOf::Cos(AngleOf angle) { + return cosf(angle.InRadians()); } -template float AngleOf::Sin(AngleOf a) { - return sinf(a.InRadians()); +template float AngleOf::Sin(AngleOf angle) { + return sinf(angle.InRadians()); } -template float AngleOf::Tan(AngleOf a) { - return tanf(a.InRadians()); +template float AngleOf::Tan(AngleOf angle) { + return tanf(angle.InRadians()); } template AngleOf AngleOf::Acos(float f) { @@ -267,8 +286,8 @@ template AngleOf AngleOf::Atan(float f) { } template -AngleOf Passer::LinearAlgebra::AngleOf::Atan2(float f1, float f2) { - return AngleOf::Radians(atan2f(f1, f2)); +AngleOf Passer::LinearAlgebra::AngleOf::Atan2(float y, float x) { + return AngleOf::Radians(atan2f(y, x)); } // template <> @@ -353,4 +372,4 @@ AngleOf AngleOf::SineRuleAngle(float a, AngleOf beta, float b) { template class AngleOf; template class AngleOf; -template class AngleOf; +template class AngleOf; \ No newline at end of file diff --git a/Angle.h b/Angle.h index cd37350..1e4b445 100644 --- a/Angle.h +++ b/Angle.h @@ -13,77 +13,205 @@ static float pi = 3.1415927410125732421875F; static float Rad2Deg = 360.0f / (pi * 2); static float Deg2Rad = (pi * 2) / 360.0f; +/// @brief An angle in various representations. +/// @tparam T The internal type used for the representation of the angle. +/// The angle is internally limited to <-180..180] degrees or <-PI...PI] +/// radians. When an angle exceeds this range, it is normalized to a value +/// within the range. template class AngleOf { public: + /// @brief Create a new angle with a zero value AngleOf(); - static AngleOf Degrees(float f); - static AngleOf Radians(float f); - static AngleOf Binary(T x); - - // const static AngleOf zero; + /// @brief An zero value angle + const static AngleOf zero; // const static AngleOf deg90; // const static AngleOf deg180; + /// @brief Creates an angle in degrees + /// @param degrees the angle in degrees + /// @return The angle value + static AngleOf Degrees(float degrees); + /// @brief Short-hand Deg alias for the Degrees function + constexpr static auto Deg = Degrees; + /// @brief Creates an angle in radians + /// @param radians the angle in radians + /// @return The angle value + static AngleOf Radians(float radians); + /// @brief Short-hand Rad alias for the Radians function + constexpr static auto Rad = Radians; + /// @brief Creates an angle from a raw value + /// @param rawValue the raw value to use for the angle + /// @return The the angle + static AngleOf Binary(T rawValue); + + /// @brief Get the angle value in degrees + /// @return The angle value in degrees float InDegrees() const; + /// @brief Get the angle value in radians + /// @return The angle value in radians float InRadians() const; - inline T GetBinary() const { return this->value; } - inline void SetBinary(T x) { this->value = x; } + /// @brief Get the raw value for the angle + /// @return The raw value + T GetBinary() const; + /// @brief Set the raw value of the angle + /// @param rawValue The raw value + void SetBinary(T rawValue); - bool operator==(const AngleOf a) const; - bool operator>(AngleOf a) const; - bool operator>=(AngleOf a) const; - bool operator<(AngleOf a) const; - bool operator<=(AngleOf a) const; + /// @brief Tests whether this angle is equal to the given angle + /// @param angle The angle to compare to + /// @return True when the angles are equal, False otherwise + /// @note The equality is determine within the limits of precision of the raw + /// type T + bool operator==(const AngleOf angle) const; + /// @brief Tests if this angle is greater than the given angle + /// @param angle The given angle + /// @return True when this angle is greater than the given angle, False + /// otherwise + bool operator>(AngleOf angle) const; + /// @brief Tests if this angle is greater than or equal to the given angle + /// @param angle The given angle + /// @return True when this angle is greater than or equal to the given angle. + /// False otherwise. + bool operator>=(AngleOf angle) const; + /// @brief Tests if this angle is less than the given angle + /// @param angle The given angle + /// @return True when this angle is less than the given angle. False + /// otherwise. + bool operator<(AngleOf angle) const; + /// @brief Tests if this angle is less than or equal to the given angle + /// @param angle The given angle + /// @return True when this angle is less than or equal to the given angle. + /// False otherwise. + bool operator<=(AngleOf angle) const; - static signed int Sign(AngleOf a); - static AngleOf Abs(AngleOf a); + /// @brief Returns the sign of the angle + /// @param angle The angle + /// @return -1 when the angle is negative, 1 when it is positive and 0 + /// otherwise. + static signed int Sign(AngleOf angle); + /// @brief Returns the magnitude of the angle + /// @param angle The angle + /// @return The positive magitude of the angle. + /// Negative values are negated to get a positive result + static AngleOf Abs(AngleOf angle); + /// @brief Negate the angle + /// @return The negated angle AngleOf operator-() const; - AngleOf operator-(const AngleOf &a) const; - AngleOf operator+(const AngleOf &a) const; - AngleOf operator+=(const AngleOf &a); + /// @brief Substract another angle from this angle + /// @param angle The angle to subtract from this angle + /// @return The result of the subtraction + AngleOf operator-(const AngleOf &angle) const; + /// @brief Add another angle from this angle + /// @param angle The angle to add to this angle + /// @return The result of the addition + AngleOf operator+(const AngleOf &angle) const; + /// @brief Add another angle to this angle + /// @param angle The angle to add to this angle + /// @return The result of the addition + AngleOf operator+=(const AngleOf &angle); - friend AngleOf operator*(const AngleOf &a, float f) { - return AngleOf::Degrees((float)a.InDegrees() * f); + /// @brief Mutliplies the angle + /// @param angle The angle to multiply + /// @param factor The factor by which the angle is multiplied + /// @return The multiplied angle + friend AngleOf operator*(const AngleOf &angle, float factor) { + return AngleOf::Degrees((float)angle.InDegrees() * factor); } - friend AngleOf operator*(float f, const AngleOf &a) { - return AngleOf::Degrees((float)f * a.InDegrees()); + /// @brief Multiplies the angle + /// @param factor The factor by which the angle is multiplies + /// @param angle The angle to multiply + /// @return The multiplied angle + friend AngleOf operator*(float factor, const AngleOf &angle) { + return AngleOf::Degrees((float)factor * angle.InDegrees()); } + /// @brief Normalizes the angle to (-180..180] or (-PI..PI] + /// Should not be needed but available in case it is. void Normalize(); + /// @brief Normalizes the angle to (-180..180] or (-PI..PI] + /// @param angle The angle to normalize + /// @return The normalized angle; static AngleOf Normalize(AngleOf angle); - static AngleOf Clamp(AngleOf a, AngleOf min, AngleOf max); + /// @brief Clamps the angle value between the two given angles + /// @param angle The angle to clamp + /// @param min The minimum angle + /// @param max The maximum angle + /// @return The clamped value + /// @remark When the min value is greater than the max value, angle is + /// returned unclamped. + static AngleOf Clamp(AngleOf angle, AngleOf min, AngleOf max); // static AngleOf Difference(AngleOf a, AngleOf b) { // AngleOf r = Normalize(b.InDegrees() - a.InDegrees()); // return r; // }; + + /// @brief Rotates an angle towards another angle with a max distance + /// @param fromAngle The angle to start from + /// @param toAngle The angle to rotate towards + /// @param maxAngle The maximum angle to rotate + /// @return The rotated angle static AngleOf MoveTowards(AngleOf fromAngle, AngleOf toAngle, float maxAngle); - static float Cos(AngleOf a); - static float Sin(AngleOf a); - static float Tan(AngleOf a); + /// @brief Calculates the cosine of an angle + /// @param angle The given angle + /// @return The cosine of the angle + static float Cos(AngleOf angle); + /// @brief Calculates the sine of an angle + /// @param angle The given angle + /// @return The sine of the angle + static float Sin(AngleOf angle); + /// @brief Calculates the tangent of an angle + /// @param angle The given angle + /// @return The tangent of the angle + static float Tan(AngleOf angle); + /// @brief Calculates the arc cosine angle + /// @param f The value + /// @return The arc cosine for the given value static AngleOf Acos(float f); + /// @brief Calculates the arc sine angle + /// @param f The value + /// @return The arc sine for the given value static AngleOf Asin(float f); + /// @brief Calculates the arc tangent angle + /// @param f The value + /// @return The arc tangent for the given value static AngleOf Atan(float f); - static AngleOf Atan2(float f1, float f2); + /// @brief Calculates the tangent for the given values + /// @param y The vertical value + /// @param x The horizontal value + /// @return The tanget for the given values + /// Uses the y and x signs to compute the quadrant + static AngleOf Atan2(float y, float x); + /// @brief Computes the length of a side using the rule of cosines + /// @param a The length of side A + /// @param b The length of side B + /// @param gamma The angle of the corner opposing side C + /// @return The length of side C static float CosineRuleSide(float a, float b, AngleOf gamma); + /// @brief Computes the angle of a corner using the rule of cosines + /// @param a The length of side A + /// @param b The length of side B + /// @param c The length of side C + /// @return The angle of the corner opposing side C static AngleOf CosineRuleAngle(float a, float b, float c); + /// @brief Computes the angle of a corner using the rule of sines + /// @param a The length of side A + /// @param beta the angle of the corner opposing side B + /// @param c The length of side C + /// @return The angle of the corner opposing side A static AngleOf SineRuleAngle(float a, AngleOf beta, float c); private: T value; AngleOf(T value); - // These are deprecated, will move to private. - // Use Degrees/Radians instead - // AngleOf(signed int f); - // AngleOf(float f); }; using Angle = AngleOf; diff --git a/FloatSingle.cpp b/FloatSingle.cpp index a8c5db1..902d85e 100644 --- a/FloatSingle.cpp +++ b/FloatSingle.cpp @@ -9,6 +9,8 @@ const float Float::epsilon = 1e-05f; const float Float::sqrEpsilon = 1e-10f; float Float::Clamp(float f, float min, float max) { + if (max < min) + return f; if (f < min) return min; if (f > max) diff --git a/test/Angle16_test.cc b/test/Angle16_test.cc index 3f0fec2..16d8451 100644 --- a/test/Angle16_test.cc +++ b/test/Angle16_test.cc @@ -1,8 +1,8 @@ #if GTEST #include -#include #include +#include #include "Angle.h" @@ -86,7 +86,7 @@ TEST(Angle16, Normalize) { r = Angle16::Normalize(Angle16::Degrees(0)); EXPECT_FLOAT_EQ(r.InDegrees(), 0) << "Normalize 0"; - if (false) { // std::numeric_limits::is_iec559) { + if (false) { // std::numeric_limits::is_iec559) { // Infinites are not supported r = Angle16::Normalize(Angle16::Degrees(FLOAT_INFINITY)); EXPECT_FLOAT_EQ(r.InDegrees(), FLOAT_INFINITY) << "Normalize INFINITY"; @@ -123,9 +123,9 @@ TEST(Angle16, Clamp) { r = Angle16::Clamp(Angle16::Degrees(0), Angle16::Degrees(10), Angle16::Degrees(-10)); - EXPECT_NEAR(r.InDegrees(), 10, 1.0e-2) << "Clamp 0 10 -10"; + EXPECT_FLOAT_EQ(r.InDegrees(), 0) << "Clamp 0 10 -10"; - if (false) { // std::numeric_limits::is_iec559) { + if (false) { // std::numeric_limits::is_iec559) { // Infinites are not supported r = Angle16::Clamp(Angle16::Degrees(10), Angle16::Degrees(0), Angle16::Degrees(FLOAT_INFINITY)); @@ -216,7 +216,7 @@ TEST(Angle16, MoveTowards) { r = Angle16::MoveTowards(Angle16::Degrees(0), Angle16::Degrees(0), 30); EXPECT_FLOAT_EQ(r.InDegrees(), 0) << "MoveTowards 0 0 30"; - if (false) { // std::numeric_limits::is_iec559) { + if (false) { // std::numeric_limits::is_iec559) { // infinites are not supported r = Angle16::MoveTowards(Angle16::Degrees(0), Angle16::Degrees(90), FLOAT_INFINITY); diff --git a/test/Angle8_test.cc b/test/Angle8_test.cc index bb220c5..158694f 100644 --- a/test/Angle8_test.cc +++ b/test/Angle8_test.cc @@ -1,8 +1,8 @@ #if GTEST #include -#include #include +#include #include "Angle.h" @@ -86,7 +86,7 @@ TEST(Angle8, Normalize) { r = Angle8::Normalize(Angle8::Degrees(0)); EXPECT_FLOAT_EQ(r.InDegrees(), 0) << "Normalize 0"; - if (false) { // std::numeric_limits::is_iec559) { + if (false) { // std::numeric_limits::is_iec559) { // Infinites are not supported r = Angle8::Normalize(Angle8::Degrees(FLOAT_INFINITY)); EXPECT_FLOAT_EQ(r.InDegrees(), FLOAT_INFINITY) << "Normalize INFINITY"; @@ -122,9 +122,9 @@ TEST(Angle8, Clamp) { r = Angle8::Clamp(Angle8::Degrees(0), Angle8::Degrees(10), Angle8::Degrees(-10)); - EXPECT_NEAR(r.InDegrees(), 10, 1.0e-0) << "Clamp 0 10 -10"; + EXPECT_FLOAT_EQ(r.InDegrees(), 0) << "Clamp 0 10 -10"; - if (false) { // std::numeric_limits::is_iec559) { + if (false) { // std::numeric_limits::is_iec559) { // Infinites are not supported r = Angle8::Clamp(Angle8::Degrees(10), Angle8::Degrees(0), Angle8::Degrees(FLOAT_INFINITY)); @@ -215,7 +215,7 @@ TEST(Angle8, MoveTowards) { r = Angle8::MoveTowards(Angle8::Degrees(0), Angle8::Degrees(0), 30); EXPECT_FLOAT_EQ(r.InDegrees(), 0) << "MoveTowards 0 0 30"; - if (false) { // std::numeric_limits::is_iec559) { + if (false) { // std::numeric_limits::is_iec559) { // infinites are not supported r = Angle8::MoveTowards(Angle8::Degrees(0), Angle8::Degrees(90), FLOAT_INFINITY); diff --git a/test/AngleSingle_test.cc b/test/AngleSingle_test.cc index cc0774e..9537ffb 100644 --- a/test/AngleSingle_test.cc +++ b/test/AngleSingle_test.cc @@ -1,8 +1,8 @@ #if GTEST #include -#include #include +#include #include "Angle.h" @@ -120,7 +120,7 @@ TEST(AngleSingle, Clamp) { r = AngleSingle::Clamp(AngleSingle::Degrees(0), AngleSingle::Degrees(1), AngleSingle::Degrees(-1)); - EXPECT_FLOAT_EQ(r.InDegrees(), 1) << "Clamp 0 1 -1"; + EXPECT_FLOAT_EQ(r.InDegrees(), 0) << "Clamp 0 1 -1"; if (std::numeric_limits::is_iec559) { r = AngleSingle::Clamp(AngleSingle::Degrees(1), AngleSingle::Degrees(0), diff --git a/test/FloatSingle_test.cc b/test/FloatSingle_test.cc index 58e9d6d..9673ade 100644 --- a/test/FloatSingle_test.cc +++ b/test/FloatSingle_test.cc @@ -1,8 +1,8 @@ #if GTEST #include -#include #include +#include #include "FloatSingle.h" @@ -27,7 +27,7 @@ TEST(FloatC, Clamp) { EXPECT_FLOAT_EQ(r, 0) << "Clamp 0 0 0"; r = Float::Clamp(0, 1, -1); - EXPECT_FLOAT_EQ(r, 1) << "Clamp 0 1 -1"; + EXPECT_FLOAT_EQ(r, 0) << "Clamp 0 1 -1"; if (std::numeric_limits::is_iec559) { r = Float::Clamp(1, 0, FLOAT_INFINITY);