slim.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755
  1. // This file is part of libigl, a simple c++ geometry processing library.
  2. //
  3. // Copyright (C) 2016 Michael Rabinovich
  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 "slim.h"
  9. #include <igl/boundary_loop.h>
  10. #include <igl/cotmatrix.h>
  11. #include <igl/edge_lengths.h>
  12. #include <igl/grad.h>
  13. #include <igl/local_basis.h>
  14. #include <igl/readOBJ.h>
  15. #include <igl/repdiag.h>
  16. #include <igl/vector_area_matrix.h>
  17. #include <iostream>
  18. #include <Eigen/Dense>
  19. #include <Eigen/Sparse>
  20. #include <map>
  21. #include <set>
  22. #include <vector>
  23. #include "igl/arap.h"
  24. #include "igl/cat.h"
  25. #include "igl/doublearea.h"
  26. #include "igl/grad.h"
  27. #include "igl/local_basis.h"
  28. #include "igl/per_face_normals.h"
  29. #include "igl/slice_into.h"
  30. #include "igl/volume.h"
  31. #include "igl/polar_svd.h"
  32. #include <Eigen/IterativeLinearSolvers>
  33. #include <Eigen/Sparse>
  34. #include <Eigen/SparseQR>
  35. #include <Eigen/SparseCholesky>
  36. #include <Eigen/IterativeLinearSolvers>
  37. #include "flip_avoiding_line_search.h"
  38. using namespace std;
  39. using namespace Eigen;
  40. ///////// Helper functions to compute gradient matrices
  41. void compute_surface_gradient_matrix(const Eigen::MatrixXd& V, const Eigen::MatrixXi& F,
  42. const Eigen::MatrixXd& F1, const Eigen::MatrixXd& F2,
  43. Eigen::SparseMatrix<double>& D1, Eigen::SparseMatrix<double>& D2) {
  44. Eigen::SparseMatrix<double> G;
  45. igl::grad(V,F,G);
  46. Eigen::SparseMatrix<double> Dx = G.block(0,0,F.rows(),V.rows());
  47. Eigen::SparseMatrix<double> Dy = G.block(F.rows(),0,F.rows(),V.rows());
  48. Eigen::SparseMatrix<double> Dz = G.block(2*F.rows(),0,F.rows(),V.rows());
  49. D1 = F1.col(0).asDiagonal()*Dx + F1.col(1).asDiagonal()*Dy + F1.col(2).asDiagonal()*Dz;
  50. D2 = F2.col(0).asDiagonal()*Dx + F2.col(1).asDiagonal()*Dy + F2.col(2).asDiagonal()*Dz;
  51. }
  52. // Computes the weights and solve the linear system for the quadratic proxy specified in the paper
  53. // The output of this is used to generate a search direction that will be fed to the Linesearch class
  54. class WeightedGlobalLocal {
  55. public:
  56. WeightedGlobalLocal(igl::SLIMData& state);
  57. // Compute necessary information before solving the proxy quadratic
  58. void pre_calc();
  59. // Solve the weighted proxy global step
  60. // Output:
  61. // V_new #V by dim list of mesh positions (will be fed to a linesearch algorithm)
  62. void solve_weighted_proxy(Eigen::MatrixXd& V_new);
  63. // Compute the energy specified in the SLIMData structure + the soft constraint energy (in case there are soft constraints)
  64. // Input:
  65. // V_new #V by dim list of mesh positions
  66. virtual double compute_energy(Eigen::MatrixXd& V_new);
  67. //private:
  68. void compute_jacobians(const Eigen::MatrixXd& V_o);
  69. double compute_energy_with_jacobians(const Eigen::MatrixXd& V, const Eigen::MatrixXi& F,
  70. const Eigen::MatrixXd& Ji, Eigen::MatrixXd& V_o, Eigen::VectorXd& areas);
  71. double compute_soft_const_energy(const Eigen::MatrixXd& V, const Eigen::MatrixXi& F,
  72. Eigen::MatrixXd& V_o);
  73. void update_weights_and_closest_rotations(const Eigen::MatrixXd& V, const Eigen::MatrixXi& F, Eigen::MatrixXd& uv);
  74. void solve_weighted_arap(const Eigen::MatrixXd& V, const Eigen::MatrixXi& F, Eigen::MatrixXd& uv, Eigen::VectorXi& b,
  75. Eigen::MatrixXd& bc);
  76. void build_linear_system(Eigen::SparseMatrix<double> &L);
  77. void buildA(Eigen::SparseMatrix<double>& A);
  78. void buildRhs(const Eigen::SparseMatrix<double>& At);
  79. void add_soft_constraints(Eigen::SparseMatrix<double> &L);
  80. void add_proximal_penalty();
  81. igl::SLIMData& m_state;
  82. Eigen::VectorXd M;
  83. Eigen::VectorXd rhs;
  84. Eigen::MatrixXd Ri,Ji;
  85. Eigen::VectorXd W_11; Eigen::VectorXd W_12; Eigen::VectorXd W_13;
  86. Eigen::VectorXd W_21; Eigen::VectorXd W_22; Eigen::VectorXd W_23;
  87. Eigen::VectorXd W_31; Eigen::VectorXd W_32; Eigen::VectorXd W_33;
  88. Eigen::SparseMatrix<double> Dx,Dy,Dz;
  89. int f_n,v_n;
  90. bool first_solve;
  91. bool has_pre_calc = false;
  92. int dim;
  93. };
  94. //// Implementation
  95. WeightedGlobalLocal::WeightedGlobalLocal(igl::SLIMData& state) :
  96. m_state(state) {
  97. }
  98. void WeightedGlobalLocal::solve_weighted_proxy(Eigen::MatrixXd& V_new) {
  99. update_weights_and_closest_rotations(m_state.V,m_state.F,V_new);
  100. solve_weighted_arap(m_state.V,m_state.F,V_new,m_state.b,m_state.bc);
  101. }
  102. void WeightedGlobalLocal::compute_jacobians(const Eigen::MatrixXd& uv) {
  103. if (m_state.F.cols() == 3){
  104. // Ji=[D1*u,D2*u,D1*v,D2*v];
  105. Ji.col(0) = Dx*uv.col(0); Ji.col(1) = Dy*uv.col(0);
  106. Ji.col(2) = Dx*uv.col(1); Ji.col(3) = Dy*uv.col(1);
  107. } else /*tet mesh*/{
  108. // Ji=[D1*u,D2*u,D3*u, D1*v,D2*v, D3*v, D1*w,D2*w,D3*w];
  109. Ji.col(0) = Dx*uv.col(0); Ji.col(1) = Dy*uv.col(0); Ji.col(2) = Dz*uv.col(0);
  110. Ji.col(3) = Dx*uv.col(1); Ji.col(4) = Dy*uv.col(1); Ji.col(5) = Dz*uv.col(1);
  111. Ji.col(6) = Dx*uv.col(2); Ji.col(7) = Dy*uv.col(2); Ji.col(8) = Dz*uv.col(2);
  112. }
  113. }
  114. void WeightedGlobalLocal::update_weights_and_closest_rotations(const Eigen::MatrixXd& V,
  115. const Eigen::MatrixXi& F, Eigen::MatrixXd& uv) {
  116. compute_jacobians(uv);
  117. const double eps = 1e-8;
  118. double exp_f = m_state.exp_factor;
  119. if (dim==2) {
  120. for(int i=0; i <Ji.rows(); ++i ) {
  121. typedef Eigen::Matrix<double,2,2> Mat2;
  122. typedef Eigen::Matrix<double,2,1> Vec2;
  123. Mat2 ji,ri,ti,ui,vi; Vec2 sing; Vec2 closest_sing_vec;Mat2 mat_W;
  124. Vec2 m_sing_new;
  125. double s1,s2;
  126. ji(0,0) = Ji(i,0); ji(0,1) = Ji(i,1);
  127. ji(1,0) = Ji(i,2); ji(1,1) = Ji(i,3);
  128. igl::polar_svd(ji,ri,ti,ui,sing,vi);
  129. s1 = sing(0); s2 = sing(1);
  130. // Update Weights according to energy
  131. switch(m_state.slim_energy) {
  132. case igl::SLIMData::ARAP: {
  133. m_sing_new << 1,1;
  134. break;
  135. } case igl::SLIMData::SYMMETRIC_DIRICHLET: {
  136. double s1_g = 2* (s1-pow(s1,-3));
  137. double s2_g = 2 * (s2-pow(s2,-3));
  138. m_sing_new << sqrt(s1_g/(2*(s1-1))), sqrt(s2_g/(2*(s2-1)));
  139. break;
  140. } case igl::SLIMData::LOG_ARAP: {
  141. double s1_g = 2 * (log(s1)/s1);
  142. double s2_g = 2 * (log(s2)/s2);
  143. m_sing_new << sqrt(s1_g/(2*(s1-1))), sqrt(s2_g/(2*(s2-1)));
  144. break;
  145. } case igl::SLIMData::CONFORMAL: {
  146. double s1_g = 1/(2*s2) - s2/(2*pow(s1,2));
  147. double s2_g = 1/(2*s1) - s1/(2*pow(s2,2));
  148. double geo_avg = sqrt(s1*s2);
  149. double s1_min = geo_avg; double s2_min = geo_avg;
  150. m_sing_new << sqrt(s1_g/(2*(s1-s1_min))), sqrt(s2_g/(2*(s2-s2_min)));
  151. // change local step
  152. closest_sing_vec << s1_min,s2_min;
  153. ri = ui*closest_sing_vec.asDiagonal()*vi.transpose();
  154. break;
  155. } case igl::SLIMData::EXP_CONFORMAL: {
  156. double s1_g = 2* (s1-pow(s1,-3));
  157. double s2_g = 2 * (s2-pow(s2,-3));
  158. double geo_avg = sqrt(s1*s2);
  159. double s1_min = geo_avg; double s2_min = geo_avg;
  160. double in_exp = exp_f*((pow(s1,2)+pow(s2,2))/(2*s1*s2));
  161. double exp_thing = exp(in_exp);
  162. s1_g *= exp_thing*exp_f;
  163. s2_g *= exp_thing*exp_f;
  164. m_sing_new << sqrt(s1_g/(2*(s1-1))), sqrt(s2_g/(2*(s2-1)));
  165. break;
  166. } case igl::SLIMData::EXP_SYMMETRIC_DIRICHLET: {
  167. double s1_g = 2* (s1-pow(s1,-3));
  168. double s2_g = 2 * (s2-pow(s2,-3));
  169. double in_exp = exp_f*(pow(s1,2)+pow(s1,-2)+pow(s2,2)+pow(s2,-2));
  170. double exp_thing = exp(in_exp);
  171. s1_g *= exp_thing*exp_f;
  172. s2_g *= exp_thing*exp_f;
  173. m_sing_new << sqrt(s1_g/(2*(s1-1))), sqrt(s2_g/(2*(s2-1)));
  174. break;
  175. }
  176. }
  177. if (abs(s1-1) < eps) m_sing_new(0) = 1; if (abs(s2-1) < eps) m_sing_new(1) = 1;
  178. mat_W = ui*m_sing_new.asDiagonal()*ui.transpose();
  179. W_11(i) = mat_W(0,0); W_12(i) = mat_W(0,1); W_21(i) = mat_W(1,0); W_22(i) = mat_W(1,1);
  180. // 2) Update local step (doesn't have to be a rotation, for instance in case of conformal energy)
  181. Ri(i,0) = ri(0,0); Ri(i,1) = ri(1,0); Ri(i,2) = ri(0,1); Ri(i,3) = ri(1,1);
  182. }
  183. } else {
  184. typedef Eigen::Matrix<double,3,1> Vec3; typedef Eigen::Matrix<double,3,3> Mat3;
  185. Mat3 ji; Vec3 m_sing_new; Vec3 closest_sing_vec;
  186. const double sqrt_2 = sqrt(2);
  187. for(int i=0; i <Ji.rows(); ++i ) {
  188. ji(0,0) = Ji(i,0); ji(0,1) = Ji(i,1); ji(0,2) = Ji(i,2);
  189. ji(1,0) = Ji(i,3); ji(1,1) = Ji(i,4); ji(1,2) = Ji(i,5);
  190. ji(2,0) = Ji(i,6); ji(2,1) = Ji(i,7); ji(2,2) = Ji(i,8);
  191. Mat3 ri,ti,ui,vi;
  192. Vec3 sing;
  193. igl::polar_svd(ji,ri,ti,ui,sing,vi);
  194. double s1 = sing(0); double s2 = sing(1); double s3 = sing(2);
  195. // 1) Update Weights
  196. switch(m_state.slim_energy) {
  197. case igl::SLIMData::ARAP: {
  198. m_sing_new << 1,1,1;
  199. break;
  200. } case igl::SLIMData::LOG_ARAP: {
  201. double s1_g = 2 * (log(s1)/s1);
  202. double s2_g = 2 * (log(s2)/s2);
  203. double s3_g = 2 * (log(s3)/s3);
  204. m_sing_new << sqrt(s1_g/(2*(s1-1))), sqrt(s2_g/(2*(s2-1))), sqrt(s3_g/(2*(s3-1)));
  205. break;
  206. } case igl::SLIMData::SYMMETRIC_DIRICHLET: {
  207. double s1_g = 2* (s1-pow(s1,-3));
  208. double s2_g = 2 * (s2-pow(s2,-3));
  209. double s3_g = 2 * (s3-pow(s3,-3));
  210. m_sing_new << sqrt(s1_g/(2*(s1-1))), sqrt(s2_g/(2*(s2-1))), sqrt(s3_g/(2*(s3-1)));
  211. break;
  212. } case igl::SLIMData::EXP_SYMMETRIC_DIRICHLET: {
  213. double s1_g = 2* (s1-pow(s1,-3));
  214. double s2_g = 2 * (s2-pow(s2,-3));
  215. double s3_g = 2 * (s3-pow(s3,-3));
  216. m_sing_new << sqrt(s1_g/(2*(s1-1))), sqrt(s2_g/(2*(s2-1))), sqrt(s3_g/(2*(s3-1)));
  217. double in_exp = exp_f*(pow(s1,2)+pow(s1,-2)+pow(s2,2)+pow(s2,-2)+pow(s3,2)+pow(s3,-2));
  218. double exp_thing = exp(in_exp);
  219. s1_g *= exp_thing*exp_f;
  220. s2_g *= exp_thing*exp_f;
  221. s3_g *= exp_thing*exp_f;
  222. m_sing_new << sqrt(s1_g/(2*(s1-1))), sqrt(s2_g/(2*(s2-1))), sqrt(s3_g/(2*(s3-1)));
  223. break;
  224. }
  225. case igl::SLIMData::CONFORMAL: {
  226. double common_div = 9*(pow(s1*s2*s3,5./3.));
  227. double s1_g = (-2*s2*s3*(pow(s2,2)+pow(s3,2)-2*pow(s1,2)) ) / common_div;
  228. double s2_g = (-2*s1*s3*(pow(s1,2)+pow(s3,2)-2*pow(s2,2)) ) / common_div;
  229. double s3_g = (-2*s1*s2*(pow(s1,2)+pow(s2,2)-2*pow(s3,2)) ) / common_div;
  230. double closest_s = sqrt(pow(s1,2)+pow(s3,2)) / sqrt_2;
  231. double s1_min = closest_s; double s2_min = closest_s; double s3_min = closest_s;
  232. m_sing_new << sqrt(s1_g/(2*(s1-s1_min))), sqrt(s2_g/(2*(s2-s2_min))), sqrt(s3_g/(2*(s3-s3_min)));
  233. // change local step
  234. closest_sing_vec << s1_min,s2_min,s3_min;
  235. ri = ui*closest_sing_vec.asDiagonal()*vi.transpose();
  236. break;
  237. }
  238. case igl::SLIMData::EXP_CONFORMAL: {
  239. // E_conf = (s1^2 + s2^2 + s3^2)/(3*(s1*s2*s3)^(2/3) )
  240. // dE_conf/ds1 = (-2*(s2*s3)*(s2^2+s3^2 -2*s1^2) ) / (9*(s1*s2*s3)^(5/3))
  241. // Argmin E_conf(s1): s1 = sqrt(s1^2+s2^2)/sqrt(2)
  242. double common_div = 9*(pow(s1*s2*s3,5./3.));
  243. double s1_g = (-2*s2*s3*(pow(s2,2)+pow(s3,2)-2*pow(s1,2)) ) / common_div;
  244. double s2_g = (-2*s1*s3*(pow(s1,2)+pow(s3,2)-2*pow(s2,2)) ) / common_div;
  245. double s3_g = (-2*s1*s2*(pow(s1,2)+pow(s2,2)-2*pow(s3,2)) ) / common_div;
  246. double in_exp = exp_f*( (pow(s1,2)+pow(s2,2)+pow(s3,2))/ (3*pow((s1*s2*s3),2./3)) ); ;
  247. double exp_thing = exp(in_exp);
  248. double closest_s = sqrt(pow(s1,2)+pow(s3,2)) / sqrt_2;
  249. double s1_min = closest_s; double s2_min = closest_s; double s3_min = closest_s;
  250. s1_g *= exp_thing*exp_f;
  251. s2_g *= exp_thing*exp_f;
  252. s3_g *= exp_thing*exp_f;
  253. m_sing_new << sqrt(s1_g/(2*(s1-s1_min))), sqrt(s2_g/(2*(s2-s2_min))), sqrt(s3_g/(2*(s3-s3_min)));
  254. // change local step
  255. closest_sing_vec << s1_min,s2_min,s3_min;
  256. ri = ui*closest_sing_vec.asDiagonal()*vi.transpose();
  257. }
  258. }
  259. if (abs(s1-1) < eps) m_sing_new(0) = 1; if (abs(s2-1) < eps) m_sing_new(1) = 1; if (abs(s3-1) < eps) m_sing_new(2) = 1;
  260. Mat3 mat_W;
  261. mat_W = ui*m_sing_new.asDiagonal()*ui.transpose();
  262. W_11(i) = mat_W(0,0);
  263. W_12(i) = mat_W(0,1);
  264. W_13(i) = mat_W(0,2);
  265. W_21(i) = mat_W(1,0);
  266. W_22(i) = mat_W(1,1);
  267. W_23(i) = mat_W(1,2);
  268. W_31(i) = mat_W(2,0);
  269. W_32(i) = mat_W(2,1);
  270. W_33(i) = mat_W(2,2);
  271. // 2) Update closest rotations (not rotations in case of conformal energy)
  272. Ri(i,0) = ri(0,0); Ri(i,1) = ri(1,0); Ri(i,2) = ri(2,0);
  273. Ri(i,3) = ri(0,1); Ri(i,4) = ri(1,1); Ri(i,5) = ri(2,1);
  274. Ri(i,6) = ri(0,2); Ri(i,7) = ri(1,2); Ri(i,8) = ri(2,2);
  275. } // for loop end
  276. } // if dim end
  277. }
  278. void WeightedGlobalLocal::solve_weighted_arap(const Eigen::MatrixXd& V, const Eigen::MatrixXi& F,
  279. Eigen::MatrixXd& uv, Eigen::VectorXi& soft_b_p, Eigen::MatrixXd& soft_bc_p) {
  280. using namespace Eigen;
  281. Eigen::SparseMatrix<double> L;
  282. build_linear_system(L);
  283. // solve
  284. Eigen::VectorXd Uc;
  285. if (dim == 2) {
  286. SimplicialLDLT<SparseMatrix<double> > solver;
  287. Uc = solver.compute(L).solve(rhs);
  288. } else { // seems like CG performs much worse for 2D and way better for 3D
  289. Eigen::VectorXd guess(uv.rows()*dim);
  290. for (int i = 0; i < dim; i++) for (int j = 0; j < dim; j++) guess(uv.rows()*i + j) = uv(i,j); // flatten vector
  291. ConjugateGradient<SparseMatrix<double>, Eigen::Upper> solver;
  292. solver.setTolerance(1e-8);
  293. Uc = solver.compute(L).solveWithGuess(rhs,guess);
  294. }
  295. for (int i = 0; i < dim; i++)
  296. uv.col(i) = Uc.block(i*v_n,0,v_n,1);
  297. }
  298. void WeightedGlobalLocal::pre_calc() {
  299. if (!has_pre_calc) {
  300. v_n = m_state.v_num; f_n = m_state.f_num;
  301. if (m_state.F.cols() == 3) {
  302. dim = 2;
  303. Eigen::MatrixXd F1,F2,F3;
  304. igl::local_basis(m_state.V,m_state.F,F1,F2,F3);
  305. compute_surface_gradient_matrix(m_state.V,m_state.F,F1,F2,Dx,Dy);
  306. W_11.resize(f_n); W_12.resize(f_n); W_21.resize(f_n); W_22.resize(f_n);
  307. } else {
  308. dim = 3;
  309. Eigen::SparseMatrix<double> G;
  310. igl::grad(m_state.V,m_state.F,G,m_state.mesh_improvement_3d /*use normal gradient, or one from a "regular" tet*/);
  311. Dx = G.block(0,0,m_state.F.rows(),m_state.V.rows());
  312. Dy = G.block(m_state.F.rows(),0,m_state.F.rows(),m_state.V.rows());
  313. Dz = G.block(2*m_state.F.rows(),0,m_state.F.rows(),m_state.V.rows());
  314. W_11.resize(f_n);W_12.resize(f_n);W_13.resize(f_n);
  315. W_21.resize(f_n);W_22.resize(f_n);W_23.resize(f_n);
  316. W_31.resize(f_n);W_32.resize(f_n);W_33.resize(f_n);
  317. }
  318. Dx.makeCompressed();Dy.makeCompressed(); Dz.makeCompressed();
  319. Ri.resize(f_n, dim*dim); Ji.resize(f_n, dim*dim);
  320. rhs.resize(dim*m_state.v_num);
  321. // flattened weight matrix
  322. M.resize(dim*dim*f_n);
  323. for (int i = 0; i < dim*dim; i++)
  324. for (int j = 0; j < f_n; j++)
  325. M(i*f_n + j) = m_state.M(j);
  326. first_solve = true;
  327. has_pre_calc = true;
  328. }
  329. }
  330. void WeightedGlobalLocal::build_linear_system(Eigen::SparseMatrix<double> &L) {
  331. // formula (35) in paper
  332. Eigen::SparseMatrix<double> A(dim*dim*f_n, dim*v_n);
  333. buildA(A);
  334. Eigen::SparseMatrix<double> At = A.transpose();
  335. At.makeCompressed();
  336. Eigen::SparseMatrix<double> id_m(At.rows(),At.rows()); id_m.setIdentity();
  337. // add proximal penalty
  338. L = At*M.asDiagonal()*A + m_state.proximal_p * id_m; //add also a proximal term
  339. L.makeCompressed();
  340. buildRhs(At);
  341. Eigen::SparseMatrix<double> OldL = L;
  342. add_soft_constraints(L);
  343. L.makeCompressed();
  344. }
  345. void WeightedGlobalLocal::add_soft_constraints(Eigen::SparseMatrix<double> &L) {
  346. int v_n = m_state.v_num;
  347. for (int d = 0; d < dim; d++) {
  348. for (int i = 0; i < m_state.b.rows(); i++) {
  349. int v_idx = m_state.b(i);
  350. rhs(d*v_n + v_idx) += m_state.soft_const_p * m_state.bc(i,d); // rhs
  351. L.coeffRef(d*v_n + v_idx, d*v_n + v_idx) += m_state.soft_const_p; // diagonal of matrix
  352. }
  353. }
  354. }
  355. double WeightedGlobalLocal::compute_energy(Eigen::MatrixXd& V_new) {
  356. compute_jacobians(V_new);
  357. return compute_energy_with_jacobians(m_state.V,m_state.F, Ji, V_new,m_state.M) + compute_soft_const_energy(m_state.V,m_state.F,V_new);
  358. }
  359. double WeightedGlobalLocal::compute_soft_const_energy(const Eigen::MatrixXd& V,
  360. const Eigen::MatrixXi& F,
  361. Eigen::MatrixXd& V_o) {
  362. double e = 0;
  363. for (int i = 0; i < m_state.b.rows(); i++) {
  364. e += m_state.soft_const_p*(m_state.bc.row(i)-V_o.row(m_state.b(i))).squaredNorm();
  365. }
  366. return e;
  367. }
  368. double WeightedGlobalLocal::compute_energy_with_jacobians(const Eigen::MatrixXd& V,
  369. const Eigen::MatrixXi& F, const Eigen::MatrixXd& Ji, Eigen::MatrixXd& uv, Eigen::VectorXd& areas) {
  370. double energy = 0;
  371. if (dim == 2) {
  372. Eigen::Matrix<double,2,2> ji;
  373. for (int i = 0; i < f_n; i++) {
  374. ji(0,0) = Ji(i,0); ji(0,1) = Ji(i,1);
  375. ji(1,0) = Ji(i,2); ji(1,1) = Ji(i,3);
  376. typedef Eigen::Matrix<double,2,2> Mat2;
  377. typedef Eigen::Matrix<double,2,1> Vec2;
  378. Mat2 ri,ti,ui,vi; Vec2 sing;
  379. igl::polar_svd(ji,ri,ti,ui,sing,vi);
  380. double s1 = sing(0); double s2 = sing(1);
  381. switch(m_state.slim_energy) {
  382. case igl::SLIMData::ARAP: {
  383. energy+= areas(i) * (pow(s1-1,2) + pow(s2-1,2));
  384. break;
  385. }
  386. case igl::SLIMData::SYMMETRIC_DIRICHLET: {
  387. energy += areas(i) * (pow(s1,2) +pow(s1,-2) + pow(s2,2) + pow(s2,-2));
  388. break;
  389. }
  390. case igl::SLIMData::EXP_SYMMETRIC_DIRICHLET: {
  391. energy += areas(i) * exp(m_state.exp_factor*(pow(s1,2) +pow(s1,-2) + pow(s2,2) + pow(s2,-2)));
  392. break;
  393. }
  394. case igl::SLIMData::LOG_ARAP: {
  395. energy += areas(i) * (pow(log(s1),2) + pow(log(s2),2));
  396. break;
  397. }
  398. case igl::SLIMData::CONFORMAL: {
  399. energy += areas(i) * ( (pow(s1,2)+pow(s2,2))/(2*s1*s2) );
  400. break;
  401. }
  402. case igl::SLIMData::EXP_CONFORMAL: {
  403. energy += areas(i) * exp(m_state.exp_factor*((pow(s1,2)+pow(s2,2))/(2*s1*s2)));
  404. break;
  405. }
  406. }
  407. }
  408. } else {
  409. Eigen::Matrix<double,3,3> ji;
  410. for (int i = 0; i < f_n; i++) {
  411. ji(0,0) = Ji(i,0); ji(0,1) = Ji(i,1); ji(0,2) = Ji(i,2);
  412. ji(1,0) = Ji(i,3); ji(1,1) = Ji(i,4); ji(1,2) = Ji(i,5);
  413. ji(2,0) = Ji(i,6); ji(2,1) = Ji(i,7); ji(2,2) = Ji(i,8);
  414. typedef Eigen::Matrix<double,3,3> Mat3;
  415. typedef Eigen::Matrix<double,3,1> Vec3;
  416. Mat3 ri,ti,ui,vi; Vec3 sing;
  417. igl::polar_svd(ji,ri,ti,ui,sing,vi);
  418. double s1 = sing(0); double s2 = sing(1); double s3 = sing(2);
  419. switch(m_state.slim_energy) {
  420. case igl::SLIMData::ARAP: {
  421. energy+= areas(i) * (pow(s1-1,2) + pow(s2-1,2) + pow(s3-1,2));
  422. break;
  423. }
  424. case igl::SLIMData::SYMMETRIC_DIRICHLET: {
  425. energy += areas(i) * (pow(s1,2) +pow(s1,-2) + pow(s2,2) + pow(s2,-2) + pow(s3,2) + pow(s3,-2));
  426. break;
  427. }
  428. case igl::SLIMData::EXP_SYMMETRIC_DIRICHLET: {
  429. energy += areas(i) * exp(m_state.exp_factor*(pow(s1,2) +pow(s1,-2) + pow(s2,2) + pow(s2,-2) + pow(s3,2) + pow(s3,-2)));
  430. break;
  431. }
  432. case igl::SLIMData::LOG_ARAP: {
  433. energy += areas(i) * (pow(log(s1),2) + pow(log(abs(s2)),2) + pow(log(abs(s3)),2));
  434. break;
  435. }
  436. case igl::SLIMData::CONFORMAL: {
  437. energy += areas(i) * ( ( pow(s1,2)+pow(s2,2)+pow(s3,2) ) /(3*pow(s1*s2*s3,2./3.)) );
  438. break;
  439. }
  440. case igl::SLIMData::EXP_CONFORMAL: {
  441. energy += areas(i) * exp( ( pow(s1,2)+pow(s2,2)+pow(s3,2) ) /(3*pow(s1*s2*s3,2./3.)) );
  442. break;
  443. }
  444. }
  445. }
  446. }
  447. return energy;
  448. }
  449. void WeightedGlobalLocal::buildA(Eigen::SparseMatrix<double>& A) {
  450. // formula (35) in paper
  451. std::vector<Triplet<double> > IJV;
  452. if (dim == 2) {
  453. IJV.reserve(4*(Dx.outerSize()+ Dy.outerSize()));
  454. /*A = [W11*Dx, W12*Dx;
  455. W11*Dy, W12*Dy;
  456. W21*Dx, W22*Dx;
  457. W21*Dy, W22*Dy];*/
  458. for (int k=0; k<Dx.outerSize(); ++k) {
  459. for (SparseMatrix<double>::InnerIterator it(Dx,k); it; ++it) {
  460. int dx_r = it.row();
  461. int dx_c = it.col();
  462. double val = it.value();
  463. IJV.push_back(Triplet<double>(dx_r,dx_c, val*W_11(dx_r)));
  464. IJV.push_back(Triplet<double>(dx_r,v_n + dx_c, val*W_12(dx_r)));
  465. IJV.push_back(Triplet<double>(2*f_n+dx_r,dx_c, val*W_21(dx_r)));
  466. IJV.push_back(Triplet<double>(2*f_n+dx_r,v_n + dx_c, val*W_22(dx_r)));
  467. }
  468. }
  469. for (int k=0; k<Dy.outerSize(); ++k) {
  470. for (SparseMatrix<double>::InnerIterator it(Dy,k); it; ++it) {
  471. int dy_r = it.row();
  472. int dy_c = it.col();
  473. double val = it.value();
  474. IJV.push_back(Triplet<double>(f_n+dy_r,dy_c, val*W_11(dy_r)));
  475. IJV.push_back(Triplet<double>(f_n+dy_r,v_n + dy_c, val*W_12(dy_r)));
  476. IJV.push_back(Triplet<double>(3*f_n+dy_r,dy_c, val*W_21(dy_r)));
  477. IJV.push_back(Triplet<double>(3*f_n+dy_r,v_n + dy_c, val*W_22(dy_r)));
  478. }
  479. }
  480. } else {
  481. /*A = [W11*Dx, W12*Dx, W13*Dx;
  482. W11*Dy, W12*Dy, W13*Dy;
  483. W11*Dz, W12*Dz, W13*Dz;
  484. W21*Dx, W22*Dx, W23*Dx;
  485. W21*Dy, W22*Dy, W23*Dy;
  486. W21*Dz, W22*Dz, W23*Dz;
  487. W31*Dx, W32*Dx, W33*Dx;
  488. W31*Dy, W32*Dy, W33*Dy;
  489. W31*Dz, W32*Dz, W33*Dz;];*/
  490. IJV.reserve(9*(Dx.outerSize()+ Dy.outerSize() + Dz.outerSize()));
  491. for (int k = 0; k < Dx.outerSize(); k++) {
  492. for (SparseMatrix<double>::InnerIterator it(Dx,k); it; ++it) {
  493. int dx_r = it.row();
  494. int dx_c = it.col();
  495. double val = it.value();
  496. IJV.push_back(Triplet<double>(dx_r,dx_c, val*W_11(dx_r)));
  497. IJV.push_back(Triplet<double>(dx_r,v_n + dx_c, val*W_12(dx_r)));
  498. IJV.push_back(Triplet<double>(dx_r,2*v_n + dx_c, val*W_13(dx_r)));
  499. IJV.push_back(Triplet<double>(3*f_n+dx_r,dx_c, val*W_21(dx_r)));
  500. IJV.push_back(Triplet<double>(3*f_n+dx_r,v_n + dx_c, val*W_22(dx_r)));
  501. IJV.push_back(Triplet<double>(3*f_n+dx_r,2*v_n + dx_c, val*W_23(dx_r)));
  502. IJV.push_back(Triplet<double>(6*f_n+dx_r,dx_c, val*W_31(dx_r)));
  503. IJV.push_back(Triplet<double>(6*f_n+dx_r,v_n + dx_c, val*W_32(dx_r)));
  504. IJV.push_back(Triplet<double>(6*f_n+dx_r,2*v_n + dx_c, val*W_33(dx_r)));
  505. }
  506. }
  507. for (int k = 0; k < Dy.outerSize(); k++) {
  508. for (SparseMatrix<double>::InnerIterator it(Dy,k); it; ++it) {
  509. int dy_r = it.row();
  510. int dy_c = it.col();
  511. double val = it.value();
  512. IJV.push_back(Triplet<double>(f_n+dy_r,dy_c, val*W_11(dy_r)));
  513. IJV.push_back(Triplet<double>(f_n+dy_r,v_n + dy_c, val*W_12(dy_r)));
  514. IJV.push_back(Triplet<double>(f_n+dy_r,2*v_n + dy_c, val*W_13(dy_r)));
  515. IJV.push_back(Triplet<double>(4*f_n+dy_r,dy_c, val*W_21(dy_r)));
  516. IJV.push_back(Triplet<double>(4*f_n+dy_r,v_n + dy_c, val*W_22(dy_r)));
  517. IJV.push_back(Triplet<double>(4*f_n+dy_r,2*v_n + dy_c, val*W_23(dy_r)));
  518. IJV.push_back(Triplet<double>(7*f_n+dy_r,dy_c, val*W_31(dy_r)));
  519. IJV.push_back(Triplet<double>(7*f_n+dy_r,v_n + dy_c, val*W_32(dy_r)));
  520. IJV.push_back(Triplet<double>(7*f_n+dy_r,2*v_n + dy_c, val*W_33(dy_r)));
  521. }
  522. }
  523. for (int k = 0; k < Dz.outerSize(); k++) {
  524. for (SparseMatrix<double>::InnerIterator it(Dz,k); it; ++it) {
  525. int dz_r = it.row();
  526. int dz_c = it.col();
  527. double val = it.value();
  528. IJV.push_back(Triplet<double>(2*f_n + dz_r,dz_c, val*W_11(dz_r)));
  529. IJV.push_back(Triplet<double>(2*f_n + dz_r,v_n + dz_c, val*W_12(dz_r)));
  530. IJV.push_back(Triplet<double>(2*f_n + dz_r,2*v_n + dz_c, val*W_13(dz_r)));
  531. IJV.push_back(Triplet<double>(5*f_n+dz_r,dz_c, val*W_21(dz_r)));
  532. IJV.push_back(Triplet<double>(5*f_n+dz_r,v_n + dz_c, val*W_22(dz_r)));
  533. IJV.push_back(Triplet<double>(5*f_n+dz_r,2*v_n + dz_c, val*W_23(dz_r)));
  534. IJV.push_back(Triplet<double>(8*f_n+dz_r,dz_c, val*W_31(dz_r)));
  535. IJV.push_back(Triplet<double>(8*f_n+dz_r,v_n + dz_c, val*W_32(dz_r)));
  536. IJV.push_back(Triplet<double>(8*f_n+dz_r,2*v_n + dz_c, val*W_33(dz_r)));
  537. }
  538. }
  539. }
  540. A.setFromTriplets(IJV.begin(),IJV.end());
  541. }
  542. void WeightedGlobalLocal::buildRhs(const Eigen::SparseMatrix<double>& At) {
  543. VectorXd f_rhs(dim*dim*f_n); f_rhs.setZero();
  544. if (dim==2) {
  545. /*b = [W11*R11 + W12*R21; (formula (36))
  546. W11*R12 + W12*R22;
  547. W21*R11 + W22*R21;
  548. W21*R12 + W22*R22];*/
  549. for (int i = 0; i < f_n; i++) {
  550. f_rhs(i+0*f_n) = W_11(i) * Ri(i,0) + W_12(i)*Ri(i,1);
  551. f_rhs(i+1*f_n) = W_11(i) * Ri(i,2) + W_12(i)*Ri(i,3);
  552. f_rhs(i+2*f_n) = W_21(i) * Ri(i,0) + W_22(i)*Ri(i,1);
  553. f_rhs(i+3*f_n) = W_21(i) * Ri(i,2) + W_22(i)*Ri(i,3);
  554. }
  555. } else {
  556. /*b = [W11*R11 + W12*R21 + W13*R31;
  557. W11*R12 + W12*R22 + W13*R32;
  558. W11*R13 + W12*R23 + W13*R33;
  559. W21*R11 + W22*R21 + W23*R31;
  560. W21*R12 + W22*R22 + W23*R32;
  561. W21*R13 + W22*R23 + W23*R33;
  562. W31*R11 + W32*R21 + W33*R31;
  563. W31*R12 + W32*R22 + W33*R32;
  564. W31*R13 + W32*R23 + W33*R33;];*/
  565. for (int i = 0; i < f_n; i++) {
  566. f_rhs(i+0*f_n) = W_11(i) * Ri(i,0) + W_12(i)*Ri(i,1) + W_13(i)*Ri(i,2);
  567. f_rhs(i+1*f_n) = W_11(i) * Ri(i,3) + W_12(i)*Ri(i,4) + W_13(i)*Ri(i,5);
  568. f_rhs(i+2*f_n) = W_11(i) * Ri(i,6) + W_12(i)*Ri(i,7) + W_13(i)*Ri(i,8);
  569. f_rhs(i+3*f_n) = W_21(i) * Ri(i,0) + W_22(i)*Ri(i,1) + W_23(i)*Ri(i,2);
  570. f_rhs(i+4*f_n) = W_21(i) * Ri(i,3) + W_22(i)*Ri(i,4) + W_23(i)*Ri(i,5);
  571. f_rhs(i+5*f_n) = W_21(i) * Ri(i,6) + W_22(i)*Ri(i,7) + W_23(i)*Ri(i,8);
  572. f_rhs(i+6*f_n) = W_31(i) * Ri(i,0) + W_32(i)*Ri(i,1) + W_33(i)*Ri(i,2);
  573. f_rhs(i+7*f_n) = W_31(i) * Ri(i,3) + W_32(i)*Ri(i,4) + W_33(i)*Ri(i,5);
  574. f_rhs(i+8*f_n) = W_31(i) * Ri(i,6) + W_32(i)*Ri(i,7) + W_33(i)*Ri(i,8);
  575. }
  576. }
  577. VectorXd uv_flat(dim*v_n);
  578. for (int i = 0; i < dim; i++)
  579. for (int j = 0; j < v_n; j++)
  580. uv_flat(v_n*i+j) = m_state.V_o(j,i);
  581. rhs = (At*M.asDiagonal()*f_rhs + m_state.proximal_p * uv_flat);
  582. }
  583. // #define TwoPi 6.28318530717958648
  584. // const double eps=1e-14;
  585. /// Slim Implementation
  586. IGL_INLINE void igl::slim_precompute(Eigen::MatrixXd& V, Eigen::MatrixXi& F, Eigen::MatrixXd& V_init, SLIMData& data,
  587. SLIMData::SLIM_ENERGY slim_energy, Eigen::VectorXi& b, Eigen::MatrixXd& bc, double soft_p) {
  588. data.V = V;
  589. data.F = F;
  590. data.V_o = V_init;
  591. data.v_num = V.rows();
  592. data.f_num = F.rows();
  593. data.slim_energy = slim_energy;
  594. data.b = b;
  595. data.bc = bc;
  596. data.soft_const_p = soft_p;
  597. data.proximal_p = 0.0001;
  598. igl::doublearea(V,F,data.M); data.M /= 2.;
  599. data.mesh_area = data.M.sum();
  600. data.mesh_improvement_3d = false; // whether to use a jacobian derived from a real mesh or an abstract regular mesh (used for mesh improvement)
  601. data.exp_factor = 1.0; // param used only for exponential energies (e.g exponential symmetric dirichlet)
  602. assert (F.cols() == 3 || F.cols() == 4);
  603. data.wGlobalLocal = new WeightedGlobalLocal(data);
  604. data.wGlobalLocal->pre_calc();
  605. data.energy = data.wGlobalLocal->compute_energy(data.V_o)/data.mesh_area;
  606. }
  607. IGL_INLINE void igl::slim_solve(SLIMData& data, int iter_num) {
  608. for (int i = 0; i < iter_num; i++) {
  609. Eigen::MatrixXd dest_res;
  610. dest_res = data.V_o;
  611. data.wGlobalLocal->solve_weighted_proxy(dest_res);
  612. double old_energy = data.energy;
  613. std::function<double(Eigen::MatrixXd&)> compute_energy = [&](Eigen::MatrixXd& aaa) { return data.wGlobalLocal->compute_energy(aaa); };
  614. data.energy = igl::flip_avoiding_line_search(data.F,data.V_o, dest_res, compute_energy,
  615. data.energy*data.mesh_area)/data.mesh_area;
  616. }
  617. }