Program Listing for File vector3.hpp

Return to documentation for file (rcppsw/math/vector3.hpp)

#pragma once

/*******************************************************************************
 * Includes
 ******************************************************************************/
#include <cmath>
#include <limits>
#include <string>

#include "rcppsw/math/radians.hpp"
#include "rcppsw/math/sphere_vector.hpp"
#include "rcppsw/math/vector2.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 vector3 final : public rer::stringizable {
 public:
  using value_type = T;

  struct key_compare {
    template<typename U,
             RCPPSW_SFINAE_DECLDEF(!std::is_floating_point<U>::value)>
    bool operator()(const vector3<U>& lhs, const vector3<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();
      }
      if (lhs.y() != rhs.y()) {
        return lhs.y() < rhs.y();
      }
      return lhs.z() < rhs.z();
    }
    template<typename U,
             RCPPSW_SFINAE_DECLDEF(std::is_floating_point<U>::value)>
    bool operator()(const vector3<U>& lhs, const vector3<U>& rhs) const {
      bool equal_x = std::fabs(lhs.x() - rhs.x()) <= kDOUBLE_EPSILON;
      bool equal_y = std::fabs(lhs.y() - rhs.y()) <= kDOUBLE_EPSILON;

      if (!equal_x) {
        return lhs.x() < rhs.x();
      }
      if (!equal_y) {
        return lhs.y() < rhs.y();
      }
      return lhs.z() < rhs.z();
    }
  };

  struct componentwise_compare {
    template <typename U>
    bool operator()(const vector3<U>& lhs, const vector3<U>& rhs) const {
      return lhs.x() <= rhs.x() && lhs.y() <= rhs.y() && lhs.z() <= rhs.z();
    }
  };

  static constexpr size_t kDIMENSIONALITY = 3;

  static const vector3<T> X; // NOLINT

  static const vector3<T> Y; // NOLINT

  static const vector3<T> Z; // NOLINT

  vector3(void) noexcept = default;

  constexpr vector3(const T& x, const T& y, const T& z)
      : m_x(x), m_y(y), m_z(z) {}

  explicit vector3(const vector2<T>& v) : vector3{v, T{0}} {}

  vector3(const vector2<T>& v, const T& z) : vector3(v.x(), v.y(), z) {}

  RCPPSW_NODISCARD T x(void) { return m_x; }
  RCPPSW_NODISCARD T y(void) { return m_y; }
  RCPPSW_NODISCARD T z(void) { return m_z; }

  RCPPSW_NODISCARD const T& x(void) const { return m_x; }
  RCPPSW_NODISCARD const T& y(void) const { return m_y; }
  RCPPSW_NODISCARD const T& z(void) const { return m_z; }

  void x(const T& x) { m_x = x; }
  void y(const T& y) { m_y = y; }
  void z(const T& z) { m_z = z; }

  vector2<T> to_2D(void) const { return vector2<T>(x(), y()); }

  void set(const T& x, const T& y, const T& z) {
    m_x = x;
    m_y = y;
    m_z = z;
  }

  bool is_pd(void) const { return m_x > 0 && m_y > 0 && m_z > 0; }

  bool is_psd(void) const { return m_x >= 0 && m_y >= 0 && m_z >= 0; }

  RCPPSW_NODISCARD T square_length(void) const {
    return (m_x * m_x) + (m_y * m_y) + (m_z * m_z);
  }

  RCPPSW_NODISCARD double length(void) const { return std::sqrt(square_length()); }

  template <typename U = T,
            RCPPSW_SFINAE_DECLDEF(std::is_floating_point<U>::value)>
  vector3& normalize(void) {
    *this /= this->length();
    return *this;
  }

  vector3 mask(const vector3& the_mask) const {
    return vector3(m_x * the_mask.m_x, m_y * the_mask.m_y, m_z * the_mask.m_z);
  }

  radians xangle(void) const { return radians(std::atan2(m_z, m_y)); }

  radians yangle(void) const { return radians(std::atan2(m_x, m_z)); }

  radians zangle(void) const { return radians(std::atan2(m_y, m_x)); }

  vector2<T> project_on_xy(void) const { return vector2<T>(m_x, m_y); }

  vector2<T> project_on_yz(void) const { return vector2<T>(m_y, m_z); }

  vector2<T> project_on_xz(void) const { return vector2<T>(m_x, m_z); }

  template <typename U = T, RCPPSW_SFINAE_DECLDEF(std::is_floating_point<U>::value)>
  sphere_vector<T> to_spherical(void) const {
    double radius = length();
    return sphere_vector<T>(
        radius, radians(std::acos(m_z / radius)), radians(std::atan2(m_y, m_x)));
  }

  vector3& scale(const T& scale_x, const T& scale_y, const T& scale_z) {
    m_x *= scale_x;
    m_y *= scale_y;
    m_z *= scale_z;
    return *this;
  }

  vector3& scale(const T& factor) { return scale(factor, factor, factor); }

  /* Relational operators */

  template <typename U = T, RCPPSW_SFINAE_DECLDEF(!std::is_floating_point<U>::value)>
  bool operator==(const vector3& other) const {
    return (m_x == other.m_x && m_y == other.m_y && m_z == other.m_z);
  }

  template <typename U = T, RCPPSW_SFINAE_DECLDEF(std::is_floating_point<U>::value)>
  bool operator==(const vector3& other) const {
    return (std::fabs(x() - other.x()) <= kDOUBLE_EPSILON) &&
        (std::fabs(y() - other.y()) <= kDOUBLE_EPSILON) &&
        (std::fabs(z() - other.z()) <= kDOUBLE_EPSILON);
  }

  template <typename U = T, RCPPSW_SFINAE_DECLDEF(!std::is_floating_point<U>::value)>
  bool operator>(const vector3& other) const {
    return (m_x > other.m_x) || ((m_x == other.m_x) && (m_y > other.m_y)) ||
           ((m_x == other.m_x) && (m_y == other.m_y) && (m_z > other.m_z));
  }

  template <typename U = T, RCPPSW_SFINAE_DECLDEF(std::is_floating_point<U>::value)>
  bool operator>(const vector3& other) const {
    bool equal_x = std::fabs(m_x - other.m_x) <= kDOUBLE_EPSILON;
    bool equal_y = std::fabs(m_y - other.m_y) <= kDOUBLE_EPSILON;

    return (m_x > other.m_x) || (equal_x && (m_y > other.m_y)) ||
           (equal_x && equal_y && (m_z > other.m_z));
  }

  bool operator!=(const vector3& other) const { return !(*this == other); }

  /* modifier operators */
  vector3& operator+=(const vector3& other) {
    m_x += other.m_x;
    m_y += other.m_y;
    m_z += other.m_z;
    return *this;
  }

  vector3& operator-=(const vector3& other) {
    m_x -= other.m_x;
    m_y -= other.m_y;
    m_z -= other.m_z;
    return *this;
  }

  vector3& operator*=(const T& val) {
    m_x *= val;
    m_y *= val;
    m_z *= val;
    return *this;
  }

  vector3& operator/=(const T& val) {
    m_x /= val;
    m_y /= val;
    m_z /= val;
    return *this;
  }

  vector3 operator+(const vector3& other) const {
    vector3 res(*this);
    res += other;
    return res;
  }

  vector3<T> operator-(const vector3& other) const {
    vector3<T> res(*this);
    res -= other;
    return res;
  }

  vector3 operator*(const T& val) const {
    vector3 res(*this);
    res *= val;
    return res;
  }

  vector3 operator/(const T& val) const {
    vector3 res(*this);
    res /= val;
    return res;
  }

  vector3 operator-(void) const { return vector3(-m_x, -m_y, -m_z); }

  friend std::ostream& operator<<(std::ostream& stream, const vector3& v) {
    stream << v.to_str();
    return stream;
  }

  friend std::istream& operator>>(std::istream& is, vector3<T>& v) {
    T values[3] = { T(), T(), T() };
    utils::parse_values<T>(is, 3, values, ',');
    v.set(values[0], values[1], values[2]);
    return is;
  }

  std::string to_str(void) const override {
    return "(" + rcppsw::to_string(m_x) + "," + rcppsw::to_string(m_y) + "," +
           rcppsw::to_string(m_z) + ")";
  }

 private:
  /* clang-format off */
  T m_x{0};
  T m_y{0};
  T m_z{0};
  /* clang-format on */
};

