2
0

decimate.cpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  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_edge.h"
  10. #include "edge_flaps.h"
  11. #include "always_try_never_care.h"
  12. #include "is_edge_manifold.h"
  13. #include "remove_unreferenced.h"
  14. #include "slice_mask.h"
  15. #include "slice.h"
  16. #include "connect_boundary_to_infinity.h"
  17. #include "parallel_for.h"
  18. #include "max_faces_stopping_condition.h"
  19. #include "shortest_edge_and_midpoint.h"
  20. IGL_INLINE bool igl::decimate(
  21. const Eigen::MatrixXd & V,
  22. const Eigen::MatrixXi & F,
  23. const size_t max_m,
  24. Eigen::MatrixXd & U,
  25. Eigen::MatrixXi & G,
  26. Eigen::VectorXi & J,
  27. Eigen::VectorXi & I)
  28. {
  29. // Original number of faces
  30. const int orig_m = F.rows();
  31. // Tracking number of faces
  32. int m = F.rows();
  33. typedef Eigen::MatrixXd DerivedV;
  34. typedef Eigen::MatrixXi DerivedF;
  35. DerivedV VO;
  36. DerivedF FO;
  37. igl::connect_boundary_to_infinity(V,F,VO,FO);
  38. Eigen::VectorXi EMAP;
  39. Eigen::MatrixXi E,EF,EI;
  40. edge_flaps(FO,E,EMAP,EF,EI);
  41. // decimate will not work correctly on non-edge-manifold meshes. By extension
  42. // this includes meshes with non-manifold vertices on the boundary since these
  43. // will create a non-manifold edge when connected to infinity.
  44. {
  45. Eigen::Array<bool,Eigen::Dynamic,Eigen::Dynamic> BF;
  46. Eigen::Array<bool,Eigen::Dynamic,1> BE;
  47. if(!is_edge_manifold(FO,E.rows(),EMAP,BF,BE))
  48. {
  49. return false;
  50. }
  51. }
  52. decimate_pre_collapse_func always_try;
  53. decimate_post_collapse_func never_care;
  54. always_try_never_care(always_try,never_care);
  55. bool ret = decimate(
  56. VO,
  57. FO,
  58. shortest_edge_and_midpoint,
  59. max_faces_stopping_condition(m,orig_m,max_m),
  60. always_try,
  61. never_care,
  62. E,
  63. EMAP,
  64. EF,
  65. EI,
  66. U,
  67. G,
  68. J,
  69. I);
  70. const Eigen::Array<bool,Eigen::Dynamic,1> keep = (J.array()<orig_m);
  71. igl::slice_mask(Eigen::MatrixXi(G),keep,1,G);
  72. igl::slice_mask(Eigen::VectorXi(J),keep,1,J);
  73. Eigen::VectorXi _1,I2;
  74. igl::remove_unreferenced(Eigen::MatrixXd(U),Eigen::MatrixXi(G),U,G,_1,I2);
  75. igl::slice(Eigen::VectorXi(I),I2,1,I);
  76. return ret;
  77. }
  78. IGL_INLINE bool igl::decimate(
  79. const Eigen::MatrixXd & V,
  80. const Eigen::MatrixXi & F,
  81. const size_t max_m,
  82. Eigen::MatrixXd & U,
  83. Eigen::MatrixXi & G,
  84. Eigen::VectorXi & J)
  85. {
  86. Eigen::VectorXi I;
  87. return igl::decimate(V,F,max_m,U,G,J,I);
  88. }
  89. IGL_INLINE bool igl::decimate(
  90. const Eigen::MatrixXd & OV,
  91. const Eigen::MatrixXi & OF,
  92. const decimate_cost_and_placement_func & cost_and_placement,
  93. const decimate_stopping_condition_func & stopping_condition,
  94. Eigen::MatrixXd & U,
  95. Eigen::MatrixXi & G,
  96. Eigen::VectorXi & J,
  97. Eigen::VectorXi & I
  98. )
  99. {
  100. decimate_pre_collapse_func always_try;
  101. decimate_post_collapse_func never_care;
  102. always_try_never_care(always_try,never_care);
  103. return igl::decimate(
  104. OV,OF,cost_and_placement,stopping_condition,always_try,never_care,U,G,J,I);
  105. }
  106. IGL_INLINE bool igl::decimate(
  107. const Eigen::MatrixXd & OV,
  108. const Eigen::MatrixXi & OF,
  109. const decimate_cost_and_placement_func & cost_and_placement,
  110. const decimate_stopping_condition_func & stopping_condition,
  111. const decimate_pre_collapse_func & pre_collapse,
  112. const decimate_post_collapse_func & post_collapse,
  113. Eigen::MatrixXd & U,
  114. Eigen::MatrixXi & G,
  115. Eigen::VectorXi & J,
  116. Eigen::VectorXi & I
  117. )
  118. {
  119. Eigen::VectorXi EMAP;
  120. Eigen::MatrixXi E,EF,EI;
  121. edge_flaps(OF,E,EMAP,EF,EI);
  122. return igl::decimate(
  123. OV,OF,
  124. cost_and_placement,stopping_condition,pre_collapse,post_collapse,
  125. E,EMAP,EF,EI,
  126. U,G,J,I);
  127. }
  128. IGL_INLINE bool igl::decimate(
  129. const Eigen::MatrixXd & OV,
  130. const Eigen::MatrixXi & OF,
  131. const decimate_cost_and_placement_func & cost_and_placement,
  132. const decimate_stopping_condition_func & stopping_condition,
  133. const decimate_pre_collapse_func & pre_collapse,
  134. const decimate_post_collapse_func & post_collapse,
  135. const Eigen::MatrixXi & OE,
  136. const Eigen::VectorXi & OEMAP,
  137. const Eigen::MatrixXi & OEF,
  138. const Eigen::MatrixXi & OEI,
  139. Eigen::MatrixXd & U,
  140. Eigen::MatrixXi & G,
  141. Eigen::VectorXi & J,
  142. Eigen::VectorXi & I
  143. )
  144. {
  145. // Decimate 1
  146. using namespace Eigen;
  147. using namespace std;
  148. // Working copies
  149. Eigen::MatrixXd V = OV;
  150. Eigen::MatrixXi F = OF;
  151. VectorXi EMAP;
  152. MatrixXi E,EF,EI;
  153. edge_flaps(F,E,EMAP,EF,EI);
  154. {
  155. Eigen::Array<bool,Eigen::Dynamic,Eigen::Dynamic> BF;
  156. Eigen::Array<bool,Eigen::Dynamic,1> BE;
  157. if(!is_edge_manifold(F,E.rows(),EMAP,BF,BE))
  158. {
  159. return false;
  160. }
  161. }
  162. igl::min_heap<std::tuple<double,int,int> > Q;
  163. // Could reserve with https://stackoverflow.com/a/29236236/148668
  164. Eigen::VectorXi EQ = Eigen::VectorXi::Zero(E.rows());
  165. // If an edge were collapsed, we'd collapse it to these points:
  166. MatrixXd C(E.rows(),V.cols());
  167. // Pushing into a vector then using constructor was slower. Maybe using
  168. // std::move + make_heap would squeeze out something?
  169. // Separating the cost/placement evaluation from the Q filling is a
  170. // performance hit for serial but faster if we can parallelize the
  171. // cost/placement.
  172. {
  173. Eigen::VectorXd costs(E.rows());
  174. igl::parallel_for(E.rows(),[&](const int e)
  175. {
  176. double cost = e;
  177. RowVectorXd p(1,3);
  178. cost_and_placement(e,V,F,E,EMAP,EF,EI,cost,p);
  179. C.row(e) = p;
  180. costs(e) = cost;
  181. },10000);
  182. for(int e = 0;e<E.rows();e++)
  183. {
  184. Q.emplace(costs(e),e,0);
  185. }
  186. }
  187. int prev_e = -1;
  188. bool clean_finish = false;
  189. while(true)
  190. {
  191. if(Q.empty())
  192. {
  193. break;
  194. }
  195. if(std::get<0>(Q.top()) == std::numeric_limits<double>::infinity())
  196. {
  197. // min cost edge is infinite cost
  198. break;
  199. }
  200. int e,e1,e2,f1,f2;
  201. if(collapse_edge(
  202. cost_and_placement, pre_collapse, post_collapse,
  203. V,F,E,EMAP,EF,EI,Q,EQ,C,e,e1,e2,f1,f2))
  204. {
  205. if(stopping_condition(V,F,E,EMAP,EF,EI,Q,EQ,C,e,e1,e2,f1,f2))
  206. {
  207. clean_finish = true;
  208. break;
  209. }
  210. }else
  211. {
  212. if(prev_e == e)
  213. {
  214. assert(false && "Edge collapse no progress... bad stopping condition?");
  215. break;
  216. }
  217. // Edge was not collapsed... must have been invalid. collapse_edge should
  218. // have updated its cost to inf... continue
  219. }
  220. prev_e = e;
  221. }
  222. // remove all IGL_COLLAPSE_EDGE_NULL faces
  223. MatrixXi F2(F.rows(),3);
  224. J.resize(F.rows());
  225. int m = 0;
  226. for(int f = 0;f<F.rows();f++)
  227. {
  228. if(
  229. F(f,0) != IGL_COLLAPSE_EDGE_NULL ||
  230. F(f,1) != IGL_COLLAPSE_EDGE_NULL ||
  231. F(f,2) != IGL_COLLAPSE_EDGE_NULL)
  232. {
  233. F2.row(m) = F.row(f);
  234. J(m) = f;
  235. m++;
  236. }
  237. }
  238. F2.conservativeResize(m,F2.cols());
  239. J.conservativeResize(m);
  240. VectorXi _1;
  241. igl::remove_unreferenced(V,F2,U,G,_1,I);
  242. return clean_finish;
  243. }