planarize_quad_mesh.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. // This file is part of libigl, a simple c++ geometry processing library.
  2. //
  3. // Copyright (C) 2015 Alec Jacobson <alecjacobson@gmail.com>
  4. //
  5. // This Source Code Form is subject to the terms of the Mozilla Public License
  6. // v. 2.0. If a copy of the MPL was not distributed with this file, You can
  7. // obtain one at http://mozilla.org/MPL/2.0/.
  8. #include "planarize_quad_mesh.h"
  9. #include "quad_planarity.h"
  10. #include <Eigen/Sparse>
  11. #include <Eigen/Eigenvalues>
  12. #include <iostream>
  13. namespace igl
  14. {
  15. template <typename DerivedV, typename DerivedF>
  16. class PlanarizerShapeUp
  17. {
  18. protected:
  19. // number of faces, number of vertices
  20. long numV, numF;
  21. // references to the input faces and vertices
  22. const Eigen::PlainObjectBase<DerivedV> &Vin;
  23. const Eigen::PlainObjectBase<DerivedF> &Fin;
  24. // vector consisting of the vertex positions stacked: [x;y;z;x;y;z...]
  25. // vector consisting of a weight per face (currently all set to 1)
  26. // vector consisting of the projected face vertices (might be different for the same vertex belonging to different faces)
  27. Eigen::Matrix<typename DerivedV::Scalar, Eigen::Dynamic, 1> Vv, weightsSqrt, P;
  28. // Matrices as in the paper
  29. // Q: lhs matrix
  30. // Ni: matrix that subtracts the mean of a face from the 4 vertices of a face
  31. Eigen::SparseMatrix<typename DerivedV::Scalar > Q, Ni;
  32. Eigen::SimplicialLDLT<Eigen::SparseMatrix<typename DerivedV::Scalar > > solver;
  33. int maxIter;
  34. double threshold;
  35. const int ni = 4;
  36. // Matrix assemblers
  37. inline void assembleQ();
  38. inline void assembleP();
  39. inline void assembleNi();
  40. // Selects out of Vv the 4 vertices belonging to face fi
  41. inline void assembleSelector(int fi,
  42. Eigen::SparseMatrix<typename DerivedV::Scalar > &S);
  43. public:
  44. // Init - assemble stacked vector and lhs matrix, factorize
  45. inline PlanarizerShapeUp(const Eigen::PlainObjectBase<DerivedV> &V_,
  46. const Eigen::PlainObjectBase<DerivedF> &F_,
  47. const int maxIter_,
  48. const double &threshold_);
  49. // Planarization - output to Vout
  50. inline void planarize(Eigen::PlainObjectBase<DerivedV> &Vout);
  51. };
  52. }
  53. //Implementation
  54. template <typename DerivedV, typename DerivedF>
  55. inline igl::PlanarizerShapeUp<DerivedV, DerivedF>::PlanarizerShapeUp(const Eigen::PlainObjectBase<DerivedV> &V_,
  56. const Eigen::PlainObjectBase<DerivedF> &F_,
  57. const int maxIter_,
  58. const double &threshold_):
  59. numV(V_.rows()),
  60. numF(F_.rows()),
  61. Vin(V_),
  62. Fin(F_),
  63. weightsSqrt(Eigen::Matrix<typename DerivedV::Scalar, Eigen::Dynamic, 1>::Ones(numF,1)),
  64. maxIter(maxIter_),
  65. threshold(threshold_)
  66. {
  67. // assemble stacked vertex position vector
  68. Vv.setZero(3*numV,1);
  69. for (int i =0;i<numV;++i)
  70. Vv.segment(3*i,3) = Vin.row(i);
  71. // assemble and factorize lhs matrix
  72. assembleQ();
  73. };
  74. template <typename DerivedV, typename DerivedF>
  75. inline void igl::PlanarizerShapeUp<DerivedV, DerivedF>::assembleQ()
  76. {
  77. std::vector<Eigen::Triplet<typename DerivedV::Scalar> > tripletList;
  78. // assemble the Ni matrix
  79. assembleNi();
  80. for (int fi = 0; fi< numF; fi++)
  81. {
  82. Eigen::SparseMatrix<typename DerivedV::Scalar > Sfi;
  83. assembleSelector(fi, Sfi);
  84. // the final matrix per face
  85. Eigen::SparseMatrix<typename DerivedV::Scalar > Qi = weightsSqrt(fi)*Ni*Sfi;
  86. // put it in the correct block of Q
  87. // todo: this can be made faster by omitting the selector matrix
  88. for (int k=0; k<Qi.outerSize(); ++k)
  89. for (typename Eigen::SparseMatrix<typename DerivedV::Scalar >::InnerIterator it(Qi,k); it; ++it)
  90. {
  91. typename DerivedV::Scalar val = it.value();
  92. int row = it.row();
  93. int col = it.col();
  94. tripletList.push_back(Eigen::Triplet<typename DerivedV::Scalar>(row+3*ni*fi,col,val));
  95. }
  96. }
  97. Q.resize(3*ni*numF,3*numV);
  98. Q.setFromTriplets(tripletList.begin(), tripletList.end());
  99. // the actual lhs matrix is Q'*Q
  100. // prefactor that matrix
  101. solver.compute(Q.transpose()*Q);
  102. if(solver.info()!=Eigen::Success)
  103. {
  104. std::cerr << "Cholesky failed - PlanarizerShapeUp.cpp" << std::endl;
  105. assert(0);
  106. }
  107. }
  108. template <typename DerivedV, typename DerivedF>
  109. inline void igl::PlanarizerShapeUp<DerivedV, DerivedF>::assembleNi()
  110. {
  111. std::vector<Eigen::Triplet<typename DerivedV::Scalar>> tripletList;
  112. for (int ii = 0; ii< ni; ii++)
  113. {
  114. for (int jj = 0; jj< ni; jj++)
  115. {
  116. tripletList.push_back(Eigen::Triplet<typename DerivedV::Scalar>(3*ii+0,3*jj+0,-1./ni));
  117. tripletList.push_back(Eigen::Triplet<typename DerivedV::Scalar>(3*ii+1,3*jj+1,-1./ni));
  118. tripletList.push_back(Eigen::Triplet<typename DerivedV::Scalar>(3*ii+2,3*jj+2,-1./ni));
  119. }
  120. tripletList.push_back(Eigen::Triplet<typename DerivedV::Scalar>(3*ii+0,3*ii+0,1.));
  121. tripletList.push_back(Eigen::Triplet<typename DerivedV::Scalar>(3*ii+1,3*ii+1,1.));
  122. tripletList.push_back(Eigen::Triplet<typename DerivedV::Scalar>(3*ii+2,3*ii+2,1.));
  123. }
  124. Ni.resize(3*ni,3*ni);
  125. Ni.setFromTriplets(tripletList.begin(), tripletList.end());
  126. }
  127. //assumes V stacked [x;y;z;x;y;z...];
  128. template <typename DerivedV, typename DerivedF>
  129. inline void igl::PlanarizerShapeUp<DerivedV, DerivedF>::assembleSelector(int fi,
  130. Eigen::SparseMatrix<typename DerivedV::Scalar > &S)
  131. {
  132. std::vector<Eigen::Triplet<typename DerivedV::Scalar>> tripletList;
  133. for (int fvi = 0; fvi< ni; fvi++)
  134. {
  135. int vi = Fin(fi,fvi);
  136. tripletList.push_back(Eigen::Triplet<typename DerivedV::Scalar>(3*fvi+0,3*vi+0,1.));
  137. tripletList.push_back(Eigen::Triplet<typename DerivedV::Scalar>(3*fvi+1,3*vi+1,1.));
  138. tripletList.push_back(Eigen::Triplet<typename DerivedV::Scalar>(3*fvi+2,3*vi+2,1.));
  139. }
  140. S.resize(3*ni,3*numV);
  141. S.setFromTriplets(tripletList.begin(), tripletList.end());
  142. }
  143. //project all faces to their closest planar face
  144. template <typename DerivedV, typename DerivedF>
  145. inline void igl::PlanarizerShapeUp<DerivedV, DerivedF>::assembleP()
  146. {
  147. P.setZero(3*ni*numF);
  148. for (int fi = 0; fi< numF; fi++)
  149. {
  150. // todo: this can be made faster by omitting the selector matrix
  151. Eigen::SparseMatrix<typename DerivedV::Scalar > Sfi;
  152. assembleSelector(fi, Sfi);
  153. Eigen::SparseMatrix<typename DerivedV::Scalar > NSi = Ni*Sfi;
  154. Eigen::Matrix<typename DerivedV::Scalar, Eigen::Dynamic, 1> Vi = NSi*Vv;
  155. Eigen::Matrix<typename DerivedV::Scalar, Eigen::Dynamic, Eigen::Dynamic> CC(3,ni);
  156. for (int i = 0; i <ni; ++i)
  157. CC.col(i) = Vi.segment(3*i, 3);
  158. Eigen::Matrix<typename DerivedV::Scalar, 3, 3> C = CC*CC.transpose();
  159. // Alec: Doesn't compile
  160. Eigen::EigenSolver<Eigen::Matrix<typename DerivedV::Scalar, 3, 3>> es(C);
  161. // the real() is for compilation purposes
  162. Eigen::Matrix<typename DerivedV::Scalar, 3, 1> lambda = es.eigenvalues().real();
  163. Eigen::Matrix<typename DerivedV::Scalar, 3, 3> U = es.eigenvectors().real();
  164. int min_i;
  165. lambda.cwiseAbs().minCoeff(&min_i);
  166. U.col(min_i).setZero();
  167. Eigen::Matrix<typename DerivedV::Scalar, Eigen::Dynamic, Eigen::Dynamic> PP = U*U.transpose()*CC;
  168. for (int i = 0; i <ni; ++i)
  169. P.segment(3*ni*fi+3*i, 3) = weightsSqrt[fi]*PP.col(i);
  170. }
  171. }
  172. template <typename DerivedV, typename DerivedF>
  173. inline void igl::PlanarizerShapeUp<DerivedV, DerivedF>::planarize(Eigen::PlainObjectBase<DerivedV> &Vout)
  174. {
  175. Eigen::Matrix<typename DerivedV::Scalar, Eigen::Dynamic, 1> planarity;
  176. Vout = Vin;
  177. for (int iter =0; iter<maxIter; ++iter)
  178. {
  179. igl::quad_planarity(Vout, Fin, planarity);
  180. typename DerivedV::Scalar nonPlanarity = planarity.cwiseAbs().maxCoeff();
  181. //std::cerr<<"iter #"<<iter<<": max non-planarity: "<<nonPlanarity<<std::endl;
  182. if (nonPlanarity<threshold)
  183. break;
  184. assembleP();
  185. Vv = solver.solve(Q.transpose()*P);
  186. if(solver.info()!=Eigen::Success)
  187. {
  188. std::cerr << "Linear solve failed - PlanarizerShapeUp.cpp" << std::endl;
  189. assert(0);
  190. }
  191. for (int i =0;i<numV;++i)
  192. Vout.row(i) << Vv.segment(3*i,3).transpose();
  193. }
  194. // set the mean of Vout to the mean of Vin
  195. Eigen::Matrix<typename DerivedV::Scalar, 1, 3> oldMean, newMean;
  196. oldMean = Vin.colwise().mean();
  197. newMean = Vout.colwise().mean();
  198. Vout.rowwise() += (oldMean - newMean);
  199. };
  200. template <typename DerivedV, typename DerivedF>
  201. IGL_INLINE void igl::planarize_quad_mesh(const Eigen::PlainObjectBase<DerivedV> &Vin,
  202. const Eigen::PlainObjectBase<DerivedF> &Fin,
  203. const int maxIter,
  204. const double &threshold,
  205. Eigen::PlainObjectBase<DerivedV> &Vout)
  206. {
  207. PlanarizerShapeUp<DerivedV, DerivedF> planarizer(Vin, Fin, maxIter, threshold);
  208. planarizer.planarize(Vout);
  209. }
  210. #ifdef IGL_STATIC_LIBRARY
  211. // Explicit template specialization
  212. template void igl::planarize_quad_mesh<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1> >(Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, int, double const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
  213. #endif