arap.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. // This file is part of libigl, a simple c++ geometry processing library.
  2. //
  3. // Copyright (C) 2013 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 "arap.h"
  9. #include "colon.h"
  10. #include "cotmatrix.h"
  11. #include "massmatrix.h"
  12. #include "group_sum_matrix.h"
  13. #include "covariance_scatter_matrix.h"
  14. #include "speye.h"
  15. #include "mode.h"
  16. #include "project_isometrically_to_plane.h"
  17. #include "slice.h"
  18. #include "arap_rhs.h"
  19. #include "repdiag.h"
  20. #include "columnize.h"
  21. #include "fit_rotations.h"
  22. #include <cassert>
  23. #include <iostream>
  24. template <
  25. typename DerivedV,
  26. typename DerivedF,
  27. typename Derivedb>
  28. IGL_INLINE bool igl::arap_precomputation(
  29. const Eigen::PlainObjectBase<DerivedV> & V,
  30. const Eigen::PlainObjectBase<DerivedF> & F,
  31. const int dim,
  32. const Eigen::PlainObjectBase<Derivedb> & b,
  33. ARAPData & data)
  34. {
  35. using namespace std;
  36. using namespace Eigen;
  37. typedef typename DerivedV::Scalar Scalar;
  38. // number of vertices
  39. const int n = V.rows();
  40. data.n = n;
  41. assert((b.size() == 0 || b.maxCoeff() < n) && "b out of bounds");
  42. assert((b.size() == 0 || b.minCoeff() >=0) && "b out of bounds");
  43. // remember b
  44. data.b = b;
  45. //assert(F.cols() == 3 && "For now only triangles");
  46. // dimension
  47. //const int dim = V.cols();
  48. assert((dim == 3 || dim ==2) && "dim should be 2 or 3");
  49. data.dim = dim;
  50. //assert(dim == 3 && "Only 3d supported");
  51. // Defaults
  52. data.f_ext = MatrixXd::Zero(n,data.dim);
  53. assert(data.dim <= V.cols() && "solve dim should be <= embedding");
  54. bool flat = (V.cols() - data.dim)==1;
  55. PlainObjectBase<DerivedV> plane_V;
  56. PlainObjectBase<DerivedF> plane_F;
  57. typedef SparseMatrix<Scalar> SparseMatrixS;
  58. SparseMatrixS ref_map,ref_map_dim;
  59. if(flat)
  60. {
  61. project_isometrically_to_plane(V,F,plane_V,plane_F,ref_map);
  62. repdiag(ref_map,dim,ref_map_dim);
  63. }
  64. const PlainObjectBase<DerivedV>& ref_V = (flat?plane_V:V);
  65. const PlainObjectBase<DerivedF>& ref_F = (flat?plane_F:F);
  66. SparseMatrixS L;
  67. cotmatrix(V,F,L);
  68. ARAPEnergyType eff_energy = data.energy;
  69. if(eff_energy == ARAP_ENERGY_TYPE_DEFAULT)
  70. {
  71. switch(F.cols())
  72. {
  73. case 3:
  74. if(data.dim == 3)
  75. {
  76. eff_energy = ARAP_ENERGY_TYPE_SPOKES_AND_RIMS;
  77. }else
  78. {
  79. eff_energy = ARAP_ENERGY_TYPE_ELEMENTS;
  80. }
  81. break;
  82. case 4:
  83. eff_energy = ARAP_ENERGY_TYPE_ELEMENTS;
  84. break;
  85. default:
  86. assert(false);
  87. }
  88. }
  89. // Get covariance scatter matrix, when applied collects the covariance
  90. // matrices used to fit rotations to during optimization
  91. covariance_scatter_matrix(ref_V,ref_F,eff_energy,data.CSM);
  92. if(flat)
  93. {
  94. data.CSM = (data.CSM * ref_map_dim.transpose()).eval();
  95. }
  96. assert(data.CSM.cols() == V.rows()*data.dim);
  97. // Get group sum scatter matrix, when applied sums all entries of the same
  98. // group according to G
  99. SparseMatrix<double> G_sum;
  100. if(data.G.size() == 0)
  101. {
  102. if(eff_energy == ARAP_ENERGY_TYPE_ELEMENTS)
  103. {
  104. speye(F.rows(),G_sum);
  105. }else
  106. {
  107. speye(n,G_sum);
  108. }
  109. }else
  110. {
  111. // groups are defined per vertex, convert to per face using mode
  112. if(eff_energy == ARAP_ENERGY_TYPE_ELEMENTS)
  113. {
  114. Eigen::Matrix<int,Eigen::Dynamic,1> GG;
  115. MatrixXi GF(F.rows(),F.cols());
  116. for(int j = 0;j<F.cols();j++)
  117. {
  118. Matrix<int,Eigen::Dynamic,1> GFj;
  119. slice(data.G,F.col(j),GFj);
  120. GF.col(j) = GFj;
  121. }
  122. mode<int>(GF,2,GG);
  123. data.G=GG;
  124. }
  125. //printf("group_sum_matrix()\n");
  126. group_sum_matrix(data.G,G_sum);
  127. }
  128. SparseMatrix<double> G_sum_dim;
  129. repdiag(G_sum,data.dim,G_sum_dim);
  130. assert(G_sum_dim.cols() == data.CSM.rows());
  131. data.CSM = (G_sum_dim * data.CSM).eval();
  132. arap_rhs(ref_V,ref_F,data.dim,eff_energy,data.K);
  133. if(flat)
  134. {
  135. data.K = (ref_map_dim * data.K).eval();
  136. }
  137. assert(data.K.rows() == data.n*data.dim);
  138. SparseMatrix<double> Q = (-L).eval();
  139. if(data.with_dynamics)
  140. {
  141. const double h = data.h;
  142. assert(h != 0);
  143. SparseMatrix<double> M;
  144. massmatrix(V,F,MASSMATRIX_TYPE_DEFAULT,data.M);
  145. const double dw = (1./data.ym)*(h*h);
  146. SparseMatrix<double> DQ = dw * 1./(h*h)*data.M;
  147. Q += DQ;
  148. // Dummy external forces
  149. data.f_ext = MatrixXd::Zero(n,data.dim);
  150. data.vel = MatrixXd::Zero(n,data.dim);
  151. }
  152. return min_quad_with_fixed_precompute(
  153. Q,b,SparseMatrix<double>(),true,data.solver_data);
  154. }
  155. template <
  156. typename Derivedbc,
  157. typename DerivedU>
  158. IGL_INLINE bool igl::arap_solve(
  159. const Eigen::PlainObjectBase<Derivedbc> & bc,
  160. ARAPData & data,
  161. Eigen::PlainObjectBase<DerivedU> & U)
  162. {
  163. using namespace Eigen;
  164. using namespace std;
  165. assert(data.b.size() == bc.rows());
  166. if(bc.size() > 0)
  167. {
  168. assert(bc.cols() == data.dim && "bc.cols() match data.dim");
  169. }
  170. const int n = data.n;
  171. int iter = 0;
  172. if(U.size() == 0)
  173. {
  174. // terrible initial guess.. should at least copy input mesh
  175. #ifndef NDEBUG
  176. cerr<<"arap_solve: Using terrible initial guess for U. Try U = V."<<endl;
  177. #endif
  178. U = MatrixXd::Zero(data.n,data.dim);
  179. }else
  180. {
  181. assert(U.cols() == data.dim && "U.cols() match data.dim");
  182. }
  183. // changes each arap iteration
  184. MatrixXd U_prev = U;
  185. // doesn't change for fixed with_dynamics timestep
  186. MatrixXd U0;
  187. if(data.with_dynamics)
  188. {
  189. U0 = U_prev;
  190. }
  191. while(iter < data.max_iter)
  192. {
  193. U_prev = U;
  194. // enforce boundary conditions exactly
  195. for(int bi = 0;bi<bc.rows();bi++)
  196. {
  197. U.row(data.b(bi)) = bc.row(bi);
  198. }
  199. const auto & Udim = U.replicate(data.dim,1);
  200. assert(U.cols() == data.dim);
  201. // As if U.col(2) was 0
  202. MatrixXd S = data.CSM * Udim;
  203. // THIS NORMALIZATION IS IMPORTANT TO GET SINGLE PRECISION SVD CODE TO WORK
  204. // CORRECTLY.
  205. S /= S.array().abs().maxCoeff();
  206. const int Rdim = data.dim;
  207. MatrixXd R(Rdim,data.CSM.rows());
  208. if(R.rows() == 2)
  209. {
  210. fit_rotations_planar(S,R);
  211. }else
  212. {
  213. fit_rotations(S,true,R);
  214. //#ifdef __SSE__ // fit_rotations_SSE will convert to float if necessary
  215. // fit_rotations_SSE(S,R);
  216. //#else
  217. // fit_rotations(S,true,R);
  218. //#endif
  219. }
  220. //for(int k = 0;k<(data.CSM.rows()/dim);k++)
  221. //{
  222. // R.block(0,dim*k,dim,dim) = MatrixXd::Identity(dim,dim);
  223. //}
  224. // Number of rotations: #vertices or #elements
  225. int num_rots = data.K.cols()/Rdim/Rdim;
  226. // distribute group rotations to vertices in each group
  227. MatrixXd eff_R;
  228. if(data.G.size() == 0)
  229. {
  230. // copy...
  231. eff_R = R;
  232. }else
  233. {
  234. eff_R.resize(Rdim,num_rots*Rdim);
  235. for(int r = 0;r<num_rots;r++)
  236. {
  237. eff_R.block(0,Rdim*r,Rdim,Rdim) =
  238. R.block(0,Rdim*data.G(r),Rdim,Rdim);
  239. }
  240. }
  241. MatrixXd Dl;
  242. if(data.with_dynamics)
  243. {
  244. assert(data.M.rows() == n &&
  245. "No mass matrix. Call arap_precomputation if changing with_dynamics");
  246. const double h = data.h;
  247. assert(h != 0);
  248. //Dl = 1./(h*h*h)*M*(-2.*V0 + Vm1) - fext;
  249. // data.vel = (V0-Vm1)/h
  250. // h*data.vel = (V0-Vm1)
  251. // -h*data.vel = -V0+Vm1)
  252. // -V0-h*data.vel = -2V0+Vm1
  253. const double dw = (1./data.ym)*(h*h);
  254. Dl = dw * (1./(h*h)*data.M*(-U0 - h*data.vel) - data.f_ext);
  255. }
  256. VectorXd Rcol;
  257. columnize(eff_R,num_rots,2,Rcol);
  258. VectorXd Bcol = -data.K * Rcol;
  259. assert(Bcol.size() == data.n*data.dim);
  260. for(int c = 0;c<data.dim;c++)
  261. {
  262. VectorXd Uc,Bc,bcc,Beq;
  263. Bc = Bcol.block(c*n,0,n,1);
  264. if(data.with_dynamics)
  265. {
  266. Bc += Dl.col(c);
  267. }
  268. if(bc.size()>0)
  269. {
  270. bcc = bc.col(c);
  271. }
  272. min_quad_with_fixed_solve(
  273. data.solver_data,
  274. Bc,bcc,Beq,
  275. Uc);
  276. U.col(c) = Uc;
  277. }
  278. iter++;
  279. }
  280. if(data.with_dynamics)
  281. {
  282. // Keep track of velocity for next time
  283. data.vel = (U-U0)/data.h;
  284. }
  285. return true;
  286. }
  287. #ifdef IGL_STATIC_LIBRARY
  288. template bool igl::arap_solve<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, igl::ARAPData&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
  289. template bool igl::arap_precomputation<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -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, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, igl::ARAPData&);
  290. #endif