Bladeren bron

Revert templating on collapse_edge, separate overloads (#2455)

* Fix 2452

* fix cachev2 issue?

* and the windows ❄️

* removed __1::

* fix tutorial to use new func

* cmake bullshit
Alec Jacobson 8 maanden geleden
bovenliggende
commit
0e360d5250

+ 6 - 2
.github/workflows/continuous.yml

@@ -60,7 +60,7 @@ jobs:
 
       - name: Cache Build
         id: cache-build
-        uses: actions/cache@v2
+        uses: actions/cache@v4
         with:
           path: ~/.ccache
           key: ${{ runner.os }}-${{ matrix.config }}-${{ matrix.build-params.static }}-cache
@@ -74,7 +74,9 @@ jobs:
         run: |
           mkdir -p build
           cd build
+          # https://github.com/eclipse-ecal/ecal/issues/2041
           cmake .. \
+            -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
             -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
             -DCMAKE_BUILD_TYPE=${{ matrix.config }} \
             -DLIBIGL_USE_STATIC_LIBRARY=${{ matrix.build-params.static }} \
@@ -128,7 +130,7 @@ jobs:
 
       - name: Cache build
         id: cache-build
-        uses: actions/cache@v2
+        uses: actions/cache@v4
         with:
           path: ${{ env.appdata }}\Mozilla\sccache
           key: ${{ runner.os }}-${{ matrix.config }}-${{ matrix.build-params.static }}-cache
@@ -145,7 +147,9 @@ jobs:
         shell: cmd
         run: |
           call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat" -arch=x64
+          # https://github.com/eclipse-ecal/ecal/issues/2041
           cmake -G Ninja ^
+            -DCMAKE_POLICY_VERSION_MINIMUM=3.5 ^
             -DCMAKE_CXX_COMPILER_LAUNCHER=sccache ^
             -DCMAKE_BUILD_TYPE=${{ matrix.config }} ^
             -DLIBIGL_USE_STATIC_LIBRARY=${{ matrix.build-params.static }} ^

+ 19 - 0
include/igl/COLLAPSE_EDGE_NULL.h

@@ -0,0 +1,19 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+// 
+// Copyright (C) 2025 Alec Jacobson <[email protected]>
+// 
+// This Source Code Form is subject to the terms of the Mozilla Public License 
+// v. 2.0. If a copy of the MPL was not distributed with this file, You can 
+// obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef IGL_COLLAPSE_EDGE_NULL_H
+#define IGL_COLLAPSE_EDGE_NULL_H
+namespace igl
+{
+#ifndef IGL_COLLAPSE_EDGE_NULL
+  /// Special value for indicating a null vertex index as the result of a
+  /// collapsed edge.
+  #define IGL_COLLAPSE_EDGE_NULL 0
+#endif
+}
+#endif

File diff suppressed because it is too large
+ 0 - 154
include/igl/collapse_edge.cpp


+ 1 - 74
include/igl/collapse_edge.h

@@ -8,18 +8,11 @@
 #ifndef IGL_COLLAPSE_EDGE_H
 #define IGL_COLLAPSE_EDGE_H
 #include "igl_inline.h"
-#include "min_heap.h"
-#include "decimate_callback_types.h"
+#include "COLLAPSE_EDGE_NULL.h"
 #include <Eigen/Core>
 #include <vector>
-#include <set>
 namespace igl
 {
-#ifndef IGL_COLLAPSE_EDGE_NULL
-  /// Special value for indicating a null vertex index as the result of a
-  /// collapsed edge.
-  #define IGL_COLLAPSE_EDGE_NULL 0
-#endif
   /// Attempt to collapse a given edge of a mesh. Assumes (V,F) is a closed
   /// manifold mesh (except for previously collapsed faces which should be set
   /// to: [IGL_COLLAPSE_EDGE_NULL IGL_COLLAPSE_EDGE_NULL
@@ -161,72 +154,6 @@ namespace igl
     int & a_e2,
     int & a_f1,
     int & a_f2);
-  /// Collapse least-cost edge from a priority queue and update queue 
-  ///
-  /// See decimate.h for more details.
-  ///
-  /// @param[in] cost_and_placement  function computing cost of collapsing an edge and 3d
-  ///     position where it should be placed:
-  ///     cost_and_placement(V,F,E,EMAP,EF,EI,cost,placement);
-  ///     **If the edges is collapsed** then this function will be called on all
-  ///     edges of all faces previously incident on the endpoints of the
-  ///     collapsed edge.
-  ///  @param[in] pre_collapse  callback called with index of edge whose collapse is about
-  ///     to be attempted. This function should return whether to **proceed**
-  ///     with the collapse: returning true means "yes, try to collapse",
-  ///     returning false means "No, consider this edge 'uncollapsable', behave
-  ///     as if collapse_edge(e) returned false.
-  ///  @param[in] post_collapse  callback called with index of edge whose collapse was
-  ///     just attempted and a flag revealing whether this was successful.
-  /// @param[in,out] V  #V by dim list of vertex positions, lesser index of E(e,:) will be set
-  ///     to midpoint of edge.
-  /// @param[in,out] F  #F by 3 list of face indices into V.
-  /// @param[in,out] E  #E by 2 list of edge indices into V.
-  /// @param[in,out] EMAP #F*3 list of indices into E, mapping each directed edge to unique
-  ///     unique edge in E
-  /// @param[in,out] EF  #E by 2 list of edge flaps, EF(e,0)=f means e=(i-->j) is the edge of
-  ///     F(f,:) opposite the vth corner, where EI(e,0)=v. Similarly EF(e,1)
-  ///     e=(j->i)
-  /// @param[in,out] EI  #E by 2 list of edge flap corners (see above).
-  /// @param[in] Q  queue containing pairs of costs and edge indices and insertion "time"
-  /// @param[in] EQ  #E list of "time" of last time pushed into Q
-  /// @param[in] C  #E by dim list of stored placements
-  /// @param[out] e  index into E of attempted collapsed edge. Set to -1 if Q is empty or
-  ///               contains only infinite cost edges.
-  /// @param[out] e1  index into E of edge collpased on left.
-  /// @param[out] e2  index into E of edge collpased on right.
-  /// @param[out] f1  index into F of face collpased on left.
-  /// @param[out] f2  index into F of face collpased on right.
-  template <
-    typename CPFunc,
-    typename PreFunc,
-    typename PostFunc,
-    typename DerivedV,
-    typename DerivedF,
-    typename DerivedE,
-    typename DerivedEMAP,
-    typename DerivedEF,
-    typename DerivedEI,
-    typename DerivedEQ,
-    typename DerivedC>
-  IGL_INLINE bool collapse_edge(
-    const CPFunc & cost_and_placement,
-    const PreFunc & pre_collapse,
-    const PostFunc & post_collapse,
-    Eigen::MatrixBase<DerivedV> & V,
-    Eigen::MatrixBase<DerivedF> & F,
-    Eigen::MatrixBase<DerivedE> & E,
-    Eigen::MatrixBase<DerivedEMAP> & EMAP,
-    Eigen::MatrixBase<DerivedEF> & EF,
-    Eigen::MatrixBase<DerivedEI> & EI,
-    igl::min_heap< std::tuple<double,int,int> > & Q,
-    Eigen::MatrixBase<DerivedEQ> & EQ,
-    Eigen::MatrixBase<DerivedC> & C,
-    int & e,
-    int & e1,
-    int & e2,
-    int & f1,
-    int & f2);
 }
 
 #ifndef IGL_STATIC_LIBRARY

