mountcontrol/mcc/mcc_angle.h
2025-11-17 18:04:40 +03:00

628 lines
19 KiB
C++

#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""_hours(long double val) // angle in hours
{
return val * std::numbers::pi / 12.0;
}
constexpr double operator""_mins(long double val) // angle in hour minutes
{
return val * std::numbers::pi / 12.0 / 60.0;
}
constexpr double operator""_secs(long double val) // angle in hour seconds
{
return val * std::numbers::pi / 12.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<double> 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<double> auto const& val, const MccDegreeTag)
// {
// _angleInRads = val * utils::deg2radCoeff;
// }
// by default angle is in radians
// template <typename T>
// MccAngle(const T& val, const MccRadianTag = MccRadianTag{})
// requires std::is_arithmetic_v<T>
// : _angleInRads(val)
// {
// }
// // construct angle in degrees, e.g.:
// // auto ang = MccAngle{180.0, mcc_degrees};
// template <typename T>
// MccAngle(const T& val, const MccDegreeTag)
// requires std::is_arithmetic_v<T>
// : _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 <std::derived_from<MccAngle> T>
constexpr auto& operator=(this T&& self, const T& other)
{
std::forward<decltype(self)>(self)._angleInRads = other._angleInRads;
return self;
}
template <std::derived_from<MccAngle> T>
constexpr auto& operator=(this T&& self, T&& other)
{
std::forward<decltype(self)>(self)._angleInRads = std::move(other._angleInRads);
return self;
}
template <typename T>
constexpr auto& operator=(this auto&& self, const T& val)
requires std::is_arithmetic_v<T>
{
std::forward<decltype(self)>(self)._angleInRads = val;
return self;
}
// normalize coordinate
template <norm_kind_t KIND>
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<NORM_KIND_0_360>();
}
// template <typename T>
// operator T() const
// requires std::is_arithmetic_v<T>
// {
// return _angleInRads;
// }
constexpr operator double() const
{
return _angleInRads;
}
template <typename T>
constexpr T degrees() const
{
return _angleInRads * 180.0 / std::numbers::pi;
}
constexpr double degrees() const
{
return degrees<double>();
}
template <typename T>
T arcmins() const
{
return _angleInRads * 10800.0 / std::numbers::pi;
}
double arcmins() const
{
return arcmins<double>();
}
template <typename T>
T arcsecs() const
{
return _angleInRads * 648000.0 / std::numbers::pi;
}
double arcsecs() const
{
return arcsecs<double>();
}
template <typename T>
T hours() const
{
return _angleInRads * 12.0 / std::numbers::pi;
}
double hours() const
{
return hours<double>();
}
template <typename T>
T minutes() const
{
return _angleInRads * 720.0 / std::numbers::pi;
}
double minutes() const
{
return minutes<double>();
}
template <typename T>
T seconds() const
{
return _angleInRads * 43200.0 / std::numbers::pi;
}
double seconds() const
{
return seconds<double>();
}
template <traits::mcc_output_char_range T>
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<std::string>(hms, prec);
}
// arithmetics
template <typename SelfT, std::convertible_to<MccAngle> T>
SelfT& operator+=(this SelfT& self, const T& v)
{
if constexpr (std::derived_from<T, MccAngle>) {
static_assert(std::derived_from<SelfT, T>, "INCOMPATIBLE TYPES!");
self._angleInRads += v._angleInRads;
} else if constexpr (std::is_arithmetic_v<T>) {
self._angleInRads += v;
} else {
self._angleInRads += MccAngle(v)._angleInRads;
}
return self;
}
template <typename SelfT, std::convertible_to<MccAngle> T>
SelfT& operator-=(this SelfT& self, const T& v)
{
if constexpr (std::derived_from<T, MccAngle>) {
static_assert(std::derived_from<SelfT, T>, "INCOMPATIBLE TYPES!");
self._angleInRads -= v._angleInRads;
} else if constexpr (std::is_arithmetic_v<T>) {
self._angleInRads -= v;
} else {
self._angleInRads -= MccAngle(v)._angleInRads;
}
return self;
}
template <typename SelfT, typename T>
SelfT& operator*=(this SelfT& self, const T& v)
requires std::is_arithmetic_v<T>
{
self._angleInRads *= v;
return self;
}
template <typename SelfT, typename T>
SelfT& operator/=(this SelfT& self, const T& v)
requires std::is_arithmetic_v<T>
{
self._angleInRads /= v;
return self;
}
// unary '-' and '+'
template <typename SelfT>
SelfT operator-(this SelfT& self)
{
SelfT res = -self._angleInRads;
return res;
}
template <typename SelfT>
SelfT operator+(this SelfT& self)
{
return self;
}
};
// binary arithmetic operations
template <std::convertible_to<MccAngle> T1, std::convertible_to<MccAngle> T2>
static auto operator+(const T1& v1, const T2& v2)
{
static_assert(std::convertible_to<T1, T2> || std::convertible_to<T2, T1>, "INCOMPATIBLE TYPES!");
using res_t = std::conditional_t<std::convertible_to<T1, T2> && std::derived_from<T1, MccAngle>, T1, T2>;
return res_t{(double)v1 + (double)v2};
}
template <std::convertible_to<MccAngle> T1, std::convertible_to<MccAngle> T2>
static auto operator-(const T1& v1, const T2& v2)
{
static_assert(std::convertible_to<T1, T2> || std::convertible_to<T2, T1>, "INCOMPATIBLE TYPES!");
using res_t = std::conditional_t<std::convertible_to<T1, T2> && std::derived_from<T1, MccAngle>, T1, T2>;
return res_t{(double)v1 - (double)v2};
}
template <std::convertible_to<MccAngle> T1, std::convertible_to<MccAngle> T2>
static auto operator*(const T1& v1, const T2& v2)
{
if constexpr (std::is_arithmetic_v<T1>) {
return T2{(double)v2 * v1};
} else if constexpr (std::is_arithmetic_v<T2>) {
return T1{(double)v1 * v2};
} else {
using res_t = std::conditional_t<std::convertible_to<T1, T2> && std::derived_from<T1, MccAngle>, T1, T2>;
return res_t{(double)v1 * (double)v2};
// static_assert(false, "INCOMPATIBLE TYPES!");
}
}
template <std::convertible_to<MccAngle> T1, typename T2>
static T1 operator/(const T1& v1, const T2& v2)
requires std::is_arithmetic_v<T2>
{
return (double)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;
};
class MccAngleUnknown : public MccAngle
{
using MccAngle::MccAngle;
};
enum class MccCoordKind : size_t {
COORDS_KIND_GENERIC = traits::mcc_type_hash<MccAngle>,
COORDS_KIND_RA_ICRS = traits::mcc_type_hash<MccAngleRA_ICRS>,
COORDS_KIND_DEC_ICRS = traits::mcc_type_hash<MccAngleDEC_ICRS>,
COORDS_KIND_RA_APP = traits::mcc_type_hash<MccAngleRA_APP>,
COORDS_KIND_DEC_APP = traits::mcc_type_hash<MccAngleDEC_APP>,
COORDS_KIND_HA = traits::mcc_type_hash<MccAngleHA>,
COORDS_KIND_AZ = traits::mcc_type_hash<MccAngleAZ>,
COORDS_KIND_ZD = traits::mcc_type_hash<MccAngleZD>,
COORDS_KIND_ALT = traits::mcc_type_hash<MccAngleALT>,
COORDS_KIND_X = traits::mcc_type_hash<MccAngleX>,
COORDS_KIND_Y = traits::mcc_type_hash<MccAngleY>,
COORDS_KIND_LAT = traits::mcc_type_hash<MccAngleLAT>,
COORDS_KIND_LON = traits::mcc_type_hash<MccAngleLON>,
COORDS_KIND_UKNOWN = traits::mcc_type_hash<MccAngleUnknown>
};
enum class MccCoordPairKind : size_t {
COORDS_KIND_GENERIC = traits::mcc_type_pair_hash<MccAngle, MccAngle>(),
COORDS_KIND_RADEC_ICRS = traits::mcc_type_pair_hash<MccAngleRA_ICRS, MccAngleDEC_ICRS>(),
COORDS_KIND_RADEC_APP = traits::mcc_type_pair_hash<MccAngleRA_APP, MccAngleDEC_APP>(),
COORDS_KIND_HADEC_APP = traits::mcc_type_pair_hash<MccAngleHA, MccAngleDEC_APP>(),
COORDS_KIND_AZZD = traits::mcc_type_pair_hash<MccAngleAZ, MccAngleZD>(),
COORDS_KIND_AZALT = traits::mcc_type_pair_hash<MccAngleAZ, MccAngleALT>(),
COORDS_KIND_XY = traits::mcc_type_pair_hash<MccAngleX, MccAngleY>(),
COORDS_KIND_LATLON = traits::mcc_type_pair_hash<MccAngleLAT, MccAngleLON>(),
COORDS_KIND_UNKNOWN = traits::mcc_type_pair_hash<MccAngleUnknown, MccAngleUnknown>()
};
static constexpr std::string_view MCC_COORDPAIR_KIND_RADEC_ICRS_STR = "RADEC-IRCS";
static constexpr std::string_view MCC_COORDPAIR_KIND_RADEC_APP_STR = "RADEC-APP";
static constexpr std::string_view MCC_COORDPAIR_KIND_HADEC_APP_STR = "HADEC-APP";
static constexpr std::string_view MCC_COORDPAIR_KIND_AZALT_STR = "AZALT";
static constexpr std::string_view MCC_COORDPAIR_KIND_AZZD_STR = "AZZD";
static constexpr std::string_view MCC_COORDPAIR_KIND_XY_STR = "XY";
static constexpr std::string_view MCC_COORDPAIR_KIND_LATLON_STR = "LATLON";
static constexpr std::string_view MCC_COORDPAIR_KIND_GENERIC_STR = "GENERIC";
static constexpr std::string_view MCC_COORDPAIR_KIND_UNKNOWN_STR = "UNKNOWN";
template <MccCoordPairKind KIND>
static constexpr std::string_view MccCoordPairKindStr =
KIND == MccCoordPairKind::COORDS_KIND_RADEC_ICRS ? MCC_COORDPAIR_KIND_RADEC_ICRS_STR
: KIND == MccCoordPairKind::COORDS_KIND_RADEC_APP ? MCC_COORDPAIR_KIND_RADEC_APP_STR
: KIND == MccCoordPairKind::COORDS_KIND_HADEC_APP ? MCC_COORDPAIR_KIND_HADEC_APP_STR
: KIND == MccCoordPairKind::COORDS_KIND_AZALT ? MCC_COORDPAIR_KIND_AZALT_STR
: KIND == MccCoordPairKind::COORDS_KIND_AZZD ? MCC_COORDPAIR_KIND_AZZD_STR
: KIND == MccCoordPairKind::COORDS_KIND_XY ? MCC_COORDPAIR_KIND_XY_STR
: KIND == MccCoordPairKind::COORDS_KIND_LATLON ? MCC_COORDPAIR_KIND_LATLON_STR
: KIND == MccCoordPairKind::COORDS_KIND_GENERIC ? MCC_COORDPAIR_KIND_GENERIC_STR
: MCC_COORDPAIR_KIND_UNKNOWN_STR;
static constexpr std::string_view MccCoordPairKindToStr(MccCoordPairKind KIND)
{
return KIND == MccCoordPairKind::COORDS_KIND_RADEC_ICRS ? MCC_COORDPAIR_KIND_RADEC_ICRS_STR
: KIND == MccCoordPairKind::COORDS_KIND_RADEC_APP ? MCC_COORDPAIR_KIND_RADEC_APP_STR
: KIND == MccCoordPairKind::COORDS_KIND_HADEC_APP ? MCC_COORDPAIR_KIND_HADEC_APP_STR
: KIND == MccCoordPairKind::COORDS_KIND_AZALT ? MCC_COORDPAIR_KIND_AZALT_STR
: KIND == MccCoordPairKind::COORDS_KIND_AZZD ? MCC_COORDPAIR_KIND_AZZD_STR
: KIND == MccCoordPairKind::COORDS_KIND_XY ? MCC_COORDPAIR_KIND_XY_STR
: KIND == MccCoordPairKind::COORDS_KIND_LATLON ? MCC_COORDPAIR_KIND_LATLON_STR
: KIND == MccCoordPairKind::COORDS_KIND_GENERIC ? MCC_COORDPAIR_KIND_GENERIC_STR
: MCC_COORDPAIR_KIND_UNKNOWN_STR;
}
template <mcc::traits::mcc_char_range R>
static constexpr MccCoordPairKind MccCoordStrToPairKind(R&& spair)
{
if constexpr (std::is_pointer_v<std::decay_t<R>>) {
return MccCoordStrToPairKind(std::string_view{spair});
}
const auto hash = mcc::utils::FNV1aHash(std::forward<R>(spair));
return hash == mcc::utils::FNV1aHash(MCC_COORDPAIR_KIND_RADEC_ICRS_STR) ? MccCoordPairKind::COORDS_KIND_RADEC_ICRS
: hash == mcc::utils::FNV1aHash(MCC_COORDPAIR_KIND_RADEC_APP_STR) ? MccCoordPairKind::COORDS_KIND_RADEC_APP
: hash == mcc::utils::FNV1aHash(MCC_COORDPAIR_KIND_HADEC_APP_STR) ? MccCoordPairKind::COORDS_KIND_HADEC_APP
: hash == mcc::utils::FNV1aHash(MCC_COORDPAIR_KIND_AZALT_STR) ? MccCoordPairKind::COORDS_KIND_AZALT
: hash == mcc::utils::FNV1aHash(MCC_COORDPAIR_KIND_AZZD_STR) ? MccCoordPairKind::COORDS_KIND_AZZD
: hash == mcc::utils::FNV1aHash(MCC_COORDPAIR_KIND_XY_STR) ? MccCoordPairKind::COORDS_KIND_XY
: hash == mcc::utils::FNV1aHash(MCC_COORDPAIR_KIND_LATLON_STR) ? MccCoordPairKind::COORDS_KIND_LATLON
: hash == mcc::utils::FNV1aHash(MCC_COORDPAIR_KIND_GENERIC_STR) ? MccCoordPairKind::COORDS_KIND_GENERIC
: MccCoordPairKind::COORDS_KIND_UNKNOWN;
}
std::string MccAngleFancyString(std::convertible_to<MccAngle> auto const& ang,
std::format_string<double> val_fmt = "{}")
{
std::string s;
double abs_ang;
if constexpr (std::is_arithmetic_v<std::decay_t<decltype(ang)>>) {
abs_ang = std::abs(ang);
} else {
abs_ang = std::abs(MccAngle{ang});
}
if (abs_ang < 1.0_arcmins) {
std::format_to(std::back_inserter(s), val_fmt, MccAngle{ang}.arcsecs());
s += " arcsecs";
} else if (abs_ang < 1.0_degs) {
std::format_to(std::back_inserter(s), val_fmt, MccAngle{ang}.arcmins());
s += " arcmins";
} else {
std::format_to(std::back_inserter(s), val_fmt, MccAngle{ang}.degrees());
s += " degs";
}
return s;
}
} // namespace mcc