slim.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925
  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. {
  45. Eigen::SparseMatrix<double> G;
  46. igl::grad(V, F, G);
  47. Eigen::SparseMatrix<double> Dx = G.block(0, 0, F.rows(), V.rows());
  48. Eigen::SparseMatrix<double> Dy = G.block(F.rows(), 0, F.rows(), V.rows());
  49. Eigen::SparseMatrix<double> Dz = G.block(2 * F.rows(), 0, F.rows(), V.rows());
  50. D1 = F1.col(0).asDiagonal() * Dx + F1.col(1).asDiagonal() * Dy + F1.col(2).asDiagonal() * Dz;
  51. D2 = F2.col(0).asDiagonal() * Dx + F2.col(1).asDiagonal() * Dy + F2.col(2).asDiagonal() * Dz;
  52. }
  53. // Computes the weights and solve the linear system for the quadratic proxy specified in the paper
  54. // The output of this is used to generate a search direction that will be fed to the Linesearch class
  55. class WeightedGlobalLocal
  56. {
  57. public:
  58. WeightedGlobalLocal(igl::SLIMData &state);
  59. // Compute necessary information before solving the proxy quadratic
  60. void pre_calc();
  61. // Solve the weighted proxy global step
  62. // Output:
  63. // V_new #V by dim list of mesh positions (will be fed to a linesearch algorithm)
  64. void solve_weighted_proxy(Eigen::MatrixXd &V_new);
  65. // Compute the energy specified in the SLIMData structure + the soft constraint energy (in case there are soft constraints)
  66. // Input:
  67. // V_new #V by dim list of mesh positions
  68. virtual double compute_energy(Eigen::MatrixXd &V_new);
  69. //private:
  70. void compute_jacobians(const Eigen::MatrixXd &V_o);
  71. double compute_energy_with_jacobians(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F,
  72. const Eigen::MatrixXd &Ji, Eigen::MatrixXd &V_o, Eigen::VectorXd &areas);
  73. double compute_soft_const_energy(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F,
  74. Eigen::MatrixXd &V_o);
  75. void update_weights_and_closest_rotations(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, Eigen::MatrixXd &uv);
  76. void solve_weighted_arap(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, Eigen::MatrixXd &uv, Eigen::VectorXi &b,
  77. Eigen::MatrixXd &bc);
  78. void build_linear_system(Eigen::SparseMatrix<double> &L);
  79. void buildA(Eigen::SparseMatrix<double> &A);
  80. void buildRhs(const Eigen::SparseMatrix<double> &At);
  81. void add_soft_constraints(Eigen::SparseMatrix<double> &L);
  82. void add_proximal_penalty();
  83. igl::SLIMData &m_state;
  84. };
  85. //// Implementation
  86. WeightedGlobalLocal::WeightedGlobalLocal(igl::SLIMData &state) :
  87. m_state(state)
  88. {
  89. }
  90. void WeightedGlobalLocal::solve_weighted_proxy(Eigen::MatrixXd &V_new)
  91. {
  92. update_weights_and_closest_rotations(m_state.V, m_state.F, V_new);
  93. solve_weighted_arap(m_state.V, m_state.F, V_new, m_state.b, m_state.bc);
  94. }
  95. void WeightedGlobalLocal::compute_jacobians(const Eigen::MatrixXd &uv)
  96. {
  97. if (m_state.F.cols() == 3)
  98. {
  99. // Ji=[D1*u,D2*u,D1*v,D2*v];
  100. m_state.Ji.col(0) = m_state.Dx * uv.col(0);
  101. m_state.Ji.col(1) = m_state.Dy * uv.col(0);
  102. m_state.Ji.col(2) = m_state.Dx * uv.col(1);
  103. m_state.Ji.col(3) = m_state.Dy * uv.col(1);
  104. }
  105. else /*tet mesh*/{
  106. // Ji=[D1*u,D2*u,D3*u, D1*v,D2*v, D3*v, D1*w,D2*w,D3*w];
  107. m_state.Ji.col(0) = m_state.Dx * uv.col(0);
  108. m_state.Ji.col(1) = m_state.Dy * uv.col(0);
  109. m_state.Ji.col(2) = m_state.Dz * uv.col(0);
  110. m_state.Ji.col(3) = m_state.Dx * uv.col(1);
  111. m_state.Ji.col(4) = m_state.Dy * uv.col(1);
  112. m_state.Ji.col(5) = m_state.Dz * uv.col(1);
  113. m_state.Ji.col(6) = m_state.Dx * uv.col(2);
  114. m_state.Ji.col(7) = m_state.Dy * uv.col(2);
  115. m_state.Ji.col(8) = m_state.Dz * uv.col(2);
  116. }
  117. }
  118. void WeightedGlobalLocal::update_weights_and_closest_rotations(const Eigen::MatrixXd &V,
  119. const Eigen::MatrixXi &F, Eigen::MatrixXd &uv)
  120. {
  121. compute_jacobians(uv);
  122. const double eps = 1e-8;
  123. double exp_f = m_state.exp_factor;
  124. if (m_state.dim == 2)
  125. {
  126. for (int i = 0; i < m_state.Ji.rows(); ++i)
  127. {
  128. typedef Eigen::Matrix<double, 2, 2> Mat2;
  129. typedef Eigen::Matrix<double, 2, 1> Vec2;
  130. Mat2 ji, ri, ti, ui, vi;
  131. Vec2 sing;
  132. Vec2 closest_sing_vec;
  133. Mat2 mat_W;
  134. Vec2 m_sing_new;
  135. double s1, s2;
  136. ji(0, 0) = m_state.Ji(i, 0);
  137. ji(0, 1) = m_state.Ji(i, 1);
  138. ji(1, 0) = m_state.Ji(i, 2);
  139. ji(1, 1) = m_state.Ji(i, 3);
  140. igl::polar_svd(ji, ri, ti, ui, sing, vi);
  141. s1 = sing(0);
  142. s2 = sing(1);
  143. // Update Weights according to energy
  144. switch (m_state.slim_energy)
  145. {
  146. case igl::SLIMData::ARAP:
  147. {
  148. m_sing_new << 1, 1;
  149. break;
  150. }
  151. case igl::SLIMData::SYMMETRIC_DIRICHLET:
  152. {
  153. double s1_g = 2 * (s1 - pow(s1, -3));
  154. double s2_g = 2 * (s2 - pow(s2, -3));
  155. m_sing_new << sqrt(s1_g / (2 * (s1 - 1))), sqrt(s2_g / (2 * (s2 - 1)));
  156. break;
  157. }
  158. case igl::SLIMData::LOG_ARAP:
  159. {
  160. double s1_g = 2 * (log(s1) / s1);
  161. double s2_g = 2 * (log(s2) / s2);
  162. m_sing_new << sqrt(s1_g / (2 * (s1 - 1))), sqrt(s2_g / (2 * (s2 - 1)));
  163. break;
  164. }
  165. case igl::SLIMData::CONFORMAL:
  166. {
  167. double s1_g = 1 / (2 * s2) - s2 / (2 * pow(s1, 2));
  168. double s2_g = 1 / (2 * s1) - s1 / (2 * pow(s2, 2));
  169. double geo_avg = sqrt(s1 * s2);
  170. double s1_min = geo_avg;
  171. double s2_min = geo_avg;
  172. m_sing_new << sqrt(s1_g / (2 * (s1 - s1_min))), sqrt(s2_g / (2 * (s2 - s2_min)));
  173. // change local step
  174. closest_sing_vec << s1_min, s2_min;
  175. ri = ui * closest_sing_vec.asDiagonal() * vi.transpose();
  176. break;
  177. }
  178. case igl::SLIMData::EXP_CONFORMAL:
  179. {
  180. double s1_g = 2 * (s1 - pow(s1, -3));
  181. double s2_g = 2 * (s2 - pow(s2, -3));
  182. double geo_avg = sqrt(s1 * s2);
  183. double s1_min = geo_avg;
  184. double s2_min = geo_avg;
  185. double in_exp = exp_f * ((pow(s1, 2) + pow(s2, 2)) / (2 * s1 * s2));
  186. double exp_thing = exp(in_exp);
  187. s1_g *= exp_thing * exp_f;
  188. s2_g *= exp_thing * exp_f;
  189. m_sing_new << sqrt(s1_g / (2 * (s1 - 1))), sqrt(s2_g / (2 * (s2 - 1)));
  190. break;
  191. }
  192. case igl::SLIMData::EXP_SYMMETRIC_DIRICHLET:
  193. {
  194. double s1_g = 2 * (s1 - pow(s1, -3));
  195. double s2_g = 2 * (s2 - pow(s2, -3));
  196. double in_exp = exp_f * (pow(s1, 2) + pow(s1, -2) + pow(s2, 2) + pow(s2, -2));
  197. double exp_thing = exp(in_exp);
  198. s1_g *= exp_thing * exp_f;
  199. s2_g *= exp_thing * exp_f;
  200. m_sing_new << sqrt(s1_g / (2 * (s1 - 1))), sqrt(s2_g / (2 * (s2 - 1)));
  201. break;
  202. }
  203. }
  204. if (abs(s1 - 1) < eps) m_sing_new(0) = 1;
  205. if (abs(s2 - 1) < eps) m_sing_new(1) = 1;
  206. mat_W = ui * m_sing_new.asDiagonal() * ui.transpose();
  207. m_state.W_11(i) = mat_W(0, 0);
  208. m_state.W_12(i) = mat_W(0, 1);
  209. m_state.W_21(i) = mat_W(1, 0);
  210. m_state.W_22(i) = mat_W(1, 1);
  211. // 2) Update local step (doesn't have to be a rotation, for instance in case of conformal energy)
  212. m_state.Ri(i, 0) = ri(0, 0);
  213. m_state.Ri(i, 1) = ri(1, 0);
  214. m_state.Ri(i, 2) = ri(0, 1);
  215. m_state.Ri(i, 3) = ri(1, 1);
  216. }
  217. }
  218. else
  219. {
  220. typedef Eigen::Matrix<double, 3, 1> Vec3;
  221. typedef Eigen::Matrix<double, 3, 3> Mat3;
  222. Mat3 ji;
  223. Vec3 m_sing_new;
  224. Vec3 closest_sing_vec;
  225. const double sqrt_2 = sqrt(2);
  226. for (int i = 0; i < m_state.Ji.rows(); ++i)
  227. {
  228. ji(0, 0) = m_state.Ji(i, 0);
  229. ji(0, 1) = m_state.Ji(i, 1);
  230. ji(0, 2) = m_state.Ji(i, 2);
  231. ji(1, 0) = m_state.Ji(i, 3);
  232. ji(1, 1) = m_state.Ji(i, 4);
  233. ji(1, 2) = m_state.Ji(i, 5);
  234. ji(2, 0) = m_state.Ji(i, 6);
  235. ji(2, 1) = m_state.Ji(i, 7);
  236. ji(2, 2) = m_state.Ji(i, 8);
  237. Mat3 ri, ti, ui, vi;
  238. Vec3 sing;
  239. igl::polar_svd(ji, ri, ti, ui, sing, vi);
  240. double s1 = sing(0);
  241. double s2 = sing(1);
  242. double s3 = sing(2);
  243. // 1) Update Weights
  244. switch (m_state.slim_energy)
  245. {
  246. case igl::SLIMData::ARAP:
  247. {
  248. m_sing_new << 1, 1, 1;
  249. break;
  250. }
  251. case igl::SLIMData::LOG_ARAP:
  252. {
  253. double s1_g = 2 * (log(s1) / s1);
  254. double s2_g = 2 * (log(s2) / s2);
  255. double s3_g = 2 * (log(s3) / s3);
  256. m_sing_new << sqrt(s1_g / (2 * (s1 - 1))), sqrt(s2_g / (2 * (s2 - 1))), sqrt(s3_g / (2 * (s3 - 1)));
  257. break;
  258. }
  259. case igl::SLIMData::SYMMETRIC_DIRICHLET:
  260. {
  261. double s1_g = 2 * (s1 - pow(s1, -3));
  262. double s2_g = 2 * (s2 - pow(s2, -3));
  263. double s3_g = 2 * (s3 - pow(s3, -3));
  264. m_sing_new << sqrt(s1_g / (2 * (s1 - 1))), sqrt(s2_g / (2 * (s2 - 1))), sqrt(s3_g / (2 * (s3 - 1)));
  265. break;
  266. }
  267. case igl::SLIMData::EXP_SYMMETRIC_DIRICHLET:
  268. {
  269. double s1_g = 2 * (s1 - pow(s1, -3));
  270. double s2_g = 2 * (s2 - pow(s2, -3));
  271. double s3_g = 2 * (s3 - pow(s3, -3));
  272. m_sing_new << sqrt(s1_g / (2 * (s1 - 1))), sqrt(s2_g / (2 * (s2 - 1))), sqrt(s3_g / (2 * (s3 - 1)));
  273. double in_exp = exp_f * (pow(s1, 2) + pow(s1, -2) + pow(s2, 2) + pow(s2, -2) + pow(s3, 2) + pow(s3, -2));
  274. double exp_thing = exp(in_exp);
  275. s1_g *= exp_thing * exp_f;
  276. s2_g *= exp_thing * exp_f;
  277. s3_g *= exp_thing * exp_f;
  278. m_sing_new << sqrt(s1_g / (2 * (s1 - 1))), sqrt(s2_g / (2 * (s2 - 1))), sqrt(s3_g / (2 * (s3 - 1)));
  279. break;
  280. }
  281. case igl::SLIMData::CONFORMAL:
  282. {
  283. double common_div = 9 * (pow(s1 * s2 * s3, 5. / 3.));
  284. double s1_g = (-2 * s2 * s3 * (pow(s2, 2) + pow(s3, 2) - 2 * pow(s1, 2))) / common_div;
  285. double s2_g = (-2 * s1 * s3 * (pow(s1, 2) + pow(s3, 2) - 2 * pow(s2, 2))) / common_div;
  286. double s3_g = (-2 * s1 * s2 * (pow(s1, 2) + pow(s2, 2) - 2 * pow(s3, 2))) / common_div;
  287. double closest_s = sqrt(pow(s1, 2) + pow(s3, 2)) / sqrt_2;
  288. double s1_min = closest_s;
  289. double s2_min = closest_s;
  290. double s3_min = closest_s;
  291. m_sing_new << sqrt(s1_g / (2 * (s1 - s1_min))), sqrt(s2_g / (2 * (s2 - s2_min))), sqrt(
  292. s3_g / (2 * (s3 - s3_min)));
  293. // change local step
  294. closest_sing_vec << s1_min, s2_min, s3_min;
  295. ri = ui * closest_sing_vec.asDiagonal() * vi.transpose();
  296. break;
  297. }
  298. case igl::SLIMData::EXP_CONFORMAL:
  299. {
  300. // E_conf = (s1^2 + s2^2 + s3^2)/(3*(s1*s2*s3)^(2/3) )
  301. // dE_conf/ds1 = (-2*(s2*s3)*(s2^2+s3^2 -2*s1^2) ) / (9*(s1*s2*s3)^(5/3))
  302. // Argmin E_conf(s1): s1 = sqrt(s1^2+s2^2)/sqrt(2)
  303. double common_div = 9 * (pow(s1 * s2 * s3, 5. / 3.));
  304. double s1_g = (-2 * s2 * s3 * (pow(s2, 2) + pow(s3, 2) - 2 * pow(s1, 2))) / common_div;
  305. double s2_g = (-2 * s1 * s3 * (pow(s1, 2) + pow(s3, 2) - 2 * pow(s2, 2))) / common_div;
  306. double s3_g = (-2 * s1 * s2 * (pow(s1, 2) + pow(s2, 2) - 2 * pow(s3, 2))) / common_div;
  307. double in_exp = exp_f * ((pow(s1, 2) + pow(s2, 2) + pow(s3, 2)) / (3 * pow((s1 * s2 * s3), 2. / 3)));;
  308. double exp_thing = exp(in_exp);
  309. double closest_s = sqrt(pow(s1, 2) + pow(s3, 2)) / sqrt_2;
  310. double s1_min = closest_s;
  311. double s2_min = closest_s;
  312. double s3_min = closest_s;
  313. s1_g *= exp_thing * exp_f;
  314. s2_g *= exp_thing * exp_f;
  315. s3_g *= exp_thing * exp_f;
  316. m_sing_new << sqrt(s1_g / (2 * (s1 - s1_min))), sqrt(s2_g / (2 * (s2 - s2_min))), sqrt(
  317. s3_g / (2 * (s3 - s3_min)));
  318. // change local step
  319. closest_sing_vec << s1_min, s2_min, s3_min;
  320. ri = ui * closest_sing_vec.asDiagonal() * vi.transpose();
  321. }
  322. }
  323. if (abs(s1 - 1) < eps) m_sing_new(0) = 1;
  324. if (abs(s2 - 1) < eps) m_sing_new(1) = 1;
  325. if (abs(s3 - 1) < eps) m_sing_new(2) = 1;
  326. Mat3 mat_W;
  327. mat_W = ui * m_sing_new.asDiagonal() * ui.transpose();
  328. m_state.W_11(i) = mat_W(0, 0);
  329. m_state.W_12(i) = mat_W(0, 1);
  330. m_state.W_13(i) = mat_W(0, 2);
  331. m_state.W_21(i) = mat_W(1, 0);
  332. m_state.W_22(i) = mat_W(1, 1);
  333. m_state.W_23(i) = mat_W(1, 2);
  334. m_state.W_31(i) = mat_W(2, 0);
  335. m_state.W_32(i) = mat_W(2, 1);
  336. m_state.W_33(i) = mat_W(2, 2);
  337. // 2) Update closest rotations (not rotations in case of conformal energy)
  338. m_state.Ri(i, 0) = ri(0, 0);
  339. m_state.Ri(i, 1) = ri(1, 0);
  340. m_state.Ri(i, 2) = ri(2, 0);
  341. m_state.Ri(i, 3) = ri(0, 1);
  342. m_state.Ri(i, 4) = ri(1, 1);
  343. m_state.Ri(i, 5) = ri(2, 1);
  344. m_state.Ri(i, 6) = ri(0, 2);
  345. m_state.Ri(i, 7) = ri(1, 2);
  346. m_state.Ri(i, 8) = ri(2, 2);
  347. } // for loop end
  348. } // if dim end
  349. }
  350. void WeightedGlobalLocal::solve_weighted_arap(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F,
  351. Eigen::MatrixXd &uv, Eigen::VectorXi &soft_b_p,
  352. Eigen::MatrixXd &soft_bc_p)
  353. {
  354. using namespace Eigen;
  355. Eigen::SparseMatrix<double> L;
  356. build_linear_system(L);
  357. // solve
  358. Eigen::VectorXd Uc;
  359. if (m_state.dim == 2)
  360. {
  361. SimplicialLDLT<SparseMatrix<double> > solver;
  362. Uc = solver.compute(L).solve(m_state.rhs);
  363. }
  364. else
  365. { // seems like CG performs much worse for 2D and way better for 3D
  366. Eigen::VectorXd guess(uv.rows() * m_state.dim);
  367. for (int i = 0; i < m_state.dim; i++) for (int j = 0; j < m_state.dim; j++) guess(uv.rows() * i + j) = uv(i, j); // flatten vector
  368. ConjugateGradient<SparseMatrix<double>, Eigen::Lower | Upper> solver;
  369. solver.setTolerance(1e-8);
  370. Uc = solver.compute(L).solveWithGuess(m_state.rhs, guess);
  371. }
  372. for (int i = 0; i < m_state.dim; i++)
  373. uv.col(i) = Uc.block(i * m_state.v_n, 0, m_state.v_n, 1);
  374. }
  375. void WeightedGlobalLocal::pre_calc()
  376. {
  377. if (!m_state.has_pre_calc)
  378. {
  379. m_state.v_n = m_state.v_num;
  380. m_state.f_n = m_state.f_num;
  381. if (m_state.F.cols() == 3)
  382. {
  383. m_state.dim = 2;
  384. Eigen::MatrixXd F1, F2, F3;
  385. igl::local_basis(m_state.V, m_state.F, F1, F2, F3);
  386. compute_surface_gradient_matrix(m_state.V, m_state.F, F1, F2, m_state.Dx, m_state.Dy);
  387. m_state.W_11.resize(m_state.f_n);
  388. m_state.W_12.resize(m_state.f_n);
  389. m_state.W_21.resize(m_state.f_n);
  390. m_state.W_22.resize(m_state.f_n);
  391. }
  392. else
  393. {
  394. m_state.dim = 3;
  395. Eigen::SparseMatrix<double> G;
  396. igl::grad(m_state.V, m_state.F, G,
  397. m_state.mesh_improvement_3d /*use normal gradient, or one from a "regular" tet*/);
  398. m_state.Dx = G.block(0, 0, m_state.F.rows(), m_state.V.rows());
  399. m_state.Dy = G.block(m_state.F.rows(), 0, m_state.F.rows(), m_state.V.rows());
  400. m_state.Dz = G.block(2 * m_state.F.rows(), 0, m_state.F.rows(), m_state.V.rows());
  401. m_state.W_11.resize(m_state.f_n);
  402. m_state.W_12.resize(m_state.f_n);
  403. m_state.W_13.resize(m_state.f_n);
  404. m_state.W_21.resize(m_state.f_n);
  405. m_state.W_22.resize(m_state.f_n);
  406. m_state.W_23.resize(m_state.f_n);
  407. m_state.W_31.resize(m_state.f_n);
  408. m_state.W_32.resize(m_state.f_n);
  409. m_state.W_33.resize(m_state.f_n);
  410. }
  411. m_state.Dx.makeCompressed();
  412. m_state.Dy.makeCompressed();
  413. m_state.Dz.makeCompressed();
  414. m_state.Ri.resize(m_state.f_n, m_state.dim * m_state.dim);
  415. m_state.Ji.resize(m_state.f_n, m_state.dim * m_state.dim);
  416. m_state.rhs.resize(m_state.dim * m_state.v_num);
  417. // flattened weight matrix
  418. m_state.WGL_M.resize(m_state.dim * m_state.dim * m_state.f_n);
  419. for (int i = 0; i < m_state.dim * m_state.dim; i++)
  420. for (int j = 0; j < m_state.f_n; j++)
  421. m_state.WGL_M(i * m_state.f_n + j) = m_state.M(j);
  422. m_state.first_solve = true;
  423. m_state.has_pre_calc = true;
  424. }
  425. }
  426. void WeightedGlobalLocal::build_linear_system(Eigen::SparseMatrix<double> &L)
  427. {
  428. // formula (35) in paper
  429. Eigen::SparseMatrix<double> A(m_state.dim * m_state.dim * m_state.f_n, m_state.dim * m_state.v_n);
  430. buildA(A);
  431. Eigen::SparseMatrix<double> At = A.transpose();
  432. At.makeCompressed();
  433. Eigen::SparseMatrix<double> id_m(At.rows(), At.rows());
  434. id_m.setIdentity();
  435. // add proximal penalty
  436. L = At * m_state.WGL_M.asDiagonal() * A + m_state.proximal_p * id_m; //add also a proximal term
  437. L.makeCompressed();
  438. buildRhs(At);
  439. Eigen::SparseMatrix<double> OldL = L;
  440. add_soft_constraints(L);
  441. L.makeCompressed();
  442. }
  443. void WeightedGlobalLocal::add_soft_constraints(Eigen::SparseMatrix<double> &L)
  444. {
  445. int v_n = m_state.v_num;
  446. for (int d = 0; d < m_state.dim; d++)
  447. {
  448. for (int i = 0; i < m_state.b.rows(); i++)
  449. {
  450. int v_idx = m_state.b(i);
  451. m_state.rhs(d * v_n + v_idx) += m_state.soft_const_p * m_state.bc(i, d); // rhs
  452. L.coeffRef(d * v_n + v_idx, d * v_n + v_idx) += m_state.soft_const_p; // diagonal of matrix
  453. }
  454. }
  455. }
  456. double WeightedGlobalLocal::compute_energy(Eigen::MatrixXd &V_new)
  457. {
  458. compute_jacobians(V_new);
  459. return compute_energy_with_jacobians(m_state.V, m_state.F, m_state.Ji, V_new, m_state.M) +
  460. compute_soft_const_energy(m_state.V, m_state.F, V_new);
  461. }
  462. double WeightedGlobalLocal::compute_soft_const_energy(const Eigen::MatrixXd &V,
  463. const Eigen::MatrixXi &F,
  464. Eigen::MatrixXd &V_o)
  465. {
  466. double e = 0;
  467. for (int i = 0; i < m_state.b.rows(); i++)
  468. {
  469. e += m_state.soft_const_p * (m_state.bc.row(i) - V_o.row(m_state.b(i))).squaredNorm();
  470. }
  471. return e;
  472. }
  473. double WeightedGlobalLocal::compute_energy_with_jacobians(const Eigen::MatrixXd &V,
  474. const Eigen::MatrixXi &F, const Eigen::MatrixXd &Ji,
  475. Eigen::MatrixXd &uv, Eigen::VectorXd &areas)
  476. {
  477. double energy = 0;
  478. if (m_state.dim == 2)
  479. {
  480. Eigen::Matrix<double, 2, 2> ji;
  481. for (int i = 0; i < m_state.f_n; i++)
  482. {
  483. ji(0, 0) = Ji(i, 0);
  484. ji(0, 1) = Ji(i, 1);
  485. ji(1, 0) = Ji(i, 2);
  486. ji(1, 1) = Ji(i, 3);
  487. typedef Eigen::Matrix<double, 2, 2> Mat2;
  488. typedef Eigen::Matrix<double, 2, 1> Vec2;
  489. Mat2 ri, ti, ui, vi;
  490. Vec2 sing;
  491. igl::polar_svd(ji, ri, ti, ui, sing, vi);
  492. double s1 = sing(0);
  493. double s2 = sing(1);
  494. switch (m_state.slim_energy)
  495. {
  496. case igl::SLIMData::ARAP:
  497. {
  498. energy += areas(i) * (pow(s1 - 1, 2) + pow(s2 - 1, 2));
  499. break;
  500. }
  501. case igl::SLIMData::SYMMETRIC_DIRICHLET:
  502. {
  503. energy += areas(i) * (pow(s1, 2) + pow(s1, -2) + pow(s2, 2) + pow(s2, -2));
  504. break;
  505. }
  506. case igl::SLIMData::EXP_SYMMETRIC_DIRICHLET:
  507. {
  508. energy += areas(i) * exp(m_state.exp_factor * (pow(s1, 2) + pow(s1, -2) + pow(s2, 2) + pow(s2, -2)));
  509. break;
  510. }
  511. case igl::SLIMData::LOG_ARAP:
  512. {
  513. energy += areas(i) * (pow(log(s1), 2) + pow(log(s2), 2));
  514. break;
  515. }
  516. case igl::SLIMData::CONFORMAL:
  517. {
  518. energy += areas(i) * ((pow(s1, 2) + pow(s2, 2)) / (2 * s1 * s2));
  519. break;
  520. }
  521. case igl::SLIMData::EXP_CONFORMAL:
  522. {
  523. energy += areas(i) * exp(m_state.exp_factor * ((pow(s1, 2) + pow(s2, 2)) / (2 * s1 * s2)));
  524. break;
  525. }
  526. }
  527. }
  528. }
  529. else
  530. {
  531. Eigen::Matrix<double, 3, 3> ji;
  532. for (int i = 0; i < m_state.f_n; i++)
  533. {
  534. ji(0, 0) = Ji(i, 0);
  535. ji(0, 1) = Ji(i, 1);
  536. ji(0, 2) = Ji(i, 2);
  537. ji(1, 0) = Ji(i, 3);
  538. ji(1, 1) = Ji(i, 4);
  539. ji(1, 2) = Ji(i, 5);
  540. ji(2, 0) = Ji(i, 6);
  541. ji(2, 1) = Ji(i, 7);
  542. ji(2, 2) = Ji(i, 8);
  543. typedef Eigen::Matrix<double, 3, 3> Mat3;
  544. typedef Eigen::Matrix<double, 3, 1> Vec3;
  545. Mat3 ri, ti, ui, vi;
  546. Vec3 sing;
  547. igl::polar_svd(ji, ri, ti, ui, sing, vi);
  548. double s1 = sing(0);
  549. double s2 = sing(1);
  550. double s3 = sing(2);
  551. switch (m_state.slim_energy)
  552. {
  553. case igl::SLIMData::ARAP:
  554. {
  555. energy += areas(i) * (pow(s1 - 1, 2) + pow(s2 - 1, 2) + pow(s3 - 1, 2));
  556. break;
  557. }
  558. case igl::SLIMData::SYMMETRIC_DIRICHLET:
  559. {
  560. energy += areas(i) * (pow(s1, 2) + pow(s1, -2) + pow(s2, 2) + pow(s2, -2) + pow(s3, 2) + pow(s3, -2));
  561. break;
  562. }
  563. case igl::SLIMData::EXP_SYMMETRIC_DIRICHLET:
  564. {
  565. energy += areas(i) * exp(m_state.exp_factor *
  566. (pow(s1, 2) + pow(s1, -2) + pow(s2, 2) + pow(s2, -2) + pow(s3, 2) + pow(s3, -2)));
  567. break;
  568. }
  569. case igl::SLIMData::LOG_ARAP:
  570. {
  571. energy += areas(i) * (pow(log(s1), 2) + pow(log(abs(s2)), 2) + pow(log(abs(s3)), 2));
  572. break;
  573. }
  574. case igl::SLIMData::CONFORMAL:
  575. {
  576. energy += areas(i) * ((pow(s1, 2) + pow(s2, 2) + pow(s3, 2)) / (3 * pow(s1 * s2 * s3, 2. / 3.)));
  577. break;
  578. }
  579. case igl::SLIMData::EXP_CONFORMAL:
  580. {
  581. energy += areas(i) * exp((pow(s1, 2) + pow(s2, 2) + pow(s3, 2)) / (3 * pow(s1 * s2 * s3, 2. / 3.)));
  582. break;
  583. }
  584. }
  585. }
  586. }
  587. return energy;
  588. }
  589. void WeightedGlobalLocal::buildA(Eigen::SparseMatrix<double> &A)
  590. {
  591. // formula (35) in paper
  592. std::vector<Triplet<double> > IJV;
  593. if (m_state.dim == 2)
  594. {
  595. IJV.reserve(4 * (m_state.Dx.outerSize() + m_state.Dy.outerSize()));
  596. /*A = [W11*Dx, W12*Dx;
  597. W11*Dy, W12*Dy;
  598. W21*Dx, W22*Dx;
  599. W21*Dy, W22*Dy];*/
  600. for (int k = 0; k < m_state.Dx.outerSize(); ++k)
  601. {
  602. for (SparseMatrix<double>::InnerIterator it(m_state.Dx, k); it; ++it)
  603. {
  604. int dx_r = it.row();
  605. int dx_c = it.col();
  606. double val = it.value();
  607. IJV.push_back(Triplet<double>(dx_r, dx_c, val * m_state.W_11(dx_r)));
  608. IJV.push_back(Triplet<double>(dx_r, m_state.v_n + dx_c, val * m_state.W_12(dx_r)));
  609. IJV.push_back(Triplet<double>(2 * m_state.f_n + dx_r, dx_c, val * m_state.W_21(dx_r)));
  610. IJV.push_back(Triplet<double>(2 * m_state.f_n + dx_r, m_state.v_n + dx_c, val * m_state.W_22(dx_r)));
  611. }
  612. }
  613. for (int k = 0; k < m_state.Dy.outerSize(); ++k)
  614. {
  615. for (SparseMatrix<double>::InnerIterator it(m_state.Dy, k); it; ++it)
  616. {
  617. int dy_r = it.row();
  618. int dy_c = it.col();
  619. double val = it.value();
  620. IJV.push_back(Triplet<double>(m_state.f_n + dy_r, dy_c, val * m_state.W_11(dy_r)));
  621. IJV.push_back(Triplet<double>(m_state.f_n + dy_r, m_state.v_n + dy_c, val * m_state.W_12(dy_r)));
  622. IJV.push_back(Triplet<double>(3 * m_state.f_n + dy_r, dy_c, val * m_state.W_21(dy_r)));
  623. IJV.push_back(Triplet<double>(3 * m_state.f_n + dy_r, m_state.v_n + dy_c, val * m_state.W_22(dy_r)));
  624. }
  625. }
  626. }
  627. else
  628. {
  629. /*A = [W11*Dx, W12*Dx, W13*Dx;
  630. W11*Dy, W12*Dy, W13*Dy;
  631. W11*Dz, W12*Dz, W13*Dz;
  632. W21*Dx, W22*Dx, W23*Dx;
  633. W21*Dy, W22*Dy, W23*Dy;
  634. W21*Dz, W22*Dz, W23*Dz;
  635. W31*Dx, W32*Dx, W33*Dx;
  636. W31*Dy, W32*Dy, W33*Dy;
  637. W31*Dz, W32*Dz, W33*Dz;];*/
  638. IJV.reserve(9 * (m_state.Dx.outerSize() + m_state.Dy.outerSize() + m_state.Dz.outerSize()));
  639. for (int k = 0; k < m_state.Dx.outerSize(); k++)
  640. {
  641. for (SparseMatrix<double>::InnerIterator it(m_state.Dx, k); it; ++it)
  642. {
  643. int dx_r = it.row();
  644. int dx_c = it.col();
  645. double val = it.value();
  646. IJV.push_back(Triplet<double>(dx_r, dx_c, val * m_state.W_11(dx_r)));
  647. IJV.push_back(Triplet<double>(dx_r, m_state.v_n + dx_c, val * m_state.W_12(dx_r)));
  648. IJV.push_back(Triplet<double>(dx_r, 2 * m_state.v_n + dx_c, val * m_state.W_13(dx_r)));
  649. IJV.push_back(Triplet<double>(3 * m_state.f_n + dx_r, dx_c, val * m_state.W_21(dx_r)));
  650. IJV.push_back(Triplet<double>(3 * m_state.f_n + dx_r, m_state.v_n + dx_c, val * m_state.W_22(dx_r)));
  651. IJV.push_back(Triplet<double>(3 * m_state.f_n + dx_r, 2 * m_state.v_n + dx_c, val * m_state.W_23(dx_r)));
  652. IJV.push_back(Triplet<double>(6 * m_state.f_n + dx_r, dx_c, val * m_state.W_31(dx_r)));
  653. IJV.push_back(Triplet<double>(6 * m_state.f_n + dx_r, m_state.v_n + dx_c, val * m_state.W_32(dx_r)));
  654. IJV.push_back(Triplet<double>(6 * m_state.f_n + dx_r, 2 * m_state.v_n + dx_c, val * m_state.W_33(dx_r)));
  655. }
  656. }
  657. for (int k = 0; k < m_state.Dy.outerSize(); k++)
  658. {
  659. for (SparseMatrix<double>::InnerIterator it(m_state.Dy, k); it; ++it)
  660. {
  661. int dy_r = it.row();
  662. int dy_c = it.col();
  663. double val = it.value();
  664. IJV.push_back(Triplet<double>(m_state.f_n + dy_r, dy_c, val * m_state.W_11(dy_r)));
  665. IJV.push_back(Triplet<double>(m_state.f_n + dy_r, m_state.v_n + dy_c, val * m_state.W_12(dy_r)));
  666. IJV.push_back(Triplet<double>(m_state.f_n + dy_r, 2 * m_state.v_n + dy_c, val * m_state.W_13(dy_r)));
  667. IJV.push_back(Triplet<double>(4 * m_state.f_n + dy_r, dy_c, val * m_state.W_21(dy_r)));
  668. IJV.push_back(Triplet<double>(4 * m_state.f_n + dy_r, m_state.v_n + dy_c, val * m_state.W_22(dy_r)));
  669. IJV.push_back(Triplet<double>(4 * m_state.f_n + dy_r, 2 * m_state.v_n + dy_c, val * m_state.W_23(dy_r)));
  670. IJV.push_back(Triplet<double>(7 * m_state.f_n + dy_r, dy_c, val * m_state.W_31(dy_r)));
  671. IJV.push_back(Triplet<double>(7 * m_state.f_n + dy_r, m_state.v_n + dy_c, val * m_state.W_32(dy_r)));
  672. IJV.push_back(Triplet<double>(7 * m_state.f_n + dy_r, 2 * m_state.v_n + dy_c, val * m_state.W_33(dy_r)));
  673. }
  674. }
  675. for (int k = 0; k < m_state.Dz.outerSize(); k++)
  676. {
  677. for (SparseMatrix<double>::InnerIterator it(m_state.Dz, k); it; ++it)
  678. {
  679. int dz_r = it.row();
  680. int dz_c = it.col();
  681. double val = it.value();
  682. IJV.push_back(Triplet<double>(2 * m_state.f_n + dz_r, dz_c, val * m_state.W_11(dz_r)));
  683. IJV.push_back(Triplet<double>(2 * m_state.f_n + dz_r, m_state.v_n + dz_c, val * m_state.W_12(dz_r)));
  684. IJV.push_back(Triplet<double>(2 * m_state.f_n + dz_r, 2 * m_state.v_n + dz_c, val * m_state.W_13(dz_r)));
  685. IJV.push_back(Triplet<double>(5 * m_state.f_n + dz_r, dz_c, val * m_state.W_21(dz_r)));
  686. IJV.push_back(Triplet<double>(5 * m_state.f_n + dz_r, m_state.v_n + dz_c, val * m_state.W_22(dz_r)));
  687. IJV.push_back(Triplet<double>(5 * m_state.f_n + dz_r, 2 * m_state.v_n + dz_c, val * m_state.W_23(dz_r)));
  688. IJV.push_back(Triplet<double>(8 * m_state.f_n + dz_r, dz_c, val * m_state.W_31(dz_r)));
  689. IJV.push_back(Triplet<double>(8 * m_state.f_n + dz_r, m_state.v_n + dz_c, val * m_state.W_32(dz_r)));
  690. IJV.push_back(Triplet<double>(8 * m_state.f_n + dz_r, 2 * m_state.v_n + dz_c, val * m_state.W_33(dz_r)));
  691. }
  692. }
  693. }
  694. A.setFromTriplets(IJV.begin(), IJV.end());
  695. }
  696. void WeightedGlobalLocal::buildRhs(const Eigen::SparseMatrix<double> &At)
  697. {
  698. VectorXd f_rhs(m_state.dim * m_state.dim * m_state.f_n);
  699. f_rhs.setZero();
  700. if (m_state.dim == 2)
  701. {
  702. /*b = [W11*R11 + W12*R21; (formula (36))
  703. W11*R12 + W12*R22;
  704. W21*R11 + W22*R21;
  705. W21*R12 + W22*R22];*/
  706. for (int i = 0; i < m_state.f_n; i++)
  707. {
  708. f_rhs(i + 0 * m_state.f_n) = m_state.W_11(i) * m_state.Ri(i, 0) + m_state.W_12(i) * m_state.Ri(i, 1);
  709. f_rhs(i + 1 * m_state.f_n) = m_state.W_11(i) * m_state.Ri(i, 2) + m_state.W_12(i) * m_state.Ri(i, 3);
  710. f_rhs(i + 2 * m_state.f_n) = m_state.W_21(i) * m_state.Ri(i, 0) + m_state.W_22(i) * m_state.Ri(i, 1);
  711. f_rhs(i + 3 * m_state.f_n) = m_state.W_21(i) * m_state.Ri(i, 2) + m_state.W_22(i) * m_state.Ri(i, 3);
  712. }
  713. }
  714. else
  715. {
  716. /*b = [W11*R11 + W12*R21 + W13*R31;
  717. W11*R12 + W12*R22 + W13*R32;
  718. W11*R13 + W12*R23 + W13*R33;
  719. W21*R11 + W22*R21 + W23*R31;
  720. W21*R12 + W22*R22 + W23*R32;
  721. W21*R13 + W22*R23 + W23*R33;
  722. W31*R11 + W32*R21 + W33*R31;
  723. W31*R12 + W32*R22 + W33*R32;
  724. W31*R13 + W32*R23 + W33*R33;];*/
  725. for (int i = 0; i < m_state.f_n; i++)
  726. {
  727. f_rhs(i + 0 * m_state.f_n) = m_state.W_11(i) * m_state.Ri(i, 0) + m_state.W_12(i) * m_state.Ri(i, 1) + m_state.W_13(i) * m_state.Ri(i, 2);
  728. f_rhs(i + 1 * m_state.f_n) = m_state.W_11(i) * m_state.Ri(i, 3) + m_state.W_12(i) * m_state.Ri(i, 4) + m_state.W_13(i) * m_state.Ri(i, 5);
  729. f_rhs(i + 2 * m_state.f_n) = m_state.W_11(i) * m_state.Ri(i, 6) + m_state.W_12(i) * m_state.Ri(i, 7) + m_state.W_13(i) * m_state.Ri(i, 8);
  730. f_rhs(i + 3 * m_state.f_n) = m_state.W_21(i) * m_state.Ri(i, 0) + m_state.W_22(i) * m_state.Ri(i, 1) + m_state.W_23(i) * m_state.Ri(i, 2);
  731. f_rhs(i + 4 * m_state.f_n) = m_state.W_21(i) * m_state.Ri(i, 3) + m_state.W_22(i) * m_state.Ri(i, 4) + m_state.W_23(i) * m_state.Ri(i, 5);
  732. f_rhs(i + 5 * m_state.f_n) = m_state.W_21(i) * m_state.Ri(i, 6) + m_state.W_22(i) * m_state.Ri(i, 7) + m_state.W_23(i) * m_state.Ri(i, 8);
  733. f_rhs(i + 6 * m_state.f_n) = m_state.W_31(i) * m_state.Ri(i, 0) + m_state.W_32(i) * m_state.Ri(i, 1) + m_state.W_33(i) * m_state.Ri(i, 2);
  734. f_rhs(i + 7 * m_state.f_n) = m_state.W_31(i) * m_state.Ri(i, 3) + m_state.W_32(i) * m_state.Ri(i, 4) + m_state.W_33(i) * m_state.Ri(i, 5);
  735. f_rhs(i + 8 * m_state.f_n) = m_state.W_31(i) * m_state.Ri(i, 6) + m_state.W_32(i) * m_state.Ri(i, 7) + m_state.W_33(i) * m_state.Ri(i, 8);
  736. }
  737. }
  738. VectorXd uv_flat(m_state.dim *m_state.v_n);
  739. for (int i = 0; i < m_state.dim; i++)
  740. for (int j = 0; j < m_state.v_n; j++)
  741. uv_flat(m_state.v_n * i + j) = m_state.V_o(j, i);
  742. m_state.rhs = (At * m_state.WGL_M.asDiagonal() * f_rhs + m_state.proximal_p * uv_flat);
  743. }
  744. // #define TwoPi 6.28318530717958648
  745. // const double eps=1e-14;
  746. /// Slim Implementation
  747. IGL_INLINE void igl::slim_precompute(Eigen::MatrixXd &V, Eigen::MatrixXi &F, Eigen::MatrixXd &V_init, SLIMData &data,
  748. SLIMData::SLIM_ENERGY slim_energy, Eigen::VectorXi &b, Eigen::MatrixXd &bc,
  749. double soft_p)
  750. {
  751. data.V = V;
  752. data.F = F;
  753. data.V_o = V_init;
  754. data.v_num = V.rows();
  755. data.f_num = F.rows();
  756. data.slim_energy = slim_energy;
  757. data.b = b;
  758. data.bc = bc;
  759. data.soft_const_p = soft_p;
  760. data.proximal_p = 0.0001;
  761. igl::doublearea(V, F, data.M);
  762. data.M /= 2.;
  763. data.mesh_area = data.M.sum();
  764. data.mesh_improvement_3d = false; // whether to use a jacobian derived from a real mesh or an abstract regular mesh (used for mesh improvement)
  765. data.exp_factor = 1.0; // param used only for exponential energies (e.g exponential symmetric dirichlet)
  766. assert (F.cols() == 3 || F.cols() == 4);
  767. data.wGlobalLocal = new WeightedGlobalLocal(data);
  768. data.wGlobalLocal->pre_calc();
  769. data.energy = data.wGlobalLocal->compute_energy(data.V_o) / data.mesh_area;
  770. }
  771. IGL_INLINE void igl::slim_solve(SLIMData &data, int iter_num)
  772. {
  773. for (int i = 0; i < iter_num; i++)
  774. {
  775. Eigen::MatrixXd dest_res;
  776. dest_res = data.V_o;
  777. data.wGlobalLocal->solve_weighted_proxy(dest_res);
  778. double old_energy = data.energy;
  779. std::function<double(Eigen::MatrixXd &)> compute_energy = [&](
  780. Eigen::MatrixXd &aaa) { return data.wGlobalLocal->compute_energy(aaa); };
  781. data.energy = igl::flip_avoiding_line_search(data.F, data.V_o, dest_res, compute_energy,
  782. data.energy * data.mesh_area) / data.mesh_area;
  783. }
  784. }