+ 148 - 0
include/igl/collapse_least_cost_edge.cpp

@@ -0,0 +1,148 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+// 
+// Copyright (C) 2025 Alec Jacobson <[email protected]>
+// 
+// This Source Code Form is subject to the terms of the Mozilla Public License 
+// v. 2.0. If a copy of the MPL was not distributed with this file, You can 
+// obtain one at http://mozilla.org/MPL/2.0/.
+#include "collapse_least_cost_edge.h"
+#include "collapse_edge.h"
+#include "circulation.h"
+
+IGL_INLINE bool igl::collapse_least_cost_edge(
+  const decimate_cost_and_placement_callback & cost_and_placement,
+  const decimate_pre_collapse_callback       & pre_collapse,
+  const decimate_post_collapse_callback      & post_collapse,
+  Eigen::MatrixXd & V,
+  Eigen::MatrixXi & F,
+  Eigen::MatrixXi & E,
+  Eigen::VectorXi & EMAP,
+  Eigen::MatrixXi & EF,
+  Eigen::MatrixXi & EI,
+  igl::min_heap< std::tuple<double,int,int> > & Q,
+  Eigen::VectorXi & EQ,
+  Eigen::MatrixXd & C,
+  int & e,
+  int & e1,
+  int & e2,
+  int & f1,
+  int & f2)
+{
+  using namespace Eigen;
+  using namespace igl;
+  std::tuple<double,int,int> p;
+  while(true)
+  {
+    // Check if Q is empty
+    if(Q.empty())
+    {
+      // no edges to collapse
+      e = -1;
+      return false;
+    }
+    // pop from Q
+    p = Q.top();
+    if(std::get<0>(p) == std::numeric_limits<double>::infinity())
+    {
+      e = -1;
+      // min cost edge is infinite cost
+      return false;
+    }
+    Q.pop();
+    e = std::get<1>(p);
+    // Check if matches timestamp
+    if(std::get<2>(p) == EQ(e))
+    {
+      break;
+    }
+    // must be stale or dead.
+    assert(std::get<2>(p)  < EQ(e) || EQ(e) == -1);
+    // try again.
+  }
+
+  // Why is this computed up here?
+  // If we just need original face neighbors of edge, could we gather that more
+  // directly than gathering face neighbors of each vertex?
+  std::vector<int> /*Nse,*/Nsf,Nsv;
+  circulation(e, true,F,EMAP,EF,EI,/*Nse,*/Nsv,Nsf);
+  std::vector<int> /*Nde,*/Ndf,Ndv;
+  circulation(e, false,F,EMAP,EF,EI,/*Nde,*/Ndv,Ndf);
+
+
+  bool collapsed = true;
+  if(pre_collapse(V,F,E,EMAP,EF,EI,Q,EQ,C,e))
+  {
+    collapsed = collapse_edge(
+      e,C.row(e),
+      Nsv,Nsf,Ndv,Ndf,
+      V,F,E,EMAP,EF,EI,e1,e2,f1,f2);
+  }else
+  {
+    // Aborted by pre collapse callback
+    collapsed = false;
+  }
+  post_collapse(V,F,E,EMAP,EF,EI,Q,EQ,C,e,e1,e2,f1,f2,collapsed);
+  if(collapsed)
+  {
+    // Erase the center edge, marking its timestamp as -1
+    EQ(e) = -1;
+    // Erase the two, other collapsed edges by marking their timestamps as -1
+    EQ(e1) = -1;
+    EQ(e2) = -1;
+    // TODO: visits edges multiple times, ~150% more updates than should
+    //
+    // update local neighbors
+    // loop over original face neighbors
+    //
+    // Can't use previous computed Nse and Nde because those refer to EMAP
+    // before it was changed...
+    std::vector<int> Nf;
+    Nf.reserve( Nsf.size() + Ndf.size() ); // preallocate memory
+    Nf.insert( Nf.end(), Nsf.begin(), Nsf.end() );
+    Nf.insert( Nf.end(), Ndf.begin(), Ndf.end() );
+    // https://stackoverflow.com/a/1041939/148668
+    std::sort( Nf.begin(), Nf.end() );
+    Nf.erase( std::unique( Nf.begin(), Nf.end() ), Nf.end() );
+    // Collect all edges that must be updated
+    std::vector<int> Ne;
+    Ne.reserve(3*Nf.size());
+    for(auto & n : Nf)
+    {
+      if(F(n,0) != IGL_COLLAPSE_EDGE_NULL ||
+          F(n,1) != IGL_COLLAPSE_EDGE_NULL ||
+          F(n,2) != IGL_COLLAPSE_EDGE_NULL)
+      {
+        for(int v = 0;v<3;v++)
+        {
+          // get edge id
+          const int ei = EMAP(v*F.rows()+n);
+          Ne.push_back(ei);
+        }
+      }
+    }
+    // Only process edge once
+    std::sort( Ne.begin(), Ne.end() );
+    Ne.erase( std::unique( Ne.begin(), Ne.end() ), Ne.end() );
+    for(auto & ei : Ne)
+    {
+       // compute cost and potential placement
+       double cost;
+       RowVectorXd place;
+       cost_and_placement(ei,V,F,E,EMAP,EF,EI,cost,place);
+       // Increment timestamp
+       EQ(ei)++;
+       // Replace in queue
+       Q.emplace(cost,ei,EQ(ei));
+       C.row(ei) = place;
+    }
+  }else
+  {
+    // reinsert with infinite weight (the provided cost function must **not**
+    // have given this un-collapsable edge inf cost already)
+    // Increment timestamp
+    EQ(e)++;
+    // Replace in queue
+    Q.emplace(std::numeric_limits<double>::infinity(),e,EQ(e));
+  }
+  return collapsed;
+}

