diff --git a/cxx/mcc_mount_coord.h b/cxx/mcc_mount_coord.h new file mode 100644 index 0000000..e4acb76 --- /dev/null +++ b/cxx/mcc_mount_coord.h @@ -0,0 +1,372 @@ +#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 diff --git a/cxx/mcc_mount_pz.h b/cxx/mcc_mount_pz.h index 78f51ed..3893624 100644 --- a/cxx/mcc_mount_pz.h +++ b/cxx/mcc_mount_pz.h @@ -7,7 +7,7 @@ #include #include -#include "mcc_coord.h" +#include "mcc_mount_coord.h" #include "mcc_traits.h" namespace mcc @@ -20,9 +20,9 @@ public: typedef std::chrono::duration pz_duration_t; struct pz_request_t { - bool in_zone{false}; // true if given coordinates are within the zone - pz_duration_t time_to; // a time duration to reach the zone - pz_duration_t time_from; // a time duration to exit the zone + bool in_zone{false}; // true if given coordinates are within the zone + pz_duration_t time_to{0.0}; // a time duration to reach the zone + pz_duration_t time_from{0.0}; // a time duration to exit the zone }; struct pz_context_t { @@ -35,19 +35,60 @@ public: return _name; } - bool inZone(const MccAngle& x, - const MccAngle& y, + // check if given position (x,y) in the zone + template XT, std::derived_from YT> + bool inZone(const XT& x, + const YT& y, const pz_context_t& context, traits::mcc_systime_c auto const& utc = std::chrono::system_clock::now()) { + static constexpr size_t coord_kind = traits::mcc_type_pair_hash(); + return false; } - pz_request_t request(const MccAngle& x, - const MccAngle& y, + // returns a time duration to reach the zone + // 0 - if already within + // Inf - if it never reaches the zone + template XT, std::derived_from YT> + pz_duration_t timeTo(const XT& x, + const YT& y, const pz_context_t& context, traits::mcc_systime_c auto const& utc = std::chrono::system_clock::now()) { + static constexpr size_t coord_kind = traits::mcc_type_pair_hash(); + } + + // returns a time duration to exit from the zone + // 0 - if given position (x,y) is out of the zone + // Inf - if (x,y) is always within the zone + template XT, std::derived_from YT> + pz_duration_t timeFrom(const XT& x, + const YT& y, + const pz_context_t& context, + traits::mcc_systime_c auto const& utc = std::chrono::system_clock::now()) + { + static constexpr size_t coord_kind = traits::mcc_type_pair_hash(); + } + + + template XT, std::derived_from YT> + pz_request_t request(const XT& x, + const YT& y, + const pz_context_t& context, + traits::mcc_systime_c auto const& utc = std::chrono::system_clock::now()) + { + pz_request_t res{.in_zone = false, .time_to = pz_duration_t{0.0}, .time_from{0.0}}; + + res.in_zone = inZone(x, y, context, utc); + + if (res.in_zone) { + res.time_from = timeFrom(x, y, context, utc); + } else { + res.time_to = timeFrom(x, y, context, utc); + } + + return res; } protected: diff --git a/cxx/mcc_traits.h b/cxx/mcc_traits.h index a23ec79..08dead7 100644 --- a/cxx/mcc_traits.h +++ b/cxx/mcc_traits.h @@ -131,4 +131,45 @@ template using mcc_func_arg1_t = typename mcc_func_traits::arg1_t; +namespace details +{ + +// compile-time hash for type +// (from https://stackoverflow.com/questions/56292104/hashing-types-at-compile-time-in-c17-c2a) +// WARNING: it does not work for unnamed struct!!! +template +static consteval size_t Hash() +{ + size_t result{}; + +#ifdef _MSC_VER + for (const auto& c : __FUNCSIG__) +#else // GCC and clang + for (const auto& c : __PRETTY_FUNCTION__) +#endif + (result ^= c) <<= 1; + + return result; +} + +} // namespace details + +template +static constexpr size_t mcc_type_hash = details::Hash(); + +static constexpr size_t mcc_hash_combine(size_t lhs, size_t rhs) +{ + constexpr size_t v_const = sizeof(size_t) >= 8 ? 0x517cc1b727220a95 : 0x9e3779b9; + + lhs ^= rhs + v_const + (lhs << 6) + (lhs >> 2); + + return lhs; +} + +template +static constexpr size_t mcc_type_pair_hash() +{ + return mcc_hash_combine(mcc_type_hash, mcc_type_hash); +} + } // namespace mcc::traits