/**************************************************************************** * VCGLib o o * * Visual and Computer Graphics Library o o * * _ O _ * * Copyright(C) 2004-2016 \/)\/ * * Visual Computing Lab /\/| * * ISTI - Italian National Research Council | * * \ * * All rights reserved. * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * * for more details. * * * ****************************************************************************/ #ifndef __VCG_IMPLICIT_SMOOTHER #define __VCG_IMPLICIT_SMOOTHER #include #include #include #include #define PENALTY 10000 namespace vcg{ template class ImplicitSmoother { typedef typename MeshType::FaceType FaceType; typedef typename MeshType::VertexType VertexType; typedef typename MeshType::CoordType CoordType; typedef typename MeshType::ScalarType ScalarType; typedef typename Eigen::Matrix MatrixXm; public: struct FaceConstraint { int numF; std::vector BarycentricW; CoordType TargetPos; FaceConstraint() { numF=-1; } FaceConstraint(int _numF, const std::vector &_BarycentricW, const CoordType &_TargetPos) { numF=_numF; BarycentricW= std::vector (_BarycentricW.begin(),_BarycentricW.end()); TargetPos=_TargetPos; } }; struct Parameter { //the amount of smoothness, useful only if we set the mass matrix ScalarType lambda; //the use of mass matrix to keep the mesh close to its original position //(weighted per area distributed on vertices) bool useMassMatrix; //this bool is used to fix the border vertices of the mesh or not bool fixBorder; //this bool is used to set if cotangent weight is used, this flag to false means uniform laplacian bool useCotWeight; //use this weight for the laplacian when the cotangent one is not used ScalarType lapWeight; //the set of fixed vertices std::vector FixedV; //the set of faces for barycentric constraints std::vector ConstrainedF; //the degree of laplacian int degree; //this is to say if we smooth the positions or the quality bool SmoothQ; Parameter() { degree=1; lambda=0.2; useMassMatrix=true; fixBorder=false; useCotWeight=false; lapWeight=1; SmoothQ=false; } }; private: static void InitSparse(const std::vector > &Index, const std::vector &Values, const int m, const int n, Eigen::SparseMatrix& X) { assert(Index.size()==Values.size()); std::vector > IJV; IJV.reserve(Index.size()); for(size_t i= 0;i(row,col,val)); } X.resize(m,n); X.setFromTriplets(IJV.begin(),IJV.end()); } static void CollectHardConstraints(MeshType &mesh,const Parameter &SParam, std::vector > &IndexC, std::vector &WeightC, bool SmoothQ=false) { std::vector To_Fix; //collect fixed vert if (SParam.fixBorder) { //add penalization constra for (size_t i=0;i::iterator it= std::unique (To_Fix.begin(), To_Fix.end()); To_Fix.resize( std::distance(To_Fix.begin(),it) ); for (size_t i=0;i(IndexV,IndexV)); WeightC.push_back((ScalarType)PENALTY); } }else { int IndexV=To_Fix[i]; IndexC.push_back(std::pair(IndexV,IndexV)); WeightC.push_back((ScalarType)PENALTY); } } } static void CollectBarycentricConstraints(MeshType &mesh, const Parameter &SParam, std::vector > &IndexC, std::vector &WeightC, std::vector &IndexRhs, std::vector &ValueRhs) { ScalarType penalty; int baseIndex=mesh.vert.size(); for (size_t i=0;i=0); assert(FaceN<(int)mesh.face.size()); assert(mesh.face[FaceN].VN()==(int)SParam.ConstrainedF[i].BarycentricW.size()); penalty=ScalarType(1) - SParam.lapWeight; assert(penalty>ScalarType(0) && penalty(ComponentConstraint,IndexV)); WeightC.push_back(currW*penalty); IndexC.push_back(std::pair(IndexV,ComponentConstraint)); WeightC.push_back(currW*penalty); //this to avoid the 1 on diagonal last entry of mass matrix IndexC.push_back(std::pair(ComponentConstraint,ComponentConstraint)); WeightC.push_back(-1); } } for (int j=0;j<3;j++) { //get the index of the current constraint int ComponentConstraint=(IndexConstraint*3)+j; //get per component value ScalarType ComponentV=SParam.ConstrainedF[i].TargetPos.V(j); //add the diagonal value IndexRhs.push_back(ComponentConstraint); ValueRhs.push_back(ComponentV*penalty); } } } public: static void Compute(MeshType &mesh, Parameter &SParam) { //calculate the size of the system int matr_size=mesh.vert.size()+SParam.ConstrainedF.size(); //the laplacian and the mass matrix Eigen::SparseMatrix L,M,B; //initialize the mass matrix std::vector > IndexM; std::vector ValuesM; //add the entries for mass matrix if (SParam.useMassMatrix) MeshToMatrix::MassMatrixEntry(mesh,IndexM,ValuesM,!SParam.SmoothQ); //then add entries for lagrange mult due to barycentric constraints for (size_t i=0;i(baseIndex,baseIndex)); ValuesM.push_back(1); } else { for (int j=0;j<3;j++) { IndexM.push_back(std::pair(baseIndex+j,baseIndex+j)); ValuesM.push_back(1); } } } //add the hard constraints CollectHardConstraints(mesh,SParam,IndexM,ValuesM,SParam.SmoothQ); //initialize sparse mass matrix if (!SParam.SmoothQ) InitSparse(IndexM,ValuesM,matr_size*3,matr_size*3,M); else InitSparse(IndexM,ValuesM,matr_size,matr_size,M); //initialize the barycentric matrix std::vector > IndexB; std::vector ValuesB; std::vector IndexRhs; std::vector ValuesRhs; //then also collect hard constraints if (!SParam.SmoothQ) { CollectBarycentricConstraints(mesh,SParam,IndexB,ValuesB,IndexRhs,ValuesRhs); //initialize sparse constraint matrix InitSparse(IndexB,ValuesB,matr_size*3,matr_size*3,B); } else InitSparse(IndexB,ValuesB,matr_size,matr_size,B); //get the entries for laplacian matrix std::vector > IndexL; std::vector ValuesL; MeshToMatrix::GetLaplacianMatrix(mesh,IndexL,ValuesL,SParam.useCotWeight,SParam.lapWeight,!SParam.SmoothQ); //initialize sparse laplacian matrix if (!SParam.SmoothQ) InitSparse(IndexL,ValuesL,matr_size*3,matr_size*3,L); else InitSparse(IndexL,ValuesL,matr_size,matr_size,L); for (int i=0;i<(SParam.degree-1);i++)L=L*L; //then solve the system Eigen::SparseMatrix S = (M + B + SParam.lambda*L); //SimplicialLDLT Eigen::SimplicialCholesky > solver(S); assert(solver.info() == Eigen::Success); MatrixXm V; if (!SParam.SmoothQ) V=MatrixXm(matr_size*3,1); else V=MatrixXm(matr_size,1); //set the first part of the matrix with vertex values if (!SParam.SmoothQ) { for (size_t i=0;i