n_polyvector.cpp 17 KB

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