+ 82 - 0
include/igl/collapse_least_cost_edge.h

@@ -0,0 +1,82 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+// 
+// Copyright (C) 2015 Alec Jacobson <[email protected]>
+// 
+// This Source Code Form is subject to the terms of the Mozilla Public License 
+// v. 2.0. If a copy of the MPL was not distributed with this file, You can 
+// obtain one at http://mozilla.org/MPL/2.0/.
+#ifndef IGL_COLLAPSE_LEAST_COST_EDGE_H
+#define IGL_COLLAPSE_LEAST_COST_EDGE_H
+#include "igl_inline.h"
+#include "min_heap.h"
+#include "decimate_callback_types.h"
+#include "COLLAPSE_EDGE_NULL.h"
+#include <Eigen/Core>
+#include <vector>
+namespace igl
+{
+  /// Collapse least-cost edge from a priority queue and update queue 
+  ///
+  /// See decimate.h for more details.
+  ///
+  /// @param[in] cost_and_placement  function computing cost of collapsing an edge and 3d
+  ///     position where it should be placed:
+  ///     cost_and_placement(V,F,E,EMAP,EF,EI,cost,placement);
+  ///     **If the edges is collapsed** then this function will be called on all
+  ///     edges of all faces previously incident on the endpoints of the
+  ///     collapsed edge.
+  ///  @param[in] pre_collapse  callback called with index of edge whose collapse is about
+  ///     to be attempted. This function should return whether to **proceed**
+  ///     with the collapse: returning true means "yes, try to collapse",
+  ///     returning false means "No, consider this edge 'uncollapsable', behave
+  ///     as if collapse_edge(e) returned false.
+  ///  @param[in] post_collapse  callback called with index of edge whose collapse was
+  ///     just attempted and a flag revealing whether this was successful.
+  /// @param[in,out] V  #V by dim list of vertex positions, lesser index of E(e,:) will be set
+  ///     to midpoint of edge.
+  /// @param[in,out] F  #F by 3 list of face indices into V.
+  /// @param[in,out] E  #E by 2 list of edge indices into V.
+  /// @param[in,out] EMAP #F*3 list of indices into E, mapping each directed edge to unique
+  ///     unique edge in E
+  /// @param[in,out] EF  #E by 2 list of edge flaps, EF(e,0)=f means e=(i-->j) is the edge of
+  ///     F(f,:) opposite the vth corner, where EI(e,0)=v. Similarly EF(e,1)
+  ///     e=(j->i)
+  /// @param[in,out] EI  #E by 2 list of edge flap corners (see above).
+  /// @param[in] Q  queue containing pairs of costs and edge indices and insertion "time"
+  /// @param[in] EQ  #E list of "time" of last time pushed into Q
+  /// @param[in] C  #E by dim list of stored placements
+  /// @param[out] e  index into E of attempted collapsed edge. Set to -1 if Q is empty or
+  ///               contains only infinite cost edges.
+  /// @param[out] e1  index into E of edge collpased on left.
+  /// @param[out] e2  index into E of edge collpased on right.
+  /// @param[out] f1  index into F of face collpased on left.
+  /// @param[out] f2  index into F of face collpased on right.
+  ///
+  /// \bug This function is not templated nicely and refactoring it and its
+  /// dependencies to do so is non-trivial, see
+  /// https://github.com/libigl/libigl/issues/2452
+  IGL_INLINE bool collapse_least_cost_edge(
+    const decimate_cost_and_placement_callback & cost_and_placement,
+    const decimate_pre_collapse_callback       & pre_collapse,
+    const decimate_post_collapse_callback      & post_collapse,
+    Eigen::MatrixXd & V,
+    Eigen::MatrixXi & F,
+    Eigen::MatrixXi & E,
+    Eigen::VectorXi & EMAP,
+    Eigen::MatrixXi & EF,
+    Eigen::MatrixXi & EI,
+    igl::min_heap< std::tuple<double,int,int> > & Q,
+    Eigen::VectorXi & EQ,
+    Eigen::MatrixXd & C,
+    int & e,
+    int & e1,
+    int & e2,
+    int & f1,
+    int & f2);
+}
+
+#ifndef IGL_STATIC_LIBRARY
+#  include "collapse_least_cost_edge.cpp"
+#endif
+#endif
+

+ 2 - 2
include/igl/decimate.cpp

@@ -6,7 +6,7 @@
 // v. 2.0. If a copy of the MPL was not distributed with this file, You can 
 // obtain one at http://mozilla.org/MPL/2.0/.
 #include "decimate.h"
-#include "collapse_edge.h"
+#include "collapse_least_cost_edge.h"
 #include "edge_flaps.h"
 #include "decimate_trivial_callbacks.h"
 #include "AABB.h"
@@ -167,7 +167,7 @@ IGL_INLINE bool igl::decimate(
   while(true)
   {
     int e,e1,e2,f1,f2;
-    if(collapse_edge(
+    if(collapse_least_cost_edge(
       cost_and_placement, pre_collapse, post_collapse,
       V,F,E,EMAP,EF,EI,Q,EQ,C,e,e1,e2,f1,f2))
     {

+ 4 - 2
include/igl/decimate.h

@@ -9,6 +9,7 @@
 #define IGL_DECIMATE_H
 #include "igl_inline.h"
 #include "decimate_callback_types.h"
+#include "COLLAPSE_EDGE_NULL.h"
 #include <Eigen/Core>
 
 /// @file decimate.h 
@@ -68,6 +69,7 @@
 /// user’s threshold).
 ///
 /// \see
+///   collapse_least_cost_edge
 ///   collapse_edge
 ///   qslim
 
@@ -119,10 +121,10 @@ namespace igl
   ///     bool should_stop =
   ///       stopping_condition(V,F,E,EMAP,EF,EI,Q,Qit,C,e,e1,e2,f1,f2);
   /// @param[in] pre_collapse  callback called with index of edge whose collapse is about
-  ///              to be attempted (see collapse_edge)
+  ///              to be attempted (see collapse_least_cost_edge)
   /// @param[in] post_collapse  callback called with index of edge whose collapse was
   ///              just attempted and a flag revealing whether this was successful (see
-  ///              collapse_edge)
+  ///              collapse_least_cost_edge)
   /// @param[out] U  #U by dim list of output vertex posistions (can be same ref as V)
   /// @param[out] G  #G by 3 list of output face indices into U (can be same ref as G)
   /// @param[out] J  #G list of indices into F of birth face

+ 0 - 1
include/igl/qslim.cpp

@@ -7,7 +7,6 @@
 // obtain one at http://mozilla.org/MPL/2.0/.
 #include "qslim.h"
 
-#include "collapse_edge.h"
 #include "connect_boundary_to_infinity.h"
 #include "decimate.h"
 #include "edge_flaps.h"

+ 2 - 2
tutorial/703_Decimation/main.cpp

@@ -1,5 +1,5 @@
 #include <igl/circulation.h>
-#include <igl/collapse_edge.h>
+#include <igl/collapse_least_cost_edge.h>
 #include <igl/edge_flaps.h>
 #include <igl/decimate.h>
 #include <igl/shortest_edge_and_midpoint.h>
@@ -92,7 +92,7 @@ int main(int argc, char * argv[])
         // Only relevant if IGL_STATIC_LIBRARY is defined
         const std::function<void (int, Eigen::Matrix<double, -1, -1, 0, -1, -1> const&, Eigen::Matrix<int, -1, -1, 0, -1, -1> const&, Eigen::Matrix<int, -1, -1, 0, -1, -1> const&, Eigen::Matrix<int, -1, 1, 0, -1, 1> const&, Eigen::Matrix<int, -1, -1, 0, -1, -1> const&, Eigen::Matrix<int, -1, -1, 0, -1, -1> const&, double&, Eigen::Matrix<double, 1, -1, 1, 1, -1>&)> cp = shortest_edge_and_midpoint;
         int e,e1,e2,f1,f2;
-        if(!collapse_edge(
+        if(!collapse_least_cost_edge(
               cp,always_try,never_care,
               V,F,E,
               EMAP,EF,EI,

Some files were not shown because too many files changed in this diff