propagate_winding_numbers.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  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 "propagate_winding_numbers.h"
  10. #include "../extract_manifold_patches.h"
  11. #include "../extract_non_manifold_edge_curves.h"
  12. #include "../facet_components.h"
  13. #include "../unique_edge_map.h"
  14. #include "../writeOBJ.h"
  15. #include "../writePLY.h"
  16. #include "order_facets_around_edge.h"
  17. #include "outer_facet.h"
  18. #include "closest_facet.h"
  19. #include "assign_scalar.h"
  20. #include "extract_cells.h"
  21. #include <stdexcept>
  22. #include <limits>
  23. #include <vector>
  24. #include <tuple>
  25. #include <queue>
  26. namespace propagate_winding_numbers_helper {
  27. template<
  28. typename DerivedF,
  29. typename DeriveduE,
  30. typename uE2EType >
  31. bool is_orientable(
  32. const Eigen::PlainObjectBase<DerivedF>& F,
  33. const Eigen::PlainObjectBase<DeriveduE>& uE,
  34. const std::vector<std::vector<uE2EType> >& uE2E) {
  35. const size_t num_faces = F.rows();
  36. const size_t num_edges = uE.rows();
  37. auto edge_index_to_face_index = [&](size_t ei) {
  38. return ei % num_faces;
  39. };
  40. auto is_consistent = [&](size_t fid, size_t s, size_t d) {
  41. if (F(fid, 0) == s && F(fid, 1) == d) return true;
  42. if (F(fid, 1) == s && F(fid, 2) == d) return true;
  43. if (F(fid, 2) == s && F(fid, 0) == d) return true;
  44. if (F(fid, 0) == d && F(fid, 1) == s) return false;
  45. if (F(fid, 1) == d && F(fid, 2) == s) return false;
  46. if (F(fid, 2) == d && F(fid, 0) == s) return false;
  47. throw "Invalid face!!";
  48. };
  49. for (size_t i=0; i<num_edges; i++) {
  50. const size_t s = uE(i,0);
  51. const size_t d = uE(i,1);
  52. int count=0;
  53. for (const auto& ei : uE2E[i]) {
  54. const size_t fid = edge_index_to_face_index(ei);
  55. if (is_consistent(fid, s, d)) count++;
  56. else count--;
  57. }
  58. if (count != 0) {
  59. return false;
  60. }
  61. }
  62. return true;
  63. }
  64. }
  65. template<
  66. typename DerivedV,
  67. typename DerivedF,
  68. typename DerivedL,
  69. typename DerivedW>
  70. IGL_INLINE void igl::cgal::propagate_winding_numbers(
  71. const Eigen::PlainObjectBase<DerivedV>& V,
  72. const Eigen::PlainObjectBase<DerivedF>& F,
  73. const Eigen::PlainObjectBase<DerivedL>& labels,
  74. Eigen::PlainObjectBase<DerivedW>& W) {
  75. const size_t num_faces = F.rows();
  76. typedef typename DerivedF::Scalar Index;
  77. Eigen::MatrixXi E, uE;
  78. Eigen::VectorXi EMAP;
  79. std::vector<std::vector<size_t> > uE2E;
  80. igl::unique_edge_map(F, E, uE, EMAP, uE2E);
  81. assert(propagate_winding_numbers_helper::is_orientable(F, uE, uE2E));
  82. Eigen::VectorXi P;
  83. const size_t num_patches = igl::extract_manifold_patches(F, EMAP, uE2E, P);
  84. DerivedW per_patch_cells;
  85. const size_t num_cells =
  86. igl::cgal::extract_cells(V, F, P, E, uE, uE2E, EMAP, per_patch_cells);
  87. typedef std::tuple<size_t, bool, size_t> CellConnection;
  88. std::vector<std::set<CellConnection> > cell_adjacency(num_cells);
  89. for (size_t i=0; i<num_patches; i++) {
  90. const int positive_cell = per_patch_cells(i,0);
  91. const int negative_cell = per_patch_cells(i,1);
  92. cell_adjacency[positive_cell].emplace(negative_cell, false, i);
  93. cell_adjacency[negative_cell].emplace(positive_cell, true, i);
  94. }
  95. auto save_cell = [&](const std::string& filename, size_t cell_id) {
  96. std::vector<size_t> faces;
  97. for (size_t i=0; i<num_patches; i++) {
  98. if ((per_patch_cells.row(i).array() == cell_id).any()) {
  99. for (size_t j=0; j<num_faces; j++) {
  100. if (P[j] == i) {
  101. faces.push_back(j);
  102. }
  103. }
  104. }
  105. }
  106. Eigen::MatrixXi cell_faces(faces.size(), 3);
  107. for (size_t i=0; i<faces.size(); i++) {
  108. cell_faces.row(i) = F.row(faces[i]);
  109. }
  110. Eigen::MatrixXd vertices(V.rows(), 3);
  111. for (size_t i=0; i<V.rows(); i++) {
  112. assign_scalar(V(i,0), vertices(i,0));
  113. assign_scalar(V(i,1), vertices(i,1));
  114. assign_scalar(V(i,2), vertices(i,2));
  115. }
  116. writePLY(filename, vertices, cell_faces);
  117. };
  118. {
  119. // Check for odd cycle.
  120. Eigen::VectorXi cell_labels(num_cells);
  121. cell_labels.setZero();
  122. Eigen::VectorXi parents(num_cells);
  123. parents.setConstant(-1);
  124. auto trace_parents = [&](size_t idx) {
  125. std::list<size_t> path;
  126. path.push_back(idx);
  127. while (parents[path.back()] != path.back()) {
  128. path.push_back(parents[path.back()]);
  129. }
  130. return path;
  131. };
  132. for (size_t i=0; i<num_cells; i++) {
  133. if (cell_labels[i] == 0) {
  134. cell_labels[i] = 1;
  135. std::queue<size_t> Q;
  136. Q.push(i);
  137. parents[i] = i;
  138. while (!Q.empty()) {
  139. size_t curr_idx = Q.front();
  140. Q.pop();
  141. int curr_label = cell_labels[curr_idx];
  142. for (const auto& neighbor : cell_adjacency[curr_idx]) {
  143. if (cell_labels[std::get<0>(neighbor)] == 0) {
  144. cell_labels[std::get<0>(neighbor)] = curr_label * -1;
  145. Q.push(std::get<0>(neighbor));
  146. parents[std::get<0>(neighbor)] = curr_idx;
  147. } else {
  148. if (cell_labels[std::get<0>(neighbor)] !=
  149. curr_label * -1) {
  150. std::cerr << "Odd cell cycle detected!" << std::endl;
  151. auto path = trace_parents(curr_idx);
  152. path.reverse();
  153. auto path2 = trace_parents(std::get<0>(neighbor));
  154. path.insert(path.end(),
  155. path2.begin(), path2.end());
  156. for (auto cell_id : path) {
  157. std::cout << cell_id << " ";
  158. std::stringstream filename;
  159. filename << "cell_" << cell_id << ".ply";
  160. save_cell(filename.str(), cell_id);
  161. }
  162. std::cout << std::endl;
  163. }
  164. assert(cell_labels[std::get<0>(neighbor)] == curr_label * -1);
  165. }
  166. }
  167. }
  168. }
  169. }
  170. }
  171. size_t outer_facet;
  172. bool flipped;
  173. Eigen::VectorXi I;
  174. I.setLinSpaced(num_faces, 0, num_faces-1);
  175. igl::cgal::outer_facet(V, F, I, outer_facet, flipped);
  176. const size_t outer_patch = P[outer_facet];
  177. const size_t infinity_cell = per_patch_cells(outer_patch, flipped?1:0);
  178. Eigen::VectorXi patch_labels(num_patches);
  179. const int INVALID = std::numeric_limits<int>::max();
  180. patch_labels.setConstant(INVALID);
  181. for (size_t i=0; i<num_faces; i++) {
  182. if (patch_labels[P[i]] == INVALID) {
  183. patch_labels[P[i]] = labels[i];
  184. } else {
  185. assert(patch_labels[P[i]] == labels[i]);
  186. }
  187. }
  188. assert((patch_labels.array() != INVALID).all());
  189. const size_t num_labels = patch_labels.maxCoeff()+1;
  190. Eigen::MatrixXi per_cell_W(num_cells, num_labels);
  191. per_cell_W.setConstant(INVALID);
  192. per_cell_W.row(infinity_cell).setZero();
  193. std::queue<size_t> Q;
  194. Q.push(infinity_cell);
  195. while (!Q.empty()) {
  196. size_t curr_cell = Q.front();
  197. Q.pop();
  198. for (const auto& neighbor : cell_adjacency[curr_cell]) {
  199. size_t neighbor_cell, patch_idx;
  200. bool direction;
  201. std::tie(neighbor_cell, direction, patch_idx) = neighbor;
  202. if ((per_cell_W.row(neighbor_cell).array() == INVALID).any()) {
  203. per_cell_W.row(neighbor_cell) = per_cell_W.row(curr_cell);
  204. for (size_t i=0; i<num_labels; i++) {
  205. int inc = (patch_labels[patch_idx] == i) ?
  206. (direction ? -1:1) :0;
  207. per_cell_W(neighbor_cell, i) =
  208. per_cell_W(curr_cell, i) + inc;
  209. }
  210. Q.push(neighbor_cell);
  211. } else {
  212. for (size_t i=0; i<num_labels; i++) {
  213. if (i == patch_labels[patch_idx]) {
  214. int inc = direction ? -1:1;
  215. assert(per_cell_W(neighbor_cell, i) ==
  216. per_cell_W(curr_cell, i) + inc);
  217. } else {
  218. assert(per_cell_W(neighbor_cell, i) ==
  219. per_cell_W(curr_cell, i));
  220. }
  221. }
  222. }
  223. }
  224. }
  225. W.resize(num_faces, num_labels*2);
  226. for (size_t i=0; i<num_faces; i++) {
  227. const size_t patch = P[i];
  228. const size_t positive_cell = per_patch_cells(patch, 0);
  229. const size_t negative_cell = per_patch_cells(patch, 1);
  230. for (size_t j=0; j<num_labels; j++) {
  231. W(i,j*2 ) = per_cell_W(positive_cell, j);
  232. W(i,j*2+1) = per_cell_W(negative_cell, j);
  233. }
  234. }
  235. //for (size_t i=0; i<num_cells; i++) {
  236. // std::stringstream filename;
  237. // filename << "cell_" << i << "_w_" << per_cell_W(i, 1) << ".ply";
  238. // save_cell(filename.str(), i);
  239. //}
  240. }
  241. #ifdef IGL_STATIC_LIBRARY
  242. template void igl::cgal::propagate_winding_numbers<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::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> >&);
  243. #endif