Program Listing for File vector2.hpp
↰ Return to documentation for file (rcppsw/math/vector2.hpp
)
#pragma once
/*******************************************************************************
* Includes
******************************************************************************/
#include <cmath>
#include <limits>
#include <string>
#include "rcppsw/math/radians.hpp"
#include "rcppsw/rcppsw.hpp"
#include "rcppsw/types/discretize_ratio.hpp"
#include "rcppsw/er/stringizable.hpp"
/*******************************************************************************
* Namespaces/Decls
******************************************************************************/
namespace rcppsw::math {
/*******************************************************************************
* Class Definitions
******************************************************************************/
template <typename T>
class vector2 final : public er::stringizable {
public:
using value_type = T;
struct key_compare {
template<typename U = T,
RCPPSW_SFINAE_DECLDEF(!std::is_floating_point<U>::value)>
bool operator()(const vector2<U>& lhs, const vector2<U>& rhs) const {
/* Order based on X unless X's are equal, if so order on Y, etc. */
if (lhs.x() != rhs.x()) {
return lhs.x() < rhs.x();
}
return lhs.y() < rhs.y();
}
template<typename U = T,
RCPPSW_SFINAE_DECLDEF(std::is_floating_point<U>::value)>
bool operator()(const vector2<U>& lhs, const vector2<U>& rhs) const {
bool equal_x = std::fabs(lhs.x() - rhs.x()) <= kDOUBLE_EPSILON;
if (!equal_x) {
return lhs.x() < rhs.x();
}
return lhs.y() < rhs.y();
}
};
struct componentwise_compare {
bool operator()(const vector2<T>& lhs, const vector2<T>& rhs) const {
return lhs.x() <= rhs.x() && lhs.y() <= rhs.y();
}
};
static constexpr size_t kDIMENSIONALITY = 2;
static const vector2<T> X; // NOLINT
static const vector2<T> Y; // NOLINT
template<typename U,
typename V,
RCPPSW_SFINAE_DECLDEF(!std::is_floating_point<U>::value &&
!std::is_floating_point<V>::value)>
static size_t l1norm(const vector2<U>& v1, const vector2<V>& v2) {
return std::abs(static_cast<int>(v1.x() - v2.x())) +
std::abs(static_cast<int>(v1.y() - v2.y()));
}
vector2(void) noexcept = default;
constexpr vector2(const T& x, const T& y) noexcept : m_x(x), m_y(y) {}
template <typename U = T, RCPPSW_SFINAE_DECLDEF(std::is_floating_point<U>::value)>
vector2(const T& length, const radians& angle) noexcept
: m_x(std::cos(angle.v()) * length),
m_y(std::sin(angle.v()) * length) {}
RCPPSW_NODISCARD T x(void) { return m_x; }
RCPPSW_NODISCARD T y(void) { return m_y; }
RCPPSW_NODISCARD const T& x(void) const { return m_x; }
RCPPSW_NODISCARD const T& y(void) const { return m_y; }
void x(const T& x) { m_x = x; }
void y(const T& y) { m_y = y; }
bool is_pd(void) const { return m_x > 0 && m_y > 0; }
bool is_psd(void) const { return m_x >= 0 && m_y >= 0; }
void set(const T& x, const T& y) {
m_x = x;
m_y = y;
}
template <typename U = T, RCPPSW_SFINAE_DECLDEF(std::is_floating_point<U>::value)>
void set_from_polar(const T& length, const radians& angle) {
m_x = std::cos(angle.v()) * length;
m_y = std::sin(angle.v()) * length;
}
RCPPSW_NODISCARD T square_length(void) const { return (m_x * m_x) + (m_y * m_y); }
RCPPSW_NODISCARD double length(void) const { return std::sqrt(square_length()); }
template <typename U = T, RCPPSW_SFINAE_DECLDEF(std::is_floating_point<U>::value)>
vector2& normalize(void) {
*this /= this->length();
return *this;
}
radians angle(void) const { return radians(std::atan2(m_y, m_x)); }
template <typename U = T, RCPPSW_SFINAE_DECLDEF(std::is_floating_point<U>::value)>
vector2& rotate(const radians& angle) {
T sin_val, cos_val;
::sincos(angle.v(), &sin_val, &cos_val);
m_x = m_x * cos_val - m_y * sin_val;
m_y = m_x * sin_val + m_y * cos_val;
return *this;
}
vector2& scale(const T& scale_x, const T& scale_y) {
m_x *= scale_x;
m_y *= scale_y;
return *this;
}
vector2& scale(const T& factor) { return scale(factor, factor); }
/* relational operators */
template <typename U = T, RCPPSW_SFINAE_DECLDEF(!std::is_floating_point<U>::value)>
bool operator==(const vector2& other) const {
return (m_x == other.m_x && m_y == other.m_y);
}
template <typename U = T, RCPPSW_SFINAE_DECLDEF(std::is_floating_point<U>::value)>
bool operator==(const vector2& other) const {
return (std::fabs(x() - other.x()) <= kDOUBLE_EPSILON) &&
(std::fabs(y() - other.y()) <= kDOUBLE_EPSILON);
}
template <typename U = T, RCPPSW_SFINAE_DECLDEF(!std::is_floating_point<U>::value)>
bool operator<(const vector2& other) const {
return (m_x < other.m_x) || ((m_x == other.m_x) && (m_y < other.m_y));
}
template <typename U = T, RCPPSW_SFINAE_DECLDEF(!std::is_floating_point<U>::value)>
bool operator>(const vector2& other) const {
return (m_x > other.m_x) || ((m_x == other.m_x) && (m_y > other.m_y));
}
bool operator<=(const vector2& other) const {
return *this < other || *this == other;
}
bool operator>=(const vector2& other) const {
return *this > other || *this == other;
}
bool operator!=(const vector2& other) const { return !(*this == other); }
/* modifier operators */
vector2& operator+=(const vector2& other) {
m_x += other.m_x;
m_y += other.m_y;
return *this;
}
vector2& operator-=(const vector2& other) {
m_x -= other.m_x;
m_y -= other.m_y;
return *this;
}
vector2& operator*=(T val) {
m_x *= val;
m_y *= val;
return *this;
}
vector2& operator/=(T val) {
m_x /= val;
m_y /= val;
return *this;
}
vector2 operator+(const vector2& other) const {
vector2 res(*this);
res += other;
return res;
}
vector2<T> operator-(const vector2& other) const {
vector2<T> res(*this);
res -= other;
return res;
}
vector2 operator*(T val) const {
vector2 res(*this);
res *= val;
return res;
}
vector2 operator/(T val) const {
vector2 res(*this);
res /= val;
return res;
}
vector2 operator-(void) const { return vector2(-m_x, -m_y); }
friend std::ostream& operator<<(std::ostream& stream, const vector2& v) {
stream << v.to_str();
return stream;
}
friend std::istream& operator>>(std::istream& is, vector2<T>& v) {
T values[2] = { T(), T() };
utils::parse_values<T>(is, 2, values, ',');
v.set(values[0], values[1]);
return is;
}
std::string to_str(void) const override {
return "(" + rcppsw::to_string(m_x) + "," + rcppsw::to_string(m_y) + ")";
}
private:
/* clang-format off */
T m_x{0};
T m_y{0};
/* clang-format on */
};
/*******************************************************************************
* Template Specializations
******************************************************************************/
using vector2i = vector2<int>;
using vector2z = vector2<size_t>;
using vector2d = vector2<double>;
template<> const vector2i vector2i::X;
template<> const vector2i vector2i::Y;
template<> const vector2z vector2z::X;
template<> const vector2z vector2z::Y;
template<> const vector2d vector2d::X;
template<> const vector2d vector2d::Y;
/*******************************************************************************
* Macros
******************************************************************************/
#define RCPPSW_MATH_VEC2_DIRECT_CONVF(prefix) \
static inline vector2d prefix##vec2dvec(const vector2##prefix& other) { \
return vector2d(other.x(), other.y()); \
}
#define RCPPSW_MATH_VEC2_SCALED_CONVF(prefix) \
static inline vector2d prefix##vec2dvec(const vector2##prefix& other, \
double scale) { \
return vector2d(other.x() * scale, other.y() * scale); \
}
#define RCPPSW_MATH_VEC2_CONV2DISC(dest_prefix, dest_type) \
static inline vector2##dest_prefix dvec2##dest_prefix##vec( \
const vector2d& other, double scale) { \
return vector2##dest_prefix(static_cast<dest_type>(other.x() / scale), \
static_cast<dest_type>(other.y() / scale)); \
}
/*******************************************************************************
* Conversion Functions
******************************************************************************/
RCPPSW_MATH_VEC2_DIRECT_CONVF(i);
RCPPSW_MATH_VEC2_DIRECT_CONVF(z);
RCPPSW_MATH_VEC2_SCALED_CONVF(i);
RCPPSW_MATH_VEC2_SCALED_CONVF(z);
RCPPSW_MATH_VEC2_CONV2DISC(z, size_t);
RCPPSW_MATH_VEC2_CONV2DISC(i, int);
} /* namespace rcppsw::math */