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 */