n_polyvector.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. // This file is part of libigl, a simple c++ geometry processing library.
  2. //
  3. // Copyright (C) 2014 Olga Diamanti <olga.diam@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 <igl/n_polyvector.h>
  9. #include <igl/edgetopology.h>
  10. #include <igl/local_basis.h>
  11. #include <igl/nchoosek.h>
  12. #include <igl/slice.h>
  13. #include <igl/polyroots.h>
  14. #include <Eigen/Sparse>
  15. #include <iostream>
  16. namespace igl {
  17. template <typename DerivedV, typename DerivedF>
  18. class PolyVectorFieldFinder
  19. {
  20. private:
  21. const Eigen::PlainObjectBase<DerivedV> &V;
  22. const Eigen::PlainObjectBase<DerivedF> &F; int numF;
  23. const int n;
  24. Eigen::MatrixXi EV; int numE;
  25. Eigen::MatrixXi F2E;
  26. Eigen::MatrixXi E2F;
  27. Eigen::VectorXd K;
  28. Eigen::VectorXi isBorderEdge;
  29. int numInteriorEdges;
  30. Eigen::Matrix<int,Eigen::Dynamic,2> E2F_int;
  31. Eigen::VectorXi indInteriorToFull;
  32. Eigen::VectorXi indFullToInterior;
  33. Eigen::PlainObjectBase<DerivedV> B1, B2, FN;
  34. IGL_INLINE void computek();
  35. IGL_INLINE void setFieldFromGeneralCoefficients(const std::vector<Eigen::Matrix<std::complex<typename DerivedV::Scalar>, Eigen::Dynamic,1>> &coeffs,
  36. std::vector<Eigen::Matrix<typename DerivedV::Scalar, Eigen::Dynamic, 2> > &pv);
  37. IGL_INLINE void computeCoefficientLaplacian(int n, Eigen::SparseMatrix<std::complex<typename DerivedV::Scalar> > &D);
  38. IGL_INLINE void getGeneralCoeffConstraints(const Eigen::VectorXi &isConstrained,
  39. const Eigen::Matrix<typename DerivedV::Scalar, Eigen::Dynamic, Eigen::Dynamic> &cfW,
  40. int k,
  41. Eigen::Matrix<std::complex<typename DerivedV::Scalar>, Eigen::Dynamic,1> &Ck);
  42. IGL_INLINE void precomputeInteriorEdges();
  43. IGL_INLINE void minQuadWithKnownMini(const Eigen::SparseMatrix<std::complex<typename DerivedV::Scalar> > &Q,
  44. const Eigen::SparseMatrix<std::complex<typename DerivedV::Scalar> > &f,
  45. const Eigen::VectorXi isConstrained,
  46. const Eigen::Matrix<std::complex<typename DerivedV::Scalar>, Eigen::Dynamic, 1> &xknown,
  47. Eigen::Matrix<std::complex<typename DerivedV::Scalar>, Eigen::Dynamic, 1> &x);
  48. public:
  49. IGL_INLINE PolyVectorFieldFinder(const Eigen::PlainObjectBase<DerivedV> &_V,
  50. const Eigen::PlainObjectBase<DerivedF> &_F,
  51. const int &_n);
  52. IGL_INLINE bool solve(const Eigen::VectorXi &isConstrained,
  53. const Eigen::Matrix<typename DerivedV::Scalar, Eigen::Dynamic, Eigen::Dynamic> &cfW,
  54. Eigen::Matrix<typename DerivedV::Scalar, Eigen::Dynamic, Eigen::Dynamic> &output);
  55. };
  56. }
  57. template<typename DerivedV, typename DerivedF>
  58. IGL_INLINE igl::PolyVectorFieldFinder<DerivedV, DerivedF>::
  59. PolyVectorFieldFinder(const Eigen::PlainObjectBase<DerivedV> &_V,
  60. const Eigen::PlainObjectBase<DerivedF> &_F,
  61. const int &_n):
  62. V(_V),
  63. F(_F),
  64. numF(_F.rows()),
  65. n(_n)
  66. {
  67. igl::edgetopology(V,F,EV,F2E,E2F);
  68. numE = EV.rows();
  69. precomputeInteriorEdges();
  70. igl::local_basis(V,F,B1,B2,FN);
  71. computek();
  72. };
  73. template<typename DerivedV, typename DerivedF>
  74. IGL_INLINE void igl::PolyVectorFieldFinder<DerivedV, DerivedF>::
  75. precomputeInteriorEdges()
  76. {
  77. // Flag border edges
  78. numInteriorEdges = 0;
  79. isBorderEdge.setZero(numE,1);
  80. indFullToInterior = -1.*Eigen::VectorXi::Ones(numE,1);
  81. for(unsigned i=0; i<numE; ++i)
  82. {
  83. if ((E2F(i,0) == -1) || ((E2F(i,1) == -1)))
  84. isBorderEdge[i] = 1;
  85. else
  86. {
  87. indFullToInterior[i] = numInteriorEdges;
  88. numInteriorEdges++;
  89. }
  90. }
  91. E2F_int.resize(numInteriorEdges, 2);
  92. indInteriorToFull.setZero(numInteriorEdges,1);
  93. int ii = 0;
  94. for (int k=0; k<numE; ++k)
  95. {
  96. if (isBorderEdge[k])
  97. continue;
  98. E2F_int.row(ii) = E2F.row(k);
  99. indInteriorToFull[ii] = k;
  100. ii++;
  101. }
  102. }
  103. template<typename DerivedV, typename DerivedF>
  104. IGL_INLINE void igl::PolyVectorFieldFinder<DerivedV, DerivedF>::
  105. minQuadWithKnownMini(const Eigen::SparseMatrix<std::complex<typename DerivedV::Scalar> > &Q,
  106. const Eigen::SparseMatrix<std::complex<typename DerivedV::Scalar> > &f,
  107. const Eigen::VectorXi isConstrained,
  108. const Eigen::Matrix<std::complex<typename DerivedV::Scalar>, Eigen::Dynamic, 1> &xknown,
  109. Eigen::Matrix<std::complex<typename DerivedV::Scalar>, Eigen::Dynamic, 1> &x)
  110. {
  111. int N = Q.rows();
  112. int nc = xknown.rows();
  113. Eigen::VectorXi known; known.setZero(nc,1);
  114. Eigen::VectorXi unknown; unknown.setZero(N-nc,1);
  115. int indk = 0, indu = 0;
  116. for (int i = 0; i<N; ++i)
  117. if (isConstrained[i])
  118. {
  119. known[indk] = i;
  120. indk++;
  121. }
  122. else
  123. {
  124. unknown[indu] = i;
  125. indu++;
  126. }
  127. Eigen::SparseMatrix<std::complex<typename DerivedV::Scalar>> Quu, Quk;
  128. igl::slice(Q,unknown, unknown, Quu);
  129. igl::slice(Q,unknown, known, Quk);
  130. std::vector<typename Eigen::Triplet<std::complex<typename DerivedV::Scalar> > > tripletList;
  131. Eigen::SparseMatrix<std::complex<typename DerivedV::Scalar> > fu(N-nc,1);
  132. igl::slice(f,unknown, Eigen::VectorXi::Zero(1,1), fu);
  133. Eigen::SparseMatrix<std::complex<typename DerivedV::Scalar> > rhs = (Quk*xknown).sparseView()+.5*fu;
  134. Eigen::SparseLU< Eigen::SparseMatrix<std::complex<typename DerivedV::Scalar>>> solver;
  135. solver.compute(-Quu);
  136. if(solver.info()!=Eigen::Success)
  137. {
  138. std::cerr<<"Decomposition failed!"<<std::endl;
  139. return;
  140. }
  141. Eigen::SparseMatrix<std::complex<typename DerivedV::Scalar>> b = solver.solve(rhs);
  142. if(solver.info()!=Eigen::Success)
  143. {
  144. std::cerr<<"Solving failed!"<<std::endl;
  145. return;
  146. }
  147. indk = 0, indu = 0;
  148. x.setZero(N,1);
  149. for (int i = 0; i<N; ++i)
  150. if (isConstrained[i])
  151. x[i] = xknown[indk++];
  152. else
  153. x[i] = b.coeff(indu++,0);
  154. }
  155. template<typename DerivedV, typename DerivedF>
  156. IGL_INLINE bool igl::PolyVectorFieldFinder<DerivedV, DerivedF>::
  157. solve(const Eigen::VectorXi &isConstrained,
  158. const Eigen::Matrix<typename DerivedV::Scalar, Eigen::Dynamic, Eigen::Dynamic> &cfW,
  159. Eigen::Matrix<typename DerivedV::Scalar, Eigen::Dynamic, Eigen::Dynamic> &output)
  160. {
  161. // polynomial is of the form:
  162. // (-1)^0 z^(2n) +
  163. // (-1)^1 c[0]z^(2n-2) +
  164. // (-1)^2 c[1]z^(2n-4) +
  165. // (-1)^3 c[2]z^(2n-6) +
  166. // ... +
  167. // (-1)^n c[n-1]
  168. std::vector<Eigen::Matrix<std::complex<typename DerivedV::Scalar>, Eigen::Dynamic,1>> coeffs(n,Eigen::Matrix<std::complex<typename DerivedV::Scalar>, Eigen::Dynamic,1>::Zero(numF, 1));
  169. for (int i =0; i<n; ++i)
  170. {
  171. int degree = 2*(i+1);
  172. Eigen::Matrix<std::complex<typename DerivedV::Scalar>, Eigen::Dynamic,1> Ck;
  173. getGeneralCoeffConstraints(isConstrained,
  174. cfW,
  175. i,
  176. Ck);
  177. Eigen::SparseMatrix<std::complex<typename DerivedV::Scalar> > DD;
  178. computeCoefficientLaplacian(degree, DD);
  179. Eigen::SparseMatrix<std::complex<typename DerivedV::Scalar> > f; f.resize(numF,1);
  180. minQuadWithKnownMini(DD, f, isConstrained, Ck, coeffs[i]);
  181. }
  182. std::vector<Eigen::Matrix<typename DerivedV::Scalar, Eigen::Dynamic, 2> > pv;
  183. setFieldFromGeneralCoefficients(coeffs, pv);
  184. output.setZero(numF,3*n);
  185. for (int fi=0; fi<numF; ++fi)
  186. {
  187. const Eigen::Matrix<typename DerivedV::Scalar, 1, 3> &b1 = B1.row(fi);
  188. const Eigen::Matrix<typename DerivedV::Scalar, 1, 3> &b2 = B2.row(fi);
  189. for (int i=0; i<n; ++i)
  190. output.block(fi,3*i, 1, 3) = pv[i](fi,0)*b1 + pv[i](fi,1)*b2;
  191. }
  192. return true;
  193. }
  194. template<typename DerivedV, typename DerivedF>
  195. IGL_INLINE void igl::PolyVectorFieldFinder<DerivedV, DerivedF>::setFieldFromGeneralCoefficients(const std::vector<Eigen::Matrix<std::complex<typename DerivedV::Scalar>, Eigen::Dynamic,1>> &coeffs,
  196. std::vector<Eigen::Matrix<typename DerivedV::Scalar, Eigen::Dynamic, 2>> &pv)
  197. {
  198. pv.assign(n, Eigen::Matrix<typename DerivedV::Scalar, Eigen::Dynamic, 2>::Zero(numF, 2));
  199. for (int i = 0; i <numF; ++i)
  200. {
  201. // poly coefficients: 1, 0, -Acoeff, 0, Bcoeff
  202. // matlab code from roots (given there are no trailing zeros in the polynomial coefficients)
  203. Eigen::Matrix<std::complex<typename DerivedV::Scalar>, Eigen::Dynamic,1> polyCoeff;
  204. polyCoeff.setZero(2*n+1,1);
  205. polyCoeff[0] = 1.;
  206. int sign = 1;
  207. for (int k =0; k<n; ++k)
  208. {
  209. sign = -sign;
  210. int degree = 2*(k+1);
  211. polyCoeff[degree] = (1.*sign)*coeffs[k](i);
  212. }
  213. Eigen::Matrix<std::complex<typename DerivedV::Scalar>, Eigen::Dynamic,1> roots;
  214. igl::polyRoots<std::complex<typename DerivedV::Scalar>, typename DerivedV::Scalar >(polyCoeff,roots);
  215. Eigen::VectorXi done; done.setZero(2*n,1);
  216. Eigen::Matrix<std::complex<typename DerivedV::Scalar>, Eigen::Dynamic,1> u(n,1);
  217. int ind =0;
  218. for (int k=0; k<2*n; ++k)
  219. {
  220. if (done[k])
  221. continue;
  222. u[ind] = roots[k];
  223. done[k] = 1;
  224. int mini = -1;
  225. double mind = 1e10;
  226. for (int l =k+1; l<2*n; ++l)
  227. {
  228. double dist = abs(roots[l]+u[ind]);
  229. if (dist<mind)
  230. {
  231. mind = dist;
  232. mini = l;
  233. }
  234. }
  235. done[mini] = 1;
  236. ind ++;
  237. }
  238. for (int k=0; k<n; ++k)
  239. {
  240. pv[k](i,0) = real(u[k]);
  241. pv[k](i,1) = imag(u[k]);
  242. }
  243. }
  244. }
  245. template<typename DerivedV, typename DerivedF>
  246. IGL_INLINE void igl::PolyVectorFieldFinder<DerivedV, DerivedF>::computeCoefficientLaplacian(int n, Eigen::SparseMatrix<std::complex<typename DerivedV::Scalar> > &D)
  247. {
  248. std::vector<Eigen::Triplet<std::complex<typename DerivedV::Scalar> >> tripletList;
  249. // For every non-border edge
  250. for (unsigned eid=0; eid<numE; ++eid)
  251. {
  252. if (!isBorderEdge[eid])
  253. {
  254. int fid0 = E2F(eid,0);
  255. int fid1 = E2F(eid,1);
  256. tripletList.push_back(Eigen::Triplet<std::complex<typename DerivedV::Scalar> >(fid0,
  257. fid0,
  258. std::complex<typename DerivedV::Scalar>(1.)));
  259. tripletList.push_back(Eigen::Triplet<std::complex<typename DerivedV::Scalar> >(fid1,
  260. fid1,
  261. std::complex<typename DerivedV::Scalar>(1.)));
  262. tripletList.push_back(Eigen::Triplet<std::complex<typename DerivedV::Scalar> >(fid0,
  263. fid1,
  264. -1.*std::polar(1.,-1.*n*K[eid])));
  265. tripletList.push_back(Eigen::Triplet<std::complex<typename DerivedV::Scalar> >(fid1,
  266. fid0,
  267. -1.*std::polar(1.,1.*n*K[eid])));
  268. }
  269. }
  270. D.resize(numF,numF);
  271. D.setFromTriplets(tripletList.begin(), tripletList.end());
  272. }
  273. template<typename DerivedV, typename DerivedF>
  274. IGL_INLINE void igl::PolyVectorFieldFinder<DerivedV, DerivedF>::getGeneralCoeffConstraints(const Eigen::VectorXi &isConstrained,
  275. const Eigen::Matrix<typename DerivedV::Scalar, Eigen::Dynamic, Eigen::Dynamic> &cfW,
  276. int k,
  277. Eigen::Matrix<std::complex<typename DerivedV::Scalar>, Eigen::Dynamic,1> &Ck)
  278. {
  279. int numConstrained = isConstrained.sum();
  280. Ck.resize(numConstrained,1);
  281. int n = cfW.cols()/3;
  282. std::vector<std::vector<int>> allCombs;
  283. igl::nchoosek(0,k+1,n,allCombs);
  284. int ind = 0;
  285. for (int fi = 0; fi <numF; ++fi)
  286. {
  287. const Eigen::Matrix<typename DerivedV::Scalar, 1, 3> &b1 = B1.row(fi);
  288. const Eigen::Matrix<typename DerivedV::Scalar, 1, 3> &b2 = B2.row(fi);
  289. if(isConstrained[fi])
  290. {
  291. std::complex<typename DerivedV::Scalar> ck(0);
  292. for (int j = 0; j < allCombs.size(); ++j)
  293. {
  294. std::complex<typename DerivedV::Scalar> tk(1.);
  295. //collect products
  296. for (int i = 0; i < allCombs[j].size(); ++i)
  297. {
  298. int index = allCombs[j][i];
  299. const Eigen::Matrix<typename DerivedV::Scalar, 1, 3> &w = cfW.block(fi,3*index,1,3);
  300. typename DerivedV::Scalar w0 = w.dot(b1);
  301. typename DerivedV::Scalar w1 = w.dot(b2);
  302. std::complex<typename DerivedV::Scalar> u(w0,w1);
  303. tk*= u*u;
  304. }
  305. //collect sum
  306. ck += tk;
  307. }
  308. Ck(ind) = ck;
  309. ind ++;
  310. }
  311. }
  312. }
  313. template<typename DerivedV, typename DerivedF>
  314. IGL_INLINE void igl::PolyVectorFieldFinder<DerivedV, DerivedF>::computek()
  315. {
  316. K.setZero(numE);
  317. // For every non-border edge
  318. for (unsigned eid=0; eid<numE; ++eid)
  319. {
  320. if (!isBorderEdge[eid])
  321. {
  322. int fid0 = E2F(eid,0);
  323. int fid1 = E2F(eid,1);
  324. Eigen::Matrix<typename DerivedV::Scalar, 1, 3> N0 = FN.row(fid0);
  325. Eigen::Matrix<typename DerivedV::Scalar, 1, 3> N1 = FN.row(fid1);
  326. // find common edge on triangle 0 and 1
  327. int fid0_vc = -1;
  328. int fid1_vc = -1;
  329. for (unsigned i=0;i<3;++i)
  330. {
  331. if (F2E(fid0,i) == eid)
  332. fid0_vc = i;
  333. if (F2E(fid1,i) == eid)
  334. fid1_vc = i;
  335. }
  336. assert(fid0_vc != -1);
  337. assert(fid1_vc != -1);
  338. Eigen::Matrix<typename DerivedV::Scalar, 1, 3> common_edge = V.row(F(fid0,(fid0_vc+1)%3)) - V.row(F(fid0,fid0_vc));
  339. common_edge.normalize();
  340. // Map the two triangles in a new space where the common edge is the x axis and the N0 the z axis
  341. Eigen::Matrix<typename DerivedV::Scalar, 3, 3> P;
  342. Eigen::Matrix<typename DerivedV::Scalar, 1, 3> o = V.row(F(fid0,fid0_vc));
  343. Eigen::Matrix<typename DerivedV::Scalar, 1, 3> tmp = -N0.cross(common_edge);
  344. P << common_edge, tmp, N0;
  345. // P.transposeInPlace();
  346. Eigen::Matrix<typename DerivedV::Scalar, 3, 3> V0;
  347. V0.row(0) = V.row(F(fid0,0)) -o;
  348. V0.row(1) = V.row(F(fid0,1)) -o;
  349. V0.row(2) = V.row(F(fid0,2)) -o;
  350. V0 = (P*V0.transpose()).transpose();
  351. // assert(V0(0,2) < 1e-10);
  352. // assert(V0(1,2) < 1e-10);
  353. // assert(V0(2,2) < 1e-10);
  354. Eigen::Matrix<typename DerivedV::Scalar, 3, 3> V1;
  355. V1.row(0) = V.row(F(fid1,0)) -o;
  356. V1.row(1) = V.row(F(fid1,1)) -o;
  357. V1.row(2) = V.row(F(fid1,2)) -o;
  358. V1 = (P*V1.transpose()).transpose();
  359. // assert(V1(fid1_vc,2) < 10e-10);
  360. // assert(V1((fid1_vc+1)%3,2) < 10e-10);
  361. // compute rotation R such that R * N1 = N0
  362. // i.e. map both triangles to the same plane
  363. double alpha = -atan2(V1((fid1_vc+2)%3,2),V1((fid1_vc+2)%3,1));
  364. Eigen::Matrix<typename DerivedV::Scalar, 3, 3> R;
  365. R << 1, 0, 0,
  366. 0, cos(alpha), -sin(alpha) ,
  367. 0, sin(alpha), cos(alpha);
  368. V1 = (R*V1.transpose()).transpose();
  369. // assert(V1(0,2) < 1e-10);
  370. // assert(V1(1,2) < 1e-10);
  371. // assert(V1(2,2) < 1e-10);
  372. // measure the angle between the reference frames
  373. // k_ij is the angle between the triangle on the left and the one on the right
  374. Eigen::Matrix<typename DerivedV::Scalar, 1, 3> ref0 = V0.row(1) - V0.row(0);
  375. Eigen::Matrix<typename DerivedV::Scalar, 1, 3> ref1 = V1.row(1) - V1.row(0);
  376. ref0.normalize();
  377. ref1.normalize();
  378. double ktemp = atan2(ref1(1),ref1(0)) - atan2(ref0(1),ref0(0));
  379. // just to be sure, rotate ref0 using angle ktemp...
  380. Eigen::Matrix<typename DerivedV::Scalar, 2, 2> R2;
  381. R2 << cos(ktemp), -sin(ktemp), sin(ktemp), cos(ktemp);
  382. Eigen::Matrix<typename DerivedV::Scalar, 1, 2> tmp1 = R2*(ref0.head(2)).transpose();
  383. // assert(tmp1(0) - ref1(0) < 1e-10);
  384. // assert(tmp1(1) - ref1(1) < 1e-10);
  385. K[eid] = ktemp;
  386. }
  387. }
  388. }
  389. template <typename DerivedV, typename DerivedF>
  390. IGL_INLINE void igl::n_polyvector(const Eigen::PlainObjectBase<DerivedV> &V,
  391. const Eigen::PlainObjectBase<DerivedF> &F,
  392. const Eigen::VectorXi &isConstrained,
  393. const Eigen::Matrix<typename DerivedV::Scalar, Eigen::Dynamic, Eigen::Dynamic> &cfW,
  394. Eigen::Matrix<typename DerivedV::Scalar, Eigen::Dynamic, Eigen::Dynamic> &output)
  395. {
  396. int n = cfW.cols()/3;
  397. igl::PolyVectorFieldFinder<DerivedV, DerivedF> pvff(V,F,n);
  398. pvff.solve(isConstrained, cfW, output);
  399. }
  400. #ifndef IGL_HEADER_ONLY
  401. // Explicit template specialization
  402. #endif