decimate.cpp 7.1 KB

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