propagate_winding_numbers.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  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 "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 "../../piecewise_constant_winding_number.h"
  15. #include "../../writeOBJ.h"
  16. #include "../../writePLY.h"
  17. #include "../../get_seconds.h"
  18. #include "../../LinSpaced.h"
  19. #include "outer_facet.h"
  20. #include "assign.h"
  21. #include "extract_cells.h"
  22. #include "cell_adjacency.h"
  23. #include <stdexcept>
  24. #include <limits>
  25. #include <vector>
  26. #include <tuple>
  27. #include <queue>
  28. //#define PROPAGATE_WINDING_NUMBER_TIMING
  29. template<
  30. typename DerivedV,
  31. typename DerivedF,
  32. typename DerivedL,
  33. typename DerivedW>
  34. IGL_INLINE bool igl::copyleft::cgal::propagate_winding_numbers(
  35. const Eigen::MatrixBase<DerivedV>& V,
  36. const Eigen::MatrixBase<DerivedF>& F,
  37. const Eigen::MatrixBase<DerivedL>& labels,
  38. Eigen::PlainObjectBase<DerivedW>& W)
  39. {
  40. using Index = typename DerivedF::Scalar;
  41. using MatrixXI = Eigen::Matrix<Index, Eigen::Dynamic, Eigen::Dynamic>;
  42. using VectorXI = Eigen::Matrix<Index, Eigen::Dynamic, 1>;
  43. #ifdef PROPAGATE_WINDING_NUMBER_TIMING
  44. const auto & tictoc = []() -> double
  45. {
  46. static double t_start = igl::get_seconds();
  47. double diff = igl::get_seconds()-t_start;
  48. t_start += diff;
  49. return diff;
  50. };
  51. const auto log_time = [&](const std::string& label) -> void {
  52. std::cout << "propagate_winding_num." << label << ": "
  53. << tictoc() << std::endl;
  54. };
  55. tictoc();
  56. #endif
  57. MatrixXI E, uE;
  58. VectorXI EMAP, uEC, uEE;
  59. igl::unique_edge_map(F, E, uE, EMAP, uEC, uEE);
  60. VectorXI P;
  61. const size_t num_patches = igl::extract_manifold_patches(F,EMAP,uEC,uEE,P);
  62. DerivedW per_patch_cells;
  63. const size_t num_cells =
  64. extract_cells(V,F,P,uE,EMAP,uEC,uEE,per_patch_cells);
  65. #ifdef PROPAGATE_WINDING_NUMBER_TIMING
  66. log_time("cell_extraction");
  67. #endif
  68. return propagate_winding_numbers(V, F,
  69. uE, uEC, uEE,
  70. num_patches, P,
  71. num_cells, per_patch_cells,
  72. labels, W);
  73. }
  74. template<
  75. typename DerivedV,
  76. typename DerivedF,
  77. typename DeriveduE,
  78. typename DeriveduEC,
  79. typename DeriveduEE,
  80. typename DerivedP,
  81. typename DerivedC,
  82. typename DerivedL,
  83. typename DerivedW>
  84. IGL_INLINE bool igl::copyleft::cgal::propagate_winding_numbers(
  85. const Eigen::MatrixBase<DerivedV>& V,
  86. const Eigen::MatrixBase<DerivedF>& F,
  87. const Eigen::MatrixBase<DeriveduE>& uE,
  88. const Eigen::MatrixBase<DeriveduEC>& uEC,
  89. const Eigen::MatrixBase<DeriveduEE>& uEE,
  90. const size_t num_patches,
  91. const Eigen::MatrixBase<DerivedP>& P,
  92. const size_t num_cells,
  93. const Eigen::MatrixBase<DerivedC>& C,
  94. const Eigen::MatrixBase<DerivedL>& labels,
  95. Eigen::PlainObjectBase<DerivedW>& W)
  96. {
  97. using Index = typename DerivedF::Scalar;
  98. using MatrixXI = Eigen::Matrix<Index, Eigen::Dynamic, Eigen::Dynamic>;
  99. using VectorXI = Eigen::Matrix<Index, Eigen::Dynamic, 1>;
  100. #ifdef PROPAGATE_WINDING_NUMBER_TIMING
  101. const auto & tictoc = []() -> double
  102. {
  103. static double t_start = igl::get_seconds();
  104. double diff = igl::get_seconds()-t_start;
  105. t_start += diff;
  106. return diff;
  107. };
  108. const auto log_time = [&](const std::string& label) -> void {
  109. std::cout << "propagate_winding_num." << label << ": "
  110. << tictoc() << std::endl;
  111. };
  112. tictoc();
  113. #endif
  114. bool valid = true;
  115. // https://github.com/libigl/libigl/issues/674
  116. if (!igl::piecewise_constant_winding_number(F, uE, uEC, uEE))
  117. {
  118. assert(false && "Input mesh is not PWN");
  119. valid = false;
  120. }
  121. const size_t num_faces = F.rows();
  122. typedef std::tuple<typename DerivedC::Scalar, bool, size_t> CellConnection;
  123. std::vector<std::set<CellConnection> > cell_adj;
  124. igl::copyleft::cgal::cell_adjacency(C, num_cells, cell_adj);
  125. #ifdef PROPAGATE_WINDING_NUMBER_TIMING
  126. log_time("cell_connectivity");
  127. #endif
  128. #ifdef IGL_COPYLEFT_CGAL_PROPAGATE_WINDING_NUMBERS_DEBUG
  129. auto save_cell = [&](const std::string& filename, size_t cell_id) -> void{
  130. std::vector<size_t> faces;
  131. for (size_t i=0; i<num_patches; i++) {
  132. if ((C.row(i).array() == cell_id).any()) {
  133. for (size_t j=0; j<num_faces; j++) {
  134. if ((size_t)P[j] == i) {
  135. faces.push_back(j);
  136. }
  137. }
  138. }
  139. }
  140. MatrixXI cell_faces(faces.size(), 3);
  141. for (size_t i=0; i<faces.size(); i++) {
  142. cell_faces.row(i) = F.row(faces[i]);
  143. }
  144. Eigen::MatrixXd vertices;
  145. assign(V,vertices);
  146. writePLY(filename, vertices, cell_faces);
  147. };
  148. #endif
  149. #ifdef IGL_COPYLEFT_CGAL_PROPAGATE_WINDING_NUMBERS_DEBUG
  150. {
  151. // Check for odd cycle.
  152. VectorXI cell_labels(num_cells);
  153. cell_labels.setZero();
  154. VectorXI parents(num_cells);
  155. parents.setConstant(-1);
  156. auto trace_parents = [&](size_t idx) -> std::list<size_t> {
  157. std::list<size_t> path;
  158. path.push_back(idx);
  159. while ((size_t)parents[path.back()] != path.back()) {
  160. path.push_back(parents[path.back()]);
  161. }
  162. return path;
  163. };
  164. for (size_t i=0; i<num_cells; i++) {
  165. if (cell_labels[i] == 0) {
  166. cell_labels[i] = 1;
  167. std::queue<size_t> Q;
  168. Q.push(i);
  169. parents[i] = i;
  170. while (!Q.empty()) {
  171. size_t curr_idx = Q.front();
  172. Q.pop();
  173. int curr_label = cell_labels[curr_idx];
  174. for (const auto& neighbor : cell_adj[curr_idx]) {
  175. if (cell_labels[std::get<0>(neighbor)] == 0)
  176. {
  177. cell_labels[std::get<0>(neighbor)] = curr_label * -1;
  178. Q.push(std::get<0>(neighbor));
  179. parents[std::get<0>(neighbor)] = curr_idx;
  180. } else
  181. {
  182. if (cell_labels[std::get<0>(neighbor)] != curr_label * -1)
  183. {
  184. std::cerr << "Odd cell cycle detected!" << std::endl;
  185. auto path = trace_parents(curr_idx);
  186. path.reverse();
  187. auto path2 = trace_parents(std::get<0>(neighbor));
  188. path.insert(path.end(), path2.begin(), path2.end());
  189. for (auto cell_id : path)
  190. {
  191. std::cout << cell_id << " ";
  192. std::stringstream filename;
  193. filename << "cell_" << cell_id << ".ply";
  194. save_cell(filename.str(), cell_id);
  195. }
  196. std::cout << std::endl;
  197. valid = false;
  198. }
  199. // Do not fail when odd cycle is detected because the resulting
  200. // integer winding number field, although inconsistent, may still
  201. // be used if the problem region is local and embedded within a
  202. // valid volume.
  203. //assert(cell_labels[std::get<0>(neighbor)] == curr_label * -1);
  204. }
  205. }
  206. }
  207. }
  208. }
  209. #ifdef PROPAGATE_WINDING_NUMBER_TIMING
  210. log_time("odd_cycle_check");
  211. #endif
  212. }
  213. #endif
  214. Eigen::Index outer_facet;
  215. bool flipped;
  216. VectorXI I = igl::LinSpaced<VectorXI>(num_faces, 0, num_faces-1);
  217. igl::copyleft::cgal::outer_facet(V, F, I, outer_facet, flipped);
  218. #ifdef PROPAGATE_WINDING_NUMBER_TIMING
  219. log_time("outer_facet");
  220. #endif
  221. const size_t outer_patch = P[outer_facet];
  222. const size_t infinity_cell = C(outer_patch, flipped?1:0);
  223. VectorXI patch_labels(num_patches);
  224. const int INVALID = std::numeric_limits<int>::max();
  225. patch_labels.setConstant(INVALID);
  226. for (size_t i=0; i<num_faces; i++) {
  227. if (patch_labels[P[i]] == INVALID) {
  228. patch_labels[P[i]] = labels[i];
  229. } else {
  230. assert(patch_labels[P[i]] == labels[i]);
  231. }
  232. }
  233. assert((patch_labels.array() != INVALID).all());
  234. const size_t num_labels = patch_labels.maxCoeff()+1;
  235. MatrixXI per_cell_W(num_cells, num_labels);
  236. per_cell_W.setConstant(INVALID);
  237. per_cell_W.row(infinity_cell).setZero();
  238. std::queue<size_t> Q;
  239. Q.push(infinity_cell);
  240. while (!Q.empty()) {
  241. size_t curr_cell = Q.front();
  242. Q.pop();
  243. for (const auto& neighbor : cell_adj[curr_cell]) {
  244. size_t neighbor_cell, patch_idx;
  245. bool direction;
  246. std::tie(neighbor_cell, direction, patch_idx) = neighbor;
  247. if ((per_cell_W.row(neighbor_cell).array() == INVALID).any()) {
  248. per_cell_W.row(neighbor_cell) = per_cell_W.row(curr_cell);
  249. for (size_t i=0; i<num_labels; i++) {
  250. int inc = (patch_labels[patch_idx] == (int)i) ?
  251. (direction ? -1:1) :0;
  252. per_cell_W(neighbor_cell, i) =
  253. per_cell_W(curr_cell, i) + inc;
  254. }
  255. Q.push(neighbor_cell);
  256. } else {
  257. #ifdef IGL_COPYLEFT_CGAL_PROPAGATE_WINDING_NUMBERS_DEBUG
  258. // Checking for winding number consistency.
  259. // This check would inevitably fail for meshes that contain open
  260. // boundary or non-orientable. However, the inconsistent winding number
  261. // field would still be useful in some cases such as when problem region
  262. // is local and embedded within the volume. This, unfortunately, is the
  263. // best we can do because the problem of computing integer winding
  264. // number is ill-defined for open and non-orientable surfaces.
  265. //
  266. // Commented this out because it wasn't actually calling the asserts...
  267. //for (size_t i=0; i<num_labels; i++) {
  268. // if ((int)i == patch_labels[patch_idx]) {
  269. // int inc = direction ? -1:1;
  270. // //assert(per_cell_W(neighbor_cell, i) ==
  271. // // per_cell_W(curr_cell, i) + inc);
  272. // } else {
  273. // //assert(per_cell_W(neighbor_cell, i) ==
  274. // // per_cell_W(curr_cell, i));
  275. // }
  276. //}
  277. #endif
  278. }
  279. }
  280. }
  281. #ifdef PROPAGATE_WINDING_NUMBER_TIMING
  282. log_time("propagate_winding_number");
  283. #endif
  284. W.resize(num_faces, num_labels*2);
  285. for (size_t i=0; i<num_faces; i++)
  286. {
  287. const size_t patch = P[i];
  288. const size_t positive_cell = C(patch, 0);
  289. const size_t negative_cell = C(patch, 1);
  290. for (size_t j=0; j<num_labels; j++) {
  291. W(i,j*2 ) = per_cell_W(positive_cell, j);
  292. W(i,j*2+1) = per_cell_W(negative_cell, j);
  293. }
  294. }
  295. #ifdef PROPAGATE_WINDING_NUMBER_TIMING
  296. log_time("store_result");
  297. #endif
  298. return valid;
  299. }
  300. #ifdef IGL_STATIC_LIBRARY
  301. // Explicit template instantiation
  302. template bool igl::copyleft::cgal::propagate_winding_numbers<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>, 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>, 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<int, -1, 1, 0, -1, 1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, size_t, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, size_t, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&);
  303. template bool igl::copyleft::cgal::propagate_winding_numbers<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>, 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>, 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<int, -1, 1, 0, -1, 1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, size_t, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, size_t, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&);
  304. template bool igl::copyleft::cgal::propagate_winding_numbers<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> >(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::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&);
  305. #ifdef WIN32
  306. #endif
  307. #endif