#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 MccAngle class construction struct MccRadianTag { }; struct MccDegreeTag { }; static constexpr MccDegreeTag mcc_degrees{}; struct MccHMSTag { }; static constexpr MccHMSTag mcc_hms{}; 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_180_180, // [-180,180] NORM_KIND_90_90, // [-90,90] }; MccAngle() = default; // by default angle is in radians MccAngle(std::convertible_to auto const& val, const MccRadianTag = 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) { _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_180_180) { if (_angleInRads > std::numbers::pi) { _angleInRads = 2.0 * std::numbers::pi - _angleInRads; } else if (_angleInRads < -std::numbers::pi) { _angleInRads += 2.0 * std::numbers::pi; } } 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(); } template operator T() const requires std::is_arithmetic_v { return _angleInRads; } 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); } // arithmetics template T> SelfT& operator+=(this SelfT& self, const T& v) { if constexpr (std::derived_from) { static_assert(std::derived_from, "INCOMPATIBLE TYPES!"); self._angleInRads += v._angleInRads; } else if constexpr (std::is_arithmetic_v) { self._angleInRads += v; } else { self._angleInRads += MccAngle(v)._angleInRads; } return self; } template T> SelfT& operator-=(this SelfT& self, const T& v) { if constexpr (std::derived_from) { static_assert(std::derived_from, "INCOMPATIBLE TYPES!"); self._angleInRads -= v._angleInRads; } else if constexpr (std::is_arithmetic_v) { self._angleInRads -= v; } else { self._angleInRads -= MccAngle(v)._angleInRads; } return self; } template SelfT& operator*=(this SelfT& self, const T& v) requires std::is_arithmetic_v { self._angleInRads *= v; return self; } template SelfT& operator/=(this SelfT& self, const T& v) requires std::is_arithmetic_v { self._angleInRads /= v; return self; } template SelfT& operator-(this SelfT& self) { self._angleInRads = -self._angleInRads; return self; } }; // binary arithmetic operations template T1, std::convertible_to T2> auto operator+(const T1& v1, const T2& v2) { static_assert(std::convertible_to || std::convertible_to, "INCOMPATIBLE TYPES!"); using res_t = std::conditional_t, T1, T2>; return res_t{(double)v1 + (double)v2}; } template T1, std::convertible_to T2> auto operator-(const T1& v1, const T2& v2) { static_assert(std::convertible_to || std::convertible_to, "INCOMPATIBLE TYPES!"); using res_t = std::conditional_t, T1, T2>; return res_t{(double)v1 - (double)v2}; } template T1, std::convertible_to T2> auto operator*(const T1& v1, const T2& v2) { if constexpr (std::is_arithmetic_v) { return v2 *= v1; } else if constexpr (std::is_arithmetic_v) { return v1 *= v2; } else { static_assert(false, "INCOMPATIBLE TYPES!"); } } template T1, typename T2> auto operator/(const T1& v1, const T2& v2) requires std::is_arithmetic_v { return v1 /= v2; } /* */ class MccAngleRA_ICRS : public MccAngle { using MccAngle::MccAngle; }; class MccAngleDEC_ICRS : public MccAngle { using MccAngle::MccAngle; }; class MccAngleRA_APP : public MccAngle { using MccAngle::MccAngle; }; class MccAngleDEC_APP : public MccAngle { using MccAngle::MccAngle; }; class MccAngleHA : public MccAngle { using MccAngle::MccAngle; }; class MccAngleAZ : public MccAngle { using MccAngle::MccAngle; }; class MccAngleZD : public MccAngle { using MccAngle::MccAngle; }; class MccAngleALT : public MccAngle { using MccAngle::MccAngle; }; class MccAngleX : public MccAngle // some co-longitude coordinate { using MccAngle::MccAngle; }; class MccAngleY : public MccAngle // some co-latitude coordinate { using MccAngle::MccAngle; }; class MccAngleLAT : public MccAngle { using MccAngle::MccAngle; }; class MccAngleLON : public MccAngle { using MccAngle::MccAngle; }; enum class MccCoordPairKind : size_t { COORDS_KIND_GENERIC = traits::mcc_type_pair_hash(), COORDS_KIND_RADEC_ICRS = traits::mcc_type_pair_hash(), COORDS_KIND_RADEC_APP = traits::mcc_type_pair_hash(), COORDS_KIND_HADEC = traits::mcc_type_pair_hash(), COORDS_KIND_AZZD = traits::mcc_type_pair_hash(), COORDS_KIND_AZALT = traits::mcc_type_pair_hash(), COORDS_KIND_XY = traits::mcc_type_pair_hash(), COORDS_KIND_LATLON = traits::mcc_type_pair_hash() }; } // namespace mcc