// This file is part of libigl, a simple c++ geometry processing library. // // Copyright (C) 2015 Alec Jacobson // // This Source Code Form is subject to the terms of the Mozilla Public License // v. 2.0. If a copy of the MPL was not distributed with this file, You can // obtain one at http://mozilla.org/MPL/2.0/. #include "planarize_quad_mesh.h" #include "quad_planarity.h" #include #include #include namespace igl { template class PlanarizerShapeUp { protected: // number of faces, number of vertices long numV, numF; // references to the input faces and vertices const Eigen::PlainObjectBase &Vin; const Eigen::PlainObjectBase &Fin; // vector consisting of the vertex positions stacked: [x;y;z;x;y;z...] // vector consisting of a weight per face (currently all set to 1) // vector consisting of the projected face vertices (might be different for the same vertex belonging to different faces) Eigen::Matrix Vv, weightsSqrt, P; // Matrices as in the paper // Q: lhs matrix // Ni: matrix that subtracts the mean of a face from the 4 vertices of a face Eigen::SparseMatrix Q, Ni; Eigen::SimplicialLDLT > solver; int maxIter; double threshold; const int ni = 4; // Matrix assemblers inline void assembleQ(); inline void assembleP(); inline void assembleNi(); // Selects out of Vv the 4 vertices belonging to face fi inline void assembleSelector(int fi, Eigen::SparseMatrix &S); public: // Init - assemble stacked vector and lhs matrix, factorize inline PlanarizerShapeUp(const Eigen::PlainObjectBase &V_, const Eigen::PlainObjectBase &F_, const int maxIter_, const double &threshold_); // Planarization - output to Vout inline void planarize(Eigen::PlainObjectBase &Vout); }; } //Implementation template inline igl::PlanarizerShapeUp::PlanarizerShapeUp(const Eigen::PlainObjectBase &V_, const Eigen::PlainObjectBase &F_, const int maxIter_, const double &threshold_): numV(V_.rows()), numF(F_.rows()), Vin(V_), Fin(F_), weightsSqrt(Eigen::Matrix::Ones(numF,1)), maxIter(maxIter_), threshold(threshold_) { // assemble stacked vertex position vector Vv.setZero(3*numV,1); for (int i =0;i inline void igl::PlanarizerShapeUp::assembleQ() { std::vector > tripletList; // assemble the Ni matrix assembleNi(); for (int fi = 0; fi< numF; fi++) { Eigen::SparseMatrix Sfi; assembleSelector(fi, Sfi); // the final matrix per face Eigen::SparseMatrix Qi = weightsSqrt(fi)*Ni*Sfi; // put it in the correct block of Q // todo: this can be made faster by omitting the selector matrix for (int k=0; k::InnerIterator it(Qi,k); it; ++it) { typename DerivedV::Scalar val = it.value(); int row = it.row(); int col = it.col(); tripletList.push_back(Eigen::Triplet(row+3*ni*fi,col,val)); } } Q.resize(3*ni*numF,3*numV); Q.setFromTriplets(tripletList.begin(), tripletList.end()); // the actual lhs matrix is Q'*Q // prefactor that matrix solver.compute(Q.transpose()*Q); if(solver.info()!=Eigen::Success) { std::cerr << "Cholesky failed - PlanarizerShapeUp.cpp" << std::endl; assert(0); } } template inline void igl::PlanarizerShapeUp::assembleNi() { std::vector> tripletList; for (int ii = 0; ii< ni; ii++) { for (int jj = 0; jj< ni; jj++) { tripletList.push_back(Eigen::Triplet(3*ii+0,3*jj+0,-1./ni)); tripletList.push_back(Eigen::Triplet(3*ii+1,3*jj+1,-1./ni)); tripletList.push_back(Eigen::Triplet(3*ii+2,3*jj+2,-1./ni)); } tripletList.push_back(Eigen::Triplet(3*ii+0,3*ii+0,1.)); tripletList.push_back(Eigen::Triplet(3*ii+1,3*ii+1,1.)); tripletList.push_back(Eigen::Triplet(3*ii+2,3*ii+2,1.)); } Ni.resize(3*ni,3*ni); Ni.setFromTriplets(tripletList.begin(), tripletList.end()); } //assumes V stacked [x;y;z;x;y;z...]; template inline void igl::PlanarizerShapeUp::assembleSelector(int fi, Eigen::SparseMatrix &S) { std::vector> tripletList; for (int fvi = 0; fvi< ni; fvi++) { int vi = Fin(fi,fvi); tripletList.push_back(Eigen::Triplet(3*fvi+0,3*vi+0,1.)); tripletList.push_back(Eigen::Triplet(3*fvi+1,3*vi+1,1.)); tripletList.push_back(Eigen::Triplet(3*fvi+2,3*vi+2,1.)); } S.resize(3*ni,3*numV); S.setFromTriplets(tripletList.begin(), tripletList.end()); } //project all faces to their closest planar face template inline void igl::PlanarizerShapeUp::assembleP() { P.setZero(3*ni*numF); for (int fi = 0; fi< numF; fi++) { // todo: this can be made faster by omitting the selector matrix Eigen::SparseMatrix Sfi; assembleSelector(fi, Sfi); Eigen::SparseMatrix NSi = Ni*Sfi; Eigen::Matrix Vi = NSi*Vv; Eigen::Matrix CC(3,ni); for (int i = 0; i C = CC*CC.transpose(); // Alec: Doesn't compile Eigen::EigenSolver> es(C); // the real() is for compilation purposes Eigen::Matrix lambda = es.eigenvalues().real(); Eigen::Matrix U = es.eigenvectors().real(); int min_i; lambda.cwiseAbs().minCoeff(&min_i); U.col(min_i).setZero(); Eigen::Matrix PP = U*U.transpose()*CC; for (int i = 0; i inline void igl::PlanarizerShapeUp::planarize(Eigen::PlainObjectBase &Vout) { Eigen::Matrix planarity; Vout = Vin; for (int iter =0; iter oldMean, newMean; oldMean = Vin.colwise().mean(); newMean = Vout.colwise().mean(); Vout.rowwise() += (oldMean - newMean); }; template IGL_INLINE void igl::planarize_quad_mesh(const Eigen::PlainObjectBase &Vin, const Eigen::PlainObjectBase &Fin, const int maxIter, const double &threshold, Eigen::PlainObjectBase &Vout) { PlanarizerShapeUp planarizer(Vin, Fin, maxIter, threshold); planarizer.planarize(Vout); } #ifdef IGL_STATIC_LIBRARY // Explicit template instantiation template void igl::planarize_quad_mesh, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, double const&, Eigen::PlainObjectBase >&); #endif