From 5f4726ca08b8bd52eea3a97f745e3756f3e825b1 Mon Sep 17 00:00:00 2001 From: nicopietroni Date: Wed, 12 Nov 2014 15:21:27 +0000 Subject: [PATCH] --- vcg/complex/algorithms/smooth_field.h | 1241 +++++++------------------ 1 file changed, 320 insertions(+), 921 deletions(-) diff --git a/vcg/complex/algorithms/smooth_field.h b/vcg/complex/algorithms/smooth_field.h index 8ba92163..2c0147e9 100644 --- a/vcg/complex/algorithms/smooth_field.h +++ b/vcg/complex/algorithms/smooth_field.h @@ -24,832 +24,25 @@ #ifndef SMOOTHER_FIELD_H #define SMOOTHER_FIELD_H +//eigen stuff #include + +//vcg stuff #include #include -#include #include -#include "mesh_to_matrix.h" +#include -#define __Delta 10e-6 +//igl related stuff +#include +#include +#include +#include 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); - } - -}; +enum SmoothMethod{SMMiq,SMNPoly}; template class FieldSmoother @@ -867,22 +60,32 @@ class FieldSmoother { ScalarType N1=fabs(mesh.vert[i].K1()); ScalarType N2=fabs(mesh.vert[i].K2()); - - ScalarType NMax=std::max(N1,N2); - //ScalarType NMin=std::min(N1,N2); - - ScalarType CurvAni=NMax;//fabs((NMax-NMin)/(NMax+NMin)); - QVal.push_back(CurvAni); - mesh.vert[i].Q()=CurvAni; + QVal.push_back(N1); + QVal.push_back(N2); } + std::sort(QVal.begin(),QVal.end()); int percUp=int(floor(QVal.size()*0.95+0.5)); - int percDown=int(floor(QVal.size()*0.05+0.5)); ScalarType trimUp=QVal[percUp]; - ScalarType trimDown=QVal[percDown]; - vcg::tri::UpdateQuality::VertexClamp(mesh,trimDown,trimUp); + + for (size_t i=0;i1)NMax=1; + if (NMax<-1)NMax=-1; + + if (NMin>1)NMin=1; + if (NMin<-1)NMin=-1; + + ScalarType CurvAni=(NMax-NMin)/2; + mesh.vert[i].Q()=CurvAni; + } vcg::tri::UpdateQuality::FaceFromVertex(mesh); - vcg::tri::UpdateColor::PerFaceQualityGray(mesh,trimUp,trimDown); } static void SetEdgeDirection(FaceType *f,int edge) @@ -939,44 +142,232 @@ class FieldSmoother if (mesh.face[i].Q()>thr)mesh.face[i].SetS(); } + //hard constraints have selected face + static void CollectHardConstraints( MeshType & mesh, + Eigen::VectorXi &HardI, + Eigen::MatrixXd &HardD, + SmoothMethod SMethod, + int Ndir) + { + //count number of hard constraints + int numS=vcg::tri::UpdateSelection::FaceCount(mesh); + HardI=Eigen::MatrixXi(numS,1); + if ((Ndir==2)||(SMethod==SMMiq)) + HardD=Eigen::MatrixXd(numS,3); + else + HardD=Eigen::MatrixXd(numS,6); + //then update them + int curr_index=0; + for (size_t i=0;i::FaceCount(mesh); + numS=mesh.fn-numS; + //allocate eigen matrix + SoftI=Eigen::MatrixXi(numS,1); + SoftD=Eigen::MatrixXd(numS,3); + SoftW=Eigen::MatrixXd(numS,1); + + //then update them + int curr_index=0; + for (size_t i=0;i::GetTriMeshData(mesh,F,V); + + Eigen::MatrixXd output_field; + Eigen::VectorXd output_sing; + + igl::nrosy(V,F,HardI,HardD,SoftI,SoftW,SoftD,Ndir,alpha_soft,output_field,output_sing); + + //finally update the principal directions + for (size_t i=0;i::GetTriMeshData(mesh,F,V); + + Eigen::MatrixXd output_field; + Eigen::VectorXd output_sing; + + igl::n_polyvector(V,F,HardI,HardD,output_field); + + //finally update the principal directions + for (size_t i=0;iN(); + dirN.Normalize(); + Dir=CoordType(1,0,0); + if (fabs(Dir*dirN)>0.9) + Dir=CoordType(0,1,0); + if (fabs(Dir*dirN)>0.9) + Dir=CoordType(0,0,1); + + Dir=dirN^Dir; + Dir.Normalize(); + } + public: struct SmoothParam { + //the 90° rotation independence while smoothing the direction field int Ndir; - - ScalarType alpha_soft; - - bool soft_weight; + //the weight of curvature if doing the smoothing keeping the field close to the original one + ScalarType alpha_curv; + //align the field to border or not bool align_borders; - bool align_sharp; - bool hard_curvature; - + //threshold to consider some edge as sharp feature and to use as hard constraint (0, not use) ScalarType sharp_thr; + //threshold to consider some edge as high curvature anisotropyand to use as hard constraint (0, not use) ScalarType curv_thr; + //the method used to smooth MIQ or "Designing N-PolyVector Fields with Complex Polynomials" + SmoothMethod SmoothM; + //the number of faces of the ring used ot esteem the curvature + int curvRing; SmoothParam() { Ndir=4; - alpha_soft=0.0; - soft_weight=false; + curvRing=2; + alpha_curv=0.0; align_borders=false; - align_sharp=false; - hard_curvature=false; - sharp_thr=0.1; - curv_thr=0.8; + SmoothM=SMMiq; + + sharp_thr=0.0; + curv_thr=0.4; } }; - static void InitByCurvature(MeshType & mesh) + static void SelectConstraints(MeshType &mesh,SmoothParam &SParam) { - tri::RequirePerVertexCurvatureDir(mesh); - vcg::tri::UpdateCurvatureFitting::computeCurvature(mesh); - vcg::tri::CrossField::SetFaceCrossVectorFromVert(mesh); - InitQualityByAnisotropyDir(mesh); + //clear all selected faces + vcg::tri::UpdateFlags::FaceClearS(mesh); + + //add curvature hard constraints + //ScalarType Ratio=mesh.bbox.Diag()*0.01; + + if (SParam.curv_thr>0) + AddCurvatureConstraints(mesh,SParam.curv_thr);///Ratio); + + //add alignment to sharp features + if (SParam.sharp_thr>0) + AddSharpEdgesConstraints(mesh,SParam.sharp_thr); + + //add border constraints + if (SParam.align_borders) + AddBorderConstraints(mesh); } static void GloballyOrient(MeshType &mesh) @@ -984,101 +375,109 @@ public: vcg::tri::CrossField::MakeDirectionFaceCoherent(mesh,true); } - static void SmoothDirections(MeshType &mesh, - SmoothParam SParam=SmoothParam()) + static void InitByCurvature(MeshType & mesh,int Nring) { - //calculathe the maximim weight if needed - ScalarType MaxQ=-1; - if (SParam.soft_weight) + tri::RequirePerVertexCurvatureDir(mesh); + + Eigen::MatrixXi F; + Eigen::MatrixXd V; + + Eigen::MatrixXd PD1,PD2,PV1,PV2; + MeshToMatrix::GetTriMeshData(mesh,F,V); + igl::principal_curvature(V,F,PD1,PD2,PV1,PV2,Nring); + + //then copy curvature per vertex + for (size_t i=0;i MinMax=vcg::tri::Stat::ComputePerFaceQualityMinMax(mesh); - MaxQ=MinMax.second; + mesh.vert[i].PD1()=CoordType(PD1(i,0),PD1(i,1),PD1(i,2)); + mesh.vert[i].PD2()=CoordType(PD2(i,0),PD2(i,1),PD2(i,2)); + mesh.vert[i].K1()=PV1(i,0); + mesh.vert[i].K2()=PV2(i,0); } + vcg::tri::CrossField::SetFaceCrossVectorFromVert(mesh); + InitQualityByAnisotropyDir(mesh); + } - assert(SParam.alpha_soft>=0); + static void SmoothDirections(MeshType &mesh, + int Ndir, + SmoothMethod SMethod=SMNPoly, + bool HardAsS=true, + ScalarType alphaSoft=0) + { - ImplicitSmoother SmoothW(mesh); - //SmoothW.initializeSmoother(); + Eigen::VectorXi HardI; //hard constraints + Eigen::MatrixXd HardD; //hard directions + Eigen::VectorXi SoftI; //soft constraints + Eigen::VectorXd SoftW; //weight of soft constraints + Eigen::MatrixXd SoftD; //soft directions - //if alpha==0 then do not consider initial constraints and set to a value by default - SmoothW.setSoftAlpha(0.001); + if (HardAsS) + CollectHardConstraints(mesh,HardI,HardD,SMethod,Ndir); - if (SParam.alpha_soft>0) - SmoothW.setSoftAlpha(SParam.alpha_soft); + //collect soft constraints , miw only one that allows for soft constraints + if ((alphaSoft>0)&&(SMethod==SMMiq)) + CollectSoftConstraints(mesh,SoftI,SoftD,SoftW); - //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) + //finally smooth + if (SMethod==SMMiq) + SmoothMIQ(mesh,HardI,HardD,SoftI,SoftD,SoftW,alphaSoft,Ndir); + else { - 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(); + static void SmoothDirections(MeshType &mesh,SmoothParam SParam) + { + //for the moment only cross and line field - SmoothW.setConstraintHard(0,dir1); - } + //initialize direction by curvature if needed + if ((SParam.alpha_curv>0)|| + (SParam.sharp_thr>0)|| + (SParam.curv_thr>0)) + InitByCurvature(mesh,SParam.curvRing); - //solve - SmoothW.solve(SParam.Ndir); - - ///assign back to vertices - std::vector Field; - SmoothW.getFieldPerFace(Field); - for (size_t i=0;i