mountcontrol/cxx/mcc_coord.h
Timur A. Fatkhullin 18628aff28 ...
2025-04-29 00:11:51 +03:00

309 lines
9.0 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 MccCoordinate class construction
struct MccRadianTag {
};
struct MccDegreeTag {
};
static constexpr MccDegreeTag mcc_degrees{};
struct MccHMSTag {
};
static constexpr MccHMSTag mcc_hms{};
// coordinate representation class
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>();
}
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);
}
friend MccAngle operator+(std::convertible_to<MccAngle> auto&& left, std::convertible_to<MccAngle> auto&& right)
{
// return std::forward<decltype(left)>(left)._angleInRads +
// std::forward<decltype(right)>(right)._angleInRads;
return MccAngle{std::forward<decltype(left)>(left)}._angleInRads +
MccAngle{std::forward<decltype(right)>(right)}._angleInRads;
}
// friend MccAngle operator+(const MccAngle& left, const MccAngle& right)
// {
// return left._angleInRads + right._angleInRads;
// }
friend MccAngle operator-(std::convertible_to<MccAngle> auto&& left, std::convertible_to<MccAngle> auto&& right)
{
// return std::forward<decltype(left)>(left)._angleInRads -
// std::forward<decltype(right)>(right)._angleInRads;
return MccAngle{std::forward<decltype(left)>(left)}._angleInRads -
MccAngle{std::forward<decltype(right)>(right)}._angleInRads;
}
// friend MccAngle operator-(const MccAngle& left, const MccAngle& right)
// {
// return left._angleInRads + right._angleInRads;
// }
template <typename T>
MccAngle operator*(T&& scalar)
requires std::is_arithmetic_v<std::remove_cvref_t<T>>
{
return _angleInRads * scalar;
}
template <typename T>
MccAngle operator/(T&& scalar)
requires std::is_arithmetic_v<std::remove_cvref_t<T>>
{
return _angleInRads / scalar;
}
template <typename T>
MccAngle operator-() // unary '-'
requires std::is_arithmetic_v<std::remove_cvref_t<T>>
{
return -_angleInRads;
}
MccAngle& operator+=(const MccAngle& v)
{
_angleInRads += v._angleInRads;
return *this;
}
MccAngle& operator-=(const MccAngle& v)
{
_angleInRads += v._angleInRads;
return *this;
}
template <typename T>
MccAngle& operator*=(T&& scalar)
requires std::is_arithmetic_v<std::remove_cvref_t<T>>
{
_angleInRads *= scalar;
return *this;
}
template <typename T>
MccAngle& operator/=(T&& scalar)
requires std::is_arithmetic_v<std::remove_cvref_t<T>>
{
_angleInRads /= scalar;
return *this;
}
auto operator<=>(const MccAngle& other) const
{
return _angleInRads <=> other._angleInRads;
}
auto operator==(const MccAngle& other) const
{
return utils::isEqual(_angleInRads, other._angleInRads);
// return _angleInRads == other._angleInRads;
}
auto operator<=>(double other) const
{
return _angleInRads <=> other;
}
auto operator==(double other) const
{
return utils::isEqual(_angleInRads, other);
// return _angleInRads == other;
}
};
enum class MccNormCoordKind {
NORM_KIND_0_360, // [0,360]
NORM_KIND_0_180, // [0,180]
NORM_KIND_90_90, // [-90,90]
};
template <MccNormCoordKind KIND = MccNormCoordKind::NORM_KIND_0_360>
class MccNormCoordinate : public MccAngle
{
public:
static constexpr MccNormCoordKind normKind = KIND;
protected:
static constexpr double pi2 = 2.0 * std::numbers::pi;
static constexpr double normScale = KIND == MccNormCoordKind::NORM_KIND_0_360 ? std::numbers::pi * 2.0
: KIND == MccNormCoordKind::NORM_KIND_0_180 ? std::numbers::pi
: std::numbers::pi / 2.0;
void norm()
{
_angleInRads = std::fmod(_angleInRads, normScale);
if constexpr (KIND == MccNormCoordKind::NORM_KIND_0_360) {
_angleInRads += normScale;
} else if constexpr (KIND == MccNormCoordKind::NORM_KIND_0_180) {
_angleInRads = -_angleInRads;
}
}
};
} // namespace mcc