diff --git a/Geo/Curvature.cpp b/Geo/Curvature.cpp index 5ccf9a00b364170310ca6f162ef6a4de37e878a3..35f66c4fd7e23b6f76eea9ae30fa82313a3fb423 100644 --- a/Geo/Curvature.cpp +++ b/Geo/Curvature.cpp @@ -10,6 +10,10 @@ #include<fstream> #include<cmath> +#define NEXT(i) ((i)<2 ? (i)+1 : (i)-2) +#define PREV(i) ((i)>0 ? (i)-1 : (i)+2) + +//std::cout << "NEXT(1) = " << NEXT(1) << std::endl; //======================================================================================================== //CONSTRUCTOR @@ -119,6 +123,8 @@ void Curvature::initializeMap() //======================================================================================================== +//COMPUTE THE NORMAL AT THE VERTEX AND THE AREA AROUND + void Curvature::computeVertexNormals() { SVector3 vector_AB; @@ -278,7 +284,7 @@ void Curvature::curvatureTensor() //======================================================================================================== -void Curvature::computeCurvature() +void Curvature::computeCurvature_Simple() { SVector3 vector_E; SVector3 vector_A; @@ -355,6 +361,394 @@ void Curvature::computeCurvature() } } +//======================================================================================================== + +// Compute per-vertex point areas +void Curvature::computePointareas() +{ + +// if (_pointareas.size() == _VertexToInt.size()) +// return; +// need_faces(); + + std::cout << "Computing point areas... " << std::endl; +// std::cout << "The mesh has " << _VertexToInt.size() << " nodes" << std::endl; + + SVector3 e[3]; + SVector3 l2; + SVector3 ew; + + _pointareas.resize(_VertexToInt.size()); + _cornerareas.resize(_ElementToInt.size()); + + + for (int i = 0; i< _ptFinalEntityList.size(); ++i) + { + GEntity* entity = _ptFinalEntityList[i]; //entity is a pointer to one surface of the group "FinalEntityList" + + for (int iElem = 0; iElem < entity->getNumMeshElements(); iElem++) //Loop over the element all the element of the "myTag"-surface + { + MElement *E = entity->getMeshElement(iElem); //Pointer to one element + // The NEW tag of the corresponding element + const int EIdx = _ElementToInt[E->getNum()]; + + MVertex* A = E->getVertex(0); //Pointers to vertices of triangle + MVertex* B = E->getVertex(1); + MVertex* C = E->getVertex(2); + + //Edges + e[0] = SVector3(C->x() - B->x(), C->y() - B->y(), C->z() - B->z()); //vector side of a triangilar element + e[1] = SVector3(A->x() - C->x(), A->y() - C->y(), A->z() - C->z()); + e[2] = SVector3(B->x() - A->x(), B->y() - A->y(), B->z() - A->z()); + + // Compute corner weights + //SVector3 len = crossprod(e[0], e[1]); + //len = norm + //len2 = normSq + double area = 0.5 * norm(crossprod(e[0], e[1])); //area of a triangle + l2 = SVector3( normSq(e[0]), normSq(e[1]), normSq(e[2]) ); + ew = SVector3( l2[0] * (l2[1] + l2[2] - l2[0]), + l2[1] * (l2[2] + l2[0] - l2[1]), + l2[2] * (l2[0] + l2[1] - l2[2]) ); + + if (ew[0] <= 0.0) + { + _cornerareas[EIdx][1] = -0.25 * l2[2] * area / dot(e[0], e[2]); + _cornerareas[EIdx][2] = -0.25 * l2[1] * area / dot(e[0], e[1]); + _cornerareas[EIdx][0] = area - _cornerareas[EIdx][1] - _cornerareas[EIdx][2]; + + } + else if (ew[1] <= 0.0) + { + _cornerareas[EIdx][2] = -0.25 * l2[0] * area / dot(e[1], e[0]); + _cornerareas[EIdx][0] = -0.25 * l2[2] * area / dot(e[1], e[2]); + _cornerareas[EIdx][1] = area - _cornerareas[EIdx][2] - _cornerareas[EIdx][0]; + } + else if (ew[2] <= 0.0) + { + _cornerareas[EIdx][0] = -0.25 * l2[1] * area / dot(e[2], e[1]); + _cornerareas[EIdx][1] = -0.25 * l2[0] * area / dot(e[2], e[0]); + _cornerareas[EIdx][2] = area - _cornerareas[EIdx][0] - _cornerareas[EIdx][1]; + } + else + { + float ewscale = 0.5 * area / (ew[0] + ew[1] + ew[2]); + for (int j = 0; j < 3; j++) + _cornerareas[EIdx][j] = ewscale * (ew[(j+1)%3] + ew[(j+2)%3]); + } + + const int V0 = _VertexToInt[A->getNum()]; + const int V1 = _VertexToInt[B->getNum()]; + const int V2 = _VertexToInt[C->getNum()]; + + _pointareas[V0] += _cornerareas[EIdx][0]; + //_pointareas[faces[i][0]] += _cornerareas[i][0]; + + _pointareas[V1] += _cornerareas[EIdx][1]; + + _pointareas[V2] += _cornerareas[EIdx][2]; + + }//End of loop over iElem + + std::cout << "Done computing pointareas" << std::endl; +// std::cout << "_pointareas.size = " << _pointareas.size() << std::endl; +// std::cout << "_cornerareas.size = " << _cornerareas.size() << std::endl; + + }//End of loop over _ptFinalEntityList + +}//End of the method "computePointareas" + + +//======================================================================================================== + +//Rotate a coordinate system to be perpendicular to the given normal + +void Curvature::rot_coord_sys(const SVector3 &old_u, const SVector3 &old_v, const SVector3 &new_norm, SVector3 &new_u, SVector3 &new_v) +{ + new_u = old_u; + new_v = old_v; + SVector3 old_norm = crossprod(old_u, old_v); + double ndot = dot(old_norm, new_norm); +// if (unlikely(ndot <= -1.0f)) + if (ndot <= -1.0f) + { + new_u = -1.0*new_u; + new_v = -1.0*new_v; + return; + }//end of if-condtion + + SVector3 perp_old = new_norm - ndot*old_norm; + SVector3 dperp = 1.0f/(1 + ndot) * (old_norm + new_norm); + new_u -= dperp*dot(new_u, perp_old); + new_v -= dperp*dot(new_v, perp_old); + +} + + +//======================================================================================================== + +//Project a curvature tensor from the basis spanned by old_u and old_v +//(which are assumed to be unit-length and perpendicular) to the new_u +//and new_v basis + +void Curvature::proj_curv( const SVector3 &old_u, const SVector3 &old_v, + double old_ku, double old_kuv, double old_kv, + const SVector3 &new_u, const SVector3 &new_v, + double &new_ku, double &new_kuv, double &new_kv) +{ + SVector3 r_new_u; + SVector3 r_new_v; + rot_coord_sys(new_u, new_v, crossprod(old_u,old_v), r_new_u, r_new_v); + + double u1 = dot(r_new_u, old_u); + double v1 = dot(r_new_u, old_v); + double u2 = dot(r_new_v, old_u); + double v2 = dot(r_new_v, old_v); + + new_ku = old_ku*u1*u1 + old_kuv*(2.0f * u1*v1) + old_kv*v1*v1; + new_kuv = old_ku*u1*u2 + old_kuv*(u1*v2 * u2*v1) + old_kv*v1*v2; + new_kv = old_ku*u2*u2 + old_kuv*(2.0f * u2*v2) + old_kv*v2*v2; + + +} + + +//======================================================================================================== + +//Given a curvature tensor, find principal directions and curvatures +//Makes sure that pdir1 and pdir2 are perpendicular to normal + +void Curvature::diagonalize_curv(const SVector3 &old_u, const SVector3 &old_v, + double ku, double kuv, double kv, + const SVector3 &new_norm, + SVector3 &pdir1, SVector3 &pdir2, double &k1, double &k2) +{ + SVector3 r_old_u; + SVector3 r_old_v; + + double c = 1.0; + double s = 0.0; + double tt = 0.0; + + rot_coord_sys(old_u, old_v, new_norm, r_old_u, r_old_v); + +// if(unlikely(kuv !=0.0f)) + if(kuv !=0.0) + { + //Jacobi rotation to diagonalize + double h= 0.5*(kv -ku)/ kuv; + tt = (h<0.0) ? + 1.0 / (h - std::sqrt(1.0 + h*h)): + 1.0 / (h + std::sqrt(1.0 + h*h)); + c = 1.0 / std::sqrt(1.0 + tt*tt); + s = tt*c; + } + + k1 = ku - tt *kuv; + k2 = kv + tt *kuv; + + if(std::abs(k1) >= std::abs(k2)) + { + pdir1 = c*r_old_u - s*r_old_v; + } + else + { + std::swap(k1,k2); + pdir1 = s*r_old_u + c*r_old_v; + } + pdir2 = crossprod(new_norm, pdir1); +} + +//======================================================================================================== + +void Curvature::computeCurvature_Rusinkiewicz() +{ + initializeMap(); + computeVertexNormals(); + computePointareas(); + + SVector3 e[3]; + STensor3 w; + SVector3 t; + SVector3 n; + SVector3 b; + SVector3 m; + SVector3 dn; + + int vj; + + double u; + double v; + double dnu; + double dnv; + double c1; + double c2; + double c12; + double wt; + + _pdir1.resize(_VertexToInt.size()); + _pdir2.resize(_VertexToInt.size()); + + _curv1.resize(_VertexToInt.size()); + _curv2.resize(_VertexToInt.size()); + _curv12.resize(_VertexToInt.size()); + + + + std::cout << "Computing Curvature.." << std::endl; + + for (int i = 0; i< _ptFinalEntityList.size(); ++i) + { + GEntity* entity = _ptFinalEntityList[i]; //entity is a pointer to one surface of the group "FinalEntityList" + + for (int iElem = 0; iElem < entity->getNumMeshElements(); iElem++) //Loop over the element all the element of the "myTag"-surface + { + MElement *E = entity->getMeshElement(iElem); //Pointer to one element + + MVertex* A = E->getVertex(0); //Pointers to vertices of triangle + MVertex* B = E->getVertex(1); + MVertex* C = E->getVertex(2); + + const int V0 = _VertexToInt[A->getNum()]; //Tag of the 1st vertex of the triangle + const int V1 = _VertexToInt[B->getNum()]; + const int V2 = _VertexToInt[C->getNum()]; + + + ///Set up an initial coordinate system per vertex: + + _pdir1[V0] = SVector3(B->x() - A->x(), B->y() - A->y(), B->z() - A->z()); + _pdir1[V1] = SVector3(C->x() - B->x(), C->y() - B->y(), C->z() - B->z()); + _pdir1[V2] = SVector3(A->x() - C->x(), A->y() - C->y(), A->z() - C->z()); + + } + } + + for (int ivertex = 0; ivertex < _VertexToInt.size(); ++ivertex) + { + _pdir1[ivertex] = crossprod(_pdir1[ivertex], _VertexNormal[ivertex]); + _pdir1[ivertex].normalize(); + _pdir2[ivertex] = crossprod(_VertexNormal[ivertex], _pdir1[ivertex]); + } + + // Compute curvature per face: + for (int i = 0; i< _ptFinalEntityList.size(); ++i) + { + GEntity* entity = _ptFinalEntityList[i]; //entity is a pointer to one surface of the group "FinalEntityList" + for (int iElem = 0; iElem < entity->getNumMeshElements(); iElem++) //Loop over the element all the element of the "myTag"-surface + { + MElement *E = entity->getMeshElement(iElem); //Pointer to one element + // The NEW tag of the corresponding element + const int EIdx = _ElementToInt[E->getNum()]; + + MVertex* A = E->getVertex(0); //Pointers to vertices of triangle + MVertex* B = E->getVertex(1); + MVertex* C = E->getVertex(2); + + e[0] = SVector3(C->x() - B->x(), C->y() - B->y(), C->z() - B->z()); + e[1] = SVector3(A->x() - C->x(), A->y() - C->y(), A->z() - C->z()); + e[2] = SVector3(B->x() - A->x(), B->y() - A->y(), B->z() - A->z()); + + //SVector3 e[3] = {SVector3(C->x() - B->x(), C->y() - B->y(), C->z() - B->z()), SVector3(A->x() - C->x(), A->y() - C->y(), A->z() - C->z()), SVector3(B->x() - A->x(), B->y() - A->y(), B->z() - A->z()) }; + + + //N-T-B coordinate system per face + t = e[0]; + t.normalize(); + n = crossprod( e[0], e[1]); + b = crossprod(n, t); + b.normalize(); + + //Estimate curvature based on variations of normals along edges: + //intialization: + m = SVector3(0.0, 0.0, 0.0); + //maybe double m[3] = { 0.0, 0.0, 0.0 }; + // w *= 0.0; //Reset w to zero + for (int i = 0; i< 3; ++i) + { + for (int j = 0; j<3; ++j) + { + w(i,j) = 0.0; + } + } + + //filling: + for (int j = 0; j< 3; ++j) + { + u = dot(e[j], t); + v = dot(e[j], b); + + w(0,0) += u*u; + w(0,1) += u*v; + w(2,2) += v*v; + + MVertex* U = E->getVertex(PREV(j)); + MVertex* V = E->getVertex(NEXT(j)); + + const int UIdx = _VertexToInt[U->getNum()]; + const int VIdx = _VertexToInt[V->getNum()]; + + dn = _VertexNormal[UIdx] - _VertexNormal[VIdx]; + + dnu = dot(dn, t); + dnv = dot(dn, b); + + m[0] += dnu*u; + m[1] += dnu*v + dnv*u; + m[2] += dnv*v; + } + + w(1,1) = w(0,0) + w(2,2); + w(1,2) = w(0,1); + + //Least Square Solution + double diag[3]; + if (!ldltdc(w, diag)) + { + std::cout << "ldltdc failed" << std::endl; + continue; + } + ldltsl(w, diag, m, m); + + //Push it back out to the vertices + for (int j = 0; j< 3; ++j) + { + const int old_vj = E->getVertex(j)->getNum(); + const int vj = _VertexToInt[old_vj]; + proj_curv(t, b, m[0], m[1], m[2], _pdir1[vj], _pdir2[vj], c1, c12, c2); + wt = _cornerareas[EIdx][j]/_pointareas[vj]; +// wt = 1.0; + + _curv1[vj] += wt*c1; + _curv12[vj] += wt*c12; + _curv2[vj] += wt*c2; + + } + + } //End of loop over the element (iElem) + + } //End of loop over "_ptFinalEntityList" + + + //Compute principal directions and curvatures at each vertex + for (int ivertex = 0; ivertex < _VertexToInt.size(); ++ivertex) + { + diagonalize_curv(_pdir1[ivertex], _pdir2[ivertex], _curv1[ivertex], _curv12[ivertex], _curv2[ivertex], + _VertexNormal[ivertex], _pdir1[ivertex], _pdir2[ivertex], _curv1[ivertex], _curv2[ivertex]); + } + + std::cout << "Done" << std::endl; + + _VertexCurve.resize( _VertexToInt.size() ); + + for (int ivertex = 0; ivertex < _VertexToInt.size(); ++ivertex) + { + _VertexCurve[ivertex] = (_curv1[ivertex]+_curv2[ivertex])*0.5; + + } + +} //End of the "computeCurvature_Rusinkiewicz" method + + //======================================================================================================== void Curvature::writeToFile( const std::string & filename) diff --git a/Geo/Curvature.h b/Geo/Curvature.h index 33dedcdabce3ffc15d205ac45576ab6ce2f67331..a11dbfca6501e4c18e7cb946e0ec9fd1d866d710 100644 --- a/Geo/Curvature.h +++ b/Geo/Curvature.h @@ -39,6 +39,20 @@ private: //Averaged vertex normals std::vector<SVector3> _VertexNormal; + // Vector of principal dircections + std::vector<SVector3> _pdir1; + std::vector<SVector3> _pdir2; + + // Vector of principal curvature + std::vector<double> _curv1; + std::vector<double> _curv2; + std::vector<double> _curv12; + + // Area around point + std::vector<double> _pointareas; + std::vector<SVector3> _cornerareas; + + //Curvature Tensor per mesh vertex std::vector<STensor3> _CurveTensor; @@ -51,13 +65,75 @@ private: std::vector<double> _VertexCurve; + //----------------------------------------- // PRIVATE METHODS void initializeMap(); void computeVertexNormals(); void curvatureTensor(); - + static void rot_coord_sys(const SVector3 &old_u, const SVector3 &old_v, + const SVector3 &new_norm, SVector3 &new_u, SVector3 &new_v); + void proj_curv( const SVector3 &old_u, const SVector3 &old_v, double old_ku, double old_kuv, + double old_kv, const SVector3 &new_u, const SVector3 &new_v, + double &new_ku, double &new_kuv, double &new_kv); + void diagonalize_curv(const SVector3 &old_u, const SVector3 &old_v, + double ku, double kuv, double kv, + const SVector3 &new_norm, + SVector3 &pdir1, SVector3 &pdir2, double &k1, double &k2); + void computePointareas(); + + // Perform LDL^T decomposition of a symmetric positive definite matrix. + // Like Cholesky, but no square roots. Overwrites lower triangle of matrix. + + static inline bool ldltdc(STensor3& A, double rdiag[3]) + { + double v[2]; + for (int i = 0; i < 3; i++) + { + for (int k = 0; k < i; k++) + v[k] = A(i,k) * rdiag[k]; + for (int j = i; j < 3; j++) + { + double sum = A(i,j); + for (int k = 0; k < i; k++) + sum -= v[k] * A(j,k); + if (i == j) + { + //if (unlikely(sum <= T(0))) + if (sum <= 0.0) + return false; + rdiag[i] = 1.0 / sum; + } + else + { + A(j,i) = sum; + } + } + } + + return true; + } + + // Solve Ax=B after ldltdc + static inline void ldltsl(STensor3& A, double rdiag[3], double B[3], double x[3]) + { + int i; + for (i = 0; i < 3; i++) + { + double sum = B[i]; + for (int k = 0; k < i; k++) + sum -= A(i,k) * x[k]; + x[i] = sum * rdiag[i]; + } + for (i = 3 - 1; i >= 0; i--) + { + double sum = 0; + for (int k = i + 1; k < 3; k++) + sum += A(k,i) * x[k]; + x[i] -= sum * rdiag[i]; + } + } public: @@ -71,7 +147,18 @@ public: ~Curvature(); void retrievePhysicalSurfaces(const std::string & face_tag); - void computeCurvature(); + /// The following function implements algorithm from: + /// Implementation of an Algorithm for Approximating the Curvature Tensor + /// on a Triangular Surface Mesh in the Vish Environment + /// Edwin Matthews, Werner Benger, Marcel Ritter + void computeCurvature_Simple(); + + /// The following function implements algorithm from: + /// Estimating Curvatures and Their Derivatives on Triangle Meshes + /// Szymon Rusinkiewicz, Princeton University + /// Code taken from Rusinkiewicz' 'trimesh2' library + void computeCurvature_Rusinkiewicz(); + void writeToFile( const std::string & filename);