nrosy.cpp 22 KB

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