diff --git a/CMakeLists.txt b/CMakeLists.txt index 7968141..2d0adfa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,12 +15,14 @@ set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest) include_directories(include) -add_library(VectorAlgebra STATIC "src/Vector2.cpp" "src/Vector3.cpp" "src/Quaternion.cpp" ) +add_library(VectorAlgebra STATIC "src/FloatSingle.cpp" "src/Angle.cpp" "src/Vector2.cpp" "src/Vector3.cpp" "src/Quaternion.cpp" ) enable_testing() add_executable( VectorAlgebraTest + "test/Angle_test.cc" + "test/FloatSingle_test.cc" "test/Vector2_test.cc" "test/Vector3_test.cc" "test/Quaternion_test.cc" diff --git a/include/Angle.h b/include/Angle.h new file mode 100644 index 0000000..29f84f1 --- /dev/null +++ b/include/Angle.h @@ -0,0 +1,16 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0.If a copy of the MPL was not distributed with this +// file, You can obtain one at https ://mozilla.org/MPL/2.0/. + +#ifndef ANGLE_H +#define ANGLE_H + +class Angle { +public: + static float Normalize(float angle); + static float Clamp(float angle, float min, float max); + static float Difference(float a, float b); + static float MoveTowards(float fromAngle, float toAngle, float maxAngle); +}; + +#endif \ No newline at end of file diff --git a/include/FloatSingle.h b/include/FloatSingle.h new file mode 100644 index 0000000..7aa2251 --- /dev/null +++ b/include/FloatSingle.h @@ -0,0 +1,15 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0.If a copy of the MPL was not distributed with this +// file, You can obtain one at https ://mozilla.org/MPL/2.0/. + +#ifndef FLOAT_H +#define FLOAT_H + +class Float { +public: + static const float epsilon; + static const float sqrEpsilon; + + static float Clamp(float f, float min, float max); +}; +#endif diff --git a/src/Angle.cpp b/src/Angle.cpp new file mode 100644 index 0000000..e2747d5 --- /dev/null +++ b/src/Angle.cpp @@ -0,0 +1,34 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0.If a copy of the MPL was not distributed with this +// file, You can obtain one at https ://mozilla.org/MPL/2.0/. + +#include +#include "Angle.h" +#include "FloatSingle.h" + +float Angle::Normalize(float angle) { + if (!isfinite(angle)) + return angle; + + while (angle <= -180) angle += 360; + while (angle > 180) angle -= 360; + return angle; +} + +float Angle::Clamp(float angle, float min, float max) { + float normalizedAngle = Normalize(angle); + float r = Float::Clamp(normalizedAngle, min, max); + return r; +} + +float Angle::Difference(float a, float b) { + float r = Normalize(b - a); + return r; +} + +float Angle::MoveTowards(float fromAngle, float toAngle, float maxAngle) { + float d = toAngle - fromAngle; + float sign = signbit(d) ? -1 : 1; + d = sign * Float::Clamp(fabs(d), 0, maxAngle); + return d; +} diff --git a/src/FloatSingle.cpp b/src/FloatSingle.cpp new file mode 100644 index 0000000..fb6f301 --- /dev/null +++ b/src/FloatSingle.cpp @@ -0,0 +1,17 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0.If a copy of the MPL was not distributed with this +// file, You can obtain one at https ://mozilla.org/MPL/2.0/. + +#include +#include "FloatSingle.h" + +const float Float::epsilon = 1e-05f; +const float Float::sqrEpsilon = 1e-10f; + +float Float::Clamp(float f, float min, float max) { + if (f < min) + return min; + if (f > max) + return max; + return f; +} \ No newline at end of file diff --git a/test/Angle_test.cc b/test/Angle_test.cc new file mode 100644 index 0000000..77ec3b6 --- /dev/null +++ b/test/Angle_test.cc @@ -0,0 +1,167 @@ +#if GTEST +#include + +#include +#include + +#include "Angle.h" + +#define FLOAT_INFINITY std::numeric_limits::infinity() + +TEST(Angle, Normalize) { + float r = 0; + + r = Angle::Normalize(90); + EXPECT_FLOAT_EQ(r, 90) << "Normalize 90"; + + r = Angle::Normalize(-90); + EXPECT_FLOAT_EQ(r, -90) << "Normalize -90"; + + r = Angle::Normalize(270); + EXPECT_FLOAT_EQ(r, -90) << "Normalize 270"; + + r = Angle::Normalize(270+360); + EXPECT_FLOAT_EQ(r, -90) << "Normalize 270+360"; + + r = Angle::Normalize(-270); + EXPECT_FLOAT_EQ(r, 90) << "Normalize -270"; + + r = Angle::Normalize(-270 - 360); + EXPECT_FLOAT_EQ(r, 90) << "Normalize -270-360"; + + r = Angle::Normalize(0); + EXPECT_FLOAT_EQ(r, 0) << "Normalize 0"; + + if (std::numeric_limits::is_iec559) { + r = Angle::Normalize(FLOAT_INFINITY); + EXPECT_FLOAT_EQ(r, FLOAT_INFINITY) << "Normalize INFINITY"; + + r = Angle::Normalize(-FLOAT_INFINITY); + EXPECT_FLOAT_EQ(r, -FLOAT_INFINITY) << "Normalize INFINITY"; + } +} + +TEST(Angle, Clamp) { + float r = 0; + + r = Angle::Clamp(1, 0, 2); + EXPECT_FLOAT_EQ(r, 1) << "Clamp 1 0 2"; + + r = Angle::Clamp(-1, 0, 2); + EXPECT_FLOAT_EQ(r, 0) << "Clamp -1 0 2"; + + r = Angle::Clamp(3, 0, 2); + EXPECT_FLOAT_EQ(r, 2) << "Clamp 3 0 2"; + + r = Angle::Clamp(1, 0, 0); + EXPECT_FLOAT_EQ(r, 0) << "Clamp 1 0 0"; + + r = Angle::Clamp(0, 0, 0); + EXPECT_FLOAT_EQ(r, 0) << "Clamp 0 0 0"; + + r = Angle::Clamp(0, 1, -1); + EXPECT_FLOAT_EQ(r, 1) << "Clamp 0 1 -1"; + + if (std::numeric_limits::is_iec559) { + r = Angle::Clamp(1, 0, FLOAT_INFINITY); + EXPECT_FLOAT_EQ(r, 1) << "Clamp 1 0 INFINITY"; + + r = Angle::Clamp(1, -FLOAT_INFINITY, 1); + EXPECT_FLOAT_EQ(r, 1) << "Clamp 1 -INFINITY 1"; + } +} + +TEST(Angle, Difference) { + float r = 0; + + r = Angle::Difference(0, 90); + EXPECT_FLOAT_EQ(r, 90) << "Difference 0 90"; + + r = Angle::Difference(0, -90); + EXPECT_FLOAT_EQ(r, -90) << "Difference 0 -90"; + + r = Angle::Difference(0, 270); + EXPECT_FLOAT_EQ(r, -90) << "Difference 0 270"; + + r = Angle::Difference(0, -270); + EXPECT_FLOAT_EQ(r, 90) << "Difference 0 -270"; + + r = Angle::Difference(90, 0); + EXPECT_FLOAT_EQ(r, -90) << "Difference 90 0"; + + r = Angle::Difference(-90, 0); + EXPECT_FLOAT_EQ(r, 90) << "Difference -90 0"; + + r = Angle::Difference(0, 0); + EXPECT_FLOAT_EQ(r, 0) << "Difference 0 0"; + + r = Angle::Difference(90, 90); + EXPECT_FLOAT_EQ(r, 0) << "Difference 90 90"; + + if (std::numeric_limits::is_iec559) { + r = Angle::Difference(0, INFINITY); + EXPECT_FLOAT_EQ(r, INFINITY) << "Difference 0 INFINITY"; + + r = Angle::Difference(0, -INFINITY); + EXPECT_FLOAT_EQ(r, -INFINITY) << "Difference 0 -INFINITY"; + + r = Angle::Difference(-INFINITY, INFINITY); + EXPECT_FLOAT_EQ(r, INFINITY) << "Difference -INFINITY INFINITY"; + } +} + +TEST(Angle, MoveTowards) { + float r = 0; + + r = Angle::MoveTowards(0, 90, 30); + EXPECT_FLOAT_EQ(r, 30) << "MoveTowards 0 90 30"; + + r = Angle::MoveTowards(0, 90, 90); + EXPECT_FLOAT_EQ(r, 90) << "MoveTowards 0 90 90"; + + r = Angle::MoveTowards(0, 90, 180); + EXPECT_FLOAT_EQ(r, 90) << "MoveTowards 0 90 180"; + + r = Angle::MoveTowards(0, 90, 270); + EXPECT_FLOAT_EQ(r, 90) << "MoveTowards 0 90 270"; + + r = Angle::MoveTowards(0, 90, -30); + EXPECT_FLOAT_EQ(r, -30) << "MoveTowards 0 90 -30"; + + r = Angle::MoveTowards(0, -90, -30); + EXPECT_FLOAT_EQ(r, 30) << "MoveTowards 0 -90 -30"; + + r = Angle::MoveTowards(0, -90, -90); + EXPECT_FLOAT_EQ(r, 90) << "MoveTowards 0 -90 -90"; + + r = Angle::MoveTowards(0, -90, -180); + EXPECT_FLOAT_EQ(r, 180) << "MoveTowards 0 -90 -180"; + + r = Angle::MoveTowards(0, -90, -270); + EXPECT_FLOAT_EQ(r, 270) << "MoveTowards 0 -90 -270"; + + r = Angle::MoveTowards(0, 90, 0); + EXPECT_FLOAT_EQ(r, 0) << "MoveTowards 0 90 0"; + + r = Angle::MoveTowards(0, 0, 0); + EXPECT_FLOAT_EQ(r, 0) << "MoveTowards 0 0 0"; + + r = Angle::MoveTowards(0, 0, 30); + EXPECT_FLOAT_EQ(r, 0) << "MoveTowards 0 0 30"; + + if (std::numeric_limits::is_iec559) { + r = Angle::MoveTowards(0, 90, FLOAT_INFINITY); + EXPECT_FLOAT_EQ(r, 90) << "MoveTowards 0 90 FLOAT_INFINITY"; + + r = Angle::MoveTowards(0, FLOAT_INFINITY, 30); + EXPECT_FLOAT_EQ(r, 30) << "MoveTowards 0 FLOAT_INFINITY 30"; + + r = Angle::MoveTowards(0, -90, -FLOAT_INFINITY); + EXPECT_FLOAT_EQ(r, FLOAT_INFINITY) << "MoveTowards 0 -90 -FLOAT_INFINITY"; + + r = Angle::MoveTowards(0, -FLOAT_INFINITY, -30); + EXPECT_FLOAT_EQ(r, 30) << "MoveTowards 0 -FLOAT_INFINITY -30"; + } +} + +#endif \ No newline at end of file diff --git a/test/FloatSingle_test.cc b/test/FloatSingle_test.cc new file mode 100644 index 0000000..3083985 --- /dev/null +++ b/test/FloatSingle_test.cc @@ -0,0 +1,41 @@ +#if GTEST +#include + +#include +#include + +#include "FloatSingle.h" + +#define FLOAT_INFINITY std::numeric_limits::infinity() + +TEST(FloatC, Clamp) { + float r = 0; + + r = Float::Clamp(1, 0, 2); + EXPECT_FLOAT_EQ(r, 1) << "Clamp 1 0 2"; + + r = Float::Clamp(-1, 0, 2); + EXPECT_FLOAT_EQ(r, 0) << "Clamp -1 0 2"; + + r = Float::Clamp(3, 0, 2); + EXPECT_FLOAT_EQ(r, 2) << "Clamp 3 0 2"; + + r = Float::Clamp(1, 0, 0); + EXPECT_FLOAT_EQ(r, 0) << "Clamp 1 0 0"; + + r = Float::Clamp(0, 0, 0); + EXPECT_FLOAT_EQ(r, 0) << "Clamp 0 0 0"; + + r = Float::Clamp(0, 1, -1); + EXPECT_FLOAT_EQ(r, 1) << "Clamp 0 1 -1"; + + if (std::numeric_limits::is_iec559) { + r = Float::Clamp(1, 0, FLOAT_INFINITY); + EXPECT_FLOAT_EQ(r, 1) << "Clamp 1 0 INFINITY"; + + r = Float::Clamp(1, -FLOAT_INFINITY, 1); + EXPECT_FLOAT_EQ(r, 1) << "Clamp 1 -INFINITY 1"; + } +} + +#endif \ No newline at end of file