#pragma once #include "vec.h" #include "mat.h" #include "inv.h" namespace cgv { namespace math { /// dimension independent implementation of quadric error metrics template class qem : public vec { public: /// standard constructor initializes qem based on dimension qem(int d = -1) : vec((d+1)*(d+2)/2) { if (d>=0) this->zeros(); } /// construct from point and normal qem(const vec& p, const vec& n) : vec((p.size()+1)*(p.size()+2)/2) { set(n, -dot(p,n)); } /// construct from normal and distance to origin qem(const vec& n, T d) : vec((n.size()+1)*(n.size()+2)/2) { set(n,d); } /// set from normal and distance to origin void set(const vec& n, T d) { this->first() = d*d; unsigned int i,j,k=n.size()+1; for (i=0;isize())-1; } ///assignment of a vector v qem& operator = (const qem& v) { *static_cast*>(this) = v; return *this; } /// return the scalar part of the qem const T& scalar_part() const { return this->first(); } /// return the vector part of the qem vec vector_part() const { vec v(dim()); for (unsigned int i=0; i matrix_part() const { unsigned int d = dim(); mat m(d,d); unsigned int i,j,k=d+1; for (i=0; i& p) const { return dot(matrix_part()*p+2.0*vector_part(),p)+scalar_part(); } static bool inside(const vec& p, const vec& minp, const vec& maxp) { for (unsigned int i=0; i maxp(i)) return false; } return true; } /** compute point that minimizes distance to qem and is inside the sphere of radius max_distance around p_ref. If max_distance is -1, no sphere inclusion test is performed. relative_epsilon gives the absolute value of the fraction betweenan eigenvalue and the largest eigenvalue before it is set to zero. epsilon is a global limit on the absolute value of a singular value before accepted as non zero. */ vec minarg(const vec& p_ref, T relative_epsilon, T max_distance = -1, T epsilon = 1e-10) const { unsigned int d = p_ref.size(); assert(d == dim()); T max_distance2 = max_distance*max_distance; mat U,V,A = matrix_part(); diag_mat W,iW; svd(A,U,W,V); U.transpose(); iW = inv(W); vec y_solve = -(inv(W)*(U*vector_part())); vec y_ref = transpose(V)*p_ref; vec y(3); for (unsigned int i = d; i > 0; ) { --i; if (fabs(W(i)) > epsilon && fabs(W(i)*iW(0)) > relative_epsilon) { unsigned int j; for (j = 0; j <= i; ++j) y(j) = y_solve(j); for (; j < d; ++j) y(j) = y_ref(j); vec p = V*y; if (max_distance != -1 && (p-p_ref).sqr_length() <= max_distance2) return p; } } return p_ref; } /// in place qem addition template qem& operator += (const qem& v) { assert(this->size() == v.size()); for (unsigned i=0;isize();++i) (*this)(i) += v(i); return *this; } ///in place qem subtraction template qem& operator -= (const qem& v) { assert(this->size() == v.size()); for (unsigned i=0;isize();++i) (*this)(i) -= v(i); return *this; } ///qem addition template const qem operator + (const qem& v) const { vec r = *this; r += v; return r; } ///qem subtraction template qem operator - (const qem& v) const { qem r = *this; r -= v; return r; } ///negates the qem qem operator-(void) const { qem r=(*this); r=(T)(-1)*r; return r; } ///multiplication with scalar s qem operator * (const T& s) const { qem r = *this; r *= s; return r; } ///divides vector by scalar s qem operator / (const T& s)const { qem r = *this; r /= s; return r; } ///resize the vector void resize(unsigned d) { vec::resize((d+1)*(d+2)/2); } ///test for equality template bool operator == (const qem& v) const { for (unsigned i=0;isize();++i) if((*this)(i) != (T)v(i)) return false; return true; } ///test for inequality template bool operator != (const qem& v) const { for (unsigned i=0;isize();++i) if((*this)(i) != (T)v(i)) return true; return false; } }; ///returns the product of a scalar s and qem v template const qem operator * (const T& s, const qem& v) { qem r = v; r *= s; return r; } } }