/*******************************************************************************
 * Template Specializations
 ******************************************************************************/
using vector3i = vector3<int>;

using vector3z = vector3<size_t>;

using vector3d = vector3<double>;

template<> const vector3i vector3i::X;
template<> const vector3i vector3i::Y;
template<> const vector3i vector3i::Z;

template<> const vector3z vector3z::X;
template<> const vector3z vector3z::Y;
template<> const vector3z vector3z::Z;

template<> const vector3d vector3d::X;
template<> const vector3d vector3d::Y;
template<> const vector3d vector3d::Z;

/*******************************************************************************
 * Macros
 ******************************************************************************/
#define RCPPSW_MATH_VEC3_DIRECT_CONV2FLT(prefix)                          \
  static inline vector3d prefix##vec2dvec(const vector3##prefix& other) { \
    return vector3d(other.x(), other.y(), other.z());                     \
  }

#define RCPPSW_MATH_VEC3_SCALED_CONV2FLT(prefix)                              \
  static inline vector3d prefix##vec2dvec(const vector3##prefix& other,       \
                                          double scale) {                     \
    return vector3d(other.x() * scale, other.y() * scale, other.z() * scale); \
  }

#define RCPPSW_MATH_VEC3_CONV2DISC(dest_prefix, dest_type)                  \
  static inline vector3##dest_prefix dvec2##dest_prefix##vec(               \
      const vector3d& other, double scale) {                                \
     return vector3##dest_prefix(static_cast<dest_type>(other.x() / scale),  \
                                static_cast<dest_type>(other.y() / scale),  \
                                static_cast<dest_type>(other.z() / scale)); \
  }

/*******************************************************************************
 * Conversion Functions
 ******************************************************************************/
RCPPSW_MATH_VEC3_DIRECT_CONV2FLT(i);
RCPPSW_MATH_VEC3_DIRECT_CONV2FLT(z);
RCPPSW_MATH_VEC3_SCALED_CONV2FLT(i);
RCPPSW_MATH_VEC3_SCALED_CONV2FLT(z);
RCPPSW_MATH_VEC3_CONV2DISC(z, size_t);
RCPPSW_MATH_VEC3_CONV2DISC(i, int);

/*******************************************************************************
 * Free Functions
 ******************************************************************************/
template<typename U,
         typename V,
         RCPPSW_SFINAE_DECLDEF(!std::is_floating_point<U>::value),
         RCPPSW_SFINAE_DECLDEF(!std::is_floating_point<V>::value)>
static inline size_t l1norm(const vector3<U>& v1, const vector3<V>& v2) {
  return std::abs(static_cast<int>(v1.x() - v2.x())) +
      std::abs(static_cast<int>(v1.y() - v2.y())) +
      std::abs(static_cast<int>(v1.z() - v2.z()));
}

} /* namespace rcppsw::math */