sort_triangles.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. // This file is part of libigl, a simple c++ geometry processing library.
  2. //
  3. // Copyright (C) 2013 Alec Jacobson <alecjacobson@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 "sort_triangles.h"
  9. #include "barycenter.h"
  10. #include "sort.h"
  11. #include "sortrows.h"
  12. #include "slice.h"
  13. #include "round.h"
  14. #include "colon.h"
  15. #include "matlab_format.h"
  16. #include <iostream>
  17. template <
  18. typename DerivedV,
  19. typename DerivedF,
  20. typename DerivedMV,
  21. typename DerivedP,
  22. typename DerivedFF,
  23. typename DerivedI>
  24. IGL_INLINE void igl::sort_triangles(
  25. const Eigen::PlainObjectBase<DerivedV> & V,
  26. const Eigen::PlainObjectBase<DerivedF> & F,
  27. const Eigen::PlainObjectBase<DerivedMV> & MV,
  28. const Eigen::PlainObjectBase<DerivedP> & P,
  29. Eigen::PlainObjectBase<DerivedFF> & FF,
  30. Eigen::PlainObjectBase<DerivedI> & I)
  31. {
  32. using namespace Eigen;
  33. using namespace igl;
  34. using namespace std;
  35. // Barycenter, centroid
  36. Eigen::Matrix<typename DerivedV::Scalar, DerivedF::RowsAtCompileTime,1> D,sD;
  37. Eigen::Matrix<typename DerivedV::Scalar, DerivedF::RowsAtCompileTime,4> BC,PBC;
  38. barycenter(V,F,BC);
  39. D = BC*(MV.transpose()*P.transpose().eval().col(2));
  40. sort(D,1,false,sD,I);
  41. //// Closest corner
  42. //Eigen::Matrix<typename DerivedV::Scalar, DerivedF::RowsAtCompileTime,1> D,sD;
  43. //D.setConstant(F.rows(),1,-1e26);
  44. //for(int c = 0;c<3;c++)
  45. //{
  46. // Eigen::Matrix<typename DerivedV::Scalar, DerivedF::RowsAtCompileTime,4> C;
  47. // Eigen::Matrix<typename DerivedV::Scalar, DerivedF::RowsAtCompileTime,1> DC;
  48. // C.resize(F.rows(),4);
  49. // for(int f = 0;f<F.rows();f++)
  50. // {
  51. // C(f,0) = V(F(f,c),0);
  52. // C(f,1) = V(F(f,c),1);
  53. // C(f,2) = V(F(f,c),2);
  54. // C(f,3) = 1;
  55. // }
  56. // DC = C*(MV.transpose()*P.transpose().eval().col(2));
  57. // D = (DC.array()>D.array()).select(DC,D).eval();
  58. //}
  59. //sort(D,1,false,sD,I);
  60. //// Closest corner with tie breaks
  61. //Eigen::Matrix<typename DerivedV::Scalar, DerivedF::RowsAtCompileTime,3> D,sD,ssD;
  62. //D.resize(F.rows(),3);
  63. //for(int c = 0;c<3;c++)
  64. //{
  65. // Eigen::Matrix<typename DerivedV::Scalar, DerivedF::RowsAtCompileTime,4> C;
  66. // C.resize(F.rows(),4);
  67. // for(int f = 0;f<F.rows();f++)
  68. // {
  69. // C(f,0) = V(F(f,c),0);
  70. // C(f,1) = V(F(f,c),1);
  71. // C(f,2) = V(F(f,c),2);
  72. // C(f,3) = 1;
  73. // }
  74. // D.col(c) = C*(MV.transpose()*P.transpose().eval().col(2));
  75. //}
  76. //VectorXi _;
  77. //sort(D,2,false,sD,_);
  78. //sortrows(sD,false,ssD,I);
  79. slice(F,I,1,FF);
  80. }
  81. #ifndef IGL_NO_OPENGL
  82. #include "OpenGL_convenience.h"
  83. template <
  84. typename DerivedV,
  85. typename DerivedF,
  86. typename DerivedFF,
  87. typename DerivedI>
  88. void igl::sort_triangles(
  89. const Eigen::PlainObjectBase<DerivedV> & V,
  90. const Eigen::PlainObjectBase<DerivedF> & F,
  91. Eigen::PlainObjectBase<DerivedFF> & FF,
  92. Eigen::PlainObjectBase<DerivedI> & I)
  93. {
  94. using namespace Eigen;
  95. using namespace igl;
  96. using namespace std;
  97. // Put model, projection, and viewport matrices into double arrays
  98. Matrix4d MV;
  99. Matrix4d P;
  100. glGetDoublev(GL_MODELVIEW_MATRIX, MV.data());
  101. glGetDoublev(GL_PROJECTION_MATRIX, P.data());
  102. if(V.cols() == 3)
  103. {
  104. Matrix<typename DerivedV::Scalar, DerivedV::RowsAtCompileTime,4> hV;
  105. hV.resize(V.rows(),4);
  106. hV.block(0,0,V.rows(),V.cols()) = V;
  107. hV.col(3).setConstant(1);
  108. return sort_triangles(hV,F,MV,P,FF,I);
  109. }else
  110. {
  111. return sort_triangles(V,F,MV,P,FF,I);
  112. }
  113. }
  114. #include "project.h"
  115. #include <iostream>
  116. template <
  117. typename DerivedV,
  118. typename DerivedF,
  119. typename DerivedFF,
  120. typename DerivedI>
  121. void igl::sort_triangles_slow(
  122. const Eigen::PlainObjectBase<DerivedV> & V,
  123. const Eigen::PlainObjectBase<DerivedF> & F,
  124. Eigen::PlainObjectBase<DerivedFF> & FF,
  125. Eigen::PlainObjectBase<DerivedI> & I)
  126. {
  127. using namespace Eigen;
  128. using namespace igl;
  129. using namespace std;
  130. // Barycenter, centroid
  131. Eigen::Matrix<typename DerivedV::Scalar, DerivedF::RowsAtCompileTime,1> D,sD;
  132. Eigen::Matrix<typename DerivedV::Scalar, DerivedF::RowsAtCompileTime,3> BC;
  133. D.resize(F.rows(),3);
  134. barycenter(V,F,BC);
  135. for(int f = 0;f<F.rows();f++)
  136. {
  137. Eigen::Matrix<typename DerivedV::Scalar, 3,1> bc,pbc;
  138. bc = BC.row(f);
  139. project(bc,pbc);
  140. D(f) = pbc(2);
  141. }
  142. sort(D,1,false,sD,I);
  143. slice(F,I,1,FF);
  144. }
  145. #include "EPS.h"
  146. #include <functional>
  147. #include <algorithm>
  148. static int tough_count = 0;
  149. template <typename Vec3>
  150. class Triangle
  151. {
  152. public:
  153. static inline bool z_comp(const Vec3 & A, const Vec3 & B)
  154. {
  155. return A(2) > B(2);
  156. }
  157. static typename Vec3::Scalar ZERO()
  158. {
  159. return igl::EPS<typename Vec3::Scalar>();
  160. return 0;
  161. }
  162. public:
  163. int id;
  164. // Sorted projected coners: c[0] has smallest z value
  165. Vec3 c[3];
  166. Vec3 n;
  167. public:
  168. Triangle():id(-1) { };
  169. Triangle(int id, const Vec3 c0, const Vec3 c1, const Vec3 c2):
  170. id(id)
  171. {
  172. using namespace std;
  173. c[0] = c0;
  174. c[1] = c1;
  175. c[2] = c2;
  176. sort(c,c+3,Triangle<Vec3>::z_comp);
  177. // normal pointed toward viewpoint
  178. n = (c0-c1).cross(c2-c0);
  179. if(n(2) < 0)
  180. {
  181. n *= -1.0;
  182. }
  183. // Avoid NaNs
  184. typename Vec3::Scalar len = n.norm();
  185. if(len == 0)
  186. {
  187. cout<<"avoid NaN"<<endl;
  188. assert(false);
  189. len = 1;
  190. }
  191. n /= len;
  192. };
  193. typename Vec3::Scalar project(const Vec3 & r) const
  194. {
  195. //return n.dot(r-c[2]);
  196. int closest = -1;
  197. typename Vec3::Scalar min_dist = 1e26;
  198. for(int ci = 0;ci<3;ci++)
  199. {
  200. typename Vec3::Scalar dist = (c[ci]-r).norm();
  201. if(dist < min_dist)
  202. {
  203. min_dist = dist;
  204. closest = ci;
  205. }
  206. }
  207. assert(closest>=0);
  208. return n.dot(r-c[closest]);
  209. }
  210. // Z-values of this are < z-values of that
  211. bool is_completely_behind(const Triangle & that) const
  212. {
  213. const typename Vec3::Scalar ac0 = that.c[0](2);
  214. const typename Vec3::Scalar ac1 = that.c[1](2);
  215. const typename Vec3::Scalar ac2 = that.c[2](2);
  216. const typename Vec3::Scalar ic0 = this->c[0](2);
  217. const typename Vec3::Scalar ic1 = this->c[1](2);
  218. const typename Vec3::Scalar ic2 = this->c[2](2);
  219. return
  220. (ic0 < ac2 && ic1 <= ac2 && ic2 <= ac2) ||
  221. (ic0 <= ac2 && ic1 < ac2 && ic2 <= ac2) ||
  222. (ic0 <= ac2 && ic1 <= ac2 && ic2 < ac2);
  223. }
  224. bool is_behind_plane(const Triangle &that) const
  225. {
  226. using namespace std;
  227. const typename Vec3::Scalar apc0 = that.project(this->c[0]);
  228. const typename Vec3::Scalar apc1 = that.project(this->c[1]);
  229. const typename Vec3::Scalar apc2 = that.project(this->c[2]);
  230. cout<<" "<<
  231. apc0<<", "<<
  232. apc1<<", "<<
  233. apc2<<", "<<endl;
  234. return (apc0 < ZERO() && apc1 < ZERO() && apc2 < ZERO());
  235. }
  236. bool is_in_front_of_plane(const Triangle &that) const
  237. {
  238. using namespace std;
  239. const typename Vec3::Scalar apc0 = that.project(this->c[0]);
  240. const typename Vec3::Scalar apc1 = that.project(this->c[1]);
  241. const typename Vec3::Scalar apc2 = that.project(this->c[2]);
  242. cout<<" "<<
  243. apc0<<", "<<
  244. apc1<<", "<<
  245. apc2<<", "<<endl;
  246. return (apc0 > ZERO() && apc1 > ZERO() && apc2 > ZERO());
  247. }
  248. bool is_coplanar(const Triangle &that) const
  249. {
  250. using namespace std;
  251. const typename Vec3::Scalar apc0 = that.project(this->c[0]);
  252. const typename Vec3::Scalar apc1 = that.project(this->c[1]);
  253. const typename Vec3::Scalar apc2 = that.project(this->c[2]);
  254. return (fabs(apc0)<=ZERO() && fabs(apc1)<=ZERO() && fabs(apc2)<=ZERO());
  255. }
  256. // http://stackoverflow.com/a/14561664/148668
  257. // a1 is line1 start, a2 is line1 end, b1 is line2 start, b2 is line2 end
  258. static bool seg_seg_intersect(const Vec3 & a1, const Vec3 & a2, const Vec3 & b1, const Vec3 & b2)
  259. {
  260. Vec3 b = a2-a1;
  261. Vec3 d = b2-b1;
  262. typename Vec3::Scalar bDotDPerp = b(0) * d(1) - b(1) * d(0);
  263. // if b dot d == 0, it means the lines are parallel so have infinite intersection points
  264. if (bDotDPerp == 0)
  265. return false;
  266. Vec3 c = b1-a1;
  267. typename Vec3::Scalar t = (c(0) * d(1) - c(1) * d(0)) / bDotDPerp;
  268. if (t < 0 || t > 1)
  269. return false;
  270. typename Vec3::Scalar u = (c(0) * b(1) - c(1) * b(0)) / bDotDPerp;
  271. if (u < 0 || u > 1)
  272. return false;
  273. return true;
  274. }
  275. bool has_corner_inside(const Triangle & that) const
  276. {
  277. // http://www.blackpawn.com/texts/pointinpoly/
  278. // Compute vectors
  279. Vec3 A = that.c[0];
  280. Vec3 B = that.c[1];
  281. Vec3 C = that.c[2];
  282. A(2) = B(2) = C(2) = 0;
  283. for(int ci = 0;ci<3;ci++)
  284. {
  285. Vec3 P = this->c[ci];
  286. P(2) = 0;
  287. Vec3 v0 = C - A;
  288. Vec3 v1 = B - A;
  289. Vec3 v2 = P - A;
  290. // Compute dot products
  291. typename Vec3::Scalar dot00 = v0.dot(v0);
  292. typename Vec3::Scalar dot01 = v0.dot(v1);
  293. typename Vec3::Scalar dot02 = v0.dot(v2);
  294. typename Vec3::Scalar dot11 = v1.dot(v1);
  295. typename Vec3::Scalar dot12 = v1.dot(v2);
  296. // Compute barycentric coordinates
  297. typename Vec3::Scalar invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
  298. typename Vec3::Scalar u = (dot11 * dot02 - dot01 * dot12) * invDenom;
  299. typename Vec3::Scalar v = (dot00 * dot12 - dot01 * dot02) * invDenom;
  300. // Check if point is in triangle
  301. if((u >= 0) && (v >= 0) && (u + v < 1))
  302. {
  303. return true;
  304. }
  305. }
  306. return false;
  307. }
  308. bool overlaps(const Triangle &that) const
  309. {
  310. // Edges cross
  311. for(int e = 0;e<3;e++)
  312. {
  313. for(int f = 0;f<3;f++)
  314. {
  315. if(seg_seg_intersect(
  316. this->c[e],this->c[(e+1)%3],
  317. that.c[e],that.c[(e+1)%3]))
  318. {
  319. return true;
  320. }
  321. }
  322. }
  323. // This could be entirely inside that
  324. if(this->has_corner_inside(that))
  325. {
  326. return true;
  327. }
  328. // vice versa
  329. if(that.has_corner_inside(*this))
  330. {
  331. return true;
  332. }
  333. return false;
  334. }
  335. bool operator< (const Triangle &that) const
  336. {
  337. // THIS < THAT if "depth" of THIS < "depth" of THAT
  338. // " if THIS should be draw before THAT
  339. using namespace std;
  340. bool ret = false;
  341. // Self compare
  342. if(that.id == this->id)
  343. {
  344. ret = false;
  345. }
  346. if(this->is_completely_behind(that))
  347. {
  348. cout<<" "<<this->id<<" completely behind "<<that.id<<endl;
  349. ret = false;
  350. }else if(that.is_completely_behind(*this))
  351. {
  352. cout<<" "<<that.id<<" completely behind "<<this->id<<endl;
  353. ret = true;
  354. }else
  355. {
  356. if(!this->overlaps(that))
  357. {
  358. assert(!that.overlaps(*this));
  359. cout<<" THIS does not overlap THAT"<<endl;
  360. // No overlap use barycenter
  361. return
  362. 1./3.*(this->c[0](2) + this->c[1](2) + this->c[2](2)) >
  363. 1./3.*(that.c[0](2) + that.c[1](2) + that.c[2](2));
  364. }else
  365. {
  366. if(this->is_coplanar(that) || that.is_coplanar(*this))
  367. {
  368. cout<<" coplanar"<<endl;
  369. // co-planar: decide based on barycenter depth
  370. ret =
  371. 1./3.*(this->c[0](2) + this->c[1](2) + this->c[2](2)) >
  372. 1./3.*(that.c[0](2) + that.c[1](2) + that.c[2](2));
  373. }else if(this->is_behind_plane(that))
  374. {
  375. cout<<" THIS behind plane of THAT"<<endl;
  376. ret = true;
  377. }else if(that.is_behind_plane(*this))
  378. {
  379. cout<<" THAT behind of plane of THIS"<<endl;
  380. ret = false;
  381. // THAT is in front of plane of THIS
  382. }else if(that.is_in_front_of_plane(*this))
  383. {
  384. cout<<" THAT in front of plane of THIS"<<endl;
  385. ret = true;
  386. // THIS is in front of plane of THAT
  387. }else if(this->is_in_front_of_plane(that))
  388. {
  389. cout<<" THIS in front plane of THAT"<<endl;
  390. ret = false;
  391. }else
  392. {
  393. cout<<" compare bary"<<endl;
  394. ret =
  395. 1./3.*(this->c[0](2) + this->c[1](2) + this->c[2](2)) >
  396. 1./3.*(that.c[0](2) + that.c[1](2) + that.c[2](2));
  397. }
  398. }
  399. }
  400. if(ret)
  401. {
  402. // THIS < THAT so better not be THAT < THIS
  403. cout<<this->id<<" < "<<that.id<<endl;
  404. assert(!(that < *this));
  405. }else
  406. {
  407. // THIS >= THAT so could be THAT < THIS or THAT == THIS
  408. }
  409. return ret;
  410. }
  411. };
  412. //#include <igl/matlab/MatlabWorkspace.h>
  413. //
  414. //template <
  415. // typename DerivedV,
  416. // typename DerivedF,
  417. // typename DerivedMV,
  418. // typename DerivedP,
  419. // typename DerivedFF,
  420. // typename DerivedI>
  421. //IGL_INLINE void igl::sort_triangles_robust(
  422. // const Eigen::PlainObjectBase<DerivedV> & V,
  423. // const Eigen::PlainObjectBase<DerivedF> & F,
  424. // const Eigen::PlainObjectBase<DerivedMV> & MV,
  425. // const Eigen::PlainObjectBase<DerivedP> & P,
  426. // Eigen::PlainObjectBase<DerivedFF> & FF,
  427. // Eigen::PlainObjectBase<DerivedI> & I)
  428. //{
  429. // assert(false &&
  430. // "THIS WILL NEVER WORK because depth sorting is not a numerical sort where"
  431. // "pairwise comparisons of triangles are transitive. Rather it is a"
  432. // "topological sort on a dependecy graph. Dependency encodes 'This triangle"
  433. // "must be drawn before that one'");
  434. // using namespace std;
  435. // using namespace Eigen;
  436. // using namespace igl;
  437. // typedef Matrix<typename DerivedV::Scalar,3,1> Vec3;
  438. // assert(V.cols() == 4);
  439. // Matrix<typename DerivedV::Scalar, DerivedV::RowsAtCompileTime,3> VMVP =
  440. // V*(MV.transpose()*P.transpose().eval().block(0,0,4,3));
  441. //
  442. // MatrixXd projV(V.rows(),3);
  443. // for(int v = 0;v<V.rows();v++)
  444. // {
  445. // Vector3d vv;
  446. // vv(0) = V(v,0);
  447. // vv(1) = V(v,1);
  448. // vv(2) = V(v,2);
  449. // Vector3d p;
  450. // project(vv,p);
  451. // projV.row(v) = p;
  452. // }
  453. //
  454. // vector<Triangle<Vec3> > vF(F.rows());
  455. // MatrixXd N(F.rows(),3);
  456. // MatrixXd C(F.rows()*3,3);
  457. // for(int f = 0;f<F.rows();f++)
  458. // {
  459. // vF[f] =
  460. // //Triangle<Vec3>(f,VMVP.row(F(f,0)),VMVP.row(F(f,1)),VMVP.row(F(f,2)));
  461. // Triangle<Vec3>(f,projV.row(F(f,0)),projV.row(F(f,1)),projV.row(F(f,2)));
  462. // N.row(f) = vF[f].n;
  463. // for(int c = 0;c<3;c++)
  464. // for(int d = 0;d<3;d++)
  465. // C(f*3+c,d) = vF[f].c[c](d);
  466. // }
  467. // MatlabWorkspace mw;
  468. // mw.save_index(F,"F");
  469. // mw.save(V,"V");
  470. // mw.save(MV,"MV");
  471. // mw.save(P,"P");
  472. // Vector4i VP;
  473. // glGetIntegerv(GL_VIEWPORT, VP.data());
  474. // mw.save(projV,"projV");
  475. // mw.save(VP,"VP");
  476. // mw.save(VMVP,"VMVP");
  477. // mw.save(N,"N");
  478. // mw.save(C,"C");
  479. // mw.write("ao.mat");
  480. // sort(vF.begin(),vF.end());
  481. //
  482. // // check
  483. // for(int f = 0;f<F.rows();f++)
  484. // {
  485. // for(int g = f+1;g<F.rows();g++)
  486. // {
  487. // assert(!(vF[g] < vF[f])); // should never happen
  488. // }
  489. // }
  490. // FF.resize(F.rows(),3);
  491. // I.resize(F.rows(),1);
  492. // for(int f = 0;f<F.rows();f++)
  493. // {
  494. // FF.row(f) = F.row(vF[f].id);
  495. // I(f) = vF[f].id;
  496. // }
  497. //
  498. // mw.save_index(FF,"FF");
  499. // mw.save_index(I,"I");
  500. // mw.write("ao.mat");
  501. //}
  502. //template <
  503. // typename DerivedV,
  504. // typename DerivedF,
  505. // typename DerivedFF,
  506. // typename DerivedI>
  507. //IGL_INLINE void igl::sort_triangles_robust(
  508. // const Eigen::PlainObjectBase<DerivedV> & V,
  509. // const Eigen::PlainObjectBase<DerivedF> & F,
  510. // Eigen::PlainObjectBase<DerivedFF> & FF,
  511. // Eigen::PlainObjectBase<DerivedI> & I)
  512. //{
  513. // using namespace Eigen;
  514. // using namespace igl;
  515. // using namespace std;
  516. // // Put model, projection, and viewport matrices into double arrays
  517. // Matrix4d MV;
  518. // Matrix4d P;
  519. // glGetDoublev(GL_MODELVIEW_MATRIX, MV.data());
  520. // glGetDoublev(GL_PROJECTION_MATRIX, P.data());
  521. // if(V.cols() == 3)
  522. // {
  523. // Matrix<typename DerivedV::Scalar, DerivedV::RowsAtCompileTime,4> hV;
  524. // hV.resize(V.rows(),4);
  525. // hV.block(0,0,V.rows(),V.cols()) = V;
  526. // hV.col(3).setConstant(1);
  527. // return sort_triangles_robust(hV,F,MV,P,FF,I);
  528. // }else
  529. // {
  530. // return sort_triangles_robust(V,F,MV,P,FF,I);
  531. // }
  532. //}
  533. #endif
  534. #ifdef IGL_STATIC_LIBRARY
  535. // Explicit template instanciation
  536. //template void igl::sort_triangles_robust<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::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> >&);
  537. #ifndef IGL_NO_OPENGL
  538. template void igl::sort_triangles<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::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> >&);
  539. template void igl::sort_triangles_slow<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::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> >&);
  540. #endif
  541. #endif