/**************************************************************************** * VCGLib o o * * Visual and Computer Graphics Library o o * * _ O _ * * Copyright(C) 2014 \/)\/ * * 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 SMOOTHER_FIELD_H #define SMOOTHER_FIELD_H #include #include #include #include #include #include "mesh_to_matrix.h" #define __Delta 10e-6 namespace vcg { namespace tri { template < typename TriMeshType > class ImplicitSmoother { // Temporary variable for the field Eigen::VectorXd angles; // Hard constraints Eigen::VectorXd hard; std::vector isHard; // Soft constraints Eigen::VectorXd soft; Eigen::VectorXd wSoft; double softAlpha; // Face Topology Eigen::MatrixXi TT, TTi; // Edge Topology Eigen::MatrixXi EV, FE, EF; std::vector isBorderEdge; // Per Edge information // Angle between two reference frames Eigen::VectorXd k; // Jumps Eigen::VectorXi p; std::vector pFixed; // Mesh Eigen::MatrixXd V; Eigen::MatrixXi F; // Normals per face Eigen::MatrixXd N; // Singularity index Eigen::VectorXd singularityIndex; // Reference frame per triangle std::vector TPs; // System stuff Eigen::SparseMatrix A; Eigen::VectorXd b; Eigen::VectorXi tag_t; Eigen::VectorXi tag_p; // define types typedef typename TriMeshType::CoordType CoordType; typedef typename TriMeshType::VertexType VertexType; typedef typename TriMeshType::ScalarType ScalarType; TriMeshType &mesh; void computek() { // For every non-border edge for (unsigned eid=0; eid(); assert(tmp(0) - ref1(0) < 1e10); assert(tmp(1) - ref1(1) < 1e10); k[eid] = ktemp; } } } void resetConstraints() { isHard.resize(F.rows()); for(unsigned i=0; i::GetTriMeshData(mesh,F,V); MeshToMatrix::GetTriFFAdjacency(mesh,TT,TTi); MeshToMatrix::GetTriEdgeAdjacency(mesh,EV,FE,EF); // Flag border edges isBorderEdge.resize(EV.rows()); for(unsigned i=0; i::GetNormalData(mesh,NV,N); // Generate reference frames for(unsigned fid=0; fid debug; // debug // MatrixXd B(F.rows(),3); // for(unsigned i=0; i visited(EV.rows()); for(unsigned i=0; i starting(EV.rows()); for(unsigned i=0; i q; for(unsigned i=0; i id_t; int count = 0; for(unsigned i=0; i id_p; for(unsigned i=0; i > T; T.reserve(3 * 4 * count_p); for(unsigned r=0; r(row,tag_t[i] , 2 )); if (isFixed_j) b(row) += 2 * hard[j]; else T.push_back(Eigen::Triplet(row,tag_t[j] ,-2 )); if (isFixed_p) b(row) += -((4 * M_PI)/Nd) * p[eid] ; else T.push_back(Eigen::Triplet(row,tag_p[eid],((4 * M_PI)/Nd))); b(row) += -2 * k[eid]; assert(hard[i] == hard[i]); assert(hard[j] == hard[j]); assert(p[eid] == p[eid]); assert(k[eid] == k[eid]); assert(b(row) == b(row)); } // (j)+1 -th row: t_j [ -2 2 -4pi/N -2 ] if (!isFixed_j) { row = tag_t[j]; if (isFixed_i) b(row) += 2 * hard[i]; else T.push_back(Eigen::Triplet(row,tag_t[i] , -2 )); if (isFixed_j) b(row) += -2 * hard[j]; else T.push_back(Eigen::Triplet(row,tag_t[j] , 2 )); if (isFixed_p) b(row) += ((4 * M_PI)/Nd) * p[eid] ; else T.push_back(Eigen::Triplet(row,tag_p[eid],-((4 * M_PI)/Nd))); b(row) += 2 * k[eid]; assert(k[eid] == k[eid]); assert(b(row) == b(row)); } // (r*3)+2 -th row: pij [ 4pi/N -4pi/N 2*(2pi/N)^2 4pi/N ] if (!isFixed_p) { row = tag_p[eid]; if (isFixed_i) b(row) += -(4 * M_PI)/Nd * hard[i]; else T.push_back(Eigen::Triplet(row,tag_t[i] , (4 * M_PI)/Nd )); if (isFixed_j) b(row) += (4 * M_PI)/Nd * hard[j]; else T.push_back(Eigen::Triplet(row,tag_t[j] , -(4 * M_PI)/Nd )); if (isFixed_p) b(row) += -(2 * pow(((2*M_PI)/Nd),2)) * p[eid] ; else T.push_back(Eigen::Triplet(row,tag_p[eid], (2 * pow(((2*M_PI)/Nd),2)))); b(row) += - (4 * M_PI)/Nd * k[eid]; assert(k[eid] == k[eid]); assert(b(row) == b(row)); } } A = Eigen::SparseMatrix(count_t + count_p, count_t + count_p); A.setFromTriplets(T.begin(), T.end()); // Soft constraints bool addSoft = false; for(unsigned i=0; i > TSoft; TSoft.reserve(2 * count_p); for(unsigned i=0; i(varid,varid,wSoft[i])); bSoft[varid] += wSoft[i] * soft[i]; } } Eigen::SparseMatrix ASoft(count_t + count_p, count_t + count_p); ASoft.setFromTriplets(TSoft.begin(), TSoft.end()); // ofstream s("./As.txt"); // for(unsigned i=0; i Atmp (count_t + count_p, count_t + count_p); Eigen::SparseMatrix Atmp2(count_t + count_p, count_t + count_p); Eigen::SparseMatrix Atmp3(count_t + count_p, count_t + count_p); // Merge the two part of the energy Atmp = (1.0 - softAlpha)*A; Atmp2 = softAlpha * ASoft; Atmp3 = Atmp+Atmp2; A = Atmp3; b = b*(1.0 - softAlpha) + bSoft * softAlpha; } // ofstream s("./A.txt"); // for (int k=0; k::InnerIterator it(A,k); it; ++it) // { // s << it.row() << " " << it.col() << " " << it.value() << endl; // } // s.close(); // // ofstream s2("./b.txt"); // for(unsigned i=0; i > gmm_A; std::vector gmm_b; std::vector ids_to_round; std::vector x; gmm_A.resize(n,n); gmm_b.resize(n); x.resize(n); // Copy A for (int k=0; k::InnerIterator it(A,k); it; ++it) { gmm_A(it.row(),it.col()) += it.value(); } // Copy b for(unsigned i=0; i > gmm_C(0, n); COMISO::ConstrainedSolver cs; //print_miso_settings(cs.misolver()); cs.solve(gmm_C, gmm_A, x, gmm_b, ids_to_round, 0.0, false, true); // Copy the result back for(unsigned i=0; i > solver; solver.compute(A); Eigen::VectorXd x = solver.solve(b); // Copy the result back for(unsigned i=0; i= 0 && alpha < 1); softAlpha = alpha; } void setConstraintSoft(const int fid, const double w, const CoordType &v) { // create eigen vector Eigen::Vector3d c; v.ToEigenVector(c); // // set smoother soft constraint // smoother->setConstraintSoft(fid, w, c); wSoft(fid) = w; soft(fid) = convert3DtoLocal(fid, c); } void setConstraintHard(const int fid, const CoordType &v) { Eigen::Vector3d c; v.ToEigenVector(c); isHard[fid] = true; hard(fid) = convert3DtoLocal(fid, c); } void solve(const int N) { // Reduce the search space by fixing matchings reduceSpace(); // Build the system prepareSystemMatrix(N); #ifdef USECOMISO // Solve with integer roundings solveRoundings(); #else // Solve with no roundings solveNoRoundings(); // Round all p and fix them roundAndFix(); // Build the system prepareSystemMatrix(N); // Solve with no roundings (they are all fixed) solveNoRoundings(); #endif // Find the cones findCones(N); } void getFieldPerFace(vector &fields) { //assert(smoother); // get fields //Eigen::MatrixXd fs = smoother->getFieldPerFace(); Eigen::MatrixXd fs(F.rows(),3); for(unsigned i=0; i > &ffields) { // get fields //Eigen::MatrixXd ffs = smoother->getFFieldPerFace(); Eigen::MatrixXd ffs(F.rows(),6); for(unsigned i=0; i &sings) { // get fields //Eigen::VectorXd s = smoother->getSingularityIndexPerVertex(); // reset output vector of singularities sings.clear(); // resize output vector of singularities sings.resize(singularityIndex.rows()); // copy fields in output vector for (int i = 0; i < singularityIndex.rows(); i++) sings[i] = singularityIndex(i); } void getSingularitiesIndexPerVertexList(vector &sIndexes, const ScalarType t = ScalarType(0)) { // get singularities vector vector sings; getSingularityIndexPerVertex(sings); // reset output list of indexes sIndexes.clear(); // search for indexes with singularty greater than or equal to t for (unsigned long i = 0; i < sings.size(); i++) if (sings[i] >= t) sIndexes.push_back(i); } }; template class FieldSmoother { typedef typename MeshType::FaceType FaceType; typedef typename MeshType::VertexType VertexType; typedef typename MeshType::ScalarType ScalarType; typedef typename MeshType::CoordType CoordType; static void InitQualityByAnisotropyDir(MeshType &mesh) { std::vector QVal; for (size_t i=0;i::VertexClamp(mesh,trimDown,trimUp); vcg::tri::UpdateQuality::FaceFromVertex(mesh); vcg::tri::UpdateColor::PerFaceQualityGray(mesh,trimUp,trimDown); } static void SetEdgeDirection(FaceType *f,int edge) { CoordType dir=f->P0(edge)-f->P1(edge); dir.Normalize(); ScalarType prod1=fabs(dir*f->PD1()); ScalarType prod2=fabs(dir*f->PD2()); if (prod1>prod2) { f->PD1()=dir; f->PD2()=f->N()^dir; }else { f->PD2()=dir; f->PD1()=f->N()^dir; } } static void AddSharpEdgesConstraints(MeshType & mesh, const ScalarType &thr=0.2) { for (size_t i=0;iFFp(j); if (f0==f1)continue; CoordType N0=f0->N(); CoordType N1=f1->N(); if ((N0*N1)>thr)continue; SetEdgeDirection(f0,j); f0->SetS(); } } static void AddBorderConstraints(MeshType & mesh) { for (size_t i=0;iFFp(j); assert(f1!=NULL); if (f0!=f1)continue; SetEdgeDirection(f0,j); f0->SetS(); } } static void AddCurvatureConstraints(MeshType & mesh,const ScalarType &thr=0.5) { for (size_t i=0;ithr)mesh.face[i].SetS(); } public: struct SmoothParam { int Ndir; ScalarType alpha_soft; bool soft_weight; bool align_borders; bool align_sharp; bool hard_curvature; ScalarType sharp_thr; ScalarType curv_thr; SmoothParam() { Ndir=4; alpha_soft=0.0; soft_weight=false; align_borders=false; align_sharp=false; hard_curvature=false; sharp_thr=0.1; curv_thr=0.8; } }; static void InitByCurvature(MeshType & mesh) { tri::RequirePerVertexCurvatureDir(mesh); vcg::tri::UpdateCurvatureFitting::computeCurvature(mesh); vcg::tri::CrossField::SetFaceCrossVectorFromVert(mesh); InitQualityByAnisotropyDir(mesh); } static void GloballyOrient(MeshType &mesh) { vcg::tri::CrossField::MakeDirectionFaceCoherent(mesh,true); } static void SmoothDirections(MeshType &mesh, SmoothParam SParam=SmoothParam()) { //calculathe the maximim weight if needed ScalarType MaxQ=-1; if (SParam.soft_weight) { std::pair MinMax=vcg::tri::Stat::ComputePerFaceQualityMinMax(mesh); MaxQ=MinMax.second; } assert(SParam.alpha_soft>=0); ImplicitSmoother SmoothW(mesh); //SmoothW.initializeSmoother(); //if alpha==0 then do not consider initial constraints and set to a value by default SmoothW.setSoftAlpha(0.001); if (SParam.alpha_soft>0) SmoothW.setSoftAlpha(SParam.alpha_soft); //set hard constraints if needed vcg::tri::UpdateFlags::FaceClearS(mesh); if (SParam.align_borders) AddBorderConstraints(mesh); if (SParam.align_sharp) AddSharpEdgesConstraints(mesh,SParam.sharp_thr); if (SParam.hard_curvature) AddCurvatureConstraints(mesh,SParam.curv_thr); //then set hard constraints int num_fixed=0; for (size_t i=0;i0 if (SParam.alpha_soft>0) { for (size_t i=0;i0.9) dir1=CoordType(0,1,0); if (fabs(dir1*dirN)>0.9) dir1=CoordType(0,0,1); dir1=dirN^dir1; dir1.Normalize(); SmoothW.setConstraintHard(0,dir1); } //solve SmoothW.solve(SParam.Ndir); ///assign back to vertices std::vector Field; SmoothW.getFieldPerFace(Field); for (size_t i=0;i