#pragma once #include "mcc_traits.h" #include "utils.h" constexpr double operator""_rads(long double val) // angle in radians (no conversion) { return val; } constexpr double operator""_degs(long double val) // angle in degrees { return val * std::numbers::pi / 180.0; } constexpr double operator""_dms(const char* s, size_t size) // as a string "DEGREES:MINUTES:SECONDS" { auto res = mcc::utils::parsAngleString(std::span{s, size}); if (res.has_value()) { return res.value() * std::numbers::pi / 180.0; } else { throw std::invalid_argument("invalid sexagesimal representation"); } } constexpr double operator""_hms(const char* s, size_t len) // as a string "HOURS:MINUTES:SECONDS" { auto res = mcc::utils::parsAngleString(std::span{s, len}, true); if (res.has_value()) { return res.value() * std::numbers::pi / 180.0; } else { throw std::invalid_argument("invalid sexagesimal representation"); } } namespace mcc { /* MCC-LIBRARY COORDINATES REPRESENTATION DEFINITIONS AND CLASS */ // tags for MccCoordinate class construction struct MccRadianTag { }; struct MccDegreeTag { }; static constexpr MccDegreeTag mcc_degrees{}; struct MccHMSTag { }; static constexpr MccHMSTag mcc_hms{}; // coordinate representation class class MccAngle { protected: double _angleInRads{0.0}; int _precision{2}; public: enum norm_kind_t { NORM_KIND_0_360, // [0,360] NORM_KIND_0_180, // [0,180] NORM_KIND_90_90, // [-90,90] }; MccAngle() = default; // by default angle is in radians MccAngle(std::convertible_to auto const& val, const MccRadianTag tag = MccRadianTag{}) : _angleInRads(val) { } // construct angle in degrees, e.g.: // auto ang = MccAngle{180.0, mcc_degrees}; MccAngle(std::convertible_to auto const& val, const MccDegreeTag tag) { _angleInRads = val * utils::deg2radCoeff; } // constuct angle from sexagesimal representation or floating-point number of degrees, e.g.: // auto ang = MccAngle{"-12:34:56.789"}; // from degrees:minutes:seconds // auto ang = MccAngle{"123.574698"}; // from degrees MccAngle(traits::mcc_input_char_range auto const& val) { auto res = utils::parsAngleString(val); if (res.has_value()) { _angleInRads = res.value() * utils::deg2radCoeff; } else { throw std::invalid_argument("invalid sexagesimal representation"); } } // construct angle from sexagesimal representation or floating-point number of degrees, e.g.: // auto ang = MccAngle{"01:23:45.6789", mcc_hms}; // from hours:minutes:seconds // auto ang = MccAngle{"123.574698"}; // from degrees MccAngle(traits::mcc_input_char_range auto const& val, const MccHMSTag) { auto res = utils::parsAngleString(val, true); if (res.has_value()) { _angleInRads = res.value() * utils::deg2radCoeff; } else { throw std::invalid_argument("invalid sexagesimal representation"); } } virtual ~MccAngle() = default; // normalize coordinate template MccAngle& normalize() { _angleInRads = std::fmod(_angleInRads, std::numbers::pi * 2.0); if constexpr (KIND == NORM_KIND_0_360) { if (_angleInRads < 0.0) { _angleInRads += 2.0 * std::numbers::pi; } } else if constexpr (KIND == NORM_KIND_0_180) { if (_angleInRads < -std::numbers::pi) { _angleInRads = 2.0 * std::numbers::pi + _angleInRads; } else if (_angleInRads < 0.0) { _angleInRads = -_angleInRads; } } else if constexpr (KIND == NORM_KIND_90_90) { if (_angleInRads >= 1.5 * std::numbers::pi) { _angleInRads = _angleInRads - 2.0 * std::numbers::pi; } else if (_angleInRads >= std::numbers::pi / 2.0) { _angleInRads = std::numbers::pi - _angleInRads; } else if (_angleInRads <= -1.5 * std::numbers::pi) { _angleInRads += 2.0 * std::numbers::pi; } else if (_angleInRads <= -std::numbers::pi / 2.0) { _angleInRads = -(std::numbers::pi + _angleInRads); } } return *this; } MccAngle& normalize() { return normalize(); } operator double() const { return _angleInRads; } template T degrees() const { return _angleInRads * 180.0 / std::numbers::pi; } double degrees() const { return degrees(); } template T sexagesimal(bool hms, int prec = 2) const { return utils::rad2sxg(_angleInRads, hms, prec >= 0 ? prec : _precision); } std::string sexagesimal(bool hms, int prec = 2) const { return sexagesimal(hms, prec); } friend MccAngle operator+(std::convertible_to auto&& left, std::convertible_to auto&& right) { // return std::forward(left)._angleInRads + // std::forward(right)._angleInRads; return MccAngle{std::forward(left)}._angleInRads + MccAngle{std::forward(right)}._angleInRads; } // friend MccAngle operator+(const MccAngle& left, const MccAngle& right) // { // return left._angleInRads + right._angleInRads; // } friend MccAngle operator-(std::convertible_to auto&& left, std::convertible_to auto&& right) { // return std::forward(left)._angleInRads - // std::forward(right)._angleInRads; return MccAngle{std::forward(left)}._angleInRads - MccAngle{std::forward(right)}._angleInRads; } // friend MccAngle operator-(const MccAngle& left, const MccAngle& right) // { // return left._angleInRads + right._angleInRads; // } template MccAngle operator*(T&& scalar) requires std::is_arithmetic_v> { return _angleInRads * scalar; } template MccAngle operator/(T&& scalar) requires std::is_arithmetic_v> { return _angleInRads / scalar; } template MccAngle operator-() // unary '-' requires std::is_arithmetic_v> { return -_angleInRads; } MccAngle& operator+=(const MccAngle& v) { _angleInRads += v._angleInRads; return *this; } MccAngle& operator-=(const MccAngle& v) { _angleInRads += v._angleInRads; return *this; } template MccAngle& operator*=(T&& scalar) requires std::is_arithmetic_v> { _angleInRads *= scalar; return *this; } template MccAngle& operator/=(T&& scalar) requires std::is_arithmetic_v> { _angleInRads /= scalar; return *this; } auto operator<=>(const MccAngle& other) const { return _angleInRads <=> other._angleInRads; } auto operator==(const MccAngle& other) const { return _angleInRads == other._angleInRads; } auto operator<=>(double other) const { return _angleInRads <=> other; } auto operator==(double other) const { return _angleInRads == other; } }; enum class MccNormCoordKind { NORM_KIND_0_360, // [0,360] NORM_KIND_0_180, // [0,180] NORM_KIND_90_90, // [-90,90] }; template class MccNormCoordinate : public MccAngle { public: static constexpr MccNormCoordKind normKind = KIND; protected: static constexpr double pi2 = 2.0 * std::numbers::pi; static constexpr double normScale = KIND == MccNormCoordKind::NORM_KIND_0_360 ? std::numbers::pi * 2.0 : KIND == MccNormCoordKind::NORM_KIND_0_180 ? std::numbers::pi : std::numbers::pi / 2.0; void norm() { _angleInRads = std::fmod(_angleInRads, normScale); if constexpr (KIND == MccNormCoordKind::NORM_KIND_0_360) { _angleInRads += normScale; } else if constexpr (KIND == MccNormCoordKind::NORM_KIND_0_180) { _angleInRads = -_angleInRads; } } }; } // namespace mcc