File CartesianVector.hpp¶
File List > astrea > astro > astro > frames > CartesianVector.hpp
Go to the documentation of this file
#pragma once
#include <array>
#include <typeinfo>
#include <mp-units/math.h>
#include <mp-units/systems/angular/math.h>
#include <units/typedefs.hpp>
#include <utilities/string_util.hpp>
#include <astro/astro.fwd.hpp>
#include <astro/frames/Frame.hpp>
#include <astro/frames/frame_concepts.hpp>
#include <astro/frames/transformations.hpp>
namespace astrea {
namespace astro {
// TODO: Generalize this class further so it can accept copy/move assignment/construction
// from other vectors in the same frame with a compatible unit (Value Type)
template <class T, template <class...> class Template>
struct is_specialization : std::false_type {};
template <template <class...> class Template, class... Args>
struct is_specialization<Template<Args...>, Template> : std::true_type {};
template <class Value_T, class Frame_T>
class CartesianVector {
public:
CartesianVector(const Value_T& x = Value_T(), const Value_T& y = Value_T(), const Value_T& z = Value_T()) :
_vector{ x, y, z }
{
}
// Explicitly deleted copy/move assignment/constructor to prevent implicit frame switches.
template <typename Frame_U>
CartesianVector(const CartesianVector<Value_T, Frame_U>& other) = delete;
template <typename Frame_U>
CartesianVector(CartesianVector<Value_T, Frame_U>&& other) = delete;
template <typename Frame_U>
CartesianVector<Value_T, Frame_T> operator=(const CartesianVector<Value_T, Frame_U>& other) = delete;
template <typename Frame_U>
CartesianVector<Value_T, Frame_T> operator=(CartesianVector<Value_T, Frame_U>&& other) = delete;
template <typename Frame_U>
CartesianVector<Value_T, Frame_U> force_frame_conversion() const
{
return CartesianVector<Value_T, Frame_U>(_vector[0], _vector[1], _vector[2]);
}
Value_T& operator[](size_t index) { return _vector[index]; }
const Value_T& operator[](size_t index) const { return _vector[index]; }
template <typename Value_U>
bool operator==(const CartesianVector<Value_U, Frame_T>& other) const
{
return _vector[0] == other._vector[0] && _vector[1] == other._vector[1] && _vector[2] == other._vector[2];
}
template <typename Frame_U>
requires(!std::is_same<Frame_T, Frame_U>::value)
bool operator==(const CartesianVector<Value_T, Frame_U>& other) const
{
return false;
}
CartesianVector<Value_T, Frame_T> operator+(const CartesianVector<Value_T, Frame_T>& other) const
{
return CartesianVector<Value_T, Frame_T>(
_vector[0] + other.get_x(), _vector[1] + other.get_y(), _vector[2] + other.get_z()
);
}
CartesianVector<Value_T, Frame_T>& operator+=(const CartesianVector<Value_T, Frame_T>& other)
{
_vector[0] += other.get_x();
_vector[1] += other.get_y();
_vector[2] += other.get_z();
return *this;
}
CartesianVector<Value_T, Frame_T> operator-(const CartesianVector<Value_T, Frame_T>& other) const
{
return CartesianVector<Value_T, Frame_T>(
_vector[0] - other.get_x(), _vector[1] - other.get_y(), _vector[2] - other.get_z()
);
}
CartesianVector<Value_T, Frame_T> operator-() const
{
return CartesianVector<Value_T, Frame_T>(-_vector[0], -_vector[1], -_vector[2]);
}
CartesianVector<Value_T, Frame_T>& operator-=(const CartesianVector<Value_T, Frame_T>& other)
{
_vector[0] -= other.get_x();
_vector[1] -= other.get_y();
_vector[2] -= other.get_z();
return *this;
}
template <typename Value_U>
requires(!is_specialization<Value_U, CartesianVector>::value)
CartesianVector<decltype(Value_T{} * Value_U{}), Frame_T> operator*(const Value_U& scalar) const
{
return CartesianVector<decltype(Value_T{} * Value_U{}), Frame_T>(_vector[0] * scalar, _vector[1] * scalar, _vector[2] * scalar);
}
CartesianVector<Value_T, Frame_T>& operator*=(const Unitless& scalar)
{
_vector[0] *= scalar;
_vector[1] *= scalar;
_vector[2] *= scalar;
return *this;
}
template <typename Value_U>
requires(!is_specialization<Value_U, CartesianVector>::value)
CartesianVector<decltype(Value_T{} / Value_U{}), Frame_T> operator/(const Value_U& scalar) const
{
return CartesianVector<decltype(Value_T{} / Value_U{}), Frame_T>(_vector[0] / scalar, _vector[1] / scalar, _vector[2] / scalar);
}
CartesianVector<Value_T, Frame_T>& operator/=(const Unitless& scalar)
{
_vector[0] /= scalar;
_vector[1] /= scalar;
_vector[2] /= scalar;
return *this;
}
Value_T& get_x() { return _vector[0]; }
const Value_T& get_x() const { return _vector[0]; }
Value_T& get_y() { return _vector[1]; }
const Value_T& get_y() const { return _vector[1]; }
Value_T& get_z() { return _vector[2]; }
const Value_T& get_z() const { return _vector[2]; }
template <typename Value_U>
decltype(Value_T{} * Value_U{}) dot(const CartesianVector<Value_U, Frame_T>& other) const
{
return _vector[0] * other.get_x() + _vector[1] * other.get_y() + _vector[2] * other.get_z();
}
template <typename Value_U>
CartesianVector<decltype(Value_T{} * Value_U{}), Frame_T> cross(const CartesianVector<Value_U, Frame_T>& other) const
{
return { _vector[1] * other.get_z() - _vector[2] * other.get_y(),
_vector[2] * other.get_x() - _vector[0] * other.get_z(),
_vector[0] * other.get_y() - _vector[1] * other.get_x() };
}
Value_T norm() const { return sqrt(_vector[0] * _vector[0] + _vector[1] * _vector[1] + _vector[2] * _vector[2]); }
CartesianVector<Unitless, Frame_T> unit() const
{
const Value_T n = norm();
if (n.numerical_value_in(n.unit) == 0) {
// Return zero vector if norm is zero
return CartesianVector<Unitless, Frame_T>(0.0 * mp_units::one, 0.0 * mp_units::one, 0.0 * mp_units::one);
}
return CartesianVector<Unitless, Frame_T>(_vector[0] / n, _vector[1] / n, _vector[2] / n);
}
Angle offset_angle(const CartesianVector<Value_T, Frame_T>& other) const
{
const Value_T v1Mag = norm();
const Value_T v2Mag = other.norm();
if (v1Mag.numerical_value_in(v1Mag.unit) == 0 || v2Mag.numerical_value_in(v2Mag.unit) == 0) {
throw std::runtime_error("Cannot calculate angle with zero-magnitude vector");
}
const auto v1DotV2 = dot(other);
const auto ratio = v1DotV2 / (v1Mag * v2Mag);
if (mp_units::abs(ratio) > 1.0 * mp_units::one) {
return 0.0 * astrea::detail::angle_unit;
} // catch rounding errors - TODO: Make this more intelligent
return mp_units::angular::acos(ratio);
}
template <IsStaticFrame Frame_U>
CartesianVector<Value_T, Frame_U> in_frame(const Date& date) const
{
return frames::rotate_vector_into_frame<Value_T, Frame_T, Frame_U>(*this, date);
}
// /**
// * @brief Transform this vector into another frame at a given date, accounting for both rotation and translation.
// *
// * @tparam Frame_U The target frame type to transform into.
// * @param date The date at which to perform the transformation.
// * @return CartesianVector<Value_T, Frame_U> A new CartesianVector in the target frame.
// * @throws std::runtime_error If the frames do not have a known transformation or if the DCM cannot be obtained.
// */
// template <typename Frame_U>
// requires(IsStaticFrame<Frame_U>)
// CartesianVector<Value_T, Frame_U> with_respect_to_frame(const Date& date) const
// {
// return frames::transform_vector_into_frame<Value_T, Frame_T, Frame_U>(*this, date);
// }
template <typename Frame_U, typename Frame_V>
requires(!IsSameFrame<Frame_T, Frame_U> && HasSameAxis<Frame_T, Frame_U> && !HasSameOrigin<Frame_T, Frame_U>)
CartesianVector<Value_T, Frame_V> translate(const CartesianVector<Value_T, Frame_U>& other) const
{
return CartesianVector<Value_T, Frame_V>(
_vector[0] + other.get_x(), _vector[1] + other.get_y(), _vector[2] + other.get_z()
);
}
template <typename Frame_U, typename Frame_V>
requires(!IsSameFrame<Frame_T, Frame_U> && HasSameAxis<Frame_T, Frame_U> && !HasSameOrigin<Frame_T, Frame_U>)
CartesianVector<Value_T, Frame_V> offset(const CartesianVector<Value_T, Frame_U>& other) const
{
return CartesianVector<Value_T, Frame_V>(
_vector[0] - other.get_x(), _vector[1] - other.get_y(), _vector[2] - other.get_z()
);
}
private:
std::array<Value_T, 3> _vector;
};
template <class Value_T, class Frame_T>
requires(std::is_constructible<Frame_T>::value)
std::ostream& operator<<(std::ostream& os, const CartesianVector<Value_T, Frame_T>& state)
{
// static const Frame_T frame;
os << "[" << state.get_x() << ", " << state.get_y() << ", " << state.get_z() << "]";
return os;
}
template <class Value_T, class Frame_T>
requires(!std::is_constructible<Frame_T>::value)
std::ostream& operator<<(std::ostream& os, const CartesianVector<Value_T, Frame_T>& state)
{
// static const std::string name = utilities::get_type_name<Frame_T>(); // Make this utilities function constexpr
os << "[" << state.get_x() << ", " << state.get_y() << ", " << state.get_z() << "]";
return os;
}
template <typename Value_T, typename Value_U, typename Frame_T>
requires(!is_specialization<Value_U, CartesianVector>::value)
CartesianVector<decltype(Value_T{} * Value_U{}), Frame_T>
operator*(const Value_U& scalar, const CartesianVector<Value_T, Frame_T>& vec)
{
return vec * scalar;
}
template <typename Value_T, typename Value_U, typename Frame_T>
requires(!is_specialization<Value_U, CartesianVector>::value)
CartesianVector<decltype(Value_T{} * Value_U{}), Frame_T>
operator/(const Value_U& scalar, const CartesianVector<Value_T, Frame_T>& vec)
{
return vec / scalar;
}
} // namespace astro
} // namespace astrea