nrosy.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775
  1. // This file is part of libigl, a simple c++ geometry processing library.
  2. //
  3. // Copyright (C) 2014 Daniele Panozzo <daniele.panozzo@gmail.com>
  4. //
  5. // This Source Code Form is subject to the terms of the Mozilla Public License
  6. // v. 2.0. If a copy of the MPL was not distributed with this file, You can
  7. // obtain one at http://mozilla.org/MPL/2.0/.
  8. #include "nrosy.h"
  9. #include <igl/copyleft/comiso/nrosy.h>
  10. #include <igl/triangle_triangle_adjacency.h>
  11. #include <igl/edge_topology.h>
  12. #include <igl/per_face_normals.h>
  13. #include <stdexcept>
  14. #include "../../PI.h"
  15. #include <Eigen/Geometry>
  16. #include <Eigen/Sparse>
  17. #include <queue>
  18. #include <vector>
  19. #include <gmm/gmm.h>
  20. #include <CoMISo/Solver/ConstrainedSolver.hh>
  21. #include <CoMISo/Solver/MISolver.hh>
  22. #include <CoMISo/Solver/GMM_Tools.hh>
  23. namespace igl
  24. {
  25. namespace copyleft
  26. {
  27. namespace comiso
  28. {
  29. class NRosyField
  30. {
  31. public:
  32. // Init
  33. IGL_INLINE NRosyField(const Eigen::MatrixXd& _V, const Eigen::MatrixXi& _F);
  34. // Generate the N-rosy field
  35. // N degree of the rosy field
  36. // round separately: round the integer variables one at a time, slower but higher quality
  37. IGL_INLINE void solve(int N = 4);
  38. // Set a hard constraint on fid
  39. // fid: face id
  40. // v: direction to fix (in 3d)
  41. IGL_INLINE void setConstraintHard(int fid, const Eigen::Vector3d& v);
  42. // Set a soft constraint on fid
  43. // fid: face id
  44. // w: weight of the soft constraint, clipped between 0 and 1
  45. // v: direction to fix (in 3d)
  46. IGL_INLINE void setConstraintSoft(int fid, double w, const Eigen::Vector3d& v);
  47. // Set the ratio between smoothness and soft constraints (0 -> smoothness only, 1 -> soft constr only)
  48. IGL_INLINE void setSoftAlpha(double alpha);
  49. // Reset constraints (at least one constraint must be present or solve will fail)
  50. IGL_INLINE void resetConstraints();
  51. // Return the current field
  52. IGL_INLINE Eigen::MatrixXd getFieldPerFace();
  53. // Compute singularity indexes
  54. IGL_INLINE void findCones(int N);
  55. // Return the singularities
  56. IGL_INLINE Eigen::VectorXd getSingularityIndexPerVertex();
  57. private:
  58. // Compute angle differences between reference frames
  59. IGL_INLINE void computek();
  60. // Remove useless matchings
  61. IGL_INLINE void reduceSpace();
  62. // Prepare the system matrix
  63. IGL_INLINE void prepareSystemMatrix(int N);
  64. // Solve with roundings using CoMIso
  65. IGL_INLINE void solveRoundings();
  66. // Convert a vector in 3d to an angle wrt the local reference system
  67. IGL_INLINE double convert3DtoLocal(unsigned fid, const Eigen::Vector3d& v);
  68. // Convert an angle wrt the local reference system to a 3d vector
  69. IGL_INLINE Eigen::Vector3d convertLocalto3D(unsigned fid, double a);
  70. // Compute the per vertex angle defect
  71. IGL_INLINE Eigen::VectorXd angleDefect();
  72. // Temporary variable for the field
  73. Eigen::VectorXd angles;
  74. // Hard constraints
  75. Eigen::VectorXd hard;
  76. std::vector<bool> isHard;
  77. // Soft constraints
  78. Eigen::VectorXd soft;
  79. Eigen::VectorXd wSoft;
  80. double softAlpha;
  81. // Face Topology
  82. Eigen::MatrixXi TT, TTi;
  83. // Edge Topology
  84. Eigen::MatrixXi EV, FE, EF;
  85. std::vector<bool> isBorderEdge;
  86. // Per Edge information
  87. // Angle between two reference frames
  88. Eigen::VectorXd k;
  89. // Jumps
  90. Eigen::VectorXi p;
  91. std::vector<bool> pFixed;
  92. // Mesh
  93. Eigen::MatrixXd V;
  94. Eigen::MatrixXi F;
  95. // Normals per face
  96. Eigen::MatrixXd N;
  97. // Singularity index
  98. Eigen::VectorXd singularityIndex;
  99. // Reference frame per triangle
  100. std::vector<Eigen::MatrixXd> TPs;
  101. // System stuff
  102. Eigen::SparseMatrix<double> A;
  103. Eigen::VectorXd b;
  104. Eigen::VectorXi tag_t;
  105. Eigen::VectorXi tag_p;
  106. };
  107. } // NAMESPACE COMISO
  108. } // NAMESPACE COPYLEFT
  109. } // NAMESPACE IGL
  110. igl::copyleft::comiso::NRosyField::NRosyField(const Eigen::MatrixXd& _V, const Eigen::MatrixXi& _F)
  111. {
  112. V = _V;
  113. F = _F;
  114. assert(V.rows() > 0);
  115. assert(F.rows() > 0);
  116. // Generate topological relations
  117. igl::triangle_triangle_adjacency(F,TT,TTi);
  118. igl::edge_topology(V,F, EV, FE, EF);
  119. // Flag border edges
  120. isBorderEdge.resize(EV.rows());
  121. for(unsigned i=0; i<EV.rows(); ++i)
  122. isBorderEdge[i] = (EF(i,0) == -1) || ((EF(i,1) == -1));
  123. // Generate normals per face
  124. igl::per_face_normals(V, F, N);
  125. // Generate reference frames
  126. for(unsigned fid=0; fid<F.rows(); ++fid)
  127. {
  128. // First edge
  129. Eigen::Vector3d e1 = V.row(F(fid,1)) - V.row(F(fid,0));
  130. e1.normalize();
  131. Eigen::Vector3d e2 = N.row(fid);
  132. e2 = e2.cross(e1);
  133. e2.normalize();
  134. Eigen::MatrixXd TP(2,3);
  135. TP << e1.transpose(), e2.transpose();
  136. TPs.push_back(TP);
  137. }
  138. // Alloc internal variables
  139. angles = Eigen::VectorXd::Zero(F.rows());
  140. p = Eigen::VectorXi::Zero(EV.rows());
  141. pFixed.resize(EV.rows());
  142. k = Eigen::VectorXd::Zero(EV.rows());
  143. singularityIndex = Eigen::VectorXd::Zero(V.rows());
  144. // Reset the constraints
  145. resetConstraints();
  146. // Compute k, differences between reference frames
  147. computek();
  148. softAlpha = 0.5;
  149. }
  150. void igl::copyleft::comiso::NRosyField::setSoftAlpha(double alpha)
  151. {
  152. assert(alpha >= 0 && alpha < 1);
  153. softAlpha = alpha;
  154. }
  155. void igl::copyleft::comiso::NRosyField::prepareSystemMatrix(const int N)
  156. {
  157. double Nd = N;
  158. // Minimize the MIQ energy
  159. // Energy on edge ij is
  160. // (t_i - t_j + kij + pij*(2*pi/N))^2
  161. // Partial derivatives:
  162. // t_i: 2 ( t_i - t_j + kij + pij*(2*pi/N)) = 0
  163. // t_j: 2 (-t_i + t_j - kij - pij*(2*pi/N)) = 0
  164. // pij: 4pi/N ( t_i - t_j + kij + pij*(2*pi/N)) = 0
  165. //
  166. // t_i t_j pij kij
  167. // t_i [ 2 -2 4pi/N 2 ]
  168. // t_j [ -2 2 -4pi/N -2 ]
  169. // pij [ 4pi/N -4pi/N 2*(2pi/N)^2 4pi/N ]
  170. // Count and tag the variables
  171. tag_t = Eigen::VectorXi::Constant(F.rows(),-1);
  172. std::vector<int> id_t;
  173. size_t count = 0;
  174. for(unsigned i=0; i<F.rows(); ++i)
  175. if (!isHard[i])
  176. {
  177. tag_t(i) = count++;
  178. id_t.push_back(i);
  179. }
  180. size_t count_t = id_t.size();
  181. tag_p = Eigen::VectorXi::Constant(EF.rows(),-1);
  182. std::vector<int> id_p;
  183. for(unsigned i=0; i<EF.rows(); ++i)
  184. {
  185. if (!pFixed[i])
  186. {
  187. // if it is not fixed then it is a variable
  188. tag_p(i) = count++;
  189. }
  190. // if it is not a border edge,
  191. if (!isBorderEdge[i])
  192. {
  193. // and it is not between two fixed faces
  194. if (!(isHard[EF(i,0)] && isHard[EF(i,1)]))
  195. {
  196. // then it participates in the energy!
  197. id_p.push_back(i);
  198. }
  199. }
  200. }
  201. size_t count_p = count - count_t;
  202. // System sizes: A (count_t + count_p) x (count_t + count_p)
  203. // b (count_t + count_p)
  204. b.resize(count_t + count_p);
  205. b.setZero();
  206. std::vector<Eigen::Triplet<double> > T;
  207. T.reserve(3 * 4 * count_p);
  208. for(auto eid : id_p)
  209. {
  210. int i = EF(eid, 0);
  211. int j = EF(eid, 1);
  212. bool isFixed_i = isHard[i];
  213. bool isFixed_j = isHard[j];
  214. bool isFixed_p = pFixed[eid];
  215. int row;
  216. // (i)-th row: t_i [ 2 -2 4pi/N 2 ]
  217. if (!isFixed_i)
  218. {
  219. row = tag_t[i];
  220. T.emplace_back(row, tag_t[i], 2);
  221. if (isFixed_j)
  222. b(row) += 2 * hard[j];
  223. else
  224. T.emplace_back(row, tag_t[j], -2);
  225. if (isFixed_p)
  226. b(row) += -((4. * igl::PI) / Nd) * p[eid];
  227. else
  228. T.emplace_back(row, tag_p[eid], ((4. * igl::PI) / Nd));
  229. b(row) += -2 * k[eid];
  230. assert(hard[i] == hard[i]);
  231. assert(hard[j] == hard[j]);
  232. assert(p[eid] == p[eid]);
  233. assert(k[eid] == k[eid]);
  234. assert(b(row) == b(row));
  235. }
  236. // (j)+1 -th row: t_j [ -2 2 -4pi/N -2 ]
  237. if (!isFixed_j)
  238. {
  239. row = tag_t[j];
  240. T.emplace_back(row, tag_t[j], 2);
  241. if (isFixed_i)
  242. b(row) += 2 * hard[i];
  243. else
  244. T.emplace_back(row, tag_t[i], -2);
  245. if (isFixed_p)
  246. b(row) += ((4. * igl::PI) / Nd) * p[eid];
  247. else
  248. T.emplace_back(row, tag_p[eid], -((4. * igl::PI) / Nd));
  249. b(row) += 2 * k[eid];
  250. assert(k[eid] == k[eid]);
  251. assert(b(row) == b(row));
  252. }
  253. // (r*3)+2 -th row: pij [ 4pi/N -4pi/N 2*(2pi/N)^2 4pi/N ]
  254. if (!isFixed_p)
  255. {
  256. row = tag_p[eid];
  257. T.emplace_back(row, tag_p[eid], (2. * pow(((2. * igl::PI) / Nd), 2)));
  258. if (isFixed_i)
  259. b(row) += -(4. * igl::PI) / Nd * hard[i];
  260. else
  261. T.emplace_back(row, tag_t[i], (4. * igl::PI) / Nd);
  262. if (isFixed_j)
  263. b(row) += (4. * igl::PI) / Nd * hard[j];
  264. else
  265. T.emplace_back(row,tag_t[j], -(4. * igl::PI) / Nd);
  266. b(row) += - (4 * igl::PI)/Nd * k[eid];
  267. assert(k[eid] == k[eid]);
  268. assert(b(row) == b(row));
  269. }
  270. }
  271. A.resize(count_t + count_p, count_t + count_p);
  272. A.setFromTriplets(T.begin(), T.end());
  273. // Soft constraints
  274. bool addSoft = false;
  275. for(unsigned i=0; i<wSoft.size();++i)
  276. if (wSoft[i] != 0)
  277. addSoft = true;
  278. if (addSoft)
  279. {
  280. Eigen::VectorXd bSoft = Eigen::VectorXd::Zero(count_t + count_p);
  281. std::vector<Eigen::Triplet<double> > TSoft;
  282. TSoft.reserve(2 * count_p);
  283. for(unsigned i=0; i<F.rows(); ++i)
  284. {
  285. int varid = tag_t[i];
  286. if (varid != -1) // if it is a variable in the system
  287. {
  288. TSoft.emplace_back(varid, varid, wSoft[i]);
  289. bSoft[varid] += wSoft[i] * soft[i];
  290. }
  291. }
  292. Eigen::SparseMatrix<double> ASoft(count_t + count_p, count_t + count_p);
  293. ASoft.setFromTriplets(TSoft.begin(), TSoft.end());
  294. A = (1.0 - softAlpha) * A + softAlpha * ASoft;
  295. b = b * (1.0 - softAlpha) + bSoft * softAlpha;
  296. }
  297. }
  298. void igl::copyleft::comiso::NRosyField::solveRoundings()
  299. {
  300. unsigned n = A.rows();
  301. gmm::col_matrix< gmm::wsvector< double > > gmm_A(n, n);
  302. std::vector<double> gmm_b(n);
  303. std::vector<int> ids_to_round;
  304. std::vector<double> x(n);
  305. // Copy A
  306. for (int k=0; k<A.outerSize(); ++k)
  307. for (Eigen::SparseMatrix<double>::InnerIterator it(A, k); it; ++it)
  308. {
  309. gmm_A(it.row(),it.col()) += it.value();
  310. }
  311. // Copy b
  312. for(unsigned int i = 0; i < n;++i)
  313. gmm_b[i] = b[i];
  314. // Set variables to round
  315. ids_to_round.clear();
  316. for(unsigned i=0; i<tag_p.size();++i)
  317. if(tag_p[i] != -1)
  318. ids_to_round.push_back(tag_p[i]);
  319. // Empty constraints
  320. gmm::row_matrix< gmm::wsvector< double > > gmm_C(0, n);
  321. COMISO::ConstrainedSolver cs;
  322. cs.solve(gmm_C, gmm_A, x, gmm_b, ids_to_round, 0.0, false, true);
  323. // Copy the result back
  324. for(unsigned i=0; i<F.rows(); ++i)
  325. if (tag_t[i] != -1)
  326. angles[i] = x[tag_t[i]];
  327. else
  328. angles[i] = hard[i];
  329. for(unsigned i=0; i<EF.rows(); ++i)
  330. if(tag_p[i] != -1)
  331. p[i] = (int)std::round(x[tag_p[i]]);
  332. }
  333. void igl::copyleft::comiso::NRosyField::solve(const int N)
  334. {
  335. // Reduce the search space by fixing matchings
  336. reduceSpace();
  337. // Build the system
  338. prepareSystemMatrix(N);
  339. // Solve with integer roundings
  340. solveRoundings();
  341. // Find the cones
  342. findCones(N);
  343. }
  344. void igl::copyleft::comiso::NRosyField::setConstraintHard(const int fid, const Eigen::Vector3d& v)
  345. {
  346. isHard[fid] = true;
  347. hard(fid) = convert3DtoLocal(fid, v);
  348. }
  349. void igl::copyleft::comiso::NRosyField::setConstraintSoft(const int fid, const double w, const Eigen::Vector3d& v)
  350. {
  351. wSoft(fid) = w;
  352. soft(fid) = convert3DtoLocal(fid, v);
  353. }
  354. void igl::copyleft::comiso::NRosyField::resetConstraints()
  355. {
  356. isHard.resize(F.rows());
  357. for(unsigned i = 0; i < F.rows(); ++i)
  358. isHard[i] = false;
  359. hard = Eigen::VectorXd::Zero(F.rows());
  360. wSoft = Eigen::VectorXd::Zero(F.rows());
  361. soft = Eigen::VectorXd::Zero(F.rows());
  362. }
  363. Eigen::MatrixXd igl::copyleft::comiso::NRosyField::getFieldPerFace()
  364. {
  365. Eigen::MatrixXd result(F.rows(),3);
  366. for(unsigned int i = 0; i < F.rows(); ++i)
  367. result.row(i) = convertLocalto3D(i, angles(i));
  368. return result;
  369. }
  370. void igl::copyleft::comiso::NRosyField::computek()
  371. {
  372. // For every non-border edge
  373. for (unsigned eid = 0; eid < EF.rows(); ++eid)
  374. {
  375. if (!isBorderEdge[eid])
  376. {
  377. int fid0 = EF(eid,0);
  378. int fid1 = EF(eid,1);
  379. Eigen::Vector3d N0 = N.row(fid0);
  380. Eigen::Vector3d N1 = N.row(fid1);
  381. // find common edge on triangle 0 and 1
  382. int fid0_vc = -1;
  383. int fid1_vc = -1;
  384. for (unsigned i=0;i<3;++i)
  385. {
  386. if (EV(eid,0) == F(fid0,i))
  387. fid0_vc = i;
  388. if (EV(eid,1) == F(fid1,i))
  389. fid1_vc = i;
  390. }
  391. assert(fid0_vc != -1);
  392. assert(fid1_vc != -1);
  393. Eigen::Vector3d common_edge = V.row(F(fid0,(fid0_vc+1)%3)) - V.row(F(fid0,fid0_vc));
  394. common_edge.normalize();
  395. // Map the two triangles in a new space where the common edge is the x axis and the N0 the z axis
  396. Eigen::MatrixXd P(3,3);
  397. Eigen::VectorXd o = V.row(F(fid0,fid0_vc));
  398. Eigen::VectorXd tmp = -N0.cross(common_edge);
  399. P << common_edge, tmp, N0;
  400. P.transposeInPlace();
  401. Eigen::MatrixXd V0(3,3);
  402. V0.row(0) = V.row(F(fid0,0)).transpose() -o;
  403. V0.row(1) = V.row(F(fid0,1)).transpose() -o;
  404. V0.row(2) = V.row(F(fid0,2)).transpose() -o;
  405. V0 = (P*V0.transpose()).transpose();
  406. assert(V0(0,2) < 10e-10);
  407. assert(V0(1,2) < 10e-10);
  408. assert(V0(2,2) < 10e-10);
  409. Eigen::MatrixXd V1(3,3);
  410. V1.row(0) = V.row(F(fid1,0)).transpose() -o;
  411. V1.row(1) = V.row(F(fid1,1)).transpose() -o;
  412. V1.row(2) = V.row(F(fid1,2)).transpose() -o;
  413. V1 = (P*V1.transpose()).transpose();
  414. assert(V1(fid1_vc,2) < 10e-10);
  415. assert(V1((fid1_vc+1)%3,2) < 10e-10);
  416. // compute rotation R such that R * N1 = N0
  417. // i.e. map both triangles to the same plane
  418. double alpha = -std::atan2(V1((fid1_vc + 2) % 3, 2), V1((fid1_vc + 2) % 3, 1));
  419. Eigen::MatrixXd R(3,3);
  420. R << 1, 0, 0,
  421. 0, std::cos(alpha), -std::sin(alpha) ,
  422. 0, std::sin(alpha), std::cos(alpha);
  423. V1 = (R*V1.transpose()).transpose();
  424. assert(V1(0,2) < 10e-10);
  425. assert(V1(1,2) < 10e-10);
  426. assert(V1(2,2) < 10e-10);
  427. // measure the angle between the reference frames
  428. // k_ij is the angle between the triangle on the left and the one on the right
  429. Eigen::VectorXd ref0 = V0.row(1) - V0.row(0);
  430. Eigen::VectorXd ref1 = V1.row(1) - V1.row(0);
  431. ref0.normalize();
  432. ref1.normalize();
  433. double ktemp = std::atan2(ref1(1), ref1(0)) - std::atan2(ref0(1), ref0(0));
  434. // just to be sure, rotate ref0 using angle ktemp...
  435. Eigen::MatrixXd R2(2,2);
  436. R2 << std::cos(ktemp), -std::sin(ktemp), std::sin(ktemp), std::cos(ktemp);
  437. tmp = R2*ref0.head<2>();
  438. assert(tmp(0) - ref1(0) < 10^10);
  439. assert(tmp(1) - ref1(1) < 10^10);
  440. k[eid] = ktemp;
  441. }
  442. }
  443. }
  444. void igl::copyleft::comiso::NRosyField::reduceSpace()
  445. {
  446. // All variables are free in the beginning
  447. for(unsigned int i = 0; i < EV.rows(); ++i)
  448. pFixed[i] = false;
  449. std::vector<bool> visited(EV.rows(), false);
  450. std::vector<bool> starting(EV.rows(), false);
  451. std::queue<int> q;
  452. for(unsigned int i = 0; i < F.rows(); ++i)
  453. if (isHard[i] || wSoft[i] != 0)
  454. {
  455. q.push(i);
  456. starting[i] = true;
  457. }
  458. // Reduce the search space (see MI paper)
  459. while (!q.empty())
  460. {
  461. int c = q.front();
  462. q.pop();
  463. visited[c] = true;
  464. for(int i=0; i<3; ++i)
  465. {
  466. int eid = FE(c,i);
  467. int fid = TT(c,i);
  468. // skip borders
  469. if (fid != -1)
  470. {
  471. assert((EF(eid,0) == c && EF(eid,1) == fid) || (EF(eid,1) == c && EF(eid,0) == fid));
  472. // for every neighbouring face
  473. if (!visited[fid] && !starting[fid])
  474. {
  475. pFixed[eid] = true;
  476. p[eid] = 0;
  477. visited[fid] = true;
  478. q.push(fid);
  479. }
  480. }
  481. else
  482. {
  483. // fix borders
  484. pFixed[eid] = true;
  485. p[eid] = 0;
  486. }
  487. }
  488. }
  489. // Force matchings between fixed faces
  490. for(unsigned int i = 0; i < F.rows();++i)
  491. {
  492. if (isHard[i])
  493. {
  494. for(unsigned int j = 0; j < 3; ++j)
  495. {
  496. int fid = TT(i,j);
  497. if ((fid!=-1) && (isHard[fid]))
  498. {
  499. // i and fid are adjacent and fixed
  500. int eid = FE(i,j);
  501. int fid0 = EF(eid,0);
  502. int fid1 = EF(eid,1);
  503. pFixed[eid] = true;
  504. p[eid] = (int)std::round(2.0 / igl::PI * (hard(fid1) - hard(fid0) - k(eid)));
  505. }
  506. }
  507. }
  508. }
  509. }
  510. double igl::copyleft::comiso::NRosyField::convert3DtoLocal(unsigned fid, const Eigen::Vector3d& v)
  511. {
  512. // Project onto the tangent plane
  513. Eigen::Vector2d vp = TPs[fid] * v;
  514. // Convert to angle
  515. return std::atan2(vp(1), vp(0));
  516. }
  517. Eigen::Vector3d igl::copyleft::comiso::NRosyField::convertLocalto3D(unsigned fid, double a)
  518. {
  519. Eigen::Vector2d vp(std::cos(a), std::sin(a));
  520. return vp.transpose() * TPs[fid];
  521. }
  522. Eigen::VectorXd igl::copyleft::comiso::NRosyField::angleDefect()
  523. {
  524. Eigen::VectorXd A = Eigen::VectorXd::Constant(V.rows(),-2*igl::PI);
  525. for (unsigned int i = 0; i < F.rows(); ++i)
  526. {
  527. for (int j = 0; j < 3; ++j)
  528. {
  529. Eigen::VectorXd a = V.row(F(i,(j+1)%3)) - V.row(F(i,j));
  530. Eigen::VectorXd b = V.row(F(i,(j+2)%3)) - V.row(F(i,j));
  531. double t = a.transpose() * b;
  532. if(a.norm() > 0. && b.norm() > 0.)
  533. t /= (a.norm() * b.norm());
  534. else
  535. throw std::runtime_error("igl::copyleft::comiso::NRosyField::angleDefect: Division by zero!");
  536. A(F(i, j)) += std::acos(std::max(std::min(t, 1.), -1.));
  537. }
  538. }
  539. return A;
  540. }
  541. void igl::copyleft::comiso::NRosyField::findCones(int N)
  542. {
  543. // Compute I0, see http://www.graphics.rwth-aachen.de/media/papers/bommes_zimmer_2009_siggraph_011.pdf for details
  544. singularityIndex = Eigen::VectorXd::Zero(V.rows());
  545. // first the k
  546. for (unsigned i = 0; i < EV.rows(); ++i)
  547. {
  548. if (!isBorderEdge[i])
  549. {
  550. singularityIndex(EV(i, 0)) -= k(i);
  551. singularityIndex(EV(i, 1)) += k(i);
  552. }
  553. }
  554. // then the A
  555. Eigen::VectorXd A = angleDefect();
  556. singularityIndex += A;
  557. // normalize
  558. singularityIndex /= (2 * igl::PI);
  559. // round to integer (remove numerical noise)
  560. for (unsigned i = 0; i < singularityIndex.size(); ++i)
  561. singularityIndex(i) = round(singularityIndex(i));
  562. for (unsigned i = 0; i < EV.rows(); ++i)
  563. {
  564. if (!isBorderEdge[i])
  565. {
  566. singularityIndex(EV(i, 0)) -= double(p(i)) / double(N);
  567. singularityIndex(EV(i, 1)) += double(p(i)) / double(N);
  568. }
  569. }
  570. // Clear the vertices on the edges
  571. for (unsigned i = 0; i < EV.rows(); ++i)
  572. {
  573. if (isBorderEdge[i])
  574. {
  575. singularityIndex(EV(i,0)) = 0;
  576. singularityIndex(EV(i,1)) = 0;
  577. }
  578. }
  579. }
  580. Eigen::VectorXd igl::copyleft::comiso::NRosyField::getSingularityIndexPerVertex()
  581. {
  582. return singularityIndex;
  583. }
  584. IGL_INLINE void igl::copyleft::comiso::nrosy(
  585. const Eigen::MatrixXd& V,
  586. const Eigen::MatrixXi& F,
  587. const Eigen::VectorXi& b,
  588. const Eigen::MatrixXd& bc,
  589. const Eigen::VectorXi& b_soft,
  590. const Eigen::VectorXd& w_soft,
  591. const Eigen::MatrixXd& bc_soft,
  592. const int N,
  593. const double soft,
  594. Eigen::MatrixXd& R,
  595. Eigen::VectorXd& S
  596. )
  597. {
  598. // Init solver
  599. igl::copyleft::comiso::NRosyField solver(V, F);
  600. // Add hard constraints
  601. for (unsigned i = 0; i < b.size(); ++i)
  602. solver.setConstraintHard(b(i), bc.row(i));
  603. // Add soft constraints
  604. for (unsigned i = 0; i < b_soft.size(); ++i)
  605. solver.setConstraintSoft(b_soft(i), w_soft(i), bc_soft.row(i));
  606. // Set the soft constraints global weight
  607. solver.setSoftAlpha(soft);
  608. // Interpolate
  609. solver.solve(N);
  610. // Copy the result back
  611. R = solver.getFieldPerFace();
  612. // Extract singularity indices
  613. S = solver.getSingularityIndexPerVertex();
  614. }
  615. IGL_INLINE void igl::copyleft::comiso::nrosy(
  616. const Eigen::MatrixXd& V,
  617. const Eigen::MatrixXi& F,
  618. const Eigen::VectorXi& b,
  619. const Eigen::MatrixXd& bc,
  620. const int N,
  621. Eigen::MatrixXd& R,
  622. Eigen::VectorXd& S
  623. )
  624. {
  625. // Init solver
  626. igl::copyleft::comiso::NRosyField solver(V, F);
  627. // Add hard constraints
  628. for (unsigned i= 0; i < b.size(); ++i)
  629. solver.setConstraintHard(b(i), bc.row(i));
  630. // Interpolate
  631. solver.solve(N);
  632. // Copy the result back
  633. R = solver.getFieldPerFace();
  634. // Extract singularity indices
  635. S = solver.getSingularityIndexPerVertex();
  636. }