arap_dof.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913
  1. // This file is part of libigl, a simple c++ geometry processing library.
  2. //
  3. // Copyright (C) 2013 Alec Jacobson <[email protected]>
  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_dof.h"
  9. #include "IGL_ASSERT.h"
  10. #include "cotmatrix.h"
  11. #include "massmatrix.h"
  12. #include "speye.h"
  13. #include "repdiag.h"
  14. #include "repmat.h"
  15. #include "slice.h"
  16. #include "colon.h"
  17. #include "is_sparse.h"
  18. #include "mode.h"
  19. #include "is_symmetric.h"
  20. #include "group_sum_matrix.h"
  21. #include "arap_rhs.h"
  22. #include "covariance_scatter_matrix.h"
  23. #include "fit_rotations.h"
  24. #include "verbose.h"
  25. #include "print_ijv.h"
  26. //#include "MKLEigenInterface.h"
  27. #include "kkt_inverse.h"
  28. #include "get_seconds.h"
  29. #include "columnize.h"
  30. #include <type_traits>
  31. // defined if no early exit is supported, i.e., always take a fixed number of iterations
  32. #define IGL_ARAP_DOF_FIXED_ITERATIONS_COUNT
  33. // To avoid putting _any_ dense slices in the static library use a work around
  34. // so that we can slice LbsMatrixType as sparse or dense below.
  35. #if __cplusplus < 201703L
  36. template <typename Mat, bool IsSparse> struct arap_dof_slice_helper;
  37. template <typename Mat> struct arap_dof_slice_helper<Mat,true>
  38. {
  39. static void slice(const Mat & A, const Eigen::VectorXi & I, const Eigen::VectorXi & J, Mat & B)
  40. {
  41. static_assert(std::is_base_of<Eigen::SparseMatrixBase<Mat>, Mat>::value, "Mat must be sparse");
  42. igl::slice(A,I,J,B);
  43. }
  44. };
  45. template <typename Mat> struct arap_dof_slice_helper<Mat,false>
  46. {
  47. static void slice(const Mat & A, const Eigen::VectorXi & I, const Eigen::VectorXi & J, Mat & B)
  48. {
  49. B = A(I,J);
  50. }
  51. };
  52. #endif
  53. // A careful derivation of this implementation is given in the corresponding
  54. // matlab function arap_dof.m
  55. template <typename LbsMatrixType, typename SSCALAR>
  56. IGL_INLINE bool igl::arap_dof_precomputation(
  57. const Eigen::MatrixXd & V,
  58. const Eigen::MatrixXi & F,
  59. const LbsMatrixType & M,
  60. const Eigen::Matrix<int,Eigen::Dynamic,1> & G,
  61. ArapDOFData<LbsMatrixType, SSCALAR> & data)
  62. {
  63. typedef Eigen::Matrix<SSCALAR, Eigen::Dynamic, Eigen::Dynamic> MatrixXS;
  64. // number of mesh (domain) vertices
  65. int n = V.rows();
  66. // cache problem size
  67. data.n = n;
  68. // dimension of mesh
  69. data.dim = V.cols();
  70. assert(data.dim == M.rows()/n);
  71. assert(data.dim*n == M.rows());
  72. if(data.dim == 3)
  73. {
  74. // Check if z-coordinate is all zeros
  75. if(V.col(2).minCoeff() == 0 && V.col(2).maxCoeff() == 0)
  76. {
  77. data.effective_dim = 2;
  78. }
  79. }else
  80. {
  81. data.effective_dim = data.dim;
  82. }
  83. // Number of handles
  84. data.m = M.cols()/data.dim/(data.dim+1);
  85. assert(data.m*data.dim*(data.dim+1) == M.cols());
  86. //assert(m == C.rows());
  87. //printf("n=%d; dim=%d; m=%d;\n",n,data.dim,data.m);
  88. // Build cotangent laplacian
  89. Eigen::SparseMatrix<double> Lcot;
  90. //printf("cotmatrix()\n");
  91. cotmatrix(V,F,Lcot);
  92. // Discrete laplacian (should be minus matlab version)
  93. Eigen::SparseMatrix<double> Lapl = -2.0*Lcot;
  94. #ifdef EXTREME_VERBOSE
  95. cout<<"LaplIJV=["<<endl;print_ijv(Lapl,1);cout<<endl<<"];"<<
  96. endl<<"Lapl=sparse(LaplIJV(:,1),LaplIJV(:,2),LaplIJV(:,3),"<<
  97. Lapl.rows()<<","<<Lapl.cols()<<");"<<endl;
  98. #endif
  99. // Get group sum scatter matrix, when applied sums all entries of the same
  100. // group according to G
  101. Eigen::SparseMatrix<double> G_sum;
  102. if(G.size() == 0)
  103. {
  104. speye(n,G_sum);
  105. }else
  106. {
  107. // groups are defined per vertex, convert to per face using mode
  108. Eigen::Matrix<int,Eigen::Dynamic,1> GG;
  109. if(data.energy == ARAP_ENERGY_TYPE_ELEMENTS)
  110. {
  111. Eigen::MatrixXi GF(F.rows(),F.cols());
  112. for(int j = 0;j<F.cols();j++)
  113. {
  114. GF.col(j) = G(F.col(j));
  115. }
  116. mode<int>(GF,2,GG);
  117. }else
  118. {
  119. GG=G;
  120. }
  121. //printf("group_sum_matrix()\n");
  122. group_sum_matrix(GG,G_sum);
  123. }
  124. #ifdef EXTREME_VERBOSE
  125. cout<<"G_sumIJV=["<<endl;print_ijv(G_sum,1);cout<<endl<<"];"<<
  126. endl<<"G_sum=sparse(G_sumIJV(:,1),G_sumIJV(:,2),G_sumIJV(:,3),"<<
  127. G_sum.rows()<<","<<G_sum.cols()<<");"<<endl;
  128. #endif
  129. // Get covariance scatter matrix, when applied collects the covariance matrices
  130. // used to fit rotations to during optimization
  131. Eigen::SparseMatrix<double> CSM;
  132. //printf("covariance_scatter_matrix()\n");
  133. covariance_scatter_matrix(V,F,data.energy,CSM);
  134. #ifdef EXTREME_VERBOSE
  135. cout<<"CSMIJV=["<<endl;print_ijv(CSM,1);cout<<endl<<"];"<<
  136. endl<<"CSM=sparse(CSMIJV(:,1),CSMIJV(:,2),CSMIJV(:,3),"<<
  137. CSM.rows()<<","<<CSM.cols()<<");"<<endl;
  138. #endif
  139. // Build the covariance matrix "constructor". This is a set of *scatter*
  140. // matrices that when multiplied on the right by column of the transformation
  141. // matrix entries (the degrees of freedom) L, we get a stack of dim by 1
  142. // covariance matrix column, with a column in the stack for each rotation
  143. // *group*. The output is a list of matrices because we construct each column
  144. // in the stack of covariance matrices with an independent matrix-vector
  145. // multiplication.
  146. //
  147. // We want to build S which is a stack of dim by dim covariance matrices.
  148. // Thus S is dim*g by dim, where dim is the number of dimensions and g is the
  149. // number of groups. We can precompute dim matrices CSM_M such that column i
  150. // in S is computed as S(:,i) = CSM_M{i} * L, where L is a column of the
  151. // skinning transformation matrix values. To be clear, the covariance matrix
  152. // for group k is then given as the dim by dim matrix pulled from the stack:
  153. // S((k-1)*dim + 1:dim,:)
  154. // Apply group sum to each dimension's block of covariance scatter matrix
  155. Eigen::SparseMatrix<double> G_sum_dim;
  156. repdiag(G_sum,data.dim,G_sum_dim);
  157. CSM = (G_sum_dim * CSM).eval();
  158. #ifdef EXTREME_VERBOSE
  159. cout<<"CSMIJV=["<<endl;print_ijv(CSM,1);cout<<endl<<"];"<<
  160. endl<<"CSM=sparse(CSMIJV(:,1),CSMIJV(:,2),CSMIJV(:,3),"<<
  161. CSM.rows()<<","<<CSM.cols()<<");"<<endl;
  162. #endif
  163. //printf("CSM_M()\n");
  164. // Precompute CSM times M for each dimension
  165. data.CSM_M.resize(data.dim);
  166. #ifdef EXTREME_VERBOSE
  167. cout<<"data.CSM_M = cell("<<data.dim<<",1);"<<endl;
  168. #endif
  169. // span of integers from 0 to n-1
  170. Eigen::Matrix<int,Eigen::Dynamic,1> span_n(n);
  171. for(int i = 0;i<n;i++)
  172. {
  173. span_n(i) = i;
  174. }
  175. // span of integers from 0 to M.cols()-1
  176. Eigen::Matrix<int,Eigen::Dynamic,1> span_mlbs_cols(M.cols());
  177. for(int i = 0;i<M.cols();i++)
  178. {
  179. span_mlbs_cols(i) = i;
  180. }
  181. // number of groups
  182. int k = CSM.rows()/data.dim;
  183. for(int i = 0;i<data.dim;i++)
  184. {
  185. //printf("CSM_M(): Mi\n");
  186. LbsMatrixType M_i;
  187. //printf("CSM_M(): slice\n");
  188. #if __cplusplus >= 201703L
  189. // Check if LbsMatrixType is a sparse matrix
  190. if constexpr (std::is_base_of<Eigen::SparseMatrixBase<LbsMatrixType>, LbsMatrixType>::value)
  191. {
  192. slice(M,(span_n.array()+i*n).matrix().eval(),span_mlbs_cols,M_i);
  193. }
  194. else
  195. {
  196. M_i = M((span_n.array()+i*n).eval(),span_mlbs_cols);
  197. }
  198. #else
  199. constexpr bool LbsMatrixTypeIsSparse = std::is_base_of<Eigen::SparseMatrixBase<LbsMatrixType>, LbsMatrixType>::value;
  200. arap_dof_slice_helper<LbsMatrixType,LbsMatrixTypeIsSparse>::slice(M,(span_n.array()+i*n).matrix().eval(),span_mlbs_cols,M_i);
  201. #endif
  202. LbsMatrixType M_i_dim;
  203. data.CSM_M[i].resize(k*data.dim,data.m*data.dim*(data.dim+1));
  204. assert(data.CSM_M[i].cols() == M.cols());
  205. for(int j = 0;j<data.dim;j++)
  206. {
  207. Eigen::SparseMatrix<double> CSMj;
  208. //printf("CSM_M(): slice\n");
  209. slice(
  210. CSM,
  211. colon<int>(j*k,(j+1)*k-1),
  212. colon<int>(j*n,(j+1)*n-1),
  213. CSMj);
  214. assert(CSMj.rows() == k);
  215. assert(CSMj.cols() == n);
  216. LbsMatrixType CSMjM_i = CSMj * M_i;
  217. if(is_sparse(CSMjM_i))
  218. {
  219. // Convert to full
  220. //printf("CSM_M(): full\n");
  221. Eigen::MatrixXd CSMjM_ifull(CSMjM_i);
  222. // printf("CSM_M[%d]: %d %d\n",i,data.CSM_M[i].rows(),data.CSM_M[i].cols());
  223. // printf("CSM_M[%d].block(%d*%d=%d,0,%d,%d): %d %d\n",i,j,k,CSMjM_i.rows(),CSMjM_i.cols(),
  224. // data.CSM_M[i].block(j*k,0,CSMjM_i.rows(),CSMjM_i.cols()).rows(),
  225. // data.CSM_M[i].block(j*k,0,CSMjM_i.rows(),CSMjM_i.cols()).cols());
  226. // printf("CSM_MjMi: %d %d\n",i,CSMjM_i.rows(),CSMjM_i.cols());
  227. // printf("CSM_MjM_ifull: %d %d\n",i,CSMjM_ifull.rows(),CSMjM_ifull.cols());
  228. data.CSM_M[i].block(j*k,0,CSMjM_i.rows(),CSMjM_i.cols()) = CSMjM_ifull;
  229. }else
  230. {
  231. data.CSM_M[i].block(j*k,0,CSMjM_i.rows(),CSMjM_i.cols()) = CSMjM_i;
  232. }
  233. }
  234. #ifdef EXTREME_VERBOSE
  235. cout<<"CSM_Mi=["<<endl<<data.CSM_M[i]<<endl<<"];"<<endl;
  236. #endif
  237. }
  238. // precompute arap_rhs matrix
  239. //printf("arap_rhs()\n");
  240. Eigen::SparseMatrix<double> K;
  241. arap_rhs(V,F,V.cols(),data.energy,K);
  242. //#ifdef EXTREME_VERBOSE
  243. // cout<<"KIJV=["<<endl;print_ijv(K,1);cout<<endl<<"];"<<
  244. // endl<<"K=sparse(KIJV(:,1),KIJV(:,2),KIJV(:,3),"<<
  245. // K.rows()<<","<<K.cols()<<");"<<endl;
  246. //#endif
  247. // Precompute left muliplication by M and right multiplication by G_sum
  248. Eigen::SparseMatrix<double> G_sumT = G_sum.transpose();
  249. Eigen::SparseMatrix<double> G_sumT_dim_dim;
  250. repdiag(G_sumT,data.dim*data.dim,G_sumT_dim_dim);
  251. LbsMatrixType MT = M.transpose();
  252. // If this is a bottle neck then consider reordering matrix multiplication
  253. data.M_KG = -4.0 * (MT * (K * G_sumT_dim_dim));
  254. //#ifdef EXTREME_VERBOSE
  255. // cout<<"data.M_KGIJV=["<<endl;print_ijv(data.M_KG,1);cout<<endl<<"];"<<
  256. // endl<<"data.M_KG=sparse(data.M_KGIJV(:,1),data.M_KGIJV(:,2),data.M_KGIJV(:,3),"<<
  257. // data.M_KG.rows()<<","<<data.M_KG.cols()<<");"<<endl;
  258. //#endif
  259. // Precompute system matrix
  260. //printf("A()\n");
  261. Eigen::SparseMatrix<double> A;
  262. repdiag(Lapl,data.dim,A);
  263. data.Q = MT * (A * M);
  264. //#ifdef EXTREME_VERBOSE
  265. // cout<<"QIJV=["<<endl;print_ijv(data.Q,1);cout<<endl<<"];"<<
  266. // endl<<"Q=sparse(QIJV(:,1),QIJV(:,2),QIJV(:,3),"<<
  267. // data.Q.rows()<<","<<data.Q.cols()<<");"<<endl;
  268. //#endif
  269. // Always do dynamics precomputation so we can hot-switch
  270. //if(data.with_dynamics)
  271. //{
  272. // Build cotangent laplacian
  273. Eigen::SparseMatrix<double> Mass;
  274. //printf("massmatrix()\n");
  275. massmatrix(V,F,(F.cols()>3?MASSMATRIX_TYPE_BARYCENTRIC:MASSMATRIX_TYPE_VORONOI),Mass);
  276. //cout<<"MIJV=["<<endl;print_ijv(Mass,1);cout<<endl<<"];"<<
  277. // endl<<"M=sparse(MIJV(:,1),MIJV(:,2),MIJV(:,3),"<<
  278. // Mass.rows()<<","<<Mass.cols()<<");"<<endl;
  279. //speye(data.n,Mass);
  280. Eigen::SparseMatrix<double> Mass_rep;
  281. repdiag(Mass,data.dim,Mass_rep);
  282. // Multiply either side by weights matrix (should be dense)
  283. data.Mass_tilde = MT * Mass_rep * M;
  284. Eigen::MatrixXd ones(data.dim*data.n,data.dim);
  285. for(int i = 0;i<data.n;i++)
  286. {
  287. for(int d = 0;d<data.dim;d++)
  288. {
  289. ones(i+d*data.n,d) = 1;
  290. }
  291. }
  292. data.fgrav = MT * (Mass_rep * ones);
  293. data.fext = MatrixXS::Zero(MT.rows(),1);
  294. //data.fgrav = MT * (ones);
  295. //}
  296. // This may/should be superfluous
  297. //printf("is_symmetric()\n");
  298. if(!is_symmetric(data.Q))
  299. {
  300. //printf("Fixing symmetry...\n");
  301. // "Fix" symmetry
  302. LbsMatrixType QT = data.Q.transpose();
  303. LbsMatrixType Q_copy = data.Q;
  304. data.Q = 0.5*(Q_copy+QT);
  305. // Check that ^^^ this really worked. It doesn't always
  306. //assert(is_symmetric(*Q));
  307. }
  308. //printf("arap_dof_precomputation() succeeded... so far...\n");
  309. verbose("Number of handles: %i\n", data.m);
  310. return true;
  311. }
  312. /////////////////////////////////////////////////////////////////////////
  313. //
  314. // STATIC FUNCTIONS (These should be removed or properly defined)
  315. //
  316. /////////////////////////////////////////////////////////////////////////
  317. namespace igl
  318. {
  319. // returns maximal difference of 'blok' from scalar times 3x3 identity:
  320. template <typename SSCALAR>
  321. inline static SSCALAR maxBlokErr(const Eigen::Matrix3f &blok)
  322. {
  323. SSCALAR mD;
  324. SSCALAR value = blok(0,0);
  325. SSCALAR diff1 = fabs(blok(1,1) - value);
  326. SSCALAR diff2 = fabs(blok(2,2) - value);
  327. if (diff1 > diff2) mD = diff1;
  328. else mD = diff2;
  329. for (int v=0; v<3; v++)
  330. {
  331. for (int w=0; w<3; w++)
  332. {
  333. if (v == w)
  334. {
  335. continue;
  336. }
  337. if (mD < fabs(blok(v, w)))
  338. {
  339. mD = fabs(blok(v, w));
  340. }
  341. }
  342. }
  343. return mD;
  344. }
  345. // converts CSM_M_SSCALAR[0], CSM_M_SSCALAR[1], CSM_M_SSCALAR[2] into one
  346. // "condensed" matrix CSM while checking we're not losing any information by
  347. // this process; specifically, returns maximal difference from scaled 3x3
  348. // identity blocks, which should be pretty small number
  349. template <typename MatrixXS>
  350. static typename MatrixXS::Scalar condense_CSM(
  351. const std::vector<MatrixXS> &CSM_M_SSCALAR,
  352. int numBones,
  353. int dim,
  354. MatrixXS &CSM)
  355. {
  356. const int numRows = CSM_M_SSCALAR[0].rows();
  357. assert(CSM_M_SSCALAR[0].cols() == dim*(dim+1)*numBones);
  358. assert(CSM_M_SSCALAR[1].cols() == dim*(dim+1)*numBones);
  359. assert(CSM_M_SSCALAR[2].cols() == dim*(dim+1)*numBones);
  360. assert(CSM_M_SSCALAR[1].rows() == numRows);
  361. assert(CSM_M_SSCALAR[2].rows() == numRows);
  362. const int numCols = (dim + 1)*numBones;
  363. CSM.resize(numRows, numCols);
  364. typedef typename MatrixXS::Scalar SSCALAR;
  365. SSCALAR maxDiff = 0.0f;
  366. for (int r=0; r<numRows; r++)
  367. {
  368. for (int coord=0; coord<dim+1; coord++)
  369. {
  370. for (int b=0; b<numBones; b++)
  371. {
  372. // this is just a test if we really have a multiple of 3x3 identity
  373. Eigen::Matrix3f blok;
  374. for (int v=0; v<3; v++)
  375. {
  376. for (int w=0; w<3; w++)
  377. {
  378. blok(v,w) = CSM_M_SSCALAR[v](r, coord*(numBones*dim) + b + w*numBones);
  379. }
  380. }
  381. //SSCALAR value[3];
  382. //for (int v=0; v<3; v++)
  383. // CSM_M_SSCALAR[v](r, coord*(numBones*dim) + b + v*numBones);
  384. SSCALAR mD = maxBlokErr<SSCALAR>(blok);
  385. if (mD > maxDiff) maxDiff = mD;
  386. // use the first value:
  387. CSM(r, coord*numBones + b) = blok(0,0);
  388. }
  389. }
  390. }
  391. return maxDiff;
  392. }
  393. // splits x_0, ... , x_dim coordinates in column vector 'L' into a numBones*(dimp1) x dim matrix 'Lsep';
  394. // assumes 'Lsep' has already been preallocated
  395. //
  396. // is this the same as uncolumnize? no.
  397. template <typename MatL, typename MatLsep>
  398. static void splitColumns(
  399. const MatL &L,
  400. int numBones,
  401. int dim,
  402. int dimp1,
  403. MatLsep &Lsep)
  404. {
  405. assert(L.cols() == 1);
  406. assert(L.rows() == dim*(dimp1)*numBones);
  407. assert(Lsep.rows() == (dimp1)*numBones && Lsep.cols() == dim);
  408. for (int b=0; b<numBones; b++)
  409. {
  410. for (int coord=0; coord<dimp1; coord++)
  411. {
  412. for (int c=0; c<dim; c++)
  413. {
  414. Lsep(coord*numBones + b, c) = L(coord*numBones*dim + c*numBones + b, 0);
  415. }
  416. }
  417. }
  418. }
  419. // the inverse of splitColumns, i.e., takes numBones*(dimp1) x dim matrix 'Lsep' and merges the dimensions
  420. // into columns vector 'L' (which is assumed to be already allocated):
  421. //
  422. // is this the same as columnize? no.
  423. template <typename MatrixXS>
  424. static void mergeColumns(const MatrixXS &Lsep, int numBones, int dim, int dimp1, MatrixXS &L)
  425. {
  426. assert(L.cols() == 1);
  427. assert(L.rows() == dim*(dimp1)*numBones);
  428. assert(Lsep.rows() == (dimp1)*numBones && Lsep.cols() == dim);
  429. for (int b=0; b<numBones; b++)
  430. {
  431. for (int coord=0; coord<dimp1; coord++)
  432. {
  433. for (int c=0; c<dim; c++)
  434. {
  435. L(coord*numBones*dim + c*numBones + b, 0) = Lsep(coord*numBones + b, c);
  436. }
  437. }
  438. }
  439. }
  440. // converts "Solve1" the "rotations" part of FullSolve matrix (the first part)
  441. // into one "condensed" matrix CSolve1 while checking we're not losing any
  442. // information by this process; specifically, returns maximal difference from
  443. // scaled 3x3 identity blocks, which should be pretty small number
  444. template <typename MatrixXS>
  445. static typename MatrixXS::Scalar condense_Solve1(MatrixXS &Solve1, int numBones, int numGroups, int dim, MatrixXS &CSolve1)
  446. {
  447. assert(Solve1.rows() == dim*(dim + 1)*numBones);
  448. assert(Solve1.cols() == dim*dim*numGroups);
  449. typedef typename MatrixXS::Scalar SSCALAR;
  450. SSCALAR maxDiff = 0.0f;
  451. CSolve1.resize((dim + 1)*numBones, dim*numGroups);
  452. for (int rowCoord=0; rowCoord<dim+1; rowCoord++)
  453. {
  454. for (int b=0; b<numBones; b++)
  455. {
  456. for (int colCoord=0; colCoord<dim; colCoord++)
  457. {
  458. for (int g=0; g<numGroups; g++)
  459. {
  460. Eigen::Matrix3f blok;
  461. for (int r=0; r<3; r++)
  462. {
  463. for (int c=0; c<3; c++)
  464. {
  465. blok(r, c) = Solve1(rowCoord*numBones*dim + r*numBones + b, colCoord*numGroups*dim + c*numGroups + g);
  466. }
  467. }
  468. SSCALAR mD = maxBlokErr<SSCALAR>(blok);
  469. if (mD > maxDiff) maxDiff = mD;
  470. CSolve1(rowCoord*numBones + b, colCoord*numGroups + g) = blok(0,0);
  471. }
  472. }
  473. }
  474. }
  475. return maxDiff;
  476. }
  477. }
  478. template <typename LbsMatrixType, typename SSCALAR>
  479. IGL_INLINE bool igl::arap_dof_recomputation(
  480. const Eigen::Matrix<int,Eigen::Dynamic,1> & fixed_dim,
  481. const Eigen::SparseMatrix<double> & A_eq,
  482. ArapDOFData<LbsMatrixType, SSCALAR> & data)
  483. {
  484. typedef Eigen::Matrix<SSCALAR, Eigen::Dynamic, Eigen::Dynamic> MatrixXS;
  485. LbsMatrixType * Q;
  486. LbsMatrixType Qdyn;
  487. if(data.with_dynamics)
  488. {
  489. // multiply by 1/timestep and to quadratic coefficients matrix
  490. // Might be missing a 0.5 here
  491. LbsMatrixType Q_copy = data.Q;
  492. Qdyn = Q_copy + (1.0/(data.h*data.h))*data.Mass_tilde;
  493. Q = &Qdyn;
  494. // This may/should be superfluous
  495. //printf("is_symmetric()\n");
  496. if(!is_symmetric(*Q))
  497. {
  498. //printf("Fixing symmetry...\n");
  499. // "Fix" symmetry
  500. LbsMatrixType QT = (*Q).transpose();
  501. LbsMatrixType Q_copy = *Q;
  502. *Q = 0.5*(Q_copy+QT);
  503. // Check that ^^^ this really worked. It doesn't always
  504. //assert(is_symmetric(*Q));
  505. }
  506. }else
  507. {
  508. Q = &data.Q;
  509. }
  510. assert((int)data.CSM_M.size() == data.dim);
  511. assert(A_eq.cols() == data.m*data.dim*(data.dim+1));
  512. data.fixed_dim = fixed_dim;
  513. if(fixed_dim.size() > 0)
  514. {
  515. assert(fixed_dim.maxCoeff() < data.m*data.dim*(data.dim+1));
  516. assert(fixed_dim.minCoeff() >= 0);
  517. }
  518. #ifdef EXTREME_VERBOSE
  519. cout<<"data.fixed_dim=["<<endl<<data.fixed_dim<<endl<<"]+1;"<<endl;
  520. #endif
  521. // Compute dense solve matrix (alternative of matrix factorization)
  522. //printf("kkt_inverse()\n");
  523. Eigen::MatrixXd Qfull(*Q);
  524. Eigen::MatrixXd A_eqfull(A_eq);
  525. Eigen::MatrixXd M_Solve;
  526. double timer0_start = get_seconds();
  527. bool use_lu = data.effective_dim != 2;
  528. //use_lu = false;
  529. //printf("use_lu: %s\n",(use_lu?"TRUE":"FALSE"));
  530. kkt_inverse(Qfull, A_eqfull, use_lu,M_Solve);
  531. double timer0_end = get_seconds();
  532. verbose("Bob timing: %.20f\n", (timer0_end - timer0_start)*1000.0);
  533. // Precompute full solve matrix:
  534. const int fsRows = data.m * data.dim * (data.dim + 1); // 12 * number_of_bones
  535. const int fsCols1 = data.M_KG.cols(); // 9 * number_of_posConstraints
  536. const int fsCols2 = A_eq.rows(); // number_of_posConstraints
  537. data.M_FullSolve.resize(fsRows, fsCols1 + fsCols2);
  538. // note the magical multiplicative constant "-0.5", I've no idea why it has
  539. // to be there :)
  540. data.M_FullSolve <<
  541. (-0.5 * M_Solve.block(0, 0, fsRows, fsRows) * data.M_KG).template cast<SSCALAR>(),
  542. M_Solve.block(0, fsRows, fsRows, fsCols2).template cast<SSCALAR>();
  543. if(data.with_dynamics)
  544. {
  545. printf(
  546. "---------------------------------------------------------------------\n"
  547. "\n\n\nWITH DYNAMICS recomputation\n\n\n"
  548. "---------------------------------------------------------------------\n"
  549. );
  550. // Also need to save Π1 before it gets multiplied by Ktilde (aka M_KG)
  551. data.Pi_1 = M_Solve.block(0, 0, fsRows, fsRows).template cast<SSCALAR>();
  552. }
  553. // Precompute condensed matrices,
  554. // first CSM:
  555. std::vector<MatrixXS> CSM_M_SSCALAR;
  556. CSM_M_SSCALAR.resize(data.dim);
  557. for (int i=0; i<data.dim; i++) CSM_M_SSCALAR[i] = data.CSM_M[i].template cast<SSCALAR>();
  558. SSCALAR maxErr1 = condense_CSM(CSM_M_SSCALAR, data.m, data.dim, data.CSM);
  559. verbose("condense_CSM maxErr = %.15f (this should be close to zero)\n", maxErr1);
  560. assert(fabs(maxErr1) < 1e-5);
  561. // and then solveBlock1:
  562. // number of groups
  563. const int k = data.CSM_M[0].rows()/data.dim;
  564. MatrixXS SolveBlock1 = data.M_FullSolve.block(0, 0, data.M_FullSolve.rows(), data.dim * data.dim * k);
  565. SSCALAR maxErr2 = condense_Solve1(SolveBlock1, data.m, k, data.dim, data.CSolveBlock1);
  566. verbose("condense_Solve1 maxErr = %.15f (this should be close to zero)\n", maxErr2);
  567. assert(fabs(maxErr2) < 1e-5);
  568. return true;
  569. }
  570. template <typename LbsMatrixType, typename SSCALAR>
  571. IGL_INLINE bool igl::arap_dof_update(
  572. const ArapDOFData<LbsMatrixType, SSCALAR> & data,
  573. const Eigen::Matrix<double,Eigen::Dynamic,1> & B_eq,
  574. const Eigen::MatrixXd & L0,
  575. const int max_iters,
  576. const double
  577. #ifdef IGL_ARAP_DOF_FIXED_ITERATIONS_COUNT
  578. tol,
  579. #else
  580. /*tol*/,
  581. #endif
  582. Eigen::MatrixXd & L
  583. )
  584. {
  585. typedef Eigen::Matrix<SSCALAR, Eigen::Dynamic, Eigen::Dynamic> MatrixXS;
  586. #ifdef ARAP_GLOBAL_TIMING
  587. double timer_start = get_seconds();
  588. #endif
  589. // number of dimensions
  590. IGL_ASSERT((int)data.CSM_M.size() == data.dim);
  591. IGL_ASSERT((int)L0.size() == (data.m)*data.dim*(data.dim+1));
  592. IGL_ASSERT(max_iters >= 0);
  593. IGL_ASSERT(tol >= 0);
  594. // timing variables
  595. double
  596. sec_start,
  597. sec_covGather,
  598. sec_fitRotations,
  599. //sec_rhs,
  600. sec_prepMult,
  601. sec_solve, sec_end;
  602. assert(L0.cols() == 1);
  603. #ifdef EXTREME_VERBOSE
  604. cout<<"dim="<<data.dim<<";"<<endl;
  605. cout<<"m="<<data.m<<";"<<endl;
  606. #endif
  607. // number of groups
  608. const int k = data.CSM_M[0].rows()/data.dim;
  609. for(int i = 0;i<data.dim;i++)
  610. {
  611. assert(data.CSM_M[i].rows()/data.dim == k);
  612. }
  613. #ifdef EXTREME_VERBOSE
  614. cout<<"k="<<k<<";"<<endl;
  615. #endif
  616. // resize output and initialize with initial guess
  617. L = L0;
  618. #ifndef IGL_ARAP_DOF_FIXED_ITERATIONS_COUNT
  619. // Keep track of last solution
  620. MatrixXS L_prev;
  621. #endif
  622. // We will be iterating on L_SSCALAR, only at the end we convert back to double
  623. MatrixXS L_SSCALAR = L.cast<SSCALAR>();
  624. int iters = 0;
  625. #ifndef IGL_ARAP_DOF_FIXED_ITERATIONS_COUNT
  626. double max_diff = tol+1;
  627. #endif
  628. MatrixXS S(k*data.dim,data.dim);
  629. MatrixXS R(data.dim,data.dim*k);
  630. Eigen::Matrix<SSCALAR,Eigen::Dynamic,1> Rcol(data.dim * data.dim * k);
  631. Eigen::Matrix<SSCALAR ,Eigen::Dynamic,1> B_eq_SSCALAR = B_eq.cast<SSCALAR>();
  632. Eigen::Matrix<SSCALAR ,Eigen::Dynamic,1> L0SSCALAR = L0.cast<SSCALAR>();
  633. Eigen::Matrix<SSCALAR ,Eigen::Dynamic,1> B_eq_fix_SSCALAR = L0SSCALAR(data.fixed_dim);
  634. //MatrixXS rhsFull(Rcol.rows() + B_eq.rows() + B_eq_fix_SSCALAR.rows(), 1);
  635. MatrixXS Lsep(data.m*(data.dim + 1), 3);
  636. const MatrixXS L_part2 =
  637. data.M_FullSolve.block(0, Rcol.rows(), data.M_FullSolve.rows(), B_eq_SSCALAR.rows()) * B_eq_SSCALAR;
  638. const MatrixXS L_part3 =
  639. data.M_FullSolve.block(0, Rcol.rows() + B_eq_SSCALAR.rows(), data.M_FullSolve.rows(), B_eq_fix_SSCALAR.rows()) * B_eq_fix_SSCALAR;
  640. MatrixXS L_part2and3 = L_part2 + L_part3;
  641. // preallocate workspace variables:
  642. MatrixXS Rxyz(k*data.dim, data.dim);
  643. MatrixXS L_part1xyz((data.dim + 1) * data.m, data.dim);
  644. MatrixXS L_part1(data.dim * (data.dim + 1) * data.m, 1);
  645. #ifdef ARAP_GLOBAL_TIMING
  646. double timer_prepFinished = get_seconds();
  647. #endif
  648. #ifdef IGL_ARAP_DOF_FIXED_ITERATIONS_COUNT
  649. while(iters < max_iters)
  650. #else
  651. while(iters < max_iters && max_diff > tol)
  652. #endif
  653. {
  654. if(data.print_timings)
  655. {
  656. sec_start = get_seconds();
  657. }
  658. #ifndef IGL_ARAP_DOF_FIXED_ITERATIONS_COUNT
  659. L_prev = L_SSCALAR;
  660. #endif
  661. ///////////////////////////////////////////////////////////////////////////
  662. // Local step: Fix positions, fit rotations
  663. ///////////////////////////////////////////////////////////////////////////
  664. // Gather covariance matrices
  665. splitColumns(L_SSCALAR, data.m, data.dim, data.dim + 1, Lsep);
  666. S = data.CSM * Lsep;
  667. // interestingly, this doesn't seem to be so slow, but
  668. //MKL is still 2x faster (probably due to AVX)
  669. //#ifdef IGL_ARAP_DOF_DOUBLE_PRECISION_SOLVE
  670. // MKL_matMatMult_double(S, data.CSM, Lsep);
  671. //#else
  672. // MKL_matMatMult_single(S, data.CSM, Lsep);
  673. //#endif
  674. if(data.print_timings)
  675. {
  676. sec_covGather = get_seconds();
  677. }
  678. #ifdef EXTREME_VERBOSE
  679. cout<<"S=["<<endl<<S<<endl<<"];"<<endl;
  680. #endif
  681. // Fit rotations to covariance matrices
  682. if(data.effective_dim == 2)
  683. {
  684. fit_rotations_planar(S,R);
  685. }else
  686. {
  687. #ifdef __SSE__ // fit_rotations_SSE will convert to float if necessary
  688. fit_rotations_SSE(S,R);
  689. #else
  690. fit_rotations(S,false,R);
  691. #endif
  692. }
  693. #ifdef EXTREME_VERBOSE
  694. cout<<"R=["<<endl<<R<<endl<<"];"<<endl;
  695. #endif
  696. if(data.print_timings)
  697. {
  698. sec_fitRotations = get_seconds();
  699. }
  700. ///////////////////////////////////////////////////////////////////////////
  701. // "Global" step: fix rotations per mesh vertex, solve for
  702. // linear transformations at handles
  703. ///////////////////////////////////////////////////////////////////////////
  704. // all this shuffling is retarded and not completely negligible time-wise;
  705. // TODO: change fit_rotations_XXX so it returns R in the format ready for
  706. // CSolveBlock1 multiplication
  707. columnize(R, k, 2, Rcol);
  708. #ifdef EXTREME_VERBOSE
  709. cout<<"Rcol=["<<endl<<Rcol<<endl<<"];"<<endl;
  710. #endif
  711. splitColumns(Rcol, k, data.dim, data.dim, Rxyz);
  712. if(data.print_timings)
  713. {
  714. sec_prepMult = get_seconds();
  715. }
  716. L_part1xyz = data.CSolveBlock1 * Rxyz;
  717. //#ifdef IGL_ARAP_DOF_DOUBLE_PRECISION_SOLVE
  718. // MKL_matMatMult_double(L_part1xyz, data.CSolveBlock1, Rxyz);
  719. //#else
  720. // MKL_matMatMult_single(L_part1xyz, data.CSolveBlock1, Rxyz);
  721. //#endif
  722. mergeColumns(L_part1xyz, data.m, data.dim, data.dim + 1, L_part1);
  723. if(data.with_dynamics)
  724. {
  725. // Consider reordering or precomputing matrix multiplications
  726. MatrixXS L_part1_dyn(data.dim * (data.dim + 1) * data.m, 1);
  727. // Eigen can't parse this:
  728. //L_part1_dyn =
  729. // -(2.0/(data.h*data.h)) * data.Pi_1 * data.Mass_tilde * data.L0 +
  730. // (1.0/(data.h*data.h)) * data.Pi_1 * data.Mass_tilde * data.Lm1;
  731. // -1.0 because we've moved these linear terms to the right hand side
  732. //MatrixXS temp = -1.0 *
  733. // ((-2.0/(data.h*data.h)) * data.L0.array() +
  734. // (1.0/(data.h*data.h)) * data.Lm1.array()).matrix();
  735. //MatrixXS temp = -1.0 *
  736. // ( (-1.0/(data.h*data.h)) * data.L0.array() +
  737. // (1.0/(data.h*data.h)) * data.Lm1.array()
  738. // (-1.0/(data.h*data.h)) * data.L0.array() +
  739. // ).matrix();
  740. //Lvel0 = (1.0/(data.h)) * data.Lm1.array() - data.L0.array();
  741. MatrixXS temp = -1.0 *
  742. ( (-1.0/(data.h*data.h)) * data.L0.array() +
  743. (1.0/(data.h)) * data.Lvel0.array()
  744. ).matrix();
  745. Eigen::MatrixXd temp_d = temp.template cast<double>();
  746. Eigen::MatrixXd temp_g = data.fgrav*(data.grav_mag*data.grav_dir);
  747. assert(data.fext.rows() == temp_g.rows());
  748. assert(data.fext.cols() == temp_g.cols());
  749. Eigen::MatrixXd temp2 = data.Mass_tilde * temp_d + temp_g + data.fext.template cast<double>();
  750. MatrixXS temp2_f = temp2.template cast<SSCALAR>();
  751. L_part1_dyn = data.Pi_1 * temp2_f;
  752. L_part1.array() = L_part1.array() + L_part1_dyn.array();
  753. }
  754. //L_SSCALAR = L_part1 + L_part2and3;
  755. assert(L_SSCALAR.rows() == L_part1.rows() && L_SSCALAR.rows() == L_part2and3.rows());
  756. for (int i=0; i<L_SSCALAR.rows(); i++)
  757. {
  758. L_SSCALAR(i, 0) = L_part1(i, 0) + L_part2and3(i, 0);
  759. }
  760. #ifdef EXTREME_VERBOSE
  761. cout<<"L=["<<endl<<L<<endl<<"];"<<endl;
  762. #endif
  763. if(data.print_timings)
  764. {
  765. sec_solve = get_seconds();
  766. }
  767. #ifndef IGL_ARAP_DOF_FIXED_ITERATIONS_COUNT
  768. // Compute maximum absolute difference with last iteration's solution
  769. max_diff = (L_SSCALAR-L_prev).eval().array().abs().matrix().maxCoeff();
  770. #endif
  771. iters++;
  772. if(data.print_timings)
  773. {
  774. sec_end = get_seconds();
  775. #ifndef WIN32
  776. // trick to get sec_* variables to compile without warning on mac
  777. if(false)
  778. #endif
  779. printf(
  780. "\ntotal iteration time = %f "
  781. "[local: covGather = %f, "
  782. "fitRotations = %f, "
  783. "global: prep = %f, "
  784. "solve = %f, "
  785. "error = %f [ms]]\n",
  786. (sec_end - sec_start)*1000.0,
  787. (sec_covGather - sec_start)*1000.0,
  788. (sec_fitRotations - sec_covGather)*1000.0,
  789. (sec_prepMult - sec_fitRotations)*1000.0,
  790. (sec_solve - sec_prepMult)*1000.0,
  791. (sec_end - sec_solve)*1000.0 );
  792. }
  793. }
  794. L = L_SSCALAR.template cast<double>();
  795. assert(L.cols() == 1);
  796. #ifdef ARAP_GLOBAL_TIMING
  797. double timer_finito = get_seconds();
  798. printf(
  799. "ARAP preparation = %f, "
  800. "all %i iterations = %f [ms]\n",
  801. (timer_prepFinished - timer_start)*1000.0,
  802. max_iters,
  803. (timer_finito - timer_prepFinished)*1000.0);
  804. #endif
  805. return true;
  806. }
  807. #ifdef IGL_STATIC_LIBRARY
  808. // Explicit template instantiation
  809. template bool igl::arap_dof_update<Eigen::Matrix<double, -1, -1, 0, -1, -1>, double>(ArapDOFData<Eigen::Matrix<double, -1, -1, 0, -1, -1>, double> const&, Eigen::Matrix<double, -1, 1, 0, -1, 1> const&, Eigen::Matrix<double, -1, -1, 0, -1, -1> const&, int, double, Eigen::Matrix<double, -1, -1, 0, -1, -1>&);
  810. template bool igl::arap_dof_recomputation<Eigen::Matrix<double, -1, -1, 0, -1, -1>, double>(Eigen::Matrix<int, -1, 1, 0, -1, 1> const&, Eigen::SparseMatrix<double, 0, int> const&, ArapDOFData<Eigen::Matrix<double, -1, -1, 0, -1, -1>, double>&);
  811. template bool igl::arap_dof_precomputation<Eigen::Matrix<double, -1, -1, 0, -1, -1>, double>(Eigen::Matrix<double, -1, -1, 0, -1, -1> const&, Eigen::Matrix<int, -1, -1, 0, -1, -1> const&, Eigen::Matrix<double, -1, -1, 0, -1, -1> const&, Eigen::Matrix<int, -1, 1, 0, -1, 1> const&, ArapDOFData<Eigen::Matrix<double, -1, -1, 0, -1, -1>, double>&);
  812. template bool igl::arap_dof_update<Eigen::Matrix<double, -1, -1, 0, -1, -1>, float>(igl::ArapDOFData<Eigen::Matrix<double, -1, -1, 0, -1, -1>, float> const&, Eigen::Matrix<double, -1, 1, 0, -1, 1> const&, Eigen::Matrix<double, -1, -1, 0, -1, -1> const&, int, double, Eigen::Matrix<double, -1, -1, 0, -1, -1>&);
  813. template bool igl::arap_dof_recomputation<Eigen::Matrix<double, -1, -1, 0, -1, -1>, float>(Eigen::Matrix<int, -1, 1, 0, -1, 1> const&, Eigen::SparseMatrix<double, 0, int> const&, igl::ArapDOFData<Eigen::Matrix<double, -1, -1, 0, -1, -1>, float>&);
  814. template bool igl::arap_dof_precomputation<Eigen::Matrix<double, -1, -1, 0, -1, -1>, float>(Eigen::Matrix<double, -1, -1, 0, -1, -1> const&, Eigen::Matrix<int, -1, -1, 0, -1, -1> const&, Eigen::Matrix<double, -1, -1, 0, -1, -1> const&, Eigen::Matrix<int, -1, 1, 0, -1, 1> const&, igl::ArapDOFData<Eigen::Matrix<double, -1, -1, 0, -1, -1>, float>&);
  815. #endif