diff --git a/robocin/CMakeLists.txt b/robocin/CMakeLists.txt index f8474db..b881332 100644 --- a/robocin/CMakeLists.txt +++ b/robocin/CMakeLists.txt @@ -1 +1,2 @@ +add_subdirectory(geometry) add_subdirectory(utility) diff --git a/robocin/geometry/CMakeLists.txt b/robocin/geometry/CMakeLists.txt new file mode 100644 index 0000000..b750d52 --- /dev/null +++ b/robocin/geometry/CMakeLists.txt @@ -0,0 +1,6 @@ +robocin_cpp_library( + NAME angle + HDRS angle.h internal/angle_internal.h + SRCS angle.cpp + DEPS angular fuzzy_compare +) diff --git a/robocin/geometry/angle.cpp b/robocin/geometry/angle.cpp new file mode 100644 index 0000000..017bd18 --- /dev/null +++ b/robocin/geometry/angle.cpp @@ -0,0 +1,14 @@ +// +// Created by José Cruz on 08/04/23. +// Copyright (c) 2023 RobôCIn. +// + +#include "robocin/geometry/angle.h" + +namespace robocin { + +template class Angle; +template class Angle; +template class Angle; + +} // namespace robocin diff --git a/robocin/geometry/angle.h b/robocin/geometry/angle.h new file mode 100644 index 0000000..13df0fa --- /dev/null +++ b/robocin/geometry/angle.h @@ -0,0 +1,270 @@ +// +// Created by José Cruz on 08/04/23. +// Copyright (c) 2023 RobôCIn. +// + +#ifndef ROBOCIN_GEOMETRY_ANGLE_H +#define ROBOCIN_GEOMETRY_ANGLE_H + +#include "robocin/geometry/internal/angle_internal.h" +#include "robocin/utility/angular.h" +#include "robocin/utility/concepts.h" +#include "robocin/utility/fuzzy_compare.h" +#include "robocin/utility/type_traits.h" + +#include +#include +#include + +namespace robocin { + +template +class Angle { + public: + // Member types ---------------------------------------------------------------------------------- + using type = F; + + // Friendships ----------------------------------------------------------------------------------- + template + friend class Angle; + + // Static constructors --------------------------------------------------------------------------- + static constexpr Angle fromDegrees(type degrees) { + return Angle{degrees * kDegreesToRadiansFactor}; + } + + // Constructors ---------------------------------------------------------------------------------- + constexpr Angle() = default; + constexpr Angle(const Angle&) = default; + constexpr Angle(Angle&&) noexcept = default; + + // NOLINTBEGIN(google-explicit-constructor, hicpp-explicit-conversions) + template G> + constexpr Angle(const Angle& other) : value_{static_cast(other)} {} + template G> + constexpr Angle(Angle&& other) noexcept : value_{static_cast(std::move(other))} {} + // NOLINTEND(google-explicit-constructor, hicpp-explicit-conversions) + + constexpr explicit Angle(type radians) : value_{radians} {} + + // Destructor ------------------------------------------------------------------------------------ + constexpr ~Angle() = default; + + // Implicit conversion operator ------------------------------------------------------------------ + // NOLINTBEGIN(google-explicit-constructor, hicpp-explicit-conversions) + constexpr operator type() const { return value_; } + // NOLINTEND(google-explicit-constructor, hicpp-explicit-conversions) + + // Assignment operators -------------------------------------------------------------------------- + // NOLINTBEGIN(cppcoreguidelines-c-copy-assignment-signature) + // NOLINTBEGIN(misc-unconventional-assign-operator) + constexpr Angle& operator=(type radians) { return value_ = radians, *this; } + // NOLINTEND(misc-unconventional-assign-operator) + // NOLINTEND(cppcoreguidelines-c-copy-assignment-signature) + constexpr Angle& operator=(const Angle&) = default; + constexpr Angle& operator=(Angle&&) noexcept = default; + + // Arithmetic-assignment operators --------------------------------------------------------------- + constexpr Angle& operator+=(type scalar) { return value_ += scalar, *this; } + constexpr Angle& operator-=(type scalar) { return value_ -= scalar, *this; } + constexpr Angle& operator*=(type scalar) { return value_ *= scalar, *this; } + constexpr Angle& operator/=(type scalar) { return value_ /= scalar, *this; } + + template + constexpr Angle& operator+=(const Angle& other) { + return value_ += other.value_, *this; + } + template + constexpr Angle& operator-=(const Angle& other) { + return value_ -= other.value_, *this; + } + template + constexpr Angle& operator*=(const Angle& other) { + return value_ *= other.value_, *this; + } + template + constexpr Angle& operator/=(const Angle& other) { + return value_ /= other.value_, *this; + } + + // Arithmetic operators -------------------------------------------------------------------------- + [[nodiscard]] constexpr Angle operator+(type scalar) const { return Angle{value_ + scalar}; } + [[nodiscard]] constexpr Angle operator-(type scalar) const { return Angle{value_ - scalar}; } + [[nodiscard]] constexpr Angle operator*(type scalar) const { return Angle{value_ * scalar}; } + [[nodiscard]] constexpr Angle operator/(type scalar) const { return Angle{value_ / scalar}; } + + template + [[nodiscard]] constexpr Angle operator+(const Angle& other) const { + return Angle{value_ + other.value_}; + } + template + [[nodiscard]] constexpr Angle operator-(const Angle& other) const { + return Angle{value_ - other.value_}; + } + template + [[nodiscard]] constexpr Angle operator*(const Angle& other) const { + return Angle{value_ * other.value_}; + } + template + [[nodiscard]] constexpr Angle operator/(const Angle& other) const { + return Angle{value_ / other.value_}; + } + + // Arithmetic friend operators ------------------------------------------------------------------- + friend constexpr Angle operator*(type scalar, const Angle& angle) { + return Angle{angle * scalar}; + } + + // Sign operators -------------------------------------------------------------------------------- + [[nodiscard]] constexpr Angle operator+() const { return *this; } + [[nodiscard]] constexpr Angle operator-() const { return Angle{-value_}; } + + // Getters --------------------------------------------------------------------------------------- + [[nodiscard]] constexpr type degrees() const { return value_ * kRadiansToDegreesFactor; } + [[nodiscard]] constexpr type radians() const { return value_; } + + // Geometry -------------------------------------------------------------------------------------- + [[nodiscard]] constexpr type sin() const { return std::sin(value_); } + [[nodiscard]] constexpr type cos() const { return std::cos(value_); } + [[nodiscard]] constexpr type tan() const { return std::tan(value_); } + + constexpr void normalize() & { value_ = normalizeAngle(value_); } + [[nodiscard]] constexpr Angle normalized() && { return normalize(), std::move(*this); } + [[nodiscard]] constexpr Angle normalized() const& { return Angle{*this}.normalized(); } + + [[nodiscard]] constexpr Angle smallestDiffTo(type other) && { + return value_ = smallestAngleDiff(value_, other), std::move(*this); + } + [[nodiscard]] constexpr Angle smallestDiffTo(type other) const& { + return Angle{*this}.smallestDiffTo(other); + } + + [[nodiscard]] constexpr Angle absSmallestDiffTo(type other) && { + return value_ = absSmallestAngleDiff(value_, other), std::move(*this); + } + [[nodiscard]] constexpr Angle absSmallestDiffTo(type other) const& { + return Angle{*this}.absSmallestDiffTo(other); + } + + // Validators ------------------------------------------------------------------------------------ + [[nodiscard]] constexpr bool isNull() const { + if constexpr (has_epsilon_v) { + return fuzzyIsZero(value_); + } + return value_ == 0; + } + + // Equality operators ---------------------------------------------------------------------------- + template + [[nodiscard]] constexpr bool operator==(const Angle& other) const { + using H = common_floating_point_for_comparison_t; + + if constexpr (has_epsilon_v) { + return fuzzyCmpEqual(value_, other.value_); + } else { + return value_ == other.value_; + } + } + template + [[nodiscard]] constexpr bool operator==(G scalar) const { + using H = common_floating_point_for_comparison_t; + + if constexpr (has_epsilon_v) { + return fuzzyCmpEqual(value_, scalar); + } else { + return value_ == scalar; + } + } + + // Equality friend operator ---------------------------------------------------------------------- + template + [[nodiscard]] friend constexpr bool operator==(G scalar, const Angle& angle) { + using H = common_floating_point_for_comparison_t; + + if constexpr (has_epsilon_v) { + return fuzzyCmpEqual(scalar, angle.value_); + } else { + return scalar == angle.value_; + } + } + + // Three-way comparison operators ---------------------------------------------------------------- + template + [[nodiscard]] constexpr auto operator<=>(const Angle& other) const { + using H = common_floating_point_for_comparison_t; + + if constexpr (has_epsilon_v) { + return fuzzyCmpThreeWay(value_, other.value_); + } else { + return value_ <=> other.value_; + } + } + template + [[nodiscard]] constexpr auto operator<=>(G scalar) const { + using H = common_floating_point_for_comparison_t; + + if constexpr (has_epsilon_v) { + return fuzzyCmpThreeWay(value_, scalar); + } else { + return value_ <=> scalar; + } + } + + // Three-way comparison friend operator ---------------------------------------------------------- + template + [[nodiscard]] friend constexpr auto operator<=>(G lhs, const Angle& rhs) { + using H = common_floating_point_for_comparison_t; + + if constexpr (has_epsilon_v) { + return fuzzyCmpThreeWay(lhs, rhs.value_); + } else { + return lhs <=> rhs.value_; + } + } + + // Input/Output operators ------------------------------------------------------------------------ + friend inline std::istream& operator>>(std::istream& is, Angle& angle) { + return is >> angle.value_; + } + + friend inline std::ostream& operator<<(std::ostream& os, const Angle& angle) { + return os << angle.degrees() << "°"; + } + + private: + static constexpr type kDegreesToRadiansFactor = std::numbers::pi_v / 180; + static constexpr type kRadiansToDegreesFactor = 180 / std::numbers::pi_v; + + type value_{0}; +}; + +// Deduction guides -------------------------------------------------------------------------------- +template +Angle(T) -> Angle, T, double>>; + +// Literals ---------------------------------------------------------------------------------------- +inline namespace literals { +inline namespace angle_literals { + +static consteval Angle operator"" _deg(long double degrees) { + return Angle::fromDegrees(degrees); +} + +static consteval Angle +operator"" _deg(unsigned long long degrees) { // NOLINT(google-runtime-int) + return Angle::fromDegrees(degrees); +} + +static consteval Angle operator"" _rad(long double radians) { return Angle{radians}; } + +static consteval Angle +operator"" _rad(unsigned long long radians) { // NOLINT(google-runtime-int) + return Angle{static_cast(radians)}; +} + +} // namespace angle_literals +} // namespace literals + +} // namespace robocin + +#endif // ROBOCIN_GEOMETRY_ANGLE_H diff --git a/robocin/geometry/internal/angle_internal.h b/robocin/geometry/internal/angle_internal.h new file mode 100644 index 0000000..a79bcf4 --- /dev/null +++ b/robocin/geometry/internal/angle_internal.h @@ -0,0 +1,20 @@ +// +// Created by José Cruz on 13/04/23. +// Copyright (c) 2023 RobôCIn. +// + +#ifndef ROBOCIN_GEOMETRY_INTERNAL_ANGLE_INTERNAL_H +#define ROBOCIN_GEOMETRY_INTERNAL_ANGLE_INTERNAL_H + +#include + +namespace robocin::angle_internal { + +template +concept other_floating_point = std::floating_point and // + std::floating_point and // + not std::same_as; + +} // namespace robocin::angle_internal + +#endif // ROBOCIN_GEOMETRY_INTERNAL_ANGLE_INTERNAL_H