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