File vector.hpp
Go to the documentation of this file
#pragma once
#include <concepts>
#include <type_traits>
template <typename T, typename U>
concept Number = requires(T obj, U thi) {
requires std::is_arithmetic_v<T> || requires {
obj + thi;
obj - thi;
obj * thi;
obj / thi;
};
};
template <typename Numeric, typename Derived>
class Vector3D;
template <typename T, typename Numeric>
concept VectorCompatible = requires(T vec) {
{ vec.X_coord } -> std::same_as<Numeric&>;
{ vec.Y_coord } -> std::same_as<Numeric&>;
{ vec.Z_coord } -> std::same_as<Numeric&>;
};
template <typename ExplicitType, typename Default>
using Either = std::conditional_t<std::is_same_v<ExplicitType, void>, Default, ExplicitType>;
template <typename Numeric, typename Derived = void>
class Vector3D {
public:
using NumericType = Numeric;
using ClassType = Either<Derived, Vector3D<NumericType>>;
constexpr static NumericType rad2DegFactor = NumericType(57.2957795131);
public:
constexpr static Vector3D<Numeric> Up = Vector3D<Numeric>(0, 0, 1);
constexpr static Vector3D<Numeric> Down = Vector3D<Numeric>(0, 0, -1);
constexpr static Vector3D<Numeric> Left = Vector3D<Numeric>(-1, 0, 0);
constexpr static Vector3D<Numeric> Right = Vector3D<Numeric>(1, 0, 0);
constexpr static Vector3D<Numeric> Forward = Vector3D<Numeric>(0, 1, 0);
constexpr static Vector3D<Numeric> Backward = Vector3D<Numeric>(0, -1, 0);
NumericType X_coord = 0;
NumericType Y_coord = 0;
NumericType Z_coord = 0;
constexpr explicit Vector3D(): X_coord(0), Y_coord(0), Z_coord(0) {}
constexpr Vector3D(NumericType initial_X_coord, NumericType initial_Y_coord, NumericType initial_Z_coord):
X_coord(initial_X_coord), Y_coord(initial_Y_coord), Z_coord(initial_Z_coord) {}
constexpr ClassType FromPolarDegrees(NumericType Pitch, NumericType Yaw, NumericType Radius) {
return Vector3D(Pitch, Yaw, Radius);
}
friend std::ostream& operator<<(std::ostream& os, const ClassType& obj) {
os << "Vec<" << obj.X_coord << ", " << obj.Y_coord << ", " << obj.Z_coord << ">";
return os;
}
operator bool() const { return X_coord || Y_coord || Z_coord; }
ClassType operator+(const VectorCompatible<NumericType> auto& other) const {
return ClassType(X_coord + other.X_coord, Y_coord + other.Y_coord, Z_coord + other.Z_coord);
}
ClassType operator-(const VectorCompatible<NumericType> auto& other) const {
return ClassType(X_coord - other.X_coord, Y_coord - other.Y_coord, Z_coord - other.Z_coord);
}
constexpr ClassType operator*(const NumericType& scalar) const {
return ClassType(X_coord * scalar, Y_coord * scalar, Z_coord * scalar);
}
ClassType operator/(NumericType scalar) const {
return ClassType(X_coord / scalar, Y_coord / scalar, Z_coord / scalar);
}
NumericType dot(const VectorCompatible<NumericType> auto& other) const {
return X_coord * other.X_coord + Y_coord * other.Y_coord + Z_coord * other.Z_coord;
}
ClassType cross(const VectorCompatible<NumericType> auto& other) const {
return ClassType(
Y_coord * other.Z_coord - Z_coord * other.Y_coord,
Z_coord * other.X_coord - X_coord * other.Z_coord,
X_coord * other.Y_coord - Y_coord * other.X_coord
);
}
NumericType magnitude() const {
auto xs = X_coord * X_coord;
auto ys = Y_coord * Y_coord;
auto zs = Z_coord * Z_coord;
return sqrt(xs + ys + zs);
}
NumericType magnitudeXY() const { return sqrt(X_coord * X_coord + Y_coord * Y_coord); }
NumericType magnitudeXZ() const { return sqrt(X_coord * X_coord + Z_coord * Z_coord); }
NumericType magnitudeYZ() const { return sqrt(Y_coord * Y_coord + Z_coord * Z_coord); }
ClassType normalize() const {
NumericType mag = magnitude();
if (mag != 0) {
return ClassType(X_coord / mag, Y_coord / mag, Z_coord / mag);
}
return ClassType(X_coord, Y_coord, Z_coord);
}
NumericType pitch() const { return atan2(Z_coord, magnitudeXY()) * rad2DegFactor; }
NumericType yaw() const { return atan2(X_coord, Y_coord) * rad2DegFactor; }
NumericType angleTo(const VectorCompatible<NumericType> auto& other) const {
if (!(*this && other)) {
return NumericType(0);
}
auto normalThis = normalize();
auto normalOther = other.normalize(); // Need a way to efficiently do this without precision loss. Need to
// change types on the fly?
auto dotted = normalThis.dot(normalOther);
return acos(dotted) * rad2DegFactor;
}
};