#pragma once #include "mcc_traits.h" #include "mcc_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""_arcmins(long double val) // angle in arc minutes { return val * std::numbers::pi / 180.0 / 60.0; } constexpr double operator""_arcsecs(long double val) // angle in arc seconds { return val * std::numbers::pi / 180.0 / 3600.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; // } // by default angle is in radians // template // MccAngle(const T& val, const MccRadianTag = MccRadianTag{}) // requires std::is_arithmetic_v // : _angleInRads(val) // { // } // // construct angle in degrees, e.g.: // // auto ang = MccAngle{180.0, mcc_degrees}; // template // MccAngle(const T& val, const MccDegreeTag) // requires std::is_arithmetic_v // : _angleInRads(val * utils::deg2radCoeff) // { // } // by default angle is in radians constexpr MccAngle(const double& val, const MccRadianTag = MccRadianTag{}) : _angleInRads(val) {} // construct angle in degrees, e.g.: // auto ang = MccAngle{180.0, mcc_degrees}; constexpr MccAngle(const double& 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 constexpr 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 constexpr 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; template T> constexpr auto& operator=(this T&& self, const T& other) { std::forward(self)._angleInRads = other._angleInRads; return self; } template T> constexpr auto& operator=(this T&& self, T&& other) { std::forward(self)._angleInRads = std::move(other._angleInRads); return self; } template constexpr auto& operator=(this auto&& self, const T& val) requires std::is_arithmetic_v { std::forward(self)._angleInRads = val; return self; } // 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; // } constexpr operator double() const { return _angleInRads; } template constexpr T degrees() const { return _angleInRads * 180.0 / std::numbers::pi; } constexpr double degrees() const { return degrees(); } template T arcmins() const { return _angleInRads * 10800.0 / std::numbers::pi; } double arcmins() const { return arcmins(); } template T arcsecs() const { return _angleInRads * 648000.0 / std::numbers::pi; } double arcsecs() const { return arcsecs(); } template T hours() const { return _angleInRads * 12.0 / std::numbers::pi; } double hours() const { return hours(); } template T minutes() const { return _angleInRads * 720.0 / std::numbers::pi; } double minutes() const { return minutes(); } template T seconds() const { return _angleInRads * 43200.0 / std::numbers::pi; } double seconds() const { return seconds(); } template T sexagesimal(bool hms = false, int prec = 2) const { return utils::rad2sxg(_angleInRads, hms, prec >= 0 ? prec : _precision); } std::string sexagesimal(bool hms = false, 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; } // unary '-' and '+' template SelfT operator-(this SelfT& self) { SelfT res = -self._angleInRads; return res; } template SelfT operator+(this SelfT& self) { return self; } }; // binary arithmetic operations template T1, std::convertible_to T2> static auto operator+(const T1& v1, const T2& v2) { static_assert(std::convertible_to || std::convertible_to, "INCOMPATIBLE TYPES!"); using res_t = std::conditional_t && std::derived_from, T1, T2>; return res_t{(double)v1 + (double)v2}; } template T1, std::convertible_to T2> static auto operator-(const T1& v1, const T2& v2) { static_assert(std::convertible_to || std::convertible_to, "INCOMPATIBLE TYPES!"); using res_t = std::conditional_t && std::derived_from, T1, T2>; return res_t{(double)v1 - (double)v2}; } template T1, std::convertible_to T2> static 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 { using res_t = std::conditional_t && std::derived_from, T1, T2>; return res_t{(double)v1 * (double)v2}; // static_assert(false, "INCOMPATIBLE TYPES!"); } } template T1, typename T2> static 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 { public: using MccAngle::MccAngle; }; class MccAngleALT : public MccAngle { public: 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 MccCoordKind : size_t { COORDS_KIND_GENERIC = traits::mcc_type_hash, COORDS_KIND_RA_ICRS = traits::mcc_type_hash, COORDS_KIND_DEC_ICRS = traits::mcc_type_hash, COORDS_KIND_RA_APP = traits::mcc_type_hash, COORDS_KIND_DEC_APP = traits::mcc_type_hash, COORDS_KIND_HA = traits::mcc_type_hash, COORDS_KIND_AZ = traits::mcc_type_hash, COORDS_KIND_ZD = traits::mcc_type_hash, COORDS_KIND_ALT = traits::mcc_type_hash, COORDS_KIND_X = traits::mcc_type_hash, COORDS_KIND_Y = traits::mcc_type_hash, COORDS_KIND_LAT = traits::mcc_type_hash, COORDS_KIND_LON = traits::mcc_type_hash }; 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_APP = 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() }; template static constexpr std::string_view MccCoordPairKindStr = KIND == MccCoordPairKind::COORDS_KIND_RADEC_ICRS ? "RADEC-IRCS" : KIND == MccCoordPairKind::COORDS_KIND_RADEC_APP ? "RADEC-APP" : KIND == MccCoordPairKind::COORDS_KIND_HADEC_APP ? "HADEC-APP" : KIND == MccCoordPairKind::COORDS_KIND_AZALT ? "Azimuth-Altitude" : KIND == MccCoordPairKind::COORDS_KIND_AZZD ? "Azimuth-Zendist" : KIND == MccCoordPairKind::COORDS_KIND_XY ? "X-Y" : KIND == MccCoordPairKind::COORDS_KIND_LATLON ? "Latitude-Longitude" : "UNKNOWN"; } // namespace mcc