collapse_least_cost_edge.cpp 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. // This file is part of libigl, a simple c++ geometry processing library.
  2. //
  3. // Copyright (C) 2025 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 "collapse_least_cost_edge.h"
  9. #include "collapse_edge.h"
  10. #include "circulation.h"
  11. IGL_INLINE bool igl::collapse_least_cost_edge(
  12. const decimate_cost_and_placement_callback & cost_and_placement,
  13. const decimate_pre_collapse_callback & pre_collapse,
  14. const decimate_post_collapse_callback & post_collapse,
  15. Eigen::MatrixXd & V,
  16. Eigen::MatrixXi & F,
  17. Eigen::MatrixXi & E,
  18. Eigen::VectorXi & EMAP,
  19. Eigen::MatrixXi & EF,
  20. Eigen::MatrixXi & EI,
  21. igl::min_heap< std::tuple<double,int,int> > & Q,
  22. Eigen::VectorXi & EQ,
  23. Eigen::MatrixXd & C,
  24. int & e,
  25. int & e1,
  26. int & e2,
  27. int & f1,
  28. int & f2)
  29. {
  30. using namespace igl;
  31. std::tuple<double,int,int> p;
  32. while(true)
  33. {
  34. // Check if Q is empty
  35. if(Q.empty())
  36. {
  37. // no edges to collapse
  38. e = -1;
  39. return false;
  40. }
  41. // pop from Q
  42. p = Q.top();
  43. if(std::get<0>(p) == std::numeric_limits<double>::infinity())
  44. {
  45. e = -1;
  46. // min cost edge is infinite cost
  47. return false;
  48. }
  49. Q.pop();
  50. e = std::get<1>(p);
  51. // Check if matches timestamp
  52. if(std::get<2>(p) == EQ(e))
  53. {
  54. break;
  55. }
  56. // must be stale or dead.
  57. assert(std::get<2>(p) < EQ(e) || EQ(e) == -1);
  58. // try again.
  59. }
  60. // Why is this computed up here?
  61. // If we just need original face neighbors of edge, could we gather that more
  62. // directly than gathering face neighbors of each vertex?
  63. std::vector<int> /*Nse,*/Nsf,Nsv;
  64. circulation(e, true,F,EMAP,EF,EI,/*Nse,*/Nsv,Nsf);
  65. std::vector<int> /*Nde,*/Ndf,Ndv;
  66. circulation(e, false,F,EMAP,EF,EI,/*Nde,*/Ndv,Ndf);
  67. bool collapsed = true;
  68. if(pre_collapse(V,F,E,EMAP,EF,EI,Q,EQ,C,e))
  69. {
  70. collapsed = collapse_edge(
  71. e,C.row(e),
  72. Nsv,Nsf,Ndv,Ndf,
  73. V,F,E,EMAP,EF,EI,e1,e2,f1,f2);
  74. }else
  75. {
  76. // Aborted by pre collapse callback
  77. collapsed = false;
  78. }
  79. post_collapse(V,F,E,EMAP,EF,EI,Q,EQ,C,e,e1,e2,f1,f2,collapsed);
  80. if(collapsed)
  81. {
  82. // Erase the center edge, marking its timestamp as -1
  83. EQ(e) = -1;
  84. // Erase the two, other collapsed edges by marking their timestamps as -1
  85. EQ(e1) = -1;
  86. EQ(e2) = -1;
  87. // TODO: visits edges multiple times, ~150% more updates than should
  88. //
  89. // update local neighbors
  90. // loop over original face neighbors
  91. //
  92. // Can't use previous computed Nse and Nde because those refer to EMAP
  93. // before it was changed...
  94. std::vector<int> Nf;
  95. Nf.reserve( Nsf.size() + Ndf.size() ); // preallocate memory
  96. Nf.insert( Nf.end(), Nsf.begin(), Nsf.end() );
  97. Nf.insert( Nf.end(), Ndf.begin(), Ndf.end() );
  98. // https://stackoverflow.com/a/1041939/148668
  99. std::sort( Nf.begin(), Nf.end() );
  100. Nf.erase( std::unique( Nf.begin(), Nf.end() ), Nf.end() );
  101. // Collect all edges that must be updated
  102. std::vector<int> Ne;
  103. Ne.reserve(3*Nf.size());
  104. for(auto & n : Nf)
  105. {
  106. if(F(n,0) != IGL_COLLAPSE_EDGE_NULL ||
  107. F(n,1) != IGL_COLLAPSE_EDGE_NULL ||
  108. F(n,2) != IGL_COLLAPSE_EDGE_NULL)
  109. {
  110. for(int v = 0;v<3;v++)
  111. {
  112. // get edge id
  113. const int ei = EMAP(v*F.rows()+n);
  114. Ne.push_back(ei);
  115. }
  116. }
  117. }
  118. // Only process edge once
  119. std::sort( Ne.begin(), Ne.end() );
  120. Ne.erase( std::unique( Ne.begin(), Ne.end() ), Ne.end() );
  121. for(auto & ei : Ne)
  122. {
  123. // compute cost and potential placement
  124. double cost;
  125. Eigen::RowVectorXd place;
  126. cost_and_placement(ei,V,F,E,EMAP,EF,EI,cost,place);
  127. // Increment timestamp
  128. EQ(ei)++;
  129. // Replace in queue
  130. Q.emplace(cost,ei,EQ(ei));
  131. C.row(ei) = place;
  132. }
  133. }else
  134. {
  135. // reinsert with infinite weight (the provided cost function must **not**
  136. // have given this un-collapsable edge inf cost already)
  137. // Increment timestamp
  138. EQ(e)++;
  139. // Replace in queue
  140. Q.emplace(std::numeric_limits<double>::infinity(),e,EQ(e));
  141. }
  142. return collapsed;
  143. }