closest_facet.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. // This file is part of libigl, a simple c++ geometry processing library.
  2. //
  3. // Copyright (C) 2015 Qingnan Zhou <[email protected]>
  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. //
  9. #include "closest_facet.h"
  10. #include <vector>
  11. #include <stdexcept>
  12. #include <unordered_map>
  13. #include "order_facets_around_edge.h"
  14. #include "submesh_aabb_tree.h"
  15. #include "../../vertex_triangle_adjacency.h"
  16. #include "../../PlainMatrix.h"
  17. #include "../../LinSpaced.h"
  18. //#include "../../writePLY.h"
  19. template<
  20. typename DerivedV,
  21. typename DerivedF,
  22. typename DerivedI,
  23. typename DerivedP,
  24. typename DerivedEMAP,
  25. typename DeriveduEC,
  26. typename DeriveduEE,
  27. typename Kernel,
  28. typename DerivedR,
  29. typename DerivedS >
  30. IGL_INLINE void igl::copyleft::cgal::closest_facet(
  31. const Eigen::MatrixBase<DerivedV>& V,
  32. const Eigen::MatrixBase<DerivedF>& F,
  33. const Eigen::MatrixBase<DerivedI>& I,
  34. const Eigen::MatrixBase<DerivedP>& P,
  35. const Eigen::MatrixBase<DerivedEMAP>& EMAP,
  36. const Eigen::MatrixBase<DeriveduEC>& uEC,
  37. const Eigen::MatrixBase<DeriveduEE>& uEE,
  38. const std::vector<std::vector<size_t> > & VF,
  39. const std::vector<std::vector<size_t> > & VFi,
  40. const CGAL::AABB_tree<
  41. CGAL::AABB_traits<
  42. Kernel,
  43. CGAL::AABB_triangle_primitive<
  44. Kernel, typename std::vector<
  45. typename Kernel::Triangle_3 >::iterator > > > & tree,
  46. const std::vector<typename Kernel::Triangle_3 > & triangles,
  47. const std::vector<bool> & in_I,
  48. Eigen::PlainObjectBase<DerivedR>& R,
  49. Eigen::PlainObjectBase<DerivedS>& S)
  50. {
  51. typedef typename Kernel::Point_3 Point_3;
  52. typedef typename Kernel::Plane_3 Plane_3;
  53. typedef typename Kernel::Segment_3 Segment_3;
  54. typedef typename Kernel::Triangle_3 Triangle;
  55. typedef typename std::vector<Triangle>::iterator Iterator;
  56. typedef typename CGAL::AABB_triangle_primitive<Kernel, Iterator> Primitive;
  57. typedef typename CGAL::AABB_traits<Kernel, Primitive> AABB_triangle_traits;
  58. typedef typename CGAL::AABB_tree<AABB_triangle_traits> Tree;
  59. if (F.rows() <= 0 || I.rows() <= 0) {
  60. throw std::runtime_error(
  61. "Closest facet cannot be computed on empty mesh.");
  62. }
  63. auto on_the_positive_side = [&](size_t fid, const Point_3& p) -> bool
  64. {
  65. const auto& f = F.row(fid).eval();
  66. Point_3 v0(V(f[0], 0), V(f[0], 1), V(f[0], 2));
  67. Point_3 v1(V(f[1], 0), V(f[1], 1), V(f[1], 2));
  68. Point_3 v2(V(f[2], 0), V(f[2], 1), V(f[2], 2));
  69. auto ori = CGAL::orientation(v0, v1, v2, p);
  70. switch (ori) {
  71. case CGAL::POSITIVE:
  72. return true;
  73. case CGAL::NEGATIVE:
  74. return false;
  75. case CGAL::COPLANAR:
  76. // Warning:
  77. // This can only happen if fid contains a boundary edge.
  78. // Categorized this ambiguous case as negative side.
  79. return false;
  80. default:
  81. throw std::runtime_error("Unknown CGAL state.");
  82. }
  83. return false;
  84. };
  85. auto get_orientation = [&](size_t fid, size_t s, size_t d) -> bool
  86. {
  87. const auto& f = F.row(fid);
  88. if ((size_t)f[0] == s && (size_t)f[1] == d) return false;
  89. else if ((size_t)f[1] == s && (size_t)f[2] == d) return false;
  90. else if ((size_t)f[2] == s && (size_t)f[0] == d) return false;
  91. else if ((size_t)f[0] == d && (size_t)f[1] == s) return true;
  92. else if ((size_t)f[1] == d && (size_t)f[2] == s) return true;
  93. else if ((size_t)f[2] == d && (size_t)f[0] == s) return true;
  94. else {
  95. throw std::runtime_error(
  96. "Cannot compute orientation due to incorrect connectivity");
  97. return false;
  98. }
  99. };
  100. auto index_to_signed_index = [&](size_t index, bool ori) -> int{
  101. return (index+1) * (ori? 1:-1);
  102. };
  103. //auto signed_index_to_index = [&](int signed_index) -> size_t {
  104. // return abs(signed_index) - 1;
  105. //};
  106. enum ElementType { VERTEX, EDGE, FACE };
  107. auto determine_element_type = [&](const Point_3& p, const size_t fid,
  108. size_t& element_index) -> ElementType {
  109. const auto& tri = triangles[fid];
  110. const Point_3 p0 = tri[0];
  111. const Point_3 p1 = tri[1];
  112. const Point_3 p2 = tri[2];
  113. if (p == p0) { element_index = 0; return VERTEX; }
  114. if (p == p1) { element_index = 1; return VERTEX; }
  115. if (p == p2) { element_index = 2; return VERTEX; }
  116. if (CGAL::collinear(p0, p1, p)) { element_index = 2; return EDGE; }
  117. if (CGAL::collinear(p1, p2, p)) { element_index = 0; return EDGE; }
  118. if (CGAL::collinear(p2, p0, p)) { element_index = 1; return EDGE; }
  119. element_index = 0;
  120. return FACE;
  121. };
  122. auto process_edge_case = [&](
  123. size_t query_idx,
  124. const size_t s, const size_t d,
  125. size_t preferred_facet,
  126. bool& orientation) -> size_t
  127. {
  128. Point_3 query_point(
  129. P(query_idx, 0),
  130. P(query_idx, 1),
  131. P(query_idx, 2));
  132. size_t corner_idx = std::numeric_limits<size_t>::max();
  133. if ((s == F(preferred_facet, 0) && d == F(preferred_facet, 1)) ||
  134. (s == F(preferred_facet, 1) && d == F(preferred_facet, 0)))
  135. {
  136. corner_idx = 2;
  137. } else if ((s == F(preferred_facet, 0) && d == F(preferred_facet, 2)) ||
  138. (s == F(preferred_facet, 2) && d == F(preferred_facet, 0)))
  139. {
  140. corner_idx = 1;
  141. } else if ((s == F(preferred_facet, 1) && d == F(preferred_facet, 2)) ||
  142. (s == F(preferred_facet, 2) && d == F(preferred_facet, 1)))
  143. {
  144. corner_idx = 0;
  145. } else
  146. {
  147. // Should never happen.
  148. //std::cerr << "s: " << s << "\t d:" << d << std::endl;
  149. //std::cerr << F.row(preferred_facet) << std::endl;
  150. throw std::runtime_error(
  151. "Invalid connectivity, edge does not belong to facet");
  152. }
  153. auto ueid = EMAP(preferred_facet + corner_idx * F.rows());
  154. std::vector<size_t> intersected_face_indices;
  155. //auto eids = uE2E[ueid];
  156. //for (auto eid : eids)
  157. for(size_t j = uEC(ueid);j<uEC(ueid+1);j++)
  158. {
  159. const size_t eid = uEE(j);
  160. const size_t fid = eid % F.rows();
  161. if (in_I[fid])
  162. {
  163. intersected_face_indices.push_back(fid);
  164. }
  165. }
  166. const size_t num_intersected_faces = intersected_face_indices.size();
  167. std::vector<int> intersected_face_signed_indices(num_intersected_faces);
  168. std::transform(
  169. intersected_face_indices.begin(),
  170. intersected_face_indices.end(),
  171. intersected_face_signed_indices.begin(),
  172. [&](size_t index) {
  173. return index_to_signed_index(
  174. index, get_orientation(index, s,d));
  175. });
  176. assert(num_intersected_faces >= 1);
  177. if (num_intersected_faces == 1)
  178. {
  179. // The edge must be a boundary edge. Thus, the orientation can be
  180. // simply determined by checking if the query point is on the
  181. // positive side of the facet.
  182. const size_t fid = intersected_face_indices[0];
  183. orientation = on_the_positive_side(fid, query_point);
  184. return fid;
  185. }
  186. Eigen::VectorXi order;
  187. PlainMatrix<DerivedP,1> pivot = P.row(query_idx).eval();
  188. igl::copyleft::cgal::order_facets_around_edge(V, F, s, d,
  189. intersected_face_signed_indices,
  190. pivot, order);
  191. // Although first and last are equivalent, make the choice based on
  192. // preferred_facet.
  193. const size_t first = order[0];
  194. const size_t last = order[num_intersected_faces-1];
  195. if (intersected_face_indices[first] == preferred_facet) {
  196. orientation = intersected_face_signed_indices[first] < 0;
  197. return intersected_face_indices[first];
  198. } else if (intersected_face_indices[last] == preferred_facet) {
  199. orientation = intersected_face_signed_indices[last] > 0;
  200. return intersected_face_indices[last];
  201. } else {
  202. orientation = intersected_face_signed_indices[order[0]] < 0;
  203. return intersected_face_indices[order[0]];
  204. }
  205. };
  206. auto process_face_case = [&F,&I,&process_edge_case](
  207. const size_t query_idx,
  208. const size_t fid,
  209. bool& orientation) -> size_t
  210. {
  211. const auto& f = F.row(I(fid, 0));
  212. return process_edge_case(query_idx, f[0], f[1], I(fid, 0), orientation);
  213. };
  214. // Given that the closest point to query point P(query_idx,:) on (V,F(I,:))
  215. // is the vertex at V(s,:) which is incident at least on triangle
  216. // F(preferred_facet,:), determine a facet incident on V(s,:) that is
  217. // _exposed_ to the query point and determine whether that facet is facing
  218. // _toward_ or _away_ from the query point.
  219. //
  220. // Inputs:
  221. // query_idx index into P of query point
  222. // s index into V of closest point at vertex
  223. // preferred_facet facet incident on s
  224. // Outputs:
  225. // orientation whether returned face is facing toward or away from
  226. // query (parity unclear)
  227. // Returns face guaranteed to be "exposed" to P(query_idx,:)
  228. auto process_vertex_case = [&](
  229. const size_t query_idx,
  230. size_t s,
  231. bool& orientation) -> size_t
  232. {
  233. const Point_3 query_point(
  234. P(query_idx, 0), P(query_idx, 1), P(query_idx, 2));
  235. const Point_3 closest_point(V(s, 0), V(s, 1), V(s, 2));
  236. std::vector<size_t> adj_faces;
  237. std::vector<size_t> adj_face_corners;
  238. {
  239. // Gather adj faces to s within I.
  240. const auto& all_adj_faces = VF[s];
  241. const auto& all_adj_face_corners = VFi[s];
  242. const size_t num_all_adj_faces = all_adj_faces.size();
  243. for (size_t i=0; i<num_all_adj_faces; i++)
  244. {
  245. const size_t fid = all_adj_faces[i];
  246. // Shouldn't this always be true if I is a full connected component?
  247. if (in_I[fid])
  248. {
  249. adj_faces.push_back(fid);
  250. adj_face_corners.push_back(all_adj_face_corners[i]);
  251. }
  252. }
  253. }
  254. const size_t num_adj_faces = adj_faces.size();
  255. assert(num_adj_faces > 0);
  256. std::set<size_t> adj_vertices_set;
  257. std::unordered_multimap<size_t, size_t> v2f;
  258. for (size_t i=0; i<num_adj_faces; i++)
  259. {
  260. const size_t fid = adj_faces[i];
  261. const size_t cid = adj_face_corners[i];
  262. const auto& f = F.row(adj_faces[i]);
  263. const size_t next = f[(cid+1)%3];
  264. const size_t prev = f[(cid+2)%3];
  265. adj_vertices_set.insert(next);
  266. adj_vertices_set.insert(prev);
  267. v2f.insert({{next, fid}, {prev, fid}});
  268. }
  269. const size_t num_adj_vertices = adj_vertices_set.size();
  270. std::vector<size_t> adj_vertices(num_adj_vertices);
  271. std::copy(adj_vertices_set.begin(), adj_vertices_set.end(),
  272. adj_vertices.begin());
  273. std::vector<Point_3> adj_points;
  274. for (size_t vid : adj_vertices)
  275. {
  276. adj_points.emplace_back(V(vid,0), V(vid,1), V(vid,2));
  277. }
  278. // A plane is on the exterior if all adj_points lies on or to
  279. // one side of the plane.
  280. auto is_on_exterior = [&](const Plane_3& separator) -> bool{
  281. size_t positive=0;
  282. size_t negative=0;
  283. for (const auto& point : adj_points) {
  284. switch(separator.oriented_side(point)) {
  285. case CGAL::ON_POSITIVE_SIDE:
  286. positive++;
  287. break;
  288. case CGAL::ON_NEGATIVE_SIDE:
  289. negative++;
  290. break;
  291. case CGAL::ON_ORIENTED_BOUNDARY:
  292. break;
  293. default:
  294. throw "Unknown plane-point orientation";
  295. }
  296. }
  297. auto query_orientation = separator.oriented_side(query_point);
  298. if (query_orientation == CGAL::ON_ORIENTED_BOUNDARY &&
  299. (positive == 0 && negative == 0)) {
  300. // All adj vertices and query point are coplanar.
  301. // In this case, all separators are equally valid.
  302. return true;
  303. } else {
  304. bool r = (positive == 0 && query_orientation == CGAL::POSITIVE)
  305. || (negative == 0 && query_orientation == CGAL::NEGATIVE);
  306. return r;
  307. }
  308. };
  309. size_t d = std::numeric_limits<size_t>::max();
  310. for (size_t i=0; i<num_adj_vertices; i++) {
  311. const size_t vi = adj_vertices[i];
  312. for (size_t j=i+1; j<num_adj_vertices; j++) {
  313. Plane_3 separator(closest_point, adj_points[i], adj_points[j]);
  314. if (separator.is_degenerate()) {
  315. continue;
  316. }
  317. if (is_on_exterior(separator)) {
  318. if (!CGAL::collinear(
  319. query_point, adj_points[i], closest_point)) {
  320. d = vi;
  321. break;
  322. } else {
  323. d = adj_vertices[j];
  324. assert(!CGAL::collinear(
  325. query_point, adj_points[j], closest_point));
  326. break;
  327. }
  328. }
  329. }
  330. }
  331. if (d == std::numeric_limits<size_t>::max()) {
  332. //PlainMatrix<DerivedV,Eigen::Dynamic> tmp_vertices(V.rows(), V.cols());
  333. //for (size_t i=0; i<V.rows(); i++) {
  334. // for (size_t j=0; j<V.cols(); j++) {
  335. // tmp_vertices(i,j) = CGAL::to_double(V(i,j));
  336. // }
  337. //}
  338. //PlainMatrix<DerivedF,Eigen::Dynamic,3> tmp_faces(adj_faces.size(), 3);
  339. //for (size_t i=0; i<adj_faces.size(); i++) {
  340. // tmp_faces.row(i) = F.row(adj_faces[i]);
  341. //}
  342. //igl::writePLY("debug.ply", tmp_vertices, tmp_faces, false);
  343. throw std::runtime_error("Invalid vertex neighborhood");
  344. }
  345. const auto itr = v2f.equal_range(d);
  346. assert(itr.first != itr.second);
  347. return process_edge_case(query_idx, s, d, itr.first->second, orientation);
  348. };
  349. const size_t num_queries = P.rows();
  350. R.resize(num_queries, 1);
  351. S.resize(num_queries, 1);
  352. for (size_t i=0; i<num_queries; i++) {
  353. const Point_3 query(P(i,0), P(i,1), P(i,2));
  354. auto projection = tree.closest_point_and_primitive(query);
  355. const Point_3 closest_point = projection.first;
  356. size_t fid = projection.second - triangles.begin();
  357. bool fid_ori = false;
  358. // Gether all facets sharing the closest point.
  359. typename std::vector<typename Tree::Primitive_id> intersected_faces;
  360. tree.all_intersected_primitives(Segment_3(closest_point, query),
  361. std::back_inserter(intersected_faces));
  362. const size_t num_intersected_faces = intersected_faces.size();
  363. std::vector<size_t> intersected_face_indices(num_intersected_faces);
  364. std::transform(intersected_faces.begin(),
  365. intersected_faces.end(),
  366. intersected_face_indices.begin(),
  367. [&](const typename Tree::Primitive_id& itr) -> size_t
  368. { return I(itr-triangles.begin(), 0); });
  369. size_t element_index;
  370. auto element_type = determine_element_type(closest_point, fid,
  371. element_index);
  372. switch(element_type) {
  373. case VERTEX:
  374. {
  375. const auto& f = F.row(I(fid, 0));
  376. const size_t s = f[element_index];
  377. fid = process_vertex_case(i, s, fid_ori);
  378. }
  379. break;
  380. case EDGE:
  381. {
  382. const auto& f = F.row(I(fid, 0));
  383. const size_t s = f[(element_index+1)%3];
  384. const size_t d = f[(element_index+2)%3];
  385. fid = process_edge_case(i, s, d, I(fid, 0), fid_ori);
  386. }
  387. break;
  388. case FACE:
  389. {
  390. fid = process_face_case(i, fid, fid_ori);
  391. }
  392. break;
  393. default:
  394. throw std::runtime_error("Unknown element type.");
  395. }
  396. R(i,0) = fid;
  397. S(i,0) = fid_ori;
  398. }
  399. }
  400. template<
  401. typename DerivedV,
  402. typename DerivedF,
  403. typename DerivedI,
  404. typename DerivedP,
  405. typename DerivedEMAP,
  406. typename DeriveduEC,
  407. typename DeriveduEE,
  408. typename DerivedR,
  409. typename DerivedS >
  410. IGL_INLINE void igl::copyleft::cgal::closest_facet(
  411. const Eigen::MatrixBase<DerivedV>& V,
  412. const Eigen::MatrixBase<DerivedF>& F,
  413. const Eigen::MatrixBase<DerivedI>& I,
  414. const Eigen::MatrixBase<DerivedP>& P,
  415. const Eigen::MatrixBase<DerivedEMAP>& EMAP,
  416. const Eigen::MatrixBase<DeriveduEC>& uEC,
  417. const Eigen::MatrixBase<DeriveduEE>& uEE,
  418. Eigen::PlainObjectBase<DerivedR>& R,
  419. Eigen::PlainObjectBase<DerivedS>& S)
  420. {
  421. typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;
  422. typedef Kernel::Triangle_3 Triangle;
  423. typedef std::vector<Triangle>::iterator Iterator;
  424. typedef CGAL::AABB_triangle_primitive<Kernel, Iterator> Primitive;
  425. typedef CGAL::AABB_traits<Kernel, Primitive> AABB_triangle_traits;
  426. typedef CGAL::AABB_tree<AABB_triangle_traits> Tree;
  427. if (F.rows() <= 0 || I.rows() <= 0) {
  428. throw std::runtime_error(
  429. "Closest facet cannot be computed on empty mesh.");
  430. }
  431. std::vector<std::vector<size_t> > VF, VFi;
  432. igl::vertex_triangle_adjacency(V.rows(), F, VF, VFi);
  433. std::vector<bool> in_I;
  434. std::vector<Triangle> triangles;
  435. Tree tree;
  436. submesh_aabb_tree(V,F,I,tree,triangles,in_I);
  437. return closest_facet(
  438. V,F,I,P,EMAP,uEC,uEE,VF,VFi,tree,triangles,in_I,R,S);
  439. }
  440. template<
  441. typename DerivedV,
  442. typename DerivedF,
  443. typename DerivedP,
  444. typename DerivedEMAP,
  445. typename DeriveduEC,
  446. typename DeriveduEE,
  447. typename DerivedR,
  448. typename DerivedS >
  449. IGL_INLINE void igl::copyleft::cgal::closest_facet(
  450. const Eigen::MatrixBase<DerivedV>& V,
  451. const Eigen::MatrixBase<DerivedF>& F,
  452. const Eigen::MatrixBase<DerivedP>& P,
  453. const Eigen::MatrixBase<DerivedEMAP>& EMAP,
  454. const Eigen::MatrixBase<DeriveduEC>& uEC,
  455. const Eigen::MatrixBase<DeriveduEE>& uEE,
  456. Eigen::PlainObjectBase<DerivedR>& R,
  457. Eigen::PlainObjectBase<DerivedS>& S) {
  458. const size_t num_faces = F.rows();
  459. Eigen::VectorXi I = igl::LinSpaced<Eigen::VectorXi>(num_faces, 0, num_faces-1);
  460. igl::copyleft::cgal::closest_facet(V, F, I, P, EMAP, uEC, uEE, R, S);
  461. }
  462. #ifdef IGL_STATIC_LIBRARY
  463. // Explicit template instantiation
  464. // generated by autoexplicit.sh
  465. template void igl::copyleft::cgal::closest_facet<Eigen::Matrix<CGAL::Epeck::FT, -1, -1, 1, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<CGAL::Epeck::FT, -1, 3, 1, -1, 3>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, CGAL::Epeck, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>>(Eigen::MatrixBase<Eigen::Matrix<CGAL::Epeck::FT, -1, -1, 1, -1, -1>> const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>> const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1>> const&, Eigen::MatrixBase<Eigen::Matrix<CGAL::Epeck::FT, -1, 3, 1, -1, 3>> const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1>> const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1>> const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1>> const&, std::vector<std::vector<size_t, std::allocator<size_t>>, std::allocator<std::vector<size_t, std::allocator<size_t>>>> const&, std::vector<std::vector<size_t, std::allocator<size_t>>, std::allocator<std::vector<size_t, std::allocator<size_t>>>> const&, CGAL::AABB_tree<CGAL::AABB_traits<CGAL::Epeck, CGAL::AABB_triangle_primitive<CGAL::Epeck, std::vector<CGAL::Epeck::Triangle_3, std::allocator<CGAL::Epeck::Triangle_3>>::iterator, CGAL::Boolean_tag<false>>, CGAL::Default>> const&, std::vector<CGAL::Epeck::Triangle_3, std::allocator<CGAL::Epeck::Triangle_3>> const&, std::vector<bool, std::allocator<bool>> const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1>>&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1>>&);
  466. // generated by autoexplicit.sh
  467. template void igl::copyleft::cgal::closest_facet<Eigen::Matrix<CGAL::Epeck::FT, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<CGAL::Epeck::FT, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, CGAL::Epeck, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>>(Eigen::MatrixBase<Eigen::Matrix<CGAL::Epeck::FT, -1, -1, 0, -1, -1>> const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>> const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1>> const&, Eigen::MatrixBase<Eigen::Matrix<CGAL::Epeck::FT, -1, 3, 0, -1, 3>> const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1>> const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1>> const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1>> const&, std::vector<std::vector<size_t, std::allocator<size_t>>, std::allocator<std::vector<size_t, std::allocator<size_t>>>> const&, std::vector<std::vector<size_t, std::allocator<size_t>>, std::allocator<std::vector<size_t, std::allocator<size_t>>>> const&, CGAL::AABB_tree<CGAL::AABB_traits<CGAL::Epeck, CGAL::AABB_triangle_primitive<CGAL::Epeck, std::vector<CGAL::Epeck::Triangle_3, std::allocator<CGAL::Epeck::Triangle_3>>::iterator, CGAL::Boolean_tag<false>>, CGAL::Default>> const&, std::vector<CGAL::Epeck::Triangle_3, std::allocator<CGAL::Epeck::Triangle_3>> const&, std::vector<bool, std::allocator<bool>> const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1>>&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1>>&);
  468. // generated by autoexplicit.sh
  469. template void igl::copyleft::cgal::closest_facet<Eigen::Matrix<CGAL::Epeck::FT, -1, -1, 1, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<CGAL::Epeck::FT, -1, -1, 1, -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>, CGAL::Epeck, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(
  470. Eigen::MatrixBase<Eigen::Matrix<CGAL::Epeck::FT, -1, -1, 1, -1, -1> > const&,
  471. Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&,
  472. Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&,
  473. Eigen::MatrixBase<Eigen::Matrix<CGAL::Epeck::FT, -1, -1, 1, -1, -1> > const&,
  474. Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&,
  475. Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&,
  476. Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, std::vector<std::vector<size_t, std::allocator<size_t> >, std::allocator<std::vector<size_t, std::allocator<size_t> > > > const&, std::vector<std::vector<size_t, std::allocator<size_t> >, std::allocator<std::vector<size_t, std::allocator<size_t> > > > const&, CGAL::AABB_tree<CGAL::AABB_traits<CGAL::Epeck, CGAL::AABB_triangle_primitive<CGAL::Epeck, std::vector<CGAL::Epeck::Triangle_3, std::allocator<CGAL::Epeck::Triangle_3> >::iterator, CGAL::Boolean_tag<false> >, CGAL::Default> > const&, std::vector<CGAL::Epeck::Triangle_3, std::allocator<CGAL::Epeck::Triangle_3> > const&, std::vector<bool, std::allocator<bool> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&);
  477. #include <cstdint>
  478. template void igl::copyleft::cgal::closest_facet<Eigen::Matrix<CGAL::Epeck::FT, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<CGAL::Epeck::FT, -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>, CGAL::Epeck, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(
  479. Eigen::MatrixBase<Eigen::Matrix<CGAL::Epeck::FT, -1, -1, 0, -1, -1> > const&,
  480. Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&,
  481. Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&,
  482. Eigen::MatrixBase<Eigen::Matrix<CGAL::Epeck::FT, -1, -1, 0, -1, -1> > const&,
  483. Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&,
  484. Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&,
  485. Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, std::vector<std::vector<size_t, std::allocator<size_t> >, std::allocator<std::vector<size_t, std::allocator<size_t> > > > const&, std::vector<std::vector<size_t, std::allocator<size_t> >, std::allocator<std::vector<size_t, std::allocator<size_t> > > > const&, CGAL::AABB_tree<CGAL::AABB_traits<CGAL::Epeck, CGAL::AABB_triangle_primitive<CGAL::Epeck, std::vector<CGAL::Epeck::Triangle_3, std::allocator<CGAL::Epeck::Triangle_3> >::iterator, CGAL::Boolean_tag<false> >, CGAL::Default> > const&, std::vector<CGAL::Epeck::Triangle_3, std::allocator<CGAL::Epeck::Triangle_3> > const&, std::vector<bool, std::allocator<bool> > const&,
  486. Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&,
  487. Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&);
  488. #endif