frame_field.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683
  1. #include "frame_field.h"
  2. #include <igl/triangle_triangle_adjacency.h>
  3. #include <igl/edge_topology.h>
  4. #include <igl/per_face_normals.h>
  5. #include <igl/comiso/nrosy.h>
  6. #include <iostream>
  7. namespace igl
  8. {
  9. namespace comiso
  10. {
  11. class FrameInterpolator
  12. {
  13. public:
  14. // Init
  15. IGL_INLINE FrameInterpolator(const Eigen::MatrixXd& _V, const Eigen::MatrixXi& _F);
  16. IGL_INLINE ~FrameInterpolator();
  17. // Reset constraints (at least one constraint must be present or solve will fail)
  18. IGL_INLINE void resetConstraints();
  19. IGL_INLINE void setConstraint(const int fid, const Eigen::VectorXd& v);
  20. IGL_INLINE void interpolateSymmetric();
  21. // Generate the frame field
  22. IGL_INLINE void solve();
  23. // Convert the frame field in the canonical representation
  24. IGL_INLINE void frame2canonical(const Eigen::MatrixXd& TP, const Eigen::RowVectorXd& v, double& theta, Eigen::VectorXd& S);
  25. // Convert the canonical representation in a frame field
  26. IGL_INLINE void canonical2frame(const Eigen::MatrixXd& TP, const double theta, const Eigen::VectorXd& S, Eigen::RowVectorXd& v);
  27. IGL_INLINE Eigen::MatrixXd getFieldPerFace();
  28. IGL_INLINE void PolarDecomposition(Eigen::MatrixXd V, Eigen::MatrixXd& U, Eigen::MatrixXd& P);
  29. // Symmetric
  30. Eigen::MatrixXd S;
  31. std::vector<bool> S_c;
  32. // -------------------------------------------------
  33. // Face Topology
  34. Eigen::MatrixXi TT, TTi;
  35. // Two faces are consistent if their representative vector are taken modulo PI
  36. std::vector<bool> edge_consistency;
  37. Eigen::MatrixXi edge_consistency_TT;
  38. private:
  39. IGL_INLINE double mod2pi(double d);
  40. IGL_INLINE double modpi2(double d);
  41. IGL_INLINE double modpi(double d);
  42. // Convert a direction on the tangent space into an angle
  43. IGL_INLINE double vector2theta(const Eigen::MatrixXd& TP, const Eigen::RowVectorXd& v);
  44. // Convert an angle in a vector in the tangent space
  45. IGL_INLINE Eigen::RowVectorXd theta2vector(const Eigen::MatrixXd& TP, const double theta);
  46. // Interpolate the cross field (theta)
  47. IGL_INLINE void interpolateCross();
  48. // Compute difference between reference frames
  49. IGL_INLINE void computek();
  50. // Compute edge consistency
  51. IGL_INLINE void compute_edge_consistency();
  52. // Cross field direction
  53. Eigen::VectorXd thetas;
  54. std::vector<bool> thetas_c;
  55. // Edge Topology
  56. Eigen::MatrixXi EV, FE, EF;
  57. std::vector<bool> isBorderEdge;
  58. // Angle between two reference frames
  59. // R(k) * t0 = t1
  60. Eigen::VectorXd k;
  61. // Mesh
  62. Eigen::MatrixXd V;
  63. Eigen::MatrixXi F;
  64. // Normals per face
  65. Eigen::MatrixXd N;
  66. // Reference frame per triangle
  67. std::vector<Eigen::MatrixXd> TPs;
  68. };
  69. FrameInterpolator::FrameInterpolator(const Eigen::MatrixXd& _V, const Eigen::MatrixXi& _F)
  70. {
  71. using namespace std;
  72. using namespace Eigen;
  73. V = _V;
  74. F = _F;
  75. assert(V.rows() > 0);
  76. assert(F.rows() > 0);
  77. // Generate topological relations
  78. igl::triangle_triangle_adjacency(V,F,TT,TTi);
  79. igl::edge_topology(V,F, EV, FE, EF);
  80. // Flag border edges
  81. isBorderEdge.resize(EV.rows());
  82. for(unsigned i=0; i<EV.rows(); ++i)
  83. isBorderEdge[i] = (EF(i,0) == -1) || ((EF(i,1) == -1));
  84. // Generate normals per face
  85. igl::per_face_normals(V, F, N);
  86. // Generate reference frames
  87. for(unsigned fid=0; fid<F.rows(); ++fid)
  88. {
  89. // First edge
  90. Vector3d e1 = V.row(F(fid,1)) - V.row(F(fid,0));
  91. e1.normalize();
  92. Vector3d e2 = N.row(fid);
  93. e2 = e2.cross(e1);
  94. e2.normalize();
  95. MatrixXd TP(2,3);
  96. TP << e1.transpose(), e2.transpose();
  97. TPs.push_back(TP);
  98. }
  99. // Reset the constraints
  100. resetConstraints();
  101. // Compute k, differences between reference frames
  102. computek();
  103. // Alloc internal variables
  104. thetas = VectorXd::Zero(F.rows());
  105. S = MatrixXd::Zero(F.rows(),3);
  106. compute_edge_consistency();
  107. }
  108. FrameInterpolator::~FrameInterpolator()
  109. {
  110. }
  111. double FrameInterpolator::mod2pi(double d)
  112. {
  113. while(d<0)
  114. d = d + (2.0*M_PI);
  115. return fmod(d, (2.0*M_PI));
  116. }
  117. double FrameInterpolator::modpi2(double d)
  118. {
  119. while(d<0)
  120. d = d + (M_PI/2.0);
  121. return fmod(d, (M_PI/2.0));
  122. }
  123. double FrameInterpolator::modpi(double d)
  124. {
  125. while(d<0)
  126. d = d + (M_PI);
  127. return fmod(d, (M_PI));
  128. }
  129. double FrameInterpolator::vector2theta(const Eigen::MatrixXd& TP, const Eigen::RowVectorXd& v)
  130. {
  131. // Project onto the tangent plane
  132. Eigen::Vector2d vp = TP * v.transpose();
  133. // Convert to angle
  134. double theta = atan2(vp(1),vp(0));
  135. return theta;
  136. }
  137. Eigen::RowVectorXd FrameInterpolator::theta2vector(const Eigen::MatrixXd& TP, const double theta)
  138. {
  139. Eigen::Vector2d vp(cos(theta),sin(theta));
  140. return vp.transpose() * TP;
  141. }
  142. void FrameInterpolator::interpolateCross()
  143. {
  144. using namespace std;
  145. using namespace Eigen;
  146. //olga: was
  147. // NRosyField nrosy(V,F);
  148. // for (unsigned i=0; i<F.rows(); ++i)
  149. // if(thetas_c[i])
  150. // nrosy.setConstraintHard(i,theta2vector(TPs[i],thetas(i)));
  151. // nrosy.solve(4);
  152. // MatrixXd R = nrosy.getFieldPerFace();
  153. //olga: is
  154. Eigen::MatrixXd R;
  155. Eigen::VectorXd S;
  156. Eigen::VectorXi b; b.resize(F.rows(),1);
  157. Eigen::MatrixXd bc; bc.resize(F.rows(),3);
  158. int num = 0;
  159. for (unsigned i=0; i<F.rows(); ++i)
  160. if(thetas_c[i])
  161. {
  162. b[num] = i;
  163. bc.row(num) = theta2vector(TPs[i],thetas(i));
  164. num++;
  165. }
  166. b.conservativeResize(num,Eigen::NoChange);
  167. bc.conservativeResize(num,Eigen::NoChange);
  168. igl::nrosy(V, F, b, bc, 4, R, S);
  169. //olga:end
  170. assert(R.rows() == F.rows());
  171. for (unsigned i=0; i<F.rows(); ++i)
  172. thetas(i) = vector2theta(TPs[i],R.row(i));
  173. }
  174. void FrameInterpolator::resetConstraints()
  175. {
  176. thetas_c.resize(F.rows());
  177. S_c.resize(F.rows());
  178. for(unsigned i=0; i<F.rows(); ++i)
  179. {
  180. thetas_c[i] = false;
  181. S_c[i] = false;
  182. }
  183. }
  184. void FrameInterpolator::compute_edge_consistency()
  185. {
  186. using namespace std;
  187. using namespace Eigen;
  188. // Compute per-edge consistency
  189. edge_consistency.resize(EF.rows());
  190. edge_consistency_TT = MatrixXi::Constant(TT.rows(),3,-1);
  191. // For every non-border edge
  192. for (unsigned eid=0; eid<EF.rows(); ++eid)
  193. {
  194. if (!isBorderEdge[eid])
  195. {
  196. int fid0 = EF(eid,0);
  197. int fid1 = EF(eid,1);
  198. double theta0 = thetas(fid0);
  199. double theta1 = thetas(fid1);
  200. theta0 = theta0 + k(eid);
  201. double r = modpi(theta0-theta1);
  202. edge_consistency[eid] = r < M_PI/4.0 || r > 3*(M_PI/4.0);
  203. // Copy it into edge_consistency_TT
  204. int i1 = -1;
  205. int i2 = -1;
  206. for (unsigned i=0; i<3; ++i)
  207. {
  208. if (TT(fid0,i) == fid1)
  209. i1 = i;
  210. if (TT(fid1,i) == fid0)
  211. i2 = i;
  212. }
  213. assert(i1 != -1);
  214. assert(i2 != -1);
  215. edge_consistency_TT(fid0,i1) = edge_consistency[eid];
  216. edge_consistency_TT(fid1,i2) = edge_consistency[eid];
  217. }
  218. }
  219. }
  220. void FrameInterpolator::computek()
  221. {
  222. using namespace std;
  223. using namespace Eigen;
  224. k.resize(EF.rows());
  225. // For every non-border edge
  226. for (unsigned eid=0; eid<EF.rows(); ++eid)
  227. {
  228. if (!isBorderEdge[eid])
  229. {
  230. int fid0 = EF(eid,0);
  231. int fid1 = EF(eid,1);
  232. Vector3d N0 = N.row(fid0);
  233. //Vector3d N1 = N.row(fid1);
  234. // find common edge on triangle 0 and 1
  235. int fid0_vc = -1;
  236. int fid1_vc = -1;
  237. for (unsigned i=0;i<3;++i)
  238. {
  239. if (EV(eid,0) == F(fid0,i))
  240. fid0_vc = i;
  241. if (EV(eid,1) == F(fid1,i))
  242. fid1_vc = i;
  243. }
  244. assert(fid0_vc != -1);
  245. assert(fid1_vc != -1);
  246. Vector3d common_edge = V.row(F(fid0,(fid0_vc+1)%3)) - V.row(F(fid0,fid0_vc));
  247. common_edge.normalize();
  248. // Map the two triangles in a new space where the common edge is the x axis and the N0 the z axis
  249. MatrixXd P(3,3);
  250. VectorXd o = V.row(F(fid0,fid0_vc));
  251. VectorXd tmp = -N0.cross(common_edge);
  252. P << common_edge, tmp, N0;
  253. P.transposeInPlace();
  254. MatrixXd V0(3,3);
  255. V0.row(0) = V.row(F(fid0,0)).transpose() -o;
  256. V0.row(1) = V.row(F(fid0,1)).transpose() -o;
  257. V0.row(2) = V.row(F(fid0,2)).transpose() -o;
  258. V0 = (P*V0.transpose()).transpose();
  259. assert(V0(0,2) < 10e-10);
  260. assert(V0(1,2) < 10e-10);
  261. assert(V0(2,2) < 10e-10);
  262. MatrixXd V1(3,3);
  263. V1.row(0) = V.row(F(fid1,0)).transpose() -o;
  264. V1.row(1) = V.row(F(fid1,1)).transpose() -o;
  265. V1.row(2) = V.row(F(fid1,2)).transpose() -o;
  266. V1 = (P*V1.transpose()).transpose();
  267. assert(V1(fid1_vc,2) < 10e-10);
  268. assert(V1((fid1_vc+1)%3,2) < 10e-10);
  269. // compute rotation R such that R * N1 = N0
  270. // i.e. map both triangles to the same plane
  271. double alpha = -atan2(V1((fid1_vc+2)%3,2),V1((fid1_vc+2)%3,1));
  272. MatrixXd R(3,3);
  273. R << 1, 0, 0,
  274. 0, cos(alpha), -sin(alpha) ,
  275. 0, sin(alpha), cos(alpha);
  276. V1 = (R*V1.transpose()).transpose();
  277. assert(V1(0,2) < 10e-10);
  278. assert(V1(1,2) < 10e-10);
  279. assert(V1(2,2) < 10e-10);
  280. // measure the angle between the reference frames
  281. // k_ij is the angle between the triangle on the left and the one on the right
  282. VectorXd ref0 = V0.row(1) - V0.row(0);
  283. VectorXd ref1 = V1.row(1) - V1.row(0);
  284. ref0.normalize();
  285. ref1.normalize();
  286. double ktemp = atan2(ref1(1),ref1(0)) - atan2(ref0(1),ref0(0));
  287. // just to be sure, rotate ref0 using angle ktemp...
  288. MatrixXd R2(2,2);
  289. R2 << cos(ktemp), -sin(ktemp), sin(ktemp), cos(ktemp);
  290. tmp = R2*ref0.head<2>();
  291. assert(tmp(0) - ref1(0) < (0.000001));
  292. assert(tmp(1) - ref1(1) < (0.000001));
  293. k[eid] = ktemp;
  294. }
  295. }
  296. }
  297. void FrameInterpolator::frame2canonical(const Eigen::MatrixXd& TP, const Eigen::RowVectorXd& v, double& theta, Eigen::VectorXd& S_v)
  298. {
  299. using namespace std;
  300. using namespace Eigen;
  301. RowVectorXd v0 = v.segment<3>(0);
  302. RowVectorXd v1 = v.segment<3>(3);
  303. // Project onto the tangent plane
  304. Vector2d vp0 = TP * v0.transpose();
  305. Vector2d vp1 = TP * v1.transpose();
  306. // Assemble matrix
  307. MatrixXd M(2,2);
  308. M << vp0, vp1;
  309. if (M.determinant() < 0)
  310. M.col(1) = -M.col(1);
  311. assert(M.determinant() > 0);
  312. // cerr << "M: " << M << endl;
  313. MatrixXd R,S;
  314. PolarDecomposition(M,R,S);
  315. // Finally, express the cross field as an angle
  316. theta = atan2(R(1,0),R(0,0));
  317. MatrixXd R2(2,2);
  318. R2 << cos(theta), -sin(theta), sin(theta), cos(theta);
  319. assert((R2-R).norm() < 10e-8);
  320. // Convert into rotation invariant form
  321. S = R * S * R.inverse();
  322. // Copy in vector form
  323. S_v = VectorXd(3);
  324. S_v << S(0,0), S(0,1), S(1,1);
  325. }
  326. void FrameInterpolator::canonical2frame(const Eigen::MatrixXd& TP, const double theta, const Eigen::VectorXd& S_v, Eigen::RowVectorXd& v)
  327. {
  328. using namespace std;
  329. using namespace Eigen;
  330. assert(S_v.size() == 3);
  331. MatrixXd S_temp(2,2);
  332. S_temp << S_v(0), S_v(1), S_v(1), S_v(2);
  333. // Convert angle in vector in the tangent plane
  334. // Vector2d vp(cos(theta),sin(theta));
  335. // First reconstruct R
  336. MatrixXd R(2,2);
  337. R << cos(theta), -sin(theta), sin(theta), cos(theta);
  338. // Rotation invariant reconstruction
  339. MatrixXd M = S_temp * R;
  340. Vector2d vp0(M(0,0),M(1,0));
  341. Vector2d vp1(M(0,1),M(1,1));
  342. // Unproject the vectors
  343. RowVectorXd v0 = vp0.transpose() * TP;
  344. RowVectorXd v1 = vp1.transpose() * TP;
  345. v.resize(6);
  346. v << v0, v1;
  347. }
  348. void FrameInterpolator::solve()
  349. {
  350. interpolateCross();
  351. interpolateSymmetric();
  352. }
  353. void FrameInterpolator::interpolateSymmetric()
  354. {
  355. using namespace std;
  356. using namespace Eigen;
  357. // Generate uniform Laplacian matrix
  358. typedef Eigen::Triplet<double> triplet;
  359. std::vector<triplet> triplets;
  360. // Variables are stacked as x1,y1,z1,x2,y2,z2
  361. triplets.reserve(3*4*F.rows());
  362. MatrixXd b = MatrixXd::Zero(3*F.rows(),1);
  363. // Build L and b
  364. for (unsigned eid=0; eid<EF.rows(); ++eid)
  365. {
  366. if (!isBorderEdge[eid])
  367. {
  368. for (int z=0;z<2;++z)
  369. {
  370. // W = [w_a, w_b
  371. // w_b, w_c]
  372. //
  373. // It is not symmetric
  374. int i = EF(eid,z==0?0:1);
  375. int j = EF(eid,z==0?1:0);
  376. int w_a_0 = (i*3)+0;
  377. int w_b_0 = (i*3)+1;
  378. int w_c_0 = (i*3)+2;
  379. int w_a_1 = (j*3)+0;
  380. int w_b_1 = (j*3)+1;
  381. int w_c_1 = (j*3)+2;
  382. // Rotation to change frame
  383. double r_a = cos(z==1?k(eid):-k(eid));
  384. double r_b = -sin(z==1?k(eid):-k(eid));
  385. double r_c = sin(z==1?k(eid):-k(eid));
  386. double r_d = cos(z==1?k(eid):-k(eid));
  387. // First term
  388. // w_a_0 = r_a^2 w_a_1 + 2 r_a r_b w_b_1 + r_b^2 w_c_1 = 0
  389. triplets.push_back(triplet(w_a_0,w_a_0, -1 ));
  390. triplets.push_back(triplet(w_a_0,w_a_1, r_a*r_a ));
  391. triplets.push_back(triplet(w_a_0,w_b_1, 2 * r_a*r_b ));
  392. triplets.push_back(triplet(w_a_0,w_c_1, r_b*r_b ));
  393. // Second term
  394. // w_b_0 = r_a r_c w_a + (r_b r_c + r_a r_d) w_b + r_b r_d w_c
  395. triplets.push_back(triplet(w_b_0,w_b_0, -1 ));
  396. triplets.push_back(triplet(w_b_0,w_a_1, r_a*r_c ));
  397. triplets.push_back(triplet(w_b_0,w_b_1, r_b*r_c + r_a*r_d ));
  398. triplets.push_back(triplet(w_b_0,w_c_1, r_b*r_d ));
  399. // Third term
  400. // w_c_0 = r_c^2 w_a + 2 r_c r_d w_b + r_d^2 w_c
  401. triplets.push_back(triplet(w_c_0,w_c_0, -1 ));
  402. triplets.push_back(triplet(w_c_0,w_a_1, r_c*r_c ));
  403. triplets.push_back(triplet(w_c_0,w_b_1, 2 * r_c*r_d ));
  404. triplets.push_back(triplet(w_c_0,w_c_1, r_d*r_d ));
  405. }
  406. }
  407. }
  408. SparseMatrix<double> L(3*F.rows(),3*F.rows());
  409. L.setFromTriplets(triplets.begin(), triplets.end());
  410. triplets.clear();
  411. // Add soft constraints
  412. double w = 100000;
  413. for (unsigned fid=0; fid < F.rows(); ++fid)
  414. {
  415. if (S_c[fid])
  416. {
  417. for (unsigned i=0;i<3;++i)
  418. {
  419. triplets.push_back(triplet(3*fid + i,3*fid + i,w));
  420. b(3*fid + i) += w*S(fid,i);
  421. }
  422. }
  423. }
  424. SparseMatrix<double> soft(3*F.rows(),3*F.rows());
  425. soft.setFromTriplets(triplets.begin(), triplets.end());
  426. SparseMatrix<double> M;
  427. M = L + soft;
  428. // Solve Lx = b;
  429. SparseLU<SparseMatrix<double> > solver;
  430. solver.compute(M);
  431. if(solver.info()!=Success)
  432. {
  433. std::cerr << "LU failed - frame_interpolator.cpp" << std::endl;
  434. assert(0);
  435. }
  436. MatrixXd x;
  437. x = solver.solve(b);
  438. if(solver.info()!=Success)
  439. {
  440. std::cerr << "Linear solve failed - frame_interpolator.cpp" << std::endl;
  441. assert(0);
  442. }
  443. S = MatrixXd::Zero(F.rows(),3);
  444. // Copy back the result
  445. for (unsigned i=0;i<F.rows();++i)
  446. S.row(i) << x(i*3+0), x(i*3+1), x(i*3+2);
  447. }
  448. void FrameInterpolator::setConstraint(const int fid, const Eigen::VectorXd& v)
  449. {
  450. using namespace std;
  451. using namespace Eigen;
  452. double t_;
  453. VectorXd S_;
  454. frame2canonical(TPs[fid],v,t_,S_);
  455. Eigen::RowVectorXd v2;
  456. canonical2frame(TPs[fid], t_, S_, v2);
  457. thetas(fid) = t_;
  458. thetas_c[fid] = true;
  459. S.row(fid) = S_;
  460. S_c[fid] = true;
  461. }
  462. Eigen::MatrixXd FrameInterpolator::getFieldPerFace()
  463. {
  464. using namespace std;
  465. using namespace Eigen;
  466. MatrixXd R(F.rows(),6);
  467. for (unsigned i=0; i<F.rows(); ++i)
  468. {
  469. RowVectorXd v;
  470. canonical2frame(TPs[i],thetas(i),S.row(i),v);
  471. R.row(i) = v;
  472. }
  473. return R;
  474. }
  475. void FrameInterpolator::PolarDecomposition(Eigen::MatrixXd V, Eigen::MatrixXd& U, Eigen::MatrixXd& P)
  476. {
  477. using namespace std;
  478. using namespace Eigen;
  479. // Polar Decomposition
  480. JacobiSVD<MatrixXd> svd(V,Eigen::ComputeFullU | Eigen::ComputeFullV);
  481. U = svd.matrixU() * svd.matrixV().transpose();
  482. P = svd.matrixV() * svd.singularValues().asDiagonal() * svd.matrixV().transpose();
  483. }
  484. }
  485. namespace igl
  486. {
  487. IGL_INLINE void igl::comiso::frame_field(
  488. const Eigen::MatrixXd& V,
  489. const Eigen::MatrixXi& F,
  490. const Eigen::VectorXi& b,
  491. const Eigen::MatrixXd& bc1,
  492. const Eigen::MatrixXd& bc2,
  493. Eigen::MatrixXd& FF1,
  494. Eigen::MatrixXd& FF2
  495. )
  496. {
  497. using namespace std;
  498. using namespace Eigen;
  499. assert(b.size() > 0);
  500. // Init Solver
  501. FrameInterpolator field(V,F);
  502. for (unsigned i=0; i<b.size(); ++i)
  503. {
  504. VectorXd t(6); t << bc1.row(i).transpose(), bc2.row(i).transpose();
  505. field.setConstraint(b(i), t);
  506. }
  507. // Solve
  508. field.solve();
  509. // Copy back
  510. MatrixXd R = field.getFieldPerFace();
  511. FF1 = R.block(0, 0, R.rows(), 3);
  512. FF2 = R.block(0, 3, R.rows(), 3);
  513. }