extract_cells.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  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 "extract_cells.h"
  10. #include "../../extract_manifold_patches.h"
  11. #include "../../facet_components.h"
  12. #include "../../triangle_triangle_adjacency.h"
  13. #include "../../unique_edge_map.h"
  14. #include "../../get_seconds.h"
  15. #include "closest_facet.h"
  16. #include "order_facets_around_edge.h"
  17. #include "outer_facet.h"
  18. #include <iostream>
  19. #include <vector>
  20. #include <queue>
  21. //#define EXTRACT_CELLS_DEBUG
  22. template<
  23. typename DerivedV,
  24. typename DerivedF,
  25. typename DerivedC >
  26. IGL_INLINE size_t igl::copyleft::cgal::extract_cells(
  27. const Eigen::PlainObjectBase<DerivedV>& V,
  28. const Eigen::PlainObjectBase<DerivedF>& F,
  29. Eigen::PlainObjectBase<DerivedC>& cells)
  30. {
  31. const size_t num_faces = F.rows();
  32. // Construct edge adjacency
  33. Eigen::MatrixXi E, uE;
  34. Eigen::VectorXi EMAP;
  35. std::vector<std::vector<size_t> > uE2E;
  36. igl::unique_edge_map(F, E, uE, EMAP, uE2E);
  37. // Cluster into manifold patches
  38. Eigen::VectorXi P;
  39. igl::extract_manifold_patches(F, EMAP, uE2E, P);
  40. // Extract cells
  41. DerivedC per_patch_cells;
  42. const size_t num_cells =
  43. igl::copyleft::cgal::extract_cells(V,F,P,E,uE,uE2E,EMAP,per_patch_cells);
  44. // Distribute per-patch cell information to each face
  45. cells.resize(num_faces, 2);
  46. for (size_t i=0; i<num_faces; i++)
  47. {
  48. cells.row(i) = per_patch_cells.row(P[i]);
  49. }
  50. return num_cells;
  51. }
  52. template<
  53. typename DerivedV,
  54. typename DerivedF,
  55. typename DerivedP,
  56. typename DerivedE,
  57. typename DeriveduE,
  58. typename uE2EType,
  59. typename DerivedEMAP,
  60. typename DerivedC >
  61. IGL_INLINE size_t igl::copyleft::cgal::extract_cells(
  62. const Eigen::PlainObjectBase<DerivedV>& V,
  63. const Eigen::PlainObjectBase<DerivedF>& F,
  64. const Eigen::PlainObjectBase<DerivedP>& P,
  65. const Eigen::PlainObjectBase<DerivedE>& E,
  66. const Eigen::PlainObjectBase<DeriveduE>& uE,
  67. const std::vector<std::vector<uE2EType> >& uE2E,
  68. const Eigen::PlainObjectBase<DerivedEMAP>& EMAP,
  69. Eigen::PlainObjectBase<DerivedC>& cells)
  70. {
  71. #ifdef EXTRACT_CELLS_DEBUG
  72. const auto & tictoc = []() -> double
  73. {
  74. static double t_start = igl::get_seconds();
  75. double diff = igl::get_seconds()-t_start;
  76. t_start += diff;
  77. return diff;
  78. };
  79. const auto log_time = [&](const std::string& label) -> void {
  80. std::cout << "extract_cells." << label << ": "
  81. << tictoc() << std::endl;
  82. };
  83. tictoc();
  84. #else
  85. // no-op
  86. const auto log_time = [](const std::string){};
  87. #endif
  88. const size_t num_faces = F.rows();
  89. typedef typename DerivedF::Scalar Index;
  90. const size_t num_patches = P.maxCoeff()+1;
  91. // Extract all cells...
  92. DerivedC raw_cells;
  93. const size_t num_raw_cells =
  94. extract_cells_single_component(V,F,P,uE,uE2E,EMAP,raw_cells);
  95. log_time("extract_single_component_cells");
  96. // Compute triangle-triangle adjacency data-structure
  97. std::vector<std::vector<std::vector<Index > > > TT,_1;
  98. igl::triangle_triangle_adjacency(E, EMAP, uE2E, false, TT, _1);
  99. log_time("compute_face_adjacency");
  100. // Compute connected components of the mesh
  101. Eigen::VectorXi C, counts;
  102. igl::facet_components(TT, C, counts);
  103. log_time("form_components");
  104. const size_t num_components = counts.size();
  105. // components[c] --> list of face indices into F of faces in component c
  106. std::vector<std::vector<size_t> > components(num_components);
  107. // Loop over all faces
  108. for (size_t i=0; i<num_faces; i++)
  109. {
  110. components[C[i]].push_back(i);
  111. }
  112. // Convert vector lists to Eigen lists...
  113. std::vector<Eigen::VectorXi> Is(num_components);
  114. for (size_t i=0; i<num_components; i++)
  115. {
  116. Is[i].resize(components[i].size());
  117. std::copy(components[i].begin(), components[i].end(),Is[i].data());
  118. }
  119. // Find outer facets, their orientations and cells for each component
  120. Eigen::VectorXi outer_facets(num_components);
  121. Eigen::VectorXi outer_facet_orientation(num_components);
  122. Eigen::VectorXi outer_cells(num_components);
  123. for (size_t i=0; i<num_components; i++)
  124. {
  125. bool flipped;
  126. igl::copyleft::cgal::outer_facet(V, F, Is[i], outer_facets[i], flipped);
  127. outer_facet_orientation[i] = flipped?1:0;
  128. outer_cells[i] = raw_cells(P[outer_facets[i]], outer_facet_orientation[i]);
  129. }
  130. #ifdef EXTRACT_CELLS_DEBUG
  131. log_time("outer_facet_per_component");
  132. #endif
  133. // Compute barycenter of a triangle in mesh (V,F)
  134. //
  135. // Inputs:
  136. // fid index into F
  137. // Returns row-vector of barycenter coordinates
  138. const auto get_triangle_center = [&V,&F](const size_t fid)
  139. {
  140. return ((V.row(F(fid,0))+V.row(F(fid,1))+V.row(F(fid,2)))/3.0).eval();
  141. };
  142. std::vector<std::vector<size_t> > nested_cells(num_raw_cells);
  143. std::vector<std::vector<size_t> > ambient_cells(num_raw_cells);
  144. std::vector<std::vector<size_t> > ambient_comps(num_components);
  145. // Only bother if there's more than one component
  146. if(num_components > 1)
  147. {
  148. // construct bounding boxes for each component
  149. DerivedV bbox_min(num_components, 3);
  150. DerivedV bbox_max(num_components, 3);
  151. // Why not just initialize to numeric_limits::min, numeric_limits::max?
  152. bbox_min.rowwise() = V.colwise().maxCoeff().eval();
  153. bbox_max.rowwise() = V.colwise().minCoeff().eval();
  154. // Loop over faces
  155. for (size_t i=0; i<num_faces; i++)
  156. {
  157. // component of this face
  158. const auto comp_id = C[i];
  159. const auto& f = F.row(i);
  160. for (size_t j=0; j<3; j++)
  161. {
  162. for(size_t d=0;d<3;d++)
  163. {
  164. bbox_min(comp_id,d) = std::min(bbox_min(comp_id,d), V(f[j],d));
  165. bbox_max(comp_id,d) = std::max(bbox_max(comp_id,d), V(f[j],d));
  166. }
  167. }
  168. }
  169. // Return true if box of component ci intersects that of cj
  170. const auto bbox_intersects = [&bbox_max,&bbox_min](size_t ci, size_t cj)
  171. {
  172. return !(
  173. bbox_max(ci,0) < bbox_min(cj,0) ||
  174. bbox_max(ci,1) < bbox_min(cj,1) ||
  175. bbox_max(ci,2) < bbox_min(cj,2) ||
  176. bbox_max(cj,0) < bbox_min(ci,0) ||
  177. bbox_max(cj,1) < bbox_min(ci,1) ||
  178. bbox_max(cj,2) < bbox_min(ci,2));
  179. };
  180. // Loop over components. This section is O(m²)
  181. for (size_t i=0; i<num_components; i++)
  182. {
  183. // List of components that could overlap with component i
  184. std::vector<size_t> candidate_comps;
  185. candidate_comps.reserve(num_components);
  186. // Loop over components
  187. for (size_t j=0; j<num_components; j++)
  188. {
  189. if (i == j) continue;
  190. if (bbox_intersects(i,j)) candidate_comps.push_back(j);
  191. }
  192. const size_t num_candidate_comps = candidate_comps.size();
  193. if (num_candidate_comps == 0) continue;
  194. // Get query points on each candidate component: barycenter of
  195. // outer-facet
  196. DerivedV queries(num_candidate_comps, 3);
  197. for (size_t j=0; j<num_candidate_comps; j++)
  198. {
  199. const size_t index = candidate_comps[j];
  200. queries.row(j) = get_triangle_center(outer_facets[index]);
  201. }
  202. // Gather closest facets in ith component to each query point and their
  203. // orientations
  204. const auto& I = Is[i];
  205. Eigen::VectorXi closest_facets, closest_facet_orientations;
  206. closest_facet(V, F, I, queries,
  207. uE2E, EMAP, closest_facets, closest_facet_orientations);
  208. // Loop over all candidates
  209. for (size_t j=0; j<num_candidate_comps; j++)
  210. {
  211. const size_t index = candidate_comps[j];
  212. const size_t closest_patch = P[closest_facets[j]];
  213. const size_t closest_patch_side = closest_facet_orientations[j] ? 0:1;
  214. // The cell id of the closest patch
  215. const size_t ambient_cell =
  216. raw_cells(closest_patch,closest_patch_side);
  217. if (ambient_cell != (size_t)outer_cells[i])
  218. {
  219. // ---> component index inside component i, because the cell of the
  220. // closest facet on i to component index is **not** the same as the
  221. // "outer cell" of component i: component index is **not** outside of
  222. // component i (therefore it's inside).
  223. nested_cells[ambient_cell].push_back(outer_cells[index]);
  224. ambient_cells[outer_cells[index]].push_back(ambient_cell);
  225. ambient_comps[index].push_back(i);
  226. }
  227. }
  228. }
  229. }
  230. #ifdef EXTRACT_CELLS_DEBUG
  231. log_time("nested_relationship");
  232. #endif
  233. const size_t INVALID = std::numeric_limits<size_t>::max();
  234. const size_t INFINITE_CELL = num_raw_cells;
  235. std::vector<size_t> embedded_cells(num_raw_cells, INVALID);
  236. for (size_t i=0; i<num_components; i++) {
  237. const size_t outer_cell = outer_cells[i];
  238. const auto& ambient_comps_i = ambient_comps[i];
  239. const auto& ambient_cells_i = ambient_cells[outer_cell];
  240. const size_t num_ambient_comps = ambient_comps_i.size();
  241. assert(num_ambient_comps == ambient_cells_i.size());
  242. if (num_ambient_comps > 0) {
  243. size_t embedded_comp = INVALID;
  244. size_t embedded_cell = INVALID;
  245. for (size_t j=0; j<num_ambient_comps; j++) {
  246. if (ambient_comps[ambient_comps_i[j]].size() ==
  247. num_ambient_comps-1) {
  248. embedded_comp = ambient_comps_i[j];
  249. embedded_cell = ambient_cells_i[j];
  250. break;
  251. }
  252. }
  253. assert(embedded_comp != INVALID);
  254. assert(embedded_cell != INVALID);
  255. embedded_cells[outer_cell] = embedded_cell;
  256. } else {
  257. embedded_cells[outer_cell] = INFINITE_CELL;
  258. }
  259. }
  260. for (size_t i=0; i<num_patches; i++) {
  261. if (embedded_cells[raw_cells(i,0)] != INVALID) {
  262. raw_cells(i,0) = embedded_cells[raw_cells(i, 0)];
  263. }
  264. if (embedded_cells[raw_cells(i,1)] != INVALID) {
  265. raw_cells(i,1) = embedded_cells[raw_cells(i, 1)];
  266. }
  267. }
  268. size_t count = 0;
  269. std::vector<size_t> mapped_indices(num_raw_cells+1, INVALID);
  270. // Always map infinite cell to index 0.
  271. mapped_indices[INFINITE_CELL] = count;
  272. count++;
  273. for (size_t i=0; i<num_patches; i++) {
  274. const size_t old_positive_cell_id = raw_cells(i, 0);
  275. const size_t old_negative_cell_id = raw_cells(i, 1);
  276. size_t positive_cell_id, negative_cell_id;
  277. if (mapped_indices[old_positive_cell_id] == INVALID) {
  278. mapped_indices[old_positive_cell_id] = count;
  279. positive_cell_id = count;
  280. count++;
  281. } else {
  282. positive_cell_id = mapped_indices[old_positive_cell_id];
  283. }
  284. if (mapped_indices[old_negative_cell_id] == INVALID) {
  285. mapped_indices[old_negative_cell_id] = count;
  286. negative_cell_id = count;
  287. count++;
  288. } else {
  289. negative_cell_id = mapped_indices[old_negative_cell_id];
  290. }
  291. raw_cells(i, 0) = positive_cell_id;
  292. raw_cells(i, 1) = negative_cell_id;
  293. }
  294. cells = raw_cells;
  295. #ifdef EXTRACT_CELLS_DEBUG
  296. log_time("finalize");
  297. #endif
  298. return count;
  299. }
  300. template<
  301. typename DerivedV,
  302. typename DerivedF,
  303. typename DerivedP,
  304. typename DeriveduE,
  305. typename uE2EType,
  306. typename DerivedEMAP,
  307. typename DerivedC>
  308. IGL_INLINE size_t igl::copyleft::cgal::extract_cells_single_component(
  309. const Eigen::PlainObjectBase<DerivedV>& V,
  310. const Eigen::PlainObjectBase<DerivedF>& F,
  311. const Eigen::PlainObjectBase<DerivedP>& P,
  312. const Eigen::PlainObjectBase<DeriveduE>& uE,
  313. const std::vector<std::vector<uE2EType> >& uE2E,
  314. const Eigen::PlainObjectBase<DerivedEMAP>& EMAP,
  315. Eigen::PlainObjectBase<DerivedC>& cells)
  316. {
  317. const size_t num_faces = F.rows();
  318. // Input:
  319. // index index into #F*3 list of undirect edges
  320. // Returns index into face
  321. const auto edge_index_to_face_index = [&num_faces](size_t index)
  322. {
  323. return index % num_faces;
  324. };
  325. // Determine if a face (containing undirected edge {s,d} is consistently
  326. // oriented with directed edge {s,d} (or otherwise it is with {d,s})
  327. //
  328. // Inputs:
  329. // fid face index into F
  330. // s source index of edge
  331. // d destination index of edge
  332. // Returns true if face F(fid,:) is consistent with {s,d}
  333. const auto is_consistent =
  334. [&F](const size_t fid, const size_t s, const size_t d) -> bool
  335. {
  336. if ((size_t)F(fid, 0) == s && (size_t)F(fid, 1) == d) return false;
  337. if ((size_t)F(fid, 1) == s && (size_t)F(fid, 2) == d) return false;
  338. if ((size_t)F(fid, 2) == s && (size_t)F(fid, 0) == d) return false;
  339. if ((size_t)F(fid, 0) == d && (size_t)F(fid, 1) == s) return true;
  340. if ((size_t)F(fid, 1) == d && (size_t)F(fid, 2) == s) return true;
  341. if ((size_t)F(fid, 2) == d && (size_t)F(fid, 0) == s) return true;
  342. throw "Invalid face!";
  343. return false;
  344. };
  345. const size_t num_unique_edges = uE.rows();
  346. const size_t num_patches = P.maxCoeff() + 1;
  347. // For each patch store a list of "representative" edges adjacent to it.
  348. // An edge is considered equivalant to another if they are adjacent to the
  349. // same set of patches. One edge is selected from each equivalent group as
  350. // the "representative."
  351. std::set<std::vector<size_t> > patch_combo;
  352. std::vector<std::vector<size_t> > patch_edge_adj(num_patches);
  353. for (size_t i=0; i<num_unique_edges; i++)
  354. {
  355. const size_t s = uE(i,0);
  356. const size_t d = uE(i,1);
  357. const auto adj_faces = uE2E[i];
  358. const size_t num_adj_faces = adj_faces.size();
  359. if (num_adj_faces > 2) {
  360. std::vector<size_t> adj_patch_ids(num_adj_faces);
  361. std::transform(adj_faces.begin(), adj_faces.end(),
  362. adj_patch_ids.begin(), [&](size_t ei) {
  363. return P[edge_index_to_face_index(ei)]; } );
  364. std::sort(adj_patch_ids.begin(), adj_patch_ids.end());
  365. if (patch_combo.find(adj_patch_ids) == patch_combo.end()) {
  366. patch_combo.insert(adj_patch_ids);
  367. for (size_t j=0; j<num_adj_faces; j++)
  368. {
  369. const size_t patch_j = P[edge_index_to_face_index(adj_faces[j])];
  370. patch_edge_adj[patch_j].push_back(adj_faces[j]);
  371. }
  372. }
  373. }
  374. }
  375. // Ordered faces around each "shared" unsigned edges. Thus the vector
  376. // "orders" will be sparsely filled.
  377. std::vector<Eigen::VectorXi> orders(num_unique_edges);
  378. // Similarly, orientations records the orientation of ordered facets around
  379. // each shared unique edge. The array will be sparse too.
  380. std::vector<std::vector<bool> > orientations(num_unique_edges);
  381. for (const auto edges_adj_to_patch_i : patch_edge_adj)
  382. {
  383. for (const auto ei : edges_adj_to_patch_i)
  384. {
  385. const size_t i = EMAP[ei];
  386. const auto adj_faces = uE2E[i];
  387. if (orders[i].size() == adj_faces.size()) continue;
  388. const size_t s = uE(i,0);
  389. const size_t d = uE(i,1);
  390. assert(adj_faces.size() > 2);
  391. std::vector<int> signed_adj_faces;
  392. for (auto ej : adj_faces)
  393. {
  394. const size_t fid = edge_index_to_face_index(ej);
  395. bool cons = is_consistent(fid, s, d);
  396. signed_adj_faces.push_back((fid+1)*(cons ? 1:-1));
  397. }
  398. {
  399. // Sort adjacent faces cyclically around {s,d}
  400. auto& order = orders[i];
  401. // order[f] will reveal the order of face f in signed_adj_faces
  402. order_facets_around_edge(V, F, s, d, signed_adj_faces, order);
  403. // Determine if each facet is oriented to point its normal clockwise or
  404. // not around the {s,d} (normals are not explicitly computed/used)
  405. auto& orientation = orientations[i];
  406. orientation.resize(order.size());
  407. std::transform(
  408. order.data(),
  409. order.data() + order.size(),
  410. orientation.begin(),
  411. [&signed_adj_faces](const int i){ return signed_adj_faces[i] > 0;});
  412. // re-index order from adjacent faces to full face list. Now order
  413. // indexes F directly
  414. std::transform(
  415. order.data(),
  416. order.data() + order.size(),
  417. order.data(),
  418. [&adj_faces](const int index){ return adj_faces[index];});
  419. }
  420. }
  421. }
  422. const int INVALID = std::numeric_limits<int>::max();
  423. // Given a "seed" patch id, a cell id, and which side of the patch that cell
  424. // lies, identify all other patches bounding this cell (and tell them that
  425. // they do)
  426. //
  427. // Inputs:
  428. // seed_patch_id index into patches
  429. // cell_idx index into cells
  430. // seed_patch_side 0 or 1 depending on whether cell_idx is on left or
  431. // right side of seed_patch_id
  432. // cells #cells by 2 list of current assignment of cells incident on each
  433. // side of a patch
  434. // Outputs:
  435. // cells udpated (see input)
  436. //
  437. const auto & peel_cell_bd =
  438. [&](
  439. const size_t seed_patch_id,
  440. const short seed_patch_side,
  441. const size_t cell_idx,
  442. Eigen::PlainObjectBase<DerivedC>& cells)
  443. {
  444. typedef std::pair<size_t, short> PatchSide;
  445. // Initialize a queue of {p,side} patch id and patch side pairs to BFS over
  446. // all patches
  447. std::queue<PatchSide> Q;
  448. Q.emplace(seed_patch_id, seed_patch_side);
  449. // assign cell id of seed patch on appropriate side
  450. cells(seed_patch_id, seed_patch_side) = cell_idx;
  451. while (!Q.empty())
  452. {
  453. // Pop patch from Q
  454. const auto entry = Q.front();
  455. Q.pop();
  456. const size_t patch_id = entry.first;
  457. const short side = entry.second;
  458. // Get a list of neighboring patches and the corresonding shared edge.
  459. const auto& adj_edges = patch_edge_adj[patch_id];
  460. for (const auto& ei: adj_edges) {
  461. const size_t uei = EMAP[ei];
  462. // ordering of face-edges stored at edge
  463. const auto& order = orders[uei];
  464. assert(order.size() > 0);
  465. // consistent orientation flags at face-edges stored at edge
  466. const auto& orientation = orientations[uei];
  467. const size_t edge_valance = order.size();
  468. // Search for ei's (i.e. patch_id's) place in the ordering: O(#patches)
  469. size_t curr_i = 0;
  470. for (curr_i=0; curr_i < edge_valance; curr_i++)
  471. {
  472. if ((size_t)order[curr_i] == ei) break;
  473. }
  474. assert(curr_i < edge_valance && "Failed to find edge.");
  475. // is the starting face consistent?
  476. const bool cons = orientation[curr_i];
  477. // Look clockwise or counter-clockwise for the next face, depending on
  478. // whether looking to left or right side and whether consistently
  479. // oriented or not.
  480. size_t next;
  481. if (side == 0)
  482. {
  483. next = (cons ? (curr_i + 1) :
  484. (curr_i + edge_valance - 1)) % edge_valance;
  485. } else {
  486. next = (cons ? (curr_i+edge_valance-1) : (curr_i+1))%edge_valance;
  487. }
  488. // Determine face-edge index of next face
  489. const size_t next_ei = order[next];
  490. // Determine whether next is consistently oriented
  491. const bool next_cons = orientation[next];
  492. // Determine patch of next
  493. const size_t next_patch_id = P[next_ei % num_faces];
  494. // Determine which side of patch cell_idx is on, based on whether the
  495. // consistency of next matches the consistency of this patch and which
  496. // side of this patch we're on.
  497. const short next_patch_side = (next_cons != cons) ? side:abs(side-1);
  498. // If that side of next's patch is not labeled, then label and add to
  499. // queue
  500. if (cells(next_patch_id, next_patch_side) == INVALID)
  501. {
  502. Q.emplace(next_patch_id, next_patch_side);
  503. cells(next_patch_id, next_patch_side) = cell_idx;
  504. }else
  505. {
  506. if (cells(next_patch_id, next_patch_side) != cell_idx) {
  507. std::cerr << cells(next_patch_id, next_patch_side) << " "
  508. << cell_idx << std::endl;
  509. }
  510. assert(
  511. (size_t)cells(next_patch_id, next_patch_side) == cell_idx &&
  512. "Encountered cell assignment inconsistency");
  513. }
  514. }
  515. }
  516. };
  517. // Initialize all patch-to-cell indices as "invalid" (unlabeled)
  518. cells.resize(num_patches, 2);
  519. cells.setConstant(INVALID);
  520. size_t count=0;
  521. // Loop over all patches
  522. for (size_t i=0; i<num_patches; i++)
  523. {
  524. // If left side of patch is still unlabeled, create a new cell and find all
  525. // patches also on its boundary
  526. if (cells(i, 0) == INVALID)
  527. {
  528. peel_cell_bd(i, 0, count,cells);
  529. count++;
  530. }
  531. // Likewise for right side
  532. if (cells(i, 1) == INVALID)
  533. {
  534. peel_cell_bd(i, 1, count,cells);
  535. count++;
  536. }
  537. }
  538. assert((cells.array() != INVALID).all());
  539. return count;
  540. }
  541. #ifdef IGL_STATIC_LIBRARY
  542. #include <CGAL/Exact_predicates_exact_constructions_kernel.h>
  543. template unsigned long igl::copyleft::cgal::extract_cells<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<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, unsigned long, 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<int, -1, -1, 0, -1, -1> > 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&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&);
  544. #endif