n_polyvector_general.cpp 17 KB

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