373 lines
9.8 KiB
C++
373 lines
9.8 KiB
C++
#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<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;
|
|
}
|
|
|
|
// 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 <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;
|
|
}
|
|
|
|
operator double() const
|
|
{
|
|
return _angleInRads;
|
|
}
|
|
|
|
|
|
template <typename T>
|
|
T degrees() const
|
|
{
|
|
return _angleInRads * 180.0 / std::numbers::pi;
|
|
}
|
|
|
|
double degrees() const
|
|
{
|
|
return degrees<double>();
|
|
}
|
|
|
|
template <traits::mcc_output_char_range T>
|
|
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<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;
|
|
}
|
|
|
|
|
|
template <typename SelfT>
|
|
SelfT& operator-(this SelfT& self)
|
|
{
|
|
self._angleInRads = -self._angleInRads;
|
|
|
|
return self;
|
|
}
|
|
};
|
|
|
|
|
|
// binary arithmetic operations
|
|
|
|
template <std::convertible_to<MccAngle> T1, std::convertible_to<MccAngle> T2>
|
|
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>, T1, T2>;
|
|
|
|
return res_t{(double)v1 + (double)v2};
|
|
}
|
|
|
|
template <std::convertible_to<MccAngle> T1, std::convertible_to<MccAngle> T2>
|
|
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>, T1, T2>;
|
|
|
|
return res_t{(double)v1 - (double)v2};
|
|
}
|
|
|
|
|
|
template <std::convertible_to<MccAngle> T1, std::convertible_to<MccAngle> T2>
|
|
auto operator*(const T1& v1, const T2& v2)
|
|
{
|
|
if constexpr (std::is_arithmetic_v<T1>) {
|
|
return v2 *= v1;
|
|
} else if constexpr (std::is_arithmetic_v<T2>) {
|
|
return v1 *= v2;
|
|
} else {
|
|
static_assert(false, "INCOMPATIBLE TYPES!");
|
|
}
|
|
}
|
|
|
|
template <std::convertible_to<MccAngle> T1, typename T2>
|
|
auto operator/(const T1& v1, const T2& v2)
|
|
requires std::is_arithmetic_v<T2>
|
|
{
|
|
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<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 = 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>()
|
|
};
|
|
|
|
} // namespace mcc
|