// Copyright (C) 2003 Davis E. King (davis@dlib.net) // License: Boost Software License See LICENSE.txt for the full license. #ifndef DLIB_VECTOr_H_ #define DLIB_VECTOr_H_ #include #include "vector_abstract.h" #include "../algs.h" #include "../serialize.h" #include #include #include "../matrix/matrix.h" #include #include #if defined(_MSC_VER) && _MSC_VER < 1400 #pragma warning(push) // Despite my efforts to disabuse visual studio of its usual nonsense I can't find a // way to make this warning go away without just disabling it. This is the warning: // dlib\geometry\vector.h(129) : warning C4805: '==' : unsafe mix of type 'std::numeric_limits<_Ty>::is_integer' and type 'bool' in operation // #pragma warning(disable:4805) #endif namespace dlib { template < typename T, long NR = 3 > class vector; // ---------------------------------------------------------------------------------------- template struct vect_promote; template struct largest_type { typedef T type; }; template struct largest_type { typedef U type; }; template struct vect_promote::is_integer == std::numeric_limits::is_integer>::type> { // If both T and U are both either integral or non-integral then just // use the biggest one typedef typename largest_type::type type; }; template struct vect_promote::is_integer != std::numeric_limits::is_integer>::type> { typedef double type; }; // ---------------------------------------------------------------------------------------- // This insanity here is to work around a bug in visual studio 8. These two rebind // structures are actually declared at a few points in this file because just having the // one declaration here isn't enough for visual studio. It takes the three spread around // to avoid all its bugs. template struct vc_rebind { typedef vector type; }; template struct vc_rebind_promote { typedef vector::type,N> type; }; // ---------------------------------------------------------------------------------------- template struct vector_assign_helper { template static void assign ( vector& dest, const vector& src ) { dest.x() = static_cast(src.x()); dest.y() = static_cast(src.y()); } template static void assign ( vector& dest, const vector& src ) { dest.x() = static_cast(src.x()); dest.y() = static_cast(src.y()); dest.z() = static_cast(src.z()); } template static void assign ( vector& dest, const matrix_exp& m ) { T x = static_cast(m(0)); T y = static_cast(m(1)); dest.x() = x; dest.y() = y; } template static void assign ( vector& dest, const matrix_exp& m ) { T x = static_cast(m(0)); T y = static_cast(m(1)); T z = static_cast(m(2)); dest.x() = x; dest.y() = y; dest.z() = z; } }; // This is an overload for the case where you are converting from a floating point // type to an integral type. These overloads make sure values are rounded to // the nearest integral value. template struct vector_assign_helper::is_integer == true && std::numeric_limits::is_integer == false>::type> { template static void assign ( vector& dest, const vector& src ) { dest.x() = static_cast(std::floor(src.x() + 0.5)); dest.y() = static_cast(std::floor(src.y() + 0.5)); } template static void assign ( vector& dest, const vector& src ) { dest.x() = static_cast(std::floor(src.x() + 0.5)); dest.y() = static_cast(std::floor(src.y() + 0.5)); dest.z() = static_cast(std::floor(src.z() + 0.5)); } template static void assign ( vector& dest, const matrix_exp& m ) { dest.x() = static_cast(std::floor(m(0) + 0.5)); dest.y() = static_cast(std::floor(m(1) + 0.5)); dest.z() = static_cast(std::floor(m(2) + 0.5)); } template static void assign ( vector& dest, const matrix_exp& m ) { dest.x() = static_cast(std::floor(m(0) + 0.5)); dest.y() = static_cast(std::floor(m(1) + 0.5)); } }; // ---------------------------------------------------------------------------------------- template class vector : public matrix { /*! INITIAL VALUE - x() == 0 - y() == 0 - z() == 0 CONVENTION - (*this)(0) == x() - (*this)(1) == y() - (*this)(2) == z() !*/ // This insanity here is to work around a bug in visual studio 8. template struct vc_rebind { typedef vector type; }; template struct vc_rebind_promote { typedef vector::type,N> type; }; public: typedef T type; vector ( ) { x() = 0; y() = 0; z() = 0; } // --------------------------------------- vector ( const T _x, const T _y, const T _z ) { x() = _x; y() = _y; z() = _z; } // --------------------------------------- vector ( const vector& item ) : matrix(item) { } // --------------------------------------- template vector ( const vector& item ) { // Do this so that we get the appropriate rounding depending on the relative // type of T and U. vector temp(item); x() = temp.x(); y() = temp.y(); z() = 0; } // --------------------------------------- vector ( const vector& item ) { x() = item.x(); y() = item.y(); z() = 0; } // --------------------------------------- template vector ( const vector& item ) { (*this) = item; } // --------------------------------------- template vector ( const matrix_exp& m) { (*this) = m; } // --------------------------------------- template vector& operator = ( const matrix_exp& m ) { // you can only assign vectors with 3 elements to a dlib::vector object COMPILE_TIME_ASSERT(EXP::NR*EXP::NC == 3 || EXP::NR*EXP::NC == 0); // make sure requires clause is not broken DLIB_ASSERT((m.nr() == 1 || m.nc() == 1) && (m.size() == 3), "\t vector(const matrix_exp& m)" << "\n\t the given matrix is of the wrong size" << "\n\t m.nr(): " << m.nr() << "\n\t m.nc(): " << m.nc() << "\n\t m.size(): " << m.size() << "\n\t this: " << this ); vector_assign_helper::assign(*this, m); return *this; } // --------------------------------------- template vector& operator = ( const vector& item ) { vector_assign_helper::assign(*this, item); return *this; } // --------------------------------------- vector& operator= ( const vector& item ) { x() = item.x(); y() = item.y(); z() = item.z(); return *this; } // --------------------------------------- double length( ) const { return std::sqrt((double)(x()*x() + y()*y() + z()*z())); } // --------------------------------------- double length_squared( ) const { return (double)(x()*x() + y()*y() + z()*z()); } // --------------------------------------- typename vc_rebind::type normalize ( ) const { const double tmp = std::sqrt((double)(x()*x() + y()*y() + z()*z())); return vector ( x()/tmp, y()/tmp, z()/tmp ); } // --------------------------------------- T& x ( ) { return (*this)(0); } // --------------------------------------- T& y ( ) { return (*this)(1); } // --------------------------------------- T& z ( ) { return (*this)(2); } // --------------------------------------- const T& x ( ) const { return (*this)(0); } // --------------------------------------- const T& y ( ) const { return (*this)(1); } // --------------------------------------- const T& z ( ) const { return (*this)(2); } // --------------------------------------- T dot ( const vector& rhs ) const { return x()*rhs.x() + y()*rhs.y() + z()*rhs.z(); } // --------------------------------------- template typename vect_promote::type dot ( const vector& rhs ) const { return x()*rhs.x() + y()*rhs.y() + z()*rhs.z(); } // --------------------------------------- template typename vc_rebind_promote::type cross ( const vector& rhs ) const { typedef vector::type,3> ret_type; return ret_type ( y()*rhs.z() - z()*rhs.y(), z()*rhs.x() - x()*rhs.z(), x()*rhs.y() - y()*rhs.x() ); } // --------------------------------------- vector& operator += ( const vector& rhs ) { x() += rhs.x(); y() += rhs.y(); z() += rhs.z(); return *this; } // --------------------------------------- vector& operator -= ( const vector& rhs ) { x() -= rhs.x(); y() -= rhs.y(); z() -= rhs.z(); return *this; } // --------------------------------------- vector& operator /= ( const T& rhs ) { x() /= rhs; y() /= rhs; z() /= rhs; return *this; } // --------------------------------------- vector& operator *= ( const T& rhs ) { x() *= rhs; y() *= rhs; z() *= rhs; return *this; } // --------------------------------------- vector operator - ( ) const { return vector(-x(), -y(), -z()); } // --------------------------------------- template typename vc_rebind_promote::type operator / ( const U& val ) const { typedef vector::type,3> ret_type; return ret_type(x()/val, y()/val, z()/val); } // --------------------------------------- template bool operator== ( const vector& rhs ) const { return x()==rhs.x() && y()==rhs.y() && z()==rhs.z(); } // --------------------------------------- template bool operator!= ( const vector& rhs ) const { return !(*this == rhs); } // --------------------------------------- void swap ( vector& item ) { dlib::exchange(x(), item.x()); dlib::exchange(y(), item.y()); dlib::exchange(z(), item.z()); } // --------------------------------------- }; // ---------------------------------------------------------------------------------------- template class vector : public matrix { /*! INITIAL VALUE - x() == 0 - y() == 0 CONVENTION - (*this)(0) == x() - (*this)(1) == y() - z() == 0 !*/ // This insanity here is to work around a bug in visual studio 8. template struct vc_rebind { typedef vector type; }; template struct vc_rebind_promote { typedef vector::type,N> type; }; public: typedef T type; vector ( ) { x() = 0; y() = 0; } // --------------------------------------- vector ( const T _x, const T _y ) { x() = _x; y() = _y; } // --------------------------------------- template vector ( const vector& item ) { // Do this so that we get the appropriate rounding depending on the relative // type of T and U. vector temp(item); x() = temp.x(); y() = temp.y(); } // --------------------------------------- vector ( const vector& item ) : matrix(item) { } // --------------------------------------- vector ( const vector& item ) { x() = item.x(); y() = item.y(); } // --------------------------------------- template vector ( const vector& item ) { (*this) = item; } // --------------------------------------- template vector ( const matrix_exp& m) { (*this) = m; } // --------------------------------------- template vector& operator = ( const matrix_exp& m ) { // you can only assign vectors with 2 elements to a dlib::vector object COMPILE_TIME_ASSERT(EXP::NR*EXP::NC == 2 || EXP::NR*EXP::NC == 0); // make sure requires clause is not broken DLIB_ASSERT((m.nr() == 1 || m.nc() == 1) && (m.size() == 2), "\t vector(const matrix_exp& m)" << "\n\t the given matrix is of the wrong size" << "\n\t m.nr(): " << m.nr() << "\n\t m.nc(): " << m.nc() << "\n\t m.size(): " << m.size() << "\n\t this: " << this ); vector_assign_helper::assign(*this, m); return *this; } // --------------------------------------- template vector& operator = ( const vector& item ) { vector_assign_helper::assign(*this, item); return *this; } // --------------------------------------- vector& operator= ( const vector& item ) { x() = item.x(); y() = item.y(); return *this; } // --------------------------------------- double length( ) const { return std::sqrt((double)(x()*x() + y()*y())); } // --------------------------------------- double length_squared( ) const { return (double)(x()*x() + y()*y()); } // --------------------------------------- typename vc_rebind::type normalize ( ) const { const double tmp = std::sqrt((double)(x()*x() + y()*y())); return vector ( x()/tmp, y()/tmp ); } // --------------------------------------- T& x ( ) { return (*this)(0); } // --------------------------------------- T& y ( ) { return (*this)(1); } // --------------------------------------- const T& x ( ) const { return (*this)(0); } // --------------------------------------- const T& y ( ) const { return (*this)(1); } // --------------------------------------- const T z ( ) const { return 0; } // --------------------------------------- T dot ( const vector& rhs ) const { return x()*rhs.x() + y()*rhs.y(); } // --------------------------------------- template typename vect_promote::type dot ( const vector& rhs ) const { return x()*rhs.x() + y()*rhs.y() + z()*rhs.z(); } // --------------------------------------- vector& operator += ( const vector& rhs ) { x() += rhs.x(); y() += rhs.y(); return *this; } // --------------------------------------- vector& operator -= ( const vector& rhs ) { x() -= rhs.x(); y() -= rhs.y(); return *this; } // --------------------------------------- vector& operator /= ( const T& rhs ) { x() /= rhs; y() /= rhs; return *this; } // --------------------------------------- vector& operator *= ( const T& rhs ) { x() *= rhs; y() *= rhs; return *this; } // --------------------------------------- vector operator - ( ) const { return vector(-x(), -y()); } // --------------------------------------- template typename vc_rebind_promote::type operator / ( const U& val ) const { typedef vector::type,2> ret_type; return ret_type(x()/val, y()/val); } // --------------------------------------- template bool operator== ( const vector& rhs ) const { return x()==rhs.x() && y()==rhs.y() && z()==rhs.z(); } // --------------------------------------- bool operator== ( const vector& rhs ) const { return x()==rhs.x() && y()==rhs.y(); } // --------------------------------------- template bool operator!= ( const vector& rhs ) const { return !(*this == rhs); } // --------------------------------------- bool operator!= ( const vector& rhs ) const { return !(*this == rhs); } // --------------------------------------- void swap ( vector& item ) { dlib::exchange(x(), item.x()); dlib::exchange(y(), item.y()); } // --------------------------------------- template typename vc_rebind_promote::type cross ( const vector& rhs ) const { typedef vector::type,3> ret_type; return ret_type ( y()*rhs.z(), - x()*rhs.z(), x()*rhs.y() - y()*rhs.x() ); } // --------------------------------------- }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- template inline const typename vc_rebind_promote::type operator+ ( const vector& lhs, const vector& rhs ) { typedef typename vc_rebind_promote::type ret_type; return ret_type(lhs.x()+rhs.x(), lhs.y()+rhs.y()); } // ---------------------------------------------------------------------------------------- template inline const typename vc_rebind_promote::type operator+ ( const vector& lhs, const vector& rhs ) { typedef typename vc_rebind_promote::type ret_type; return ret_type(lhs.x()+rhs.x(), lhs.y()+rhs.y(), lhs.z()+rhs.z()); } // ---------------------------------------------------------------------------------------- template inline const typename vc_rebind_promote::type operator+ ( const vector& lhs, const vector& rhs ) { typedef typename vc_rebind_promote::type ret_type; return ret_type(lhs.x()+rhs.x(), lhs.y()+rhs.y(), rhs.z()); } // ---------------------------------------------------------------------------------------- template inline const typename vc_rebind_promote::type operator+ ( const vector& lhs, const vector& rhs ) { typedef typename vc_rebind_promote::type ret_type; return ret_type(lhs.x()+rhs.x(), lhs.y()+rhs.y(), lhs.z()); } // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- template inline const typename vc_rebind_promote::type operator- ( const vector& lhs, const vector& rhs ) { typedef typename vc_rebind_promote::type ret_type; return ret_type(lhs.x()-rhs.x(), lhs.y()-rhs.y()); } // ---------------------------------------------------------------------------------------- template inline const typename vc_rebind_promote::type operator- ( const vector& lhs, const vector& rhs ) { typedef typename vc_rebind_promote::type ret_type; return ret_type(lhs.x()-rhs.x(), lhs.y()-rhs.y(), lhs.z()-rhs.z()); } // ---------------------------------------------------------------------------------------- template inline const typename vc_rebind_promote::type operator- ( const vector& lhs, const vector& rhs ) { typedef typename vc_rebind_promote::type ret_type; return ret_type(lhs.x()-rhs.x(), lhs.y()-rhs.y(), -rhs.z()); } // ---------------------------------------------------------------------------------------- template inline const typename vc_rebind_promote::type operator- ( const vector& lhs, const vector& rhs ) { typedef typename vc_rebind_promote::type ret_type; return ret_type(lhs.x()-rhs.x(), lhs.y()-rhs.y(), lhs.z()); } // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- template inline typename disable_if, const typename vc_rebind_promote::type >::type operator* ( const vector& v, const U& s ) { typedef typename vc_rebind_promote::type ret_type; return ret_type(v.x()*s, v.y()*s); } // ---------------------------------------------------------------------------------------- template inline typename disable_if, const typename vc_rebind_promote::type >::type operator* ( const U& s, const vector& v ) { typedef typename vc_rebind_promote::type ret_type; return ret_type(v.x()*s, v.y()*s); } // ---------------------------------------------------------------------------------------- template inline typename disable_if, const typename vc_rebind_promote::type >::type operator* ( const vector& v, const U& s ) { typedef typename vc_rebind_promote::type ret_type; return ret_type(v.x()*s, v.y()*s, v.z()*s); } // ---------------------------------------------------------------------------------------- template inline typename disable_if, const typename vc_rebind_promote::type >::type operator* ( const U& s, const vector& v ) { typedef typename vc_rebind_promote::type ret_type; return ret_type(v.x()*s, v.y()*s, v.z()*s); } // ---------------------------------------------------------------------------------------- template inline void swap ( vector & a, vector & b ) { a.swap(b); } // ---------------------------------------------------------------------------------------- template inline void serialize ( const vector& item, std::ostream& out ) { try { serialize(item.x(),out); serialize(item.y(),out); serialize(item.z(),out); } catch (serialization_error& e) { throw serialization_error(e.info + "\n while serializing object of type vector"); } } template inline void deserialize ( vector& item, std::istream& in ) { try { deserialize(item.x(),in); deserialize(item.y(),in); deserialize(item.z(),in); } catch (serialization_error& e) { item.x() = 0; item.y() = 0; item.z() = 0; throw serialization_error(e.info + "\n while deserializing object of type vector"); } } // ---------------------------------------------------------------------------------------- template inline void serialize ( const vector& item, std::ostream& out ) { try { serialize(item.x(),out); serialize(item.y(),out); } catch (serialization_error& e) { throw serialization_error(e.info + "\n while serializing object of type vector"); } } template inline void deserialize ( vector& item, std::istream& in ) { try { deserialize(item.x(),in); deserialize(item.y(),in); } catch (serialization_error& e) { item.x() = 0; item.y() = 0; throw serialization_error(e.info + "\n while deserializing object of type vector"); } } // ---------------------------------------------------------------------------------------- template std::ostream& operator<< ( std::ostream& out, const vector& item ) { out << "(" << item.x() << ", " << item.y() << ", " << item.z() << ")"; return out; } template std::istream& operator>>( std::istream& in, vector& item ) { // eat all the crap up to the '(' while (in.peek() == ' ' || in.peek() == '\t' || in.peek() == '\r' || in.peek() == '\n') in.get(); // there should be a '(' if not then this is an error if (in.get() != '(') { in.setstate(in.rdstate() | std::ios::failbit); return in; } // eat all the crap up to the first number while (in.peek() == ' ' || in.peek() == '\t') in.get(); in >> item.x(); if (!in.good()) return in; // eat all the crap up to the next number while (in.peek() == ' ' || in.peek() == '\t' || in.peek() == ',') in.get(); in >> item.y(); if (!in.good()) return in; // eat all the crap up to the next number while (in.peek() == ' ' || in.peek() == '\t' || in.peek() == ',') in.get(); in >> item.z(); if (!in.good()) return in; // eat all the crap up to the ')' while (in.peek() == ' ' || in.peek() == '\t') in.get(); // there should be a ')' if not then this is an error if (in.get() != ')') in.setstate(in.rdstate() | std::ios::failbit); return in; } // ---------------------------------------------------------------------------------------- template std::ostream& operator<< ( std::ostream& out, const vector& item ) { out << "(" << item.x() << ", " << item.y() << ")"; return out; } template std::istream& operator>>( std::istream& in, vector& item ) { // eat all the crap up to the '(' while (in.peek() == ' ' || in.peek() == '\t' || in.peek() == '\r' || in.peek() == '\n') in.get(); // there should be a '(' if not then this is an error if (in.get() != '(') { in.setstate(in.rdstate() | std::ios::failbit); return in; } // eat all the crap up to the first number while (in.peek() == ' ' || in.peek() == '\t') in.get(); in >> item.x(); if (!in.good()) return in; // eat all the crap up to the next number while (in.peek() == ' ' || in.peek() == '\t' || in.peek() == ',') in.get(); in >> item.y(); if (!in.good()) return in; // eat all the crap up to the ')' while (in.peek() == ' ' || in.peek() == '\t') in.get(); // there should be a ')' if not then this is an error if (in.get() != ')') in.setstate(in.rdstate() | std::ios::failbit); return in; } // ---------------------------------------------------------------------------------------- typedef vector point; typedef vector dpoint; // ---------------------------------------------------------------------------------------- inline bool is_convex_quadrilateral ( const std::array& pts ) { auto orientation = [&](size_t i) { size_t a = (i+1)%4; size_t b = (i+3)%4; return (pts[a]-pts[i]).cross(pts[b]-pts[i]).z(); }; // If pts has any infinite points then this isn't a valid quadrilateral. for (auto& p : pts) { if (p.x() == std::numeric_limits::infinity()) return false; if (p.y() == std::numeric_limits::infinity()) return false; } double s0 = orientation(0); double s1 = orientation(1); double s2 = orientation(2); double s3 = orientation(3); // if all these things have the same sign then it's convex. return (s0>0&&s1>0&&s2>0&&s3>0) || (s0<0&&s1<0&&s2<0&&s3<0); } // ---------------------------------------------------------------------------------------- template < typename array_of_dpoints > inline double polygon_area ( const array_of_dpoints& pts ) { if (pts.size() <= 2) return 0; double val = 0; for (size_t i = 1; i < pts.size(); ++i) val += (double)pts[i].x()*pts[i-1].y() - pts[i].y()*pts[i-1].x(); const size_t end = pts.size()-1; val += (double)pts[0].x()*pts[end].y() - pts[0].y()*pts[end].x(); return std::abs(val)/2.0; } // ---------------------------------------------------------------------------------------- } namespace std { /*! Define std::less > so that you can use vectors in the associative containers. !*/ template struct less > { typedef dlib::vector first_argument_type; typedef dlib::vector second_argument_type; typedef bool result_type; inline bool operator() (const dlib::vector & a, const dlib::vector & b) const { if (a.x() < b.x()) return true; else if (a.x() > b.x()) return false; else if (a.y() < b.y()) return true; else if (a.y() > b.y()) return false; else if (a.z() < b.z()) return true; else if (a.z() > b.z()) return false; else return false; } }; /*! Define std::less > so that you can use vectors in the associative containers. !*/ template struct less > { typedef dlib::vector first_argument_type; typedef dlib::vector second_argument_type; typedef bool result_type; inline bool operator() (const dlib::vector & a, const dlib::vector & b) const { if (a.x() < b.x()) return true; else if (a.x() > b.x()) return false; else if (a.y() < b.y()) return true; else if (a.y() > b.y()) return false; else return false; } }; } #if defined(_MSC_VER) && _MSC_VER < 1400 // restore warnings back to their previous settings #pragma warning(pop) #endif #endif // DLIB_VECTOr_H_