decimate.cpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. // This file is part of libigl, a simple c++ geometry processing library.
  2. //
  3. // Copyright (C) 2016 Alec Jacobson <[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. #include "decimate.h"
  9. #include "collapse_least_cost_edge.h"
  10. #include "edge_flaps.h"
  11. #include "decimate_trivial_callbacks.h"
  12. #include "AABB.h"
  13. #include "intersection_blocking_collapse_edge_callbacks.h"
  14. #include "is_edge_manifold.h"
  15. #include "remove_unreferenced.h"
  16. #include "placeholders.h"
  17. #include "find.h"
  18. #include "connect_boundary_to_infinity.h"
  19. #include "parallel_for.h"
  20. #include "max_faces_stopping_condition.h"
  21. #include "shortest_edge_and_midpoint.h"
  22. #include "PlainMatrix.h"
  23. IGL_INLINE bool igl::decimate(
  24. const Eigen::MatrixXd & V,
  25. const Eigen::MatrixXi & F,
  26. const int max_m,
  27. const bool block_intersections,
  28. Eigen::MatrixXd & U,
  29. Eigen::MatrixXi & G,
  30. Eigen::VectorXi & J,
  31. Eigen::VectorXi & I)
  32. {
  33. igl::AABB<Eigen::MatrixXd, 3> * tree = nullptr;
  34. if(block_intersections)
  35. {
  36. tree = new igl::AABB<Eigen::MatrixXd, 3>();
  37. tree->init(V,F);
  38. }
  39. // Original number of faces
  40. const int orig_m = F.rows();
  41. // Tracking number of faces
  42. int m = F.rows();
  43. typedef Eigen::MatrixXd DerivedV;
  44. typedef Eigen::MatrixXi DerivedF;
  45. PlainMatrix<DerivedV> VO;
  46. PlainMatrix<DerivedF> FO;
  47. igl::connect_boundary_to_infinity(V,F,VO,FO);
  48. // These will unfortunately be immediately recomputed in decimate.
  49. Eigen::VectorXi EMAP;
  50. Eigen::MatrixXi E,EF,EI;
  51. edge_flaps(FO,E,EMAP,EF,EI);
  52. // decimate will not work correctly on non-edge-manifold meshes. By extension
  53. // this includes meshes with non-manifold vertices on the boundary since these
  54. // will create a non-manifold edge when connected to infinity.
  55. {
  56. Eigen::Array<bool,Eigen::Dynamic,Eigen::Dynamic> BF;
  57. Eigen::Array<bool,Eigen::Dynamic,1> BE;
  58. if(!is_edge_manifold(FO,E.rows(),EMAP,BF,BE))
  59. {
  60. return false;
  61. }
  62. }
  63. decimate_pre_collapse_callback pre_collapse;
  64. decimate_post_collapse_callback post_collapse;
  65. decimate_trivial_callbacks(pre_collapse,post_collapse);
  66. if(block_intersections)
  67. {
  68. igl::intersection_blocking_collapse_edge_callbacks(
  69. pre_collapse, post_collapse, // These will get copied as needed
  70. tree,
  71. pre_collapse, post_collapse);
  72. }
  73. bool ret = decimate(
  74. VO,
  75. FO,
  76. shortest_edge_and_midpoint,
  77. max_faces_stopping_condition(m,orig_m,max_m),
  78. pre_collapse,
  79. post_collapse,
  80. U,
  81. G,
  82. J,
  83. I);
  84. const Eigen::Array<bool,Eigen::Dynamic,1> keep = (J.array()<orig_m);
  85. G = G(igl::find(keep),igl::placeholders::all).eval();
  86. J = J(igl::find(keep)).eval();
  87. Eigen::VectorXi _1,I2;
  88. igl::remove_unreferenced(Eigen::MatrixXd(U),Eigen::MatrixXi(G),U,G,_1,I2);
  89. I = I(I2).eval();
  90. assert(tree == nullptr || tree == tree->root());
  91. delete tree;
  92. return ret;
  93. }
  94. IGL_INLINE bool igl::decimate(
  95. const Eigen::MatrixXd & OV,
  96. const Eigen::MatrixXi & OF,
  97. const decimate_cost_and_placement_callback & cost_and_placement,
  98. const decimate_stopping_condition_callback & stopping_condition,
  99. const decimate_pre_collapse_callback & pre_collapse,
  100. const decimate_post_collapse_callback & post_collapse,
  101. Eigen::MatrixXd & U,
  102. Eigen::MatrixXi & G,
  103. Eigen::VectorXi & J,
  104. Eigen::VectorXi & I
  105. )
  106. {
  107. // Decimate 1
  108. // Working copies
  109. Eigen::MatrixXd V = OV;
  110. Eigen::MatrixXi F = OF;
  111. // Why recompute this rather than copy input?
  112. Eigen::VectorXi EMAP;
  113. Eigen::MatrixXi E,EF,EI;
  114. edge_flaps(F,E,EMAP,EF,EI);
  115. {
  116. Eigen::Array<bool,Eigen::Dynamic,Eigen::Dynamic> BF;
  117. Eigen::Array<bool,Eigen::Dynamic,1> BE;
  118. if(!is_edge_manifold(F,E.rows(),EMAP,BF,BE))
  119. {
  120. return false;
  121. }
  122. }
  123. igl::min_heap<std::tuple<double,int,int> > Q;
  124. // Could reserve with https://stackoverflow.com/a/29236236/148668
  125. Eigen::VectorXi EQ = Eigen::VectorXi::Zero(E.rows());
  126. // If an edge were collapsed, we'd collapse it to these points:
  127. Eigen::MatrixXd C(E.rows(),V.cols());
  128. // Pushing into a vector then using constructor was slower. Maybe using
  129. // std::move + make_heap would squeeze out something?
  130. // Separating the cost/placement evaluation from the Q filling is a
  131. // performance hit for serial but faster if we can parallelize the
  132. // cost/placement.
  133. {
  134. Eigen::VectorXd costs(E.rows());
  135. igl::parallel_for(E.rows(),[&](const int e)
  136. {
  137. double cost = e;
  138. // RowVectorXd here means that cost_and_placement needs to expect that
  139. // output parameter type, which makes it hard to use a generic template
  140. // for cost_and_placement.
  141. // If we were using more modern C++ then cost_and_placement could return a
  142. // tuple and could be unpacked into auto [cost,p] =
  143. // cost_and_placement(...) without much issue.
  144. Eigen::RowVectorXd p(1,3);
  145. cost_and_placement(e,V,F,E,EMAP,EF,EI,cost,p);
  146. C.row(e) = p;
  147. costs(e) = cost;
  148. },
  149. 10000
  150. );
  151. for(int e = 0;e<E.rows();e++)
  152. {
  153. Q.emplace(costs(e),e,0);
  154. }
  155. }
  156. int prev_e = -1;
  157. bool clean_finish = false;
  158. while(true)
  159. {
  160. int e,e1,e2,f1,f2;
  161. if(collapse_least_cost_edge(
  162. cost_and_placement, pre_collapse, post_collapse,
  163. V,F,E,EMAP,EF,EI,Q,EQ,C,e,e1,e2,f1,f2))
  164. {
  165. if(stopping_condition(V,F,E,EMAP,EF,EI,Q,EQ,C,e,e1,e2,f1,f2))
  166. {
  167. clean_finish = true;
  168. break;
  169. }
  170. }else
  171. {
  172. if(e == -1)
  173. {
  174. // a candidate edge was not even found in Q.
  175. break;
  176. }
  177. if(prev_e == e)
  178. {
  179. assert(false && "Edge collapse no progress... bad stopping condition?");
  180. break;
  181. }
  182. // Edge was not collapsed... must have been invalid. collapse_edge should
  183. // have updated its cost to inf... continue
  184. }
  185. prev_e = e;
  186. }
  187. // remove all IGL_COLLAPSE_EDGE_NULL faces
  188. Eigen::MatrixXi F2(F.rows(),3);
  189. J.resize(F.rows());
  190. int m = 0;
  191. for(int f = 0;f<F.rows();f++)
  192. {
  193. if(
  194. F(f,0) != IGL_COLLAPSE_EDGE_NULL ||
  195. F(f,1) != IGL_COLLAPSE_EDGE_NULL ||
  196. F(f,2) != IGL_COLLAPSE_EDGE_NULL)
  197. {
  198. F2.row(m) = F.row(f);
  199. J(m) = f;
  200. m++;
  201. }
  202. }
  203. F2.conservativeResize(m,F2.cols());
  204. J.conservativeResize(m);
  205. Eigen::VectorXi _1;
  206. igl::remove_unreferenced(V,F2,U,G,_1,I);
  207. return clean_finish;
  208. }