nrosy.cpp 23 KB

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