outer_hull.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. #include "outer_hull.h"
  2. #include "outer_facet.h"
  3. #include "sortrows.h"
  4. #include "facet_components.h"
  5. #include "winding_number.h"
  6. #include "triangle_triangle_adjacency.h"
  7. #include "unique_edge_map.h"
  8. #include "barycenter.h"
  9. #include "per_face_normals.h"
  10. #include "writePLY.h"
  11. #include "sort_angles.h"
  12. #include "order_facets_around_edges.h"
  13. #include <Eigen/Geometry>
  14. #include <vector>
  15. #include <map>
  16. #include <queue>
  17. #include <iostream>
  18. #include <CGAL/number_utils.h>
  19. //#define IGL_OUTER_HULL_DEBUG
  20. template <
  21. typename DerivedV,
  22. typename DerivedF,
  23. typename DerivedN,
  24. typename DerivedG,
  25. typename DerivedJ,
  26. typename Derivedflip>
  27. IGL_INLINE void igl::outer_hull(
  28. const Eigen::PlainObjectBase<DerivedV> & V,
  29. const Eigen::PlainObjectBase<DerivedF> & F,
  30. const Eigen::PlainObjectBase<DerivedN> & N,
  31. Eigen::PlainObjectBase<DerivedG> & G,
  32. Eigen::PlainObjectBase<DerivedJ> & J,
  33. Eigen::PlainObjectBase<Derivedflip> & flip)
  34. {
  35. #ifdef IGL_OUTER_HULL_DEBUG
  36. std::cerr << "Extracting outer hull" << std::endl;
  37. #endif
  38. using namespace Eigen;
  39. using namespace std;
  40. typedef typename DerivedF::Index Index;
  41. Matrix<Index,DerivedF::RowsAtCompileTime,1> C;
  42. typedef Matrix<typename DerivedV::Scalar,Dynamic,DerivedV::ColsAtCompileTime> MatrixXV;
  43. typedef Matrix<typename DerivedF::Scalar,Dynamic,DerivedF::ColsAtCompileTime> MatrixXF;
  44. typedef Matrix<typename DerivedG::Scalar,Dynamic,DerivedG::ColsAtCompileTime> MatrixXG;
  45. typedef Matrix<typename DerivedJ::Scalar,Dynamic,DerivedJ::ColsAtCompileTime> MatrixXJ;
  46. typedef Matrix<typename DerivedN::Scalar,1,3> RowVector3N;
  47. const Index m = F.rows();
  48. const auto & duplicate_simplex = [&F](const int f, const int g)->bool
  49. {
  50. return
  51. (F(f,0) == F(g,0) && F(f,1) == F(g,1) && F(f,2) == F(g,2)) ||
  52. (F(f,1) == F(g,0) && F(f,2) == F(g,1) && F(f,0) == F(g,2)) ||
  53. (F(f,2) == F(g,0) && F(f,0) == F(g,1) && F(f,1) == F(g,2)) ||
  54. (F(f,0) == F(g,2) && F(f,1) == F(g,1) && F(f,2) == F(g,0)) ||
  55. (F(f,1) == F(g,2) && F(f,2) == F(g,1) && F(f,0) == F(g,0)) ||
  56. (F(f,2) == F(g,2) && F(f,0) == F(g,1) && F(f,1) == F(g,0));
  57. };
  58. #ifdef IGL_OUTER_HULL_DEBUG
  59. cout<<"outer hull..."<<endl;
  60. #endif
  61. #ifdef IGL_OUTER_HULL_DEBUG
  62. cout<<"edge map..."<<endl;
  63. #endif
  64. typedef Matrix<typename DerivedF::Scalar,Dynamic,2> MatrixX2I;
  65. typedef Matrix<typename DerivedF::Index,Dynamic,1> VectorXI;
  66. typedef Matrix<typename DerivedV::Scalar, 3, 1> Vector3F;
  67. MatrixX2I E,uE;
  68. VectorXI EMAP;
  69. vector<vector<typename DerivedF::Index> > uE2E;
  70. unique_edge_map(F,E,uE,EMAP,uE2E);
  71. #ifdef IGL_OUTER_HULL_DEBUG
  72. for (size_t ui=0; ui<uE.rows(); ui++) {
  73. std::cout << ui << ": " << uE2E[ui].size() << " -- (";
  74. for (size_t i=0; i<uE2E[ui].size(); i++) {
  75. std::cout << uE2E[ui][i] << ", ";
  76. }
  77. std::cout << ")" << std::endl;
  78. }
  79. #endif
  80. std::vector<std::vector<typename DerivedF::Index> > uE2oE;
  81. std::vector<std::vector<bool> > uE2C;
  82. order_facets_around_edges(V, F, N, E, uE, EMAP, uE2E, uE2oE, uE2C);
  83. uE2E = uE2oE;
  84. VectorXI diIM(3*m);
  85. for (auto ue : uE2E) {
  86. for (size_t i=0; i<ue.size(); i++) {
  87. auto fe = ue[i];
  88. diIM[fe] = i;
  89. }
  90. }
  91. vector<vector<vector<Index > > > TT,_1;
  92. triangle_triangle_adjacency(E,EMAP,uE2E,false,TT,_1);
  93. VectorXI counts;
  94. #ifdef IGL_OUTER_HULL_DEBUG
  95. cout<<"facet components..."<<endl;
  96. #endif
  97. facet_components(TT,C,counts);
  98. assert(C.maxCoeff()+1 == counts.rows());
  99. const size_t ncc = counts.rows();
  100. G.resize(0,F.cols());
  101. J.resize(0,1);
  102. flip.setConstant(m,1,false);
  103. #ifdef IGL_OUTER_HULL_DEBUG
  104. cout<<"reindex..."<<endl;
  105. #endif
  106. // H contains list of faces on outer hull;
  107. vector<bool> FH(m,false);
  108. vector<bool> EH(3*m,false);
  109. vector<MatrixXG> vG(ncc);
  110. vector<MatrixXJ> vJ(ncc);
  111. vector<MatrixXJ> vIM(ncc);
  112. size_t face_count = 0;
  113. for(size_t id = 0;id<ncc;id++)
  114. {
  115. vIM[id].resize(counts[id],1);
  116. }
  117. // current index into each IM
  118. vector<size_t> g(ncc,0);
  119. // place order of each face in its respective component
  120. for(Index f = 0;f<m;f++)
  121. {
  122. vIM[C(f)](g[C(f)]++) = f;
  123. }
  124. #ifdef IGL_OUTER_HULL_DEBUG
  125. cout<<"barycenters..."<<endl;
  126. #endif
  127. // assumes that "resolve" has handled any coplanar cases correctly and nearly
  128. // coplanar cases can be sorted based on barycenter.
  129. MatrixXV BC;
  130. barycenter(V,F,BC);
  131. #ifdef IGL_OUTER_HULL_DEBUG
  132. cout<<"loop over CCs (="<<ncc<<")..."<<endl;
  133. #endif
  134. for(Index id = 0;id<(Index)ncc;id++)
  135. {
  136. auto & IM = vIM[id];
  137. // starting face that's guaranteed to be on the outer hull and in this
  138. // component
  139. int f;
  140. bool f_flip;
  141. #ifdef IGL_OUTER_HULL_DEBUG
  142. cout<<"outer facet..."<<endl;
  143. #endif
  144. outer_facet(V,F,N,IM,f,f_flip);
  145. #ifdef IGL_OUTER_HULL_DEBUG
  146. cout<<"outer facet: "<<f<<endl;
  147. cout << V.row(F(f, 0)) << std::endl;
  148. cout << V.row(F(f, 1)) << std::endl;
  149. cout << V.row(F(f, 2)) << std::endl;
  150. #endif
  151. int FHcount = 1;
  152. FH[f] = true;
  153. // Q contains list of face edges to continue traversing upong
  154. queue<int> Q;
  155. Q.push(f+0*m);
  156. Q.push(f+1*m);
  157. Q.push(f+2*m);
  158. flip(f) = f_flip;
  159. //std::cout << "face " << face_count++ << ": " << f << std::endl;
  160. //std::cout << "f " << F.row(f).array()+1 << std::endl;
  161. //cout<<"flip("<<f<<") = "<<(flip(f)?"true":"false")<<endl;
  162. #ifdef IGL_OUTER_HULL_DEBUG
  163. cout<<"BFS..."<<endl;
  164. #endif
  165. while(!Q.empty())
  166. {
  167. // face-edge
  168. const int e = Q.front();
  169. Q.pop();
  170. // face
  171. const int f = e%m;
  172. // corner
  173. const int c = e/m;
  174. #ifdef IGL_OUTER_HULL_DEBUG
  175. std::cout << "edge: " << e << ", ue: " << EMAP(e) << std::endl;
  176. std::cout << "face: " << f << std::endl;
  177. std::cout << "corner: " << c << std::endl;
  178. std::cout << "consistent: " << uE2C[EMAP(e)][diIM[e]] << std::endl;
  179. #endif
  180. // Should never see edge again...
  181. if(EH[e] == true)
  182. {
  183. continue;
  184. }
  185. EH[e] = true;
  186. // source of edge according to f
  187. const int fs = flip(f)?F(f,(c+2)%3):F(f,(c+1)%3);
  188. // destination of edge according to f
  189. const int fd = flip(f)?F(f,(c+1)%3):F(f,(c+2)%3);
  190. // edge valence
  191. const size_t val = uE2E[EMAP(e)].size();
  192. #ifdef IGL_OUTER_HULL_DEBUG
  193. std::cout << "vd: " << V.row(fd) << std::endl;
  194. std::cout << "vs: " << V.row(fs) << std::endl;
  195. std::cout << "edge: " << V.row(fd) - V.row(fs) << std::endl;
  196. for (size_t i=0; i<val; i++) {
  197. if (i == diIM(e)) {
  198. std::cout << "* ";
  199. } else {
  200. std::cout << " ";
  201. }
  202. std::cout << i << ": "
  203. << " (e: " << uE2E[EMAP(e)][i] << ", f: "
  204. << uE2E[EMAP(e)][i] % m * (uE2C[EMAP(e)][i] ? 1:-1) << ")" << std::endl;
  205. }
  206. #endif
  207. //// find overlapping face-edges
  208. //const auto & neighbors = uE2E[EMAP(e)];
  209. //// normal after possible flipping
  210. //const auto & fN = (flip(f)?-1.:1.)*N.row(f);
  211. //// Edge vector according to f's (flipped) orientation.
  212. ////const auto & eV = (V.row(fd)-V.row(fs)).normalized();
  213. //#warning "EXPERIMENTAL, DO NOT USE"
  214. //// THIS IS WRONG! The first face is---after sorting---no longer the face
  215. //// used for orienting the sort.
  216. //const auto ui = EMAP(e);
  217. //const auto fe0 = uE2E[ui][0];
  218. //const auto es = F(fe0%m,((fe0/m)+1)%3);
  219. // is edge consistent with edge of face used for sorting
  220. const int e_cons = (uE2C[EMAP(e)][diIM(e)] ? 1: -1);
  221. int nfei = -1;
  222. // Loop once around trying to find suitable next face
  223. for(size_t step = 1; step<val+2;step++)
  224. {
  225. const int nfei_new = (diIM(e) + 2*val + e_cons*step*(flip(f)?-1:1))%val;
  226. const int nf = uE2E[EMAP(e)][nfei_new] % m;
  227. // Don't consider faces with identical dihedral angles
  228. //if ((di[EMAP(e)][diIM(e)].array() != di[EMAP(e)][nfei_new].array()).any())
  229. //if((di[EMAP(e)][diIM(e)] != di[EMAP(e)][nfei_new]))
  230. //#warning "THIS IS HACK, FIX ME"
  231. // if( abs(di[EMAP(e)][diIM(e)] - di[EMAP(e)][nfei_new]) < 1e-15 )
  232. {
  233. #ifdef IGL_OUTER_HULL_DEBUG
  234. //cout<<"Next facet: "<<(f+1)<<" --> "<<(nf+1)<<", |"<<
  235. // di[EMAP(e)][diIM(e)]<<" - "<<di[EMAP(e)][nfei_new]<<"| = "<<
  236. // abs(di[EMAP(e)][diIM(e)] - di[EMAP(e)][nfei_new])
  237. // <<endl;
  238. #endif
  239. // Only use this face if not already seen
  240. if(!FH[nf])
  241. {
  242. nfei = nfei_new;
  243. //} else {
  244. // std::cout << "skipping face " << nfei_new << " because it is seen before"
  245. // << std::endl;
  246. }
  247. break;
  248. //} else {
  249. // std::cout << di[EMAP(e)][diIM(e)].transpose() << std::endl;
  250. // std::cout << di[EMAP(e)][diIM(nfei_new)].transpose() << std::endl;
  251. // std::cout << "skipping face " << nfei_new << " with identical dihedral angle"
  252. // << std::endl;
  253. }
  254. //#ifdef IGL_OUTER_HULL_DEBUG
  255. // cout<<"Skipping co-planar facet: "<<(f+1)<<" --> "<<(nf+1)<<endl;
  256. //#endif
  257. }
  258. int max_ne = -1;
  259. //// Loop over and find max dihedral angle
  260. //typename DerivedV::Scalar max_di = -1;
  261. //for(const auto & ne : neighbors)
  262. //{
  263. // const int nf = ne%m;
  264. // if(nf == f)
  265. // {
  266. // continue;
  267. // }
  268. // // Corner of neighbor
  269. // const int nc = ne/m;
  270. // // Is neighbor oriented consistently with (flipped) f?
  271. // //const int ns = F(nf,(nc+1)%3);
  272. // const int nd = F(nf,(nc+2)%3);
  273. // const bool cons = (flip(f)?fd:fs) == nd;
  274. // // Normal after possibly flipping to match flip or orientation of f
  275. // const auto & nN = (cons? (flip(f)?-1:1.) : (flip(f)?1.:-1.) )*N.row(nf);
  276. // // Angle between n and f
  277. // const auto & ndi = M_PI - atan2( fN.cross(nN).dot(eV), fN.dot(nN));
  278. // if(ndi>=max_di)
  279. // {
  280. // max_ne = ne;
  281. // max_di = ndi;
  282. // }
  283. //}
  284. ////cout<<(max_ne != max_ne_2)<<" =?= "<<e_cons<<endl;
  285. //if(max_ne != max_ne_2)
  286. //{
  287. // cout<<(f+1)<<" ---> "<<(max_ne%m)+1<<" != "<<(max_ne_2%m)+1<<" ... "<<e_cons<<" "<<flip(f)<<endl;
  288. // typename DerivedV::Scalar max_di = -1;
  289. // for(size_t nei = 0;nei<neighbors.size();nei++)
  290. // {
  291. // const auto & ne = neighbors[nei];
  292. // const int nf = ne%m;
  293. // if(nf == f)
  294. // {
  295. // cout<<" "<<(ne%m)+1<<":\t"<<0<<"\t"<<di[EMAP[e]][nei]<<" "<<diIM(ne)<<endl;
  296. // continue;
  297. // }
  298. // // Corner of neighbor
  299. // const int nc = ne/m;
  300. // // Is neighbor oriented consistently with (flipped) f?
  301. // //const int ns = F(nf,(nc+1)%3);
  302. // const int nd = F(nf,(nc+2)%3);
  303. // const bool cons = (flip(f)?fd:fs) == nd;
  304. // // Normal after possibly flipping to match flip or orientation of f
  305. // const auto & nN = (cons? (flip(f)?-1:1.) : (flip(f)?1.:-1.) )*N.row(nf);
  306. // // Angle between n and f
  307. // const auto & ndi = M_PI - atan2( fN.cross(nN).dot(eV), fN.dot(nN));
  308. // cout<<" "<<(ne%m)+1<<":\t"<<ndi<<"\t"<<di[EMAP[e]][nei]<<" "<<diIM(ne)<<endl;
  309. // if(ndi>=max_di)
  310. // {
  311. // max_ne = ne;
  312. // max_di = ndi;
  313. // }
  314. // }
  315. //}
  316. if(nfei >= 0)
  317. {
  318. max_ne = uE2E[EMAP(e)][nfei];
  319. }
  320. if(max_ne>=0)
  321. {
  322. // face of neighbor
  323. const int nf = max_ne%m;
  324. #ifdef IGL_OUTER_HULL_DEBUG
  325. if(!FH[nf])
  326. {
  327. // first time seeing face
  328. cout<<(f+1)<<" --> "<<(nf+1)<<endl;
  329. }
  330. #endif
  331. FH[nf] = true;
  332. //std::cout << "face " << face_count++ << ": " << nf << std::endl;
  333. //std::cout << "f " << F.row(nf).array()+1 << std::endl;
  334. FHcount++;
  335. // corner of neighbor
  336. const int nc = max_ne/m;
  337. const int nd = F(nf,(nc+2)%3);
  338. const bool cons = (flip(f)?fd:fs) == nd;
  339. flip(nf) = (cons ? flip(f) : !flip(f));
  340. //cout<<"flip("<<nf<<") = "<<(flip(nf)?"true":"false")<<endl;
  341. const int ne1 = nf+((nc+1)%3)*m;
  342. const int ne2 = nf+((nc+2)%3)*m;
  343. if(!EH[ne1])
  344. {
  345. Q.push(ne1);
  346. }
  347. if(!EH[ne2])
  348. {
  349. Q.push(ne2);
  350. }
  351. }
  352. }
  353. {
  354. vG[id].resize(FHcount,3);
  355. vJ[id].resize(FHcount,1);
  356. //nG += FHcount;
  357. size_t h = 0;
  358. assert(counts(id) == IM.rows());
  359. for(int i = 0;i<counts(id);i++)
  360. {
  361. const size_t f = IM(i);
  362. //if(f_flip)
  363. //{
  364. // flip(f) = !flip(f);
  365. //}
  366. if(FH[f])
  367. {
  368. vG[id].row(h) = (flip(f)?F.row(f).reverse().eval():F.row(f));
  369. vJ[id](h,0) = f;
  370. h++;
  371. }
  372. }
  373. assert((int)h == FHcount);
  374. }
  375. }
  376. // Is A inside B? Assuming A and B are consistently oriented but closed and
  377. // non-intersecting.
  378. const auto & is_component_inside_other = [](
  379. const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> & V,
  380. const MatrixXV & BC,
  381. const MatrixXG & A,
  382. const MatrixXJ & AJ,
  383. const MatrixXG & B)->bool
  384. {
  385. typedef Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> Matrix;
  386. const auto & bounding_box = [](
  387. const Matrix & V,
  388. const MatrixXG & F)->
  389. Matrix
  390. {
  391. Matrix BB(2,3);
  392. BB<<
  393. 1e26,1e26,1e26,
  394. -1e26,-1e26,-1e26;
  395. const size_t m = F.rows();
  396. for(size_t f = 0;f<m;f++)
  397. {
  398. for(size_t c = 0;c<3;c++)
  399. {
  400. const auto & vfc = V.row(F(f,c));
  401. BB.row(0) = BB.row(0).array().min(vfc.array()).eval();
  402. BB.row(1) = BB.row(1).array().max(vfc.array()).eval();
  403. }
  404. }
  405. return BB;
  406. };
  407. // A lot of the time we're dealing with unrelated, distant components: cull
  408. // them.
  409. Matrix ABB = bounding_box(V,A);
  410. Matrix BBB = bounding_box(V,B);
  411. if( (BBB.row(0)-ABB.row(1)).maxCoeff()>0 ||
  412. (ABB.row(0)-BBB.row(1)).maxCoeff()>0 )
  413. {
  414. // bounding boxes do not overlap
  415. return false;
  416. }
  417. ////////////////////////////////////////////////////////////////////////
  418. // POTENTIAL ROBUSTNESS WEAK AREA
  419. ////////////////////////////////////////////////////////////////////////
  420. //
  421. // winding_number_3 expects colmajor
  422. // q could be so close (<~1e-15) to B that the winding number is not a robust way to
  423. // determine inside/outsideness. We could try to find a _better_ q which is
  424. // farther away, but couldn't they all be bad?
  425. double q[3] = {
  426. CGAL::to_double(BC(AJ(0), 0)),
  427. CGAL::to_double(BC(AJ(0), 1)),
  428. CGAL::to_double(BC(AJ(0), 2)) };
  429. // In a perfect world, it's enough to test a single point.
  430. double w;
  431. const double * Vdata;
  432. Vdata = V.data();
  433. winding_number_3(
  434. Vdata,V.rows(),
  435. B.data(),B.rows(),
  436. q,1,&w);
  437. return w > 0.5 || w < -0.5;
  438. };
  439. Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> Vcol(V.rows(), V.cols());
  440. for (size_t i=0; i<V.rows(); i++) {
  441. for (size_t j=0; j<V.cols(); j++) {
  442. Vcol(i, j) = CGAL::to_double(V(i, j));
  443. }
  444. }
  445. // Reject components which are completely inside other components
  446. vector<bool> keep(ncc,true);
  447. size_t nG = 0;
  448. // This is O( ncc * ncc * m)
  449. for(size_t id = 0;id<ncc;id++)
  450. {
  451. for(size_t oid = 0;oid<ncc;oid++)
  452. {
  453. if(id == oid)
  454. {
  455. continue;
  456. }
  457. const bool inside = is_component_inside_other(Vcol,BC,vG[id],vJ[id],vG[oid]);
  458. #ifdef IGL_OUTER_HULL_DEBUG
  459. cout<<id<<" is inside "<<oid<<" ? "<<inside<<endl;
  460. #endif
  461. keep[id] = keep[id] && !inside;
  462. }
  463. if(keep[id])
  464. {
  465. nG += vJ[id].rows();
  466. }
  467. }
  468. // collect G and J across components
  469. G.resize(nG,3);
  470. J.resize(nG,1);
  471. {
  472. size_t off = 0;
  473. for(Index id = 0;id<(Index)ncc;id++)
  474. {
  475. if(keep[id])
  476. {
  477. assert(vG[id].rows() == vJ[id].rows());
  478. G.block(off,0,vG[id].rows(),vG[id].cols()) = vG[id];
  479. J.block(off,0,vJ[id].rows(),vJ[id].cols()) = vJ[id];
  480. off += vG[id].rows();
  481. }
  482. }
  483. }
  484. }
  485. template <
  486. typename DerivedV,
  487. typename DerivedF,
  488. typename DerivedG,
  489. typename DerivedJ,
  490. typename Derivedflip>
  491. IGL_INLINE void igl::outer_hull(
  492. const Eigen::PlainObjectBase<DerivedV> & V,
  493. const Eigen::PlainObjectBase<DerivedF> & F,
  494. Eigen::PlainObjectBase<DerivedG> & G,
  495. Eigen::PlainObjectBase<DerivedJ> & J,
  496. Eigen::PlainObjectBase<Derivedflip> & flip)
  497. {
  498. Eigen::Matrix<typename DerivedV::Scalar,DerivedF::RowsAtCompileTime,3> N;
  499. per_face_normals_stable(V,F,N);
  500. return outer_hull(V,F,N,G,J,flip);
  501. }
  502. #ifdef IGL_STATIC_LIBRARY
  503. // Explicit template specialization
  504. template void igl::outer_hull<Eigen::Matrix<double, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, 3, 0, -1, 3>, Eigen::Matrix<long, -1, 1, 0, -1, 1>, Eigen::Matrix<bool, -1, 1, 0, -1, 1> >(Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<long, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<bool, -1, 1, 0, -1, 1> >&);
  505. template void igl::outer_hull<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&);
  506. template void igl::outer_hull<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<bool, -1, 1, 0, -1, 1> >(Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<bool, -1, 1, 0, -1, 1> >&);
  507. #endif