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
This commit is contained in:
@@ -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 }} ^
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2025 Alec Jacobson <alecjacobson@gmail.com>
|
||||
//
|
||||
// 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
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "collapse_edge.h"
|
||||
#include "circulation.h"
|
||||
#include "edge_collapse_is_valid.h"
|
||||
#include "decimate_trivial_callbacks.h"
|
||||
#include <vector>
|
||||
|
||||
template <
|
||||
@@ -195,162 +194,9 @@ IGL_INLINE bool igl::collapse_edge(
|
||||
return true;
|
||||
}
|
||||
|
||||
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 igl::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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
#ifdef IGL_STATIC_LIBRARY
|
||||
// Explicit template instantiation
|
||||
template bool igl::collapse_edge<
|
||||
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>&)>,
|
||||
std::function<bool (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&, std::priority_queue<std::tuple<double, int, int>, std::vector<std::tuple<double, int, int>, std::allocator<std::tuple<double, int, int>>>, std::greater<std::tuple<double, int, int>>> const&, Eigen::Matrix<int, -1, 1, 0, -1, 1> const&, Eigen::Matrix<double, -1, -1, 0, -1, -1> const&, int)>,
|
||||
std::function<void (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&, std::priority_queue<std::tuple<double, int, int>, std::vector<std::tuple<double, int, int>, std::allocator<std::tuple<double, int, int>>>, std::greater<std::tuple<double, int, int>>> const&, Eigen::Matrix<int, -1, 1, 0, -1, 1> const&, Eigen::Matrix<double, -1, -1, 0, -1, -1> const&, int, int, int, int, int, bool)>,
|
||||
Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>>(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>&)> const&, std::function<bool (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&, std::priority_queue<std::tuple<double, int, int>, std::vector<std::tuple<double, int, int>, std::allocator<std::tuple<double, int, int>>>, std::greater<std::tuple<double, int, int>>> const&, Eigen::Matrix<int, -1, 1, 0, -1, 1> const&, Eigen::Matrix<double, -1, -1, 0, -1, -1> const&, int)> const&, std::function<void (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&, std::priority_queue<std::tuple<double, int, int>, std::vector<std::tuple<double, int, int>, std::allocator<std::tuple<double, int, int>>>, std::greater<std::tuple<double, int, int>>> const&, Eigen::Matrix<int, -1, 1, 0, -1, 1> const&, Eigen::Matrix<double, -1, -1, 0, -1, -1> const&, int, int, int, int, int, bool)> const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1>>&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>>&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>>&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1>>&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>>&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>>&, std::priority_queue<std::tuple<double, int, int>, std::vector<std::tuple<double, int, int>, std::allocator<std::tuple<double, int, int>>>, std::greater<std::tuple<double, int, int>>>&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1>>&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1>>&, int&, int&, int&, int&, int&);
|
||||
template bool igl::collapse_edge<Eigen::Matrix<double, 1, -1, 1, 1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>>(int, Eigen::MatrixBase<Eigen::Matrix<double, 1, -1, 1, 1, -1>> const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1>>&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>>&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>>&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1>>&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>>&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>>&, int&, int&, int&, int&);
|
||||
template bool igl::collapse_edge<Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1>, 1, -1, false>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>>(int, Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1>, 1, -1, false>> const&, std::vector<int, std::allocator<int>>&, std::vector<int, std::allocator<int>> const&, std::vector<int, std::allocator<int>>&, std::vector<int, std::allocator<int>> const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1>>&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>>&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>>&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1>>&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>>&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>>&, int&, int&, int&, int&);
|
||||
#endif
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2025 Alec Jacobson <alecjacobson@gmail.com>
|
||||
//
|
||||
// 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;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2015 Alec Jacobson <alecjacobson@gmail.com>
|
||||
//
|
||||
// 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
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user