Add angle conversion functions

Signed-off-by: Slendi <slendi@socopon.com>
This commit is contained in:
2025-11-14 16:03:17 +02:00
parent df49368e9a
commit 446ab9c679
2 changed files with 90 additions and 0 deletions

View File

@@ -25,8 +25,14 @@
#include <cmath>
#include <cstddef>
#include <format>
#include <numbers>
#include <optional>
#include <type_traits>
#ifndef SMATH_ANGLE_UNIT
#define SMATH_ANGLE_UNIT rad
#endif // SMATH_ANGLE_UNIT
namespace smath {
template <std::size_t N, typename T>
@@ -35,6 +41,38 @@ struct Vec;
namespace detail {
#define SMATH_STR(x) #x
#define SMATH_XSTR(x) SMATH_STR(x)
consteval bool streq(const char *a, const char *b) {
for (;; ++a, ++b) {
if (*a != *b)
return false;
if (*a == '\0')
return true;
}
}
enum class AngularUnit {
Radians,
Degrees,
Turns,
};
consteval std::optional<AngularUnit> parse_unit(const char *s) {
if (streq(s, "rad"))
return AngularUnit::Radians;
if (streq(s, "deg"))
return AngularUnit::Degrees;
if (streq(s, "turns"))
return AngularUnit::Turns;
return std::nullopt;
}
constexpr auto SMATH_ANGLE_UNIT_ID = parse_unit(SMATH_XSTR(SMATH_ANGLE_UNIT));
static_assert(SMATH_ANGLE_UNIT_ID != std::nullopt,
"Invalid SMATH_ANGLE_UNIT. Should be rad, deg, or turns.");
template <std::size_t N> struct FixedString {
char data[N]{};
static constexpr std::size_t size = N - 1;
@@ -401,6 +439,42 @@ using Vec2d = Vec<2, double>;
using Vec3d = Vec<3, double>;
using Vec4d = Vec<4, double>;
template <class T> constexpr auto deg(T const value) -> T {
if constexpr (detail::SMATH_ANGLE_UNIT_ID == detail::AngularUnit::Degrees) {
return value;
} else if constexpr (detail::SMATH_ANGLE_UNIT_ID ==
detail::AngularUnit::Radians) {
return value * static_cast<T>(std::numbers::pi / 180.0);
} else if constexpr (detail::SMATH_ANGLE_UNIT_ID ==
detail::AngularUnit::Turns) {
return value / static_cast<T>(360.0);
}
}
template <class T> constexpr auto rad(T const value) -> T {
if constexpr (detail::SMATH_ANGLE_UNIT_ID == detail::AngularUnit::Degrees) {
return value * static_cast<T>(180.0 / std::numbers::pi);
} else if constexpr (detail::SMATH_ANGLE_UNIT_ID ==
detail::AngularUnit::Radians) {
return value;
} else if constexpr (detail::SMATH_ANGLE_UNIT_ID ==
detail::AngularUnit::Turns) {
return value / (static_cast<T>(2.0) * static_cast<T>(std::numbers::pi));
}
}
template <class T> constexpr auto turns(T const value) -> T {
if constexpr (detail::SMATH_ANGLE_UNIT_ID == detail::AngularUnit::Degrees) {
return value * static_cast<T>(360.0);
} else if constexpr (detail::SMATH_ANGLE_UNIT_ID ==
detail::AngularUnit::Radians) {
return value * (static_cast<T>(2.0) * static_cast<T>(std::numbers::pi));
} else if constexpr (detail::SMATH_ANGLE_UNIT_ID ==
detail::AngularUnit::Turns) {
return value;
}
}
} // namespace smath
template <std::size_t N, typename T>
@@ -435,4 +509,5 @@ struct tuple_element<I, smath::Vec<N, T>> {
static_assert(I < N);
using type = T;
};
} // namespace std

15
tests/angles.cpp Normal file
View File

@@ -0,0 +1,15 @@
#include <gtest/gtest.h>
#include <smath.hpp>
TEST(AngleReturnRadians, DegInput) {
EXPECT_NEAR(smath::deg(180.0), std::numbers::pi, 1e-12);
}
TEST(AngleReturnRadians, RadInput) {
EXPECT_DOUBLE_EQ(smath::rad(std::numbers::pi), std::numbers::pi);
}
TEST(AngleReturnRadians, TurnsInput) {
EXPECT_NEAR(smath::turns(0.5), std::numbers::pi, 1e-12);
}