pre and post edge collapse callbacks, qslim

This commit is contained in:
Alec Jacobson
2016-11-23 00:52:42 -05:00
parent 501a485477
commit a85dc83728
13 changed files with 613 additions and 8 deletions
+17 -3
View File
@@ -165,6 +165,8 @@ IGL_INLINE bool igl::collapse_edge(
const Eigen::MatrixXi &,
double &,
Eigen::RowVectorXd &)> & cost_and_placement,
const std::function<bool(const int )> & pre_collapse,
const std::function<void(const int , const bool)> & post_collapse,
Eigen::MatrixXd & V,
Eigen::MatrixXi & F,
Eigen::MatrixXi & E,
@@ -177,7 +179,9 @@ IGL_INLINE bool igl::collapse_edge(
{
int e,e1,e2,f1,f2;
return
collapse_edge(cost_and_placement,V,F,E,EMAP,EF,EI,Q,Qit,C,e,e1,e2,f1,f2);
collapse_edge(
cost_and_placement,pre_collapse,post_collapse,
V,F,E,EMAP,EF,EI,Q,Qit,C,e,e1,e2,f1,f2);
}
@@ -192,6 +196,8 @@ IGL_INLINE bool igl::collapse_edge(
const Eigen::MatrixXi &,
double &,
Eigen::RowVectorXd &)> & cost_and_placement,
const std::function<bool(const int )> & pre_collapse,
const std::function<void(const int , const bool)> & post_collapse,
Eigen::MatrixXd & V,
Eigen::MatrixXi & F,
Eigen::MatrixXi & E,
@@ -225,8 +231,16 @@ IGL_INLINE bool igl::collapse_edge(
std::vector<int> N = circulation(e, true,F,E,EMAP,EF,EI);
std::vector<int> Nd = circulation(e,false,F,E,EMAP,EF,EI);
N.insert(N.begin(),Nd.begin(),Nd.end());
const bool collapsed =
collapse_edge(e,C.row(e),V,F,E,EMAP,EF,EI,e1,e2,f1,f2);
bool collapsed = true;
if(pre_collapse(e))
{
collapsed = collapse_edge(e,C.row(e),V,F,E,EMAP,EF,EI,e1,e2,f1,f2);
}else
{
// Aborted by pre collapse callback
collapsed = false;
}
post_collapse(e,collapsed);
if(collapsed)
{
// Erase the two, other collapsed edges
+14
View File
@@ -70,6 +70,16 @@ namespace igl
// 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.
// 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.
// post_collapse callback called with index of edge whose collapse was
// just attempted and a flag revealing whether this was successful.
// Q queue containing pairs of costs and edge indices
// Qit list of iterators so that Qit[e] --> iterator of edge e in Q
// C #E by dim list of stored placements
@@ -84,6 +94,8 @@ namespace igl
const Eigen::MatrixXi &,
double &,
Eigen::RowVectorXd &)> & cost_and_placement,
const std::function<bool(const int )> & pre_collapse,
const std::function<void(const int , const bool)> & post_collapse,
Eigen::MatrixXd & V,
Eigen::MatrixXi & F,
Eigen::MatrixXi & E,
@@ -104,6 +116,8 @@ namespace igl
const Eigen::MatrixXi &,
double &,
Eigen::RowVectorXd &)> & cost_and_placement,
const std::function<bool(const int )> & pre_collapse,
const std::function<void(const int , const bool)> & post_collapse,
Eigen::MatrixXd & V,
Eigen::MatrixXi & F,
Eigen::MatrixXi & E,
+67 -4
View File
@@ -35,11 +35,15 @@ IGL_INLINE bool igl::decimate(
DerivedV VO;
DerivedF FO;
igl::connect_boundary_to_infinity(V,F,VO,FO);
const auto & always_try = [](const int e)->bool{return true;};
const auto & never_care = [](const int e, const bool collapsed){};
bool ret = decimate(
VO,
FO,
shortest_edge_and_midpoint,
max_faces_stopping_condition(m,orig_m,max_m),
always_try,
never_care,
U,
G,
J,
@@ -93,6 +97,61 @@ IGL_INLINE bool igl::decimate(
const int,
const int,
const int)> & stopping_condition,
const std::function<bool(const int )> & pre_collapse,
const std::function<void(const int , const bool)> & post_collapse,
Eigen::MatrixXd & U,
Eigen::MatrixXi & G,
Eigen::VectorXi & J,
Eigen::VectorXi & I
)
{
using namespace Eigen;
using namespace std;
VectorXi EMAP;
MatrixXi E,EF,EI;
edge_flaps(OF,E,EMAP,EF,EI);
return igl::decimate(
OV,OF,
cost_and_placement,stopping_condition,pre_collapse,post_collapse,
E,EMAP,EF,EI,
U,G,J,I);
}
IGL_INLINE bool igl::decimate(
const Eigen::MatrixXd & OV,
const Eigen::MatrixXi & OF,
const std::function<void(
const int,
const Eigen::MatrixXd &,
const Eigen::MatrixXi &,
const Eigen::MatrixXi &,
const Eigen::VectorXi &,
const Eigen::MatrixXi &,
const Eigen::MatrixXi &,
double &,
Eigen::RowVectorXd &)> & cost_and_placement,
const std::function<bool(
const Eigen::MatrixXd &,
const Eigen::MatrixXi &,
const Eigen::MatrixXi &,
const Eigen::VectorXi &,
const Eigen::MatrixXi &,
const Eigen::MatrixXi &,
const std::set<std::pair<double,int> > &,
const std::vector<std::set<std::pair<double,int> >::iterator > &,
const Eigen::MatrixXd &,
const int,
const int,
const int,
const int,
const int)> & stopping_condition,
const std::function<bool(const int )> & pre_collapse,
const std::function<void(const int , const bool)> & post_collapse,
const Eigen::MatrixXi & OE,
const Eigen::VectorXi & OEMAP,
const Eigen::MatrixXi & OEF,
const Eigen::MatrixXi & OEI,
Eigen::MatrixXd & U,
Eigen::MatrixXi & G,
Eigen::VectorXi & J,
@@ -104,12 +163,14 @@ IGL_INLINE bool igl::decimate(
// Working copies
Eigen::MatrixXd V = OV;
Eigen::MatrixXi F = OF;
VectorXi EMAP;
MatrixXi E,EF,EI;
Eigen::MatrixXi E = OE;
Eigen::VectorXi EMAP = OEMAP;
Eigen::MatrixXi EF = OEF;
Eigen::MatrixXi EI = OEI;
typedef std::set<std::pair<double,int> > PriorityQueue;
PriorityQueue Q;
std::vector<PriorityQueue::iterator > Qit;
edge_flaps(F,E,EMAP,EF,EI);
Qit.resize(E.rows());
// If an edge were collapsed, we'd collapse it to these points:
MatrixXd C(E.rows(),V.cols());
@@ -136,7 +197,9 @@ IGL_INLINE bool igl::decimate(
break;
}
int e,e1,e2,f1,f2;
if(collapse_edge(cost_and_placement,V,F,E,EMAP,EF,EI,Q,Qit,C,e,e1,e2,f1,f2))
if(collapse_edge(
cost_and_placement, pre_collapse, post_collapse,
V,F,E,EMAP,EF,EI,Q,Qit,C,e,e1,e2,f1,f2))
{
if(stopping_condition(V,F,E,EMAP,EF,EI,Q,Qit,C,e,e1,e2,f1,f2))
{
+54
View File
@@ -65,6 +65,11 @@ namespace igl
// collapsing edge e removing edges (e,e1,e2) and faces (f1,f2):
// bool should_stop =
// stopping_condition(V,F,E,EMAP,EF,EI,Q,Qit,C,e,e1,e2,f1,f2);
// pre_collapse callback called with index of edge whose collapse is about
// to be attempted (see collapse_edge)
// post_collapse callback called with index of edge whose collapse was
// just attempted and a flag revealing whether this was successful (see
// collapse_edge)
IGL_INLINE bool decimate(
const Eigen::MatrixXd & V,
const Eigen::MatrixXi & F,
@@ -95,6 +100,55 @@ namespace igl
const int ,/*f1*/
const int /*f2*/
)> & stopping_condition,
const std::function<bool(const int )> & pre_collapse,
const std::function<void(const int , const bool)> & post_collapse,
Eigen::MatrixXd & U,
Eigen::MatrixXi & G,
Eigen::VectorXi & J,
Eigen::VectorXi & I);
// Inputs:
// EMAP #F*3 list of indices into E, mapping each directed edge to unique
// unique edge in E
// 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)
// EI #E by 2 list of edge flap corners (see above).
IGL_INLINE bool decimate(
const Eigen::MatrixXd & V,
const Eigen::MatrixXi & F,
const std::function<void(
const int /*e*/,
const Eigen::MatrixXd &/*V*/,
const Eigen::MatrixXi &/*F*/,
const Eigen::MatrixXi &/*E*/,
const Eigen::VectorXi &/*EMAP*/,
const Eigen::MatrixXi &/*EF*/,
const Eigen::MatrixXi &/*EI*/,
double & /*cost*/,
Eigen::RowVectorXd & /*p*/
)> & cost_and_placement,
const std::function<bool(
const Eigen::MatrixXd & ,/*V*/
const Eigen::MatrixXi & ,/*F*/
const Eigen::MatrixXi & ,/*E*/
const Eigen::VectorXi & ,/*EMAP*/
const Eigen::MatrixXi & ,/*EF*/
const Eigen::MatrixXi & ,/*EI*/
const std::set<std::pair<double,int> > & ,/*Q*/
const std::vector<std::set<std::pair<double,int> >::iterator > &,/*Qit*/
const Eigen::MatrixXd & ,/*C*/
const int ,/*e*/
const int ,/*e1*/
const int ,/*e2*/
const int ,/*f1*/
const int /*f2*/
)> & stopping_condition,
const std::function<bool(const int )> & pre_collapse,
const std::function<void(const int , const bool)> & post_collapse,
const Eigen::MatrixXi & E,
const Eigen::VectorXi & EMAP,
const Eigen::MatrixXi & EF,
const Eigen::MatrixXi & EI,
Eigen::MatrixXd & U,
Eigen::MatrixXi & G,
Eigen::VectorXi & J,
@@ -0,0 +1,141 @@
#include "per_vertex_point_to_plane_quadrics.h"
#include <Eigen/QR>
#include <cassert>
IGL_INLINE void igl::per_vertex_point_to_plane_quadrics(
const Eigen::MatrixXd & V,
const Eigen::MatrixXi & F,
const Eigen::MatrixXi & EMAP,
const Eigen::MatrixXi & EF,
const Eigen::MatrixXi & EI,
std::vector<
std::tuple<Eigen::MatrixXd,Eigen::RowVectorXd,double> > & quadrics)
{
using namespace std;
typedef std::tuple<Eigen::MatrixXd,Eigen::RowVectorXd,double> Quadric;
const int dim = V.cols();
//// Quadrics per face
//std::vector<Quadric> face_quadrics(F.rows());
// Initialize each vertex quadric to zeros
quadrics.resize(
V.rows(),{Eigen::MatrixXd::Zero(dim,dim),Eigen::RowVectorXd::Zero(dim),0});
Eigen::MatrixXd I = Eigen::MatrixXd::Identity(dim,dim);
// Rather initial with zeros, initial with a small amount of energy pull
// toward original vertex position
const double w = 1e-10;
for(int v = 0;v<V.rows();v++)
{
std::get<0>(quadrics[v]) = w*I;
Eigen::RowVectorXd Vv = V.row(v);
std::get<1>(quadrics[v]) = w*-Vv;
std::get<2>(quadrics[v]) = w*Vv.dot(Vv);
}
// Generic nD qslim from "Simplifying Surfaces with Color and Texture
// using Quadric Error Metric" (follow up to original QSlim)
for(int f = 0;f<F.rows();f++)
{
int infinite_corner = -1;
for(int c = 0;c<3;c++)
{
if(isinf(V(F(f,c),0)) || isinf(V(F(f,c),1)) || isinf(V(F(f,c),2)))
{
assert(infinite_corner == -1 && "Should only be one infinite corner");
infinite_corner = c;
}
}
// Inputs:
// p 1 by n row point on the subspace
// S m by n matrix where rows coorespond to orthonormal spanning
// vectors of the subspace to which we're measuring distance (usually
// a plane, m=2)
// weight scalar weight
// Returns quadric triple {A,b,c} so that A-2*b+c measures the quadric
const auto subspace_quadric = [&I](
const Eigen::RowVectorXd & p,
const Eigen::MatrixXd & S,
const double weight)->Quadric
{
// Dimension of subspace
const int m = S.rows();
// Weight face's quadric (v'*A*v + 2*b'*v + c) by area
// e1 and e2 should be perpendicular
Eigen::MatrixXd A = I;
Eigen::RowVectorXd b = -p;
double c = p.dot(p);
for(int i = 0;i<m;i++)
{
Eigen::RowVectorXd ei = S.row(i);
for(int j = 0;j<i;j++) assert(std::abs(S.row(j).dot(ei)) < 1e-10);
A += -ei.transpose()*ei;
b += p.dot(ei)*ei;
c += -pow(p.dot(ei),2);
}
return { weight*A, weight*b, weight*c };
};
if(infinite_corner == -1)
{
// Finite (non-boundary) face
Eigen::RowVectorXd p = V.row(F(f,0));
Eigen::RowVectorXd q = V.row(F(f,1));
Eigen::RowVectorXd r = V.row(F(f,2));
Eigen::RowVectorXd pq = q-p;
Eigen::RowVectorXd pr = r-p;
// Gram Determinant = squared area of parallelogram
double area = sqrt(pq.squaredNorm()*pr.squaredNorm()-pow(pr.dot(pq),2));
Eigen::RowVectorXd e1 = pq.normalized();
Eigen::RowVectorXd e2 = (pr-e1.dot(pr)*e1).normalized();
Eigen::MatrixXd S(2,V.cols());
S<<e1,e2;
Quadric face_quadric = subspace_quadric(p,S,area);
// Throw at each corner
for(int c = 0;c<3;c++)
{
quadrics[F(f,c)] = quadrics[F(f,c)] + face_quadric;
}
}else
{
// cth corner is infinite --> edge opposite cth corner is boundary
// Boundary edge vector
const Eigen::RowVectorXd p = V.row(F(f,(infinite_corner+1)%3));
Eigen::RowVectorXd ev = V.row(F(f,(infinite_corner+2)%3)) - p;
const double length = ev.norm();
ev /= length;
// Face neighbor across boundary edge
int e = EMAP(f+F.rows()*infinite_corner);
int opp = EF(e,0) == f ? 1 : 0;
int n = EF(e,opp);
int nc = EI(e,opp);
assert(
((F(f,(infinite_corner+1)%3) == F(n,(nc+1)%3) &&
F(f,(infinite_corner+2)%3) == F(n,(nc+2)%3)) ||
(F(f,(infinite_corner+1)%3) == F(n,(nc+2)%3)
&& F(f,(infinite_corner+2)%3) == F(n,(nc+1)%3))) &&
"Edge flaps not agreeing on shared edge");
// Edge vector on opposite face
const Eigen::RowVectorXd eu = V.row(F(n,nc)) - p;
assert(!isinf(eu(0)));
// Matrix with vectors spanning plane as columns
Eigen::MatrixXd A(ev.size(),2);
A<<ev.transpose(),eu.transpose();
// Use QR decomposition to find basis for orthogonal space
Eigen::HouseholderQR<Eigen::MatrixXd> qr(A);
const Eigen::MatrixXd Q = qr.householderQ();
const Eigen::MatrixXd N =
Q.topRightCorner(ev.size(),ev.size()-2).transpose();
assert(N.cols() == ev.size());
assert(N.rows() == ev.size()-2);
Eigen::MatrixXd S(N.rows()+1,ev.size());
S<<ev,N;
Quadric boundary_edge_quadric = subspace_quadric(p,S,length);
for(int c = 0;c<3;c++)
{
if(c != infinite_corner)
{
quadrics[F(f,c)] = quadrics[F(f,c)] + boundary_edge_quadric;
}
}
}
}
}
@@ -0,0 +1,46 @@
#ifndef IGL_PER_VERTEX_POINT_TO_PLANE_QUADRICS_H
#define IGL_PER_VERTEX_POINT_TO_PLANE_QUADRICS_H
#include "igl_inline.h"
#include <Eigen/Core>
#include <vector>
#include <tuple>
namespace igl
{
// Compute quadrics per vertex of a "closed" triangle mesh (V,F). Rather than
// follow the qslim paper, this implements the lesser-known _follow up_
// "Simplifying Surfaces with Color and Texture using Quadric Error Metrics".
// This allows V to be n-dimensional (where the extra coordiantes store
// texture UVs, color RGBs, etc.
//
// Inputs:
// V #V by n list of vertex positions. Assumes that vertices with
// infinite coordinates are "points at infinity" being used to close up
// boundary edges with faces. This allows special subspace quadrice for
// boundary edges: There should never be more than one "point at
// infinity" in a single triangle.
// F #F by 3 list of triangle indices into V
// E #E by 2 list of edge indices into V.
// EMAP #F*3 list of indices into E, mapping each directed edge to unique
// unique edge in E
// 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)
// EI #E by 2 list of edge flap corners (see above).
// Outputs:
// quadrics #V list of quadrics, where a quadric is a tuple {A,b,c} such
// that the quadratic energy of moving this vertex to position x is
// given by x'Ax - 2b + c
//
IGL_INLINE void per_vertex_point_to_plane_quadrics(
const Eigen::MatrixXd & V,
const Eigen::MatrixXi & F,
const Eigen::MatrixXi & EMAP,
const Eigen::MatrixXi & EF,
const Eigen::MatrixXi & EI,
std::vector<
std::tuple<Eigen::MatrixXd,Eigen::RowVectorXd,double> > & quadrics);
};
#ifndef IGL_STATIC_LIBRAY
# include "per_vertex_point_to_plane_quadrics.cpp"
#endif
#endif
+76
View File
@@ -0,0 +1,76 @@
#include "qslim.h"
#include "collapse_edge.h"
#include "connect_boundary_to_infinity.h"
#include "decimate.h"
#include "edge_flaps.h"
#include "max_faces_stopping_condition.h"
#include "per_vertex_point_to_plane_quadrics.h"
#include "qslim_optimal_collapse_edge_callbacks.h"
#include "quadric_binary_plus_operator.h"
#include "remove_unreferenced.h"
#include "slice.h"
#include "slice_mask.h"
IGL_INLINE bool igl::qslim(
const Eigen::MatrixXd & V,
const Eigen::MatrixXi & F,
const size_t max_m,
Eigen::MatrixXd & U,
Eigen::MatrixXi & G,
Eigen::VectorXi & J,
Eigen::VectorXi & I)
{
using namespace igl;
// Original number of faces
const int orig_m = F.rows();
// Tracking number of faces
int m = F.rows();
typedef Eigen::MatrixXd DerivedV;
typedef Eigen::MatrixXi DerivedF;
DerivedV VO;
DerivedF FO;
igl::connect_boundary_to_infinity(V,F,VO,FO);
Eigen::VectorXi EMAP;
Eigen::MatrixXi E,EF,EI;
edge_flaps(FO,E,EMAP,EF,EI);
// Quadrics per vertex
typedef std::tuple<Eigen::MatrixXd,Eigen::RowVectorXd,double> Quadric;
std::vector<Quadric> quadrics;
per_vertex_point_to_plane_quadrics(VO,FO,EMAP,EF,EI,quadrics);
// State variables keeping track of edge we just collapsed
int v1 = -1;
int v2 = -1;
// Callbacks for computing and updating metric
std::function<void(
const int e,
const Eigen::MatrixXd &,
const Eigen::MatrixXi &,
const Eigen::MatrixXi &,
const Eigen::VectorXi &,
const Eigen::MatrixXi &,
const Eigen::MatrixXi &,
double &,
Eigen::RowVectorXd &)> cost_and_placement;
std::function<bool(const int)> pre_collapse;
std::function<void(const int,const bool)> post_collapse;
qslim_optimal_collapse_edge_callbacks(
E,quadrics,v1,v2, cost_and_placement, pre_collapse,post_collapse);
// Call to greedy decimator
bool ret = decimate(
VO, FO,
cost_and_placement,
max_faces_stopping_condition(m,orig_m,max_m),
pre_collapse,
post_collapse,
E, EMAP, EF, EI,
U, G, J, I);
// Remove phony boundary faces and clean up
const Eigen::Array<bool,Eigen::Dynamic,1> keep = (J.array()<orig_m);
igl::slice_mask(Eigen::MatrixXi(G),keep,1,G);
igl::slice_mask(Eigen::VectorXi(J),keep,1,J);
Eigen::VectorXi _1,I2;
igl::remove_unreferenced(Eigen::MatrixXd(U),Eigen::MatrixXi(G),U,G,_1,I2);
igl::slice(Eigen::VectorXi(I),I2,1,I);
return ret;
}
+34
View File
@@ -0,0 +1,34 @@
#ifndef IGL_QSLIM_H
#define IGL_QSLIM_H
#include "igl_inline.h"
#include <Eigen/Core>
namespace igl
{
// Decimate (simplify) a triangle mesh in nD according to the paper
// "Simplifying Surfaces with Color and Texture using Quadric Error Metrics"
// by [Garland and Heckbert, 1987] (technically a followup to qslim). The
// mesh can have open boundaries but should be edge-manifold.
//
// Inputs:
// V #V by dim list of vertex positions. Assumes that vertices w
// F #F by 3 list of triangle indices into V
// max_m desired number of output faces
// Outputs:
// U #U by dim list of output vertex posistions (can be same ref as V)
// G #G by 3 list of output face indices into U (can be same ref as G)
// J #G list of indices into F of birth face
// I #U list of indices into V of birth vertices
IGL_INLINE bool qslim(
const Eigen::MatrixXd & V,
const Eigen::MatrixXi & F,
const size_t max_m,
Eigen::MatrixXd & U,
Eigen::MatrixXi & G,
Eigen::VectorXi & J,
Eigen::VectorXi & I);
}
#ifndef IGL_STATIC_LIBRARY
# include "qslim.cpp"
#endif
#endif
@@ -0,0 +1,68 @@
#include "qslim_optimal_collapse_edge_callbacks.h"
IGL_INLINE void igl::qslim_optimal_collapse_edge_callbacks(
Eigen::MatrixXi & E,
std::vector<std::tuple<Eigen::MatrixXd,Eigen::RowVectorXd,double> > &
quadrics,
int & v1,
int & v2,
std::function<void(
const int e,
const Eigen::MatrixXd &,
const Eigen::MatrixXi &,
const Eigen::MatrixXi &,
const Eigen::VectorXi &,
const Eigen::MatrixXi &,
const Eigen::MatrixXi &,
double &,
Eigen::RowVectorXd &)> & cost_and_placement,
std::function<bool(const int)> & pre_collapse,
std::function<void(const int,const bool)> & post_collapse)
{
typedef std::tuple<Eigen::MatrixXd,Eigen::RowVectorXd,double> Quadric;
cost_and_placement = [&quadrics,&v1,&v2](
const int e,
const Eigen::MatrixXd & V,
const Eigen::MatrixXi & /*F*/,
const Eigen::MatrixXi & E,
const Eigen::VectorXi & /*EMAP*/,
const Eigen::MatrixXi & /*EF*/,
const Eigen::MatrixXi & /*EI*/,
double & cost,
Eigen::RowVectorXd & p)
{
// Combined quadric
Quadric quadric_p;
quadric_p = quadrics[E(e,0)] + quadrics[E(e,1)];
// Quadric: p'Ap + 2b'p + c
// optimal point: Ap = -b, or rather because we have row vectors: pA=-b
const auto & A = std::get<0>(quadric_p);
const auto & b = std::get<1>(quadric_p);
const auto & c = std::get<2>(quadric_p);
p = -b*A.inverse();
cost = p.dot(p*A) + 2*p.dot(b) + c;
// Force infs and nans to infinity
if(isinf(cost) || cost!=cost)
{
cost = std::numeric_limits<double>::infinity();
}
};
// Remember endpoints
pre_collapse = [&v1,&v2,&E](const int e)->bool
{
v1 = E(e,0);
v2 = E(e,1);
return true;
};
// update quadric
post_collapse = [&v1,&v2,&quadrics](
const int e,
const bool collapsed)
{
if(collapsed)
{
quadrics[v1<v2?v1:v2] = quadrics[v1] + quadrics[v2];
}
};
}
@@ -0,0 +1,46 @@
#ifndef IGL_QSLIM_OPTIMAL_COLLAPSE_EDGE_CALLBACKS_H
#define IGL_QSLIM_OPTIMAL_COLLAPSE_EDGE_CALLBACKS_H
#include "igl_inline.h"
#include <Eigen/Core>
#include <functional>
#include <vector>
#include <tuple>
namespace igl
{
// Prepare callbacks for decimating edges using the qslim optimal placement
// metric.
//
// Inputs:
// E #E by 2 list of working edges
// quadrics reference to list of working per vertex quadrics
// v1 working variable to maintain end point of collapsed edge
// v2 working variable to maintain end point of collapsed edge
// Outputs
// cost_and_placement callback for evaluating cost of edge collapse and
// determining placement of vertex (see collapse_edge)
// pre_collapse callback before edge collapse (see collapse_edge)
// post_collapse callback after edge collapse (see collapse_edge)
IGL_INLINE void qslim_optimal_collapse_edge_callbacks(
Eigen::MatrixXi & E,
std::vector<std::tuple<Eigen::MatrixXd,Eigen::RowVectorXd,double> > &
quadrics,
int & v1,
int & v2,
std::function<void(
const int e,
const Eigen::MatrixXd &,
const Eigen::MatrixXi &,
const Eigen::MatrixXi &,
const Eigen::VectorXi &,
const Eigen::MatrixXi &,
const Eigen::MatrixXi &,
double &,
Eigen::RowVectorXd &)> & cost_and_placement,
std::function<bool(const int)> & pre_collapse,
std::function<void(const int,const bool)> & post_collapse);
}
#ifndef IGL_STATIC_LIBRARY
# include "qslim_optimal_collapse_edge_callbacks.cpp"
#endif
#endif
@@ -0,0 +1,17 @@
#include "quadric_binary_plus_operator.h"
IGL_INLINE std::tuple< Eigen::MatrixXd, Eigen::RowVectorXd, double>
igl::operator+(
const std::tuple< Eigen::MatrixXd, Eigen::RowVectorXd, double> & a,
const std::tuple< Eigen::MatrixXd, Eigen::RowVectorXd, double> & b)
{
std::tuple<
Eigen::MatrixXd,
Eigen::RowVectorXd,
double> c;
std::get<0>(c) = (std::get<0>(a) + std::get<0>(b)).eval();
std::get<1>(c) = (std::get<1>(a) + std::get<1>(b)).eval();
std::get<2>(c) = (std::get<2>(a) + std::get<2>(b));
return c;
}
@@ -0,0 +1,28 @@
#ifndef IGL_QUADRIC_BINARY_PLUS_OPERATOR_H
#define IGL_QUADRIC_BINARY_PLUS_OPERATOR_H
#include "igl_inline.h"
#include <Eigen/Core>
#include <tuple>
namespace igl
{
// A binary addition operator for Quadric tuples compatible with qslim,
// computing c = a+b
//
// Inputs:
// a QSlim quadric
// b QSlim quadric
// Output
// c QSlim quadric
//
IGL_INLINE std::tuple< Eigen::MatrixXd, Eigen::RowVectorXd, double>
operator+(
const std::tuple< Eigen::MatrixXd, Eigen::RowVectorXd, double> & a,
const std::tuple< Eigen::MatrixXd, Eigen::RowVectorXd, double> & b);
}
#ifndef IGL_STATIC_LIBRARY
# include "quadric_binary_plus_operator.cpp"
#endif
#endif
+5 -1
View File
@@ -75,7 +75,11 @@ int main(int argc, char * argv[])
const int max_iter = std::ceil(0.01*Q.size());
for(int j = 0;j<max_iter;j++)
{
if(!collapse_edge(shortest_edge_and_midpoint,V,F,E,EMAP,EF,EI,Q,Qit,C))
const auto & always_try = [](const int e)->bool{return true;};
const auto & never_care = [](const int e, const bool collapsed){};
if(!collapse_edge(
shortest_edge_and_midpoint,always_try,never_care,
V,F,E,EMAP,EF,EI,Q,Qit,C))
{
break;
}