sort_triangles.cpp 17 KB

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