Skip to content
This repository has been archived by the owner on Feb 22, 2024. It is now read-only.

Add Angle wrapper library #5

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions robocin/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
add_subdirectory(geometry)
add_subdirectory(utility)
6 changes: 6 additions & 0 deletions robocin/geometry/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
robocin_cpp_library(
NAME angle
HDRS angle.h internal/angle_internal.h
SRCS angle.cpp
DEPS angular fuzzy_compare
)
14 changes: 14 additions & 0 deletions robocin/geometry/angle.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// Created by José Cruz <joseviccruz> on 08/04/23.
// Copyright (c) 2023 RobôCIn.
//

#include "robocin/geometry/angle.h"

namespace robocin {

template class Angle<float>;
template class Angle<double>;
template class Angle<long double>;

} // namespace robocin
270 changes: 270 additions & 0 deletions robocin/geometry/angle.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
//
// Created by José Cruz <joseviccruz> 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 <concepts>
#include <iostream>
#include <numbers>

namespace robocin {

template <std::floating_point F>
class Angle {
public:
// Member types ----------------------------------------------------------------------------------
using type = F;

// Friendships -----------------------------------------------------------------------------------
template <std::floating_point U>
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 <angle_internal::other_floating_point<type> G>
constexpr Angle(const Angle<G>& other) : value_{static_cast<type>(other)} {}
template <angle_internal::other_floating_point<type> G>
constexpr Angle(Angle<G>&& other) noexcept : value_{static_cast<type>(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 <std::floating_point G>
constexpr Angle& operator+=(const Angle<G>& other) {
return value_ += other.value_, *this;
}
template <std::floating_point G>
constexpr Angle& operator-=(const Angle<G>& other) {
return value_ -= other.value_, *this;
}
template <std::floating_point G>
constexpr Angle& operator*=(const Angle<G>& other) {
return value_ *= other.value_, *this;
}
template <std::floating_point G>
constexpr Angle& operator/=(const Angle<G>& 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 <std::floating_point G>
[[nodiscard]] constexpr Angle operator+(const Angle<G>& other) const {
return Angle{value_ + other.value_};
}
template <std::floating_point G>
[[nodiscard]] constexpr Angle operator-(const Angle<G>& other) const {
return Angle{value_ - other.value_};
}
template <std::floating_point G>
[[nodiscard]] constexpr Angle operator*(const Angle<G>& other) const {
return Angle{value_ * other.value_};
}
template <std::floating_point G>
[[nodiscard]] constexpr Angle operator/(const Angle<G>& 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<type>) {
return fuzzyIsZero(value_);
}
return value_ == 0;
}

// Equality operators ----------------------------------------------------------------------------
template <std::floating_point G>
[[nodiscard]] constexpr bool operator==(const Angle<G>& other) const {
using H = common_floating_point_for_comparison_t<type, G>;

if constexpr (has_epsilon_v<H>) {
return fuzzyCmpEqual(value_, other.value_);
} else {
return value_ == other.value_;
}
}
template <std::floating_point G>
[[nodiscard]] constexpr bool operator==(G scalar) const {
using H = common_floating_point_for_comparison_t<type, G>;

if constexpr (has_epsilon_v<H>) {
return fuzzyCmpEqual(value_, scalar);
} else {
return value_ == scalar;
}
}

// Equality friend operator ----------------------------------------------------------------------
template <std::floating_point G>
[[nodiscard]] friend constexpr bool operator==(G scalar, const Angle& angle) {
using H = common_floating_point_for_comparison_t<type, G>;

if constexpr (has_epsilon_v<H>) {
return fuzzyCmpEqual(scalar, angle.value_);
} else {
return scalar == angle.value_;
}
}

// Three-way comparison operators ----------------------------------------------------------------
template <std::floating_point G>
[[nodiscard]] constexpr auto operator<=>(const Angle<G>& other) const {
using H = common_floating_point_for_comparison_t<type, G>;

if constexpr (has_epsilon_v<H>) {
return fuzzyCmpThreeWay(value_, other.value_);
} else {
return value_ <=> other.value_;
}
}
template <std::floating_point G>
[[nodiscard]] constexpr auto operator<=>(G scalar) const {
using H = common_floating_point_for_comparison_t<type, G>;

if constexpr (has_epsilon_v<H>) {
return fuzzyCmpThreeWay(value_, scalar);
} else {
return value_ <=> scalar;
}
}

// Three-way comparison friend operator ----------------------------------------------------------
template <std::floating_point G>
[[nodiscard]] friend constexpr auto operator<=>(G lhs, const Angle& rhs) {
using H = common_floating_point_for_comparison_t<G, type>;

if constexpr (has_epsilon_v<H>) {
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<type> / 180;
static constexpr type kRadiansToDegreesFactor = 180 / std::numbers::pi_v<type>;

type value_{0};
};

// Deduction guides --------------------------------------------------------------------------------
template <arithmetic T>
Angle(T) -> Angle<std::conditional_t<std::is_floating_point_v<T>, T, double>>;

// Literals ----------------------------------------------------------------------------------------
inline namespace literals {
inline namespace angle_literals {

static consteval Angle<long double> operator"" _deg(long double degrees) {
return Angle<long double>::fromDegrees(degrees);
}

static consteval Angle<long double>
operator"" _deg(unsigned long long degrees) { // NOLINT(google-runtime-int)
return Angle<long double>::fromDegrees(degrees);
}

static consteval Angle<long double> operator"" _rad(long double radians) { return Angle{radians}; }

static consteval Angle<long double>
operator"" _rad(unsigned long long radians) { // NOLINT(google-runtime-int)
return Angle{static_cast<long double>(radians)};
}

} // namespace angle_literals
} // namespace literals

} // namespace robocin

#endif // ROBOCIN_GEOMETRY_ANGLE_H
20 changes: 20 additions & 0 deletions robocin/geometry/internal/angle_internal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// Created by José Cruz <joseviccruz> on 13/04/23.
// Copyright (c) 2023 RobôCIn.
//

#ifndef ROBOCIN_GEOMETRY_INTERNAL_ANGLE_INTERNAL_H
#define ROBOCIN_GEOMETRY_INTERNAL_ANGLE_INTERNAL_H

#include <concepts>

namespace robocin::angle_internal {

template <class F, class G>
concept other_floating_point = std::floating_point<F> and //
std::floating_point<G> and //
not std::same_as<F, G>;

} // namespace robocin::angle_internal

#endif // ROBOCIN_GEOMETRY_INTERNAL_ANGLE_INTERNAL_H