From ee6ea7e3d026d96f0e7ec965459cbff1d7a68056 Mon Sep 17 00:00:00 2001 From: Alec Jacobson Date: Tue, 11 Nov 2014 18:19:10 -0500 Subject: [PATCH] fixed bug in outer_hull causing incorrect booleans for non-overlapping components --- examples/skeleton-posing/example.cpp | 52 +++- include/igl/WindingNumberTree.h | 4 +- include/igl/barycenter.cpp | 1 + include/igl/exterior_edges.h | 2 +- include/igl/facet_components.cpp | 19 +- include/igl/facet_components.h | 6 +- include/igl/outer_hull.cpp | 340 ++++++++++++++++----------- include/igl/sort.cpp | 1 + include/igl/unique_edge_map.h | 4 +- include/igl/winding_number.h | 18 +- 10 files changed, 282 insertions(+), 165 deletions(-) diff --git a/examples/skeleton-posing/example.cpp b/examples/skeleton-posing/example.cpp index 7f6b8d8dd..9f1b57ab0 100644 --- a/examples/skeleton-posing/example.cpp +++ b/examples/skeleton-posing/example.cpp @@ -106,7 +106,7 @@ bool is_rotating = false; bool centroid_is_visible = true; int down_x,down_y; igl::Camera down_camera; -std::string output_prefix; +std::string output_weights_filename,output_pose_prefix; struct CameraAnimation { @@ -594,13 +594,13 @@ void redo() } } -bool save() +bool save_pose() { using namespace std; using namespace igl; using namespace Eigen; string output_filename; - next_filename(output_prefix,4,".dmat",output_filename); + next_filename(output_pose_prefix,4,".dmat",output_filename); MatrixXd T; forward_kinematics(C,BE,P,s.mouse.rotations(),T); if(writeDMAT(output_filename,T)) @@ -614,6 +614,23 @@ bool save() } } +bool save_weights() +{ + using namespace std; + using namespace igl; + using namespace Eigen; + if(writeDMAT(output_weights_filename,W)) + { + cout<::set_mesh( // Q: Would we gain even more by remove almost exactly duplicate vertices? Eigen::MatrixXi SF,SVI,SVJ; igl::remove_duplicate_vertices(_V,_F,0.0,SV,SVI,SVJ,F); - triangle_fan(exterior_edges(F),cap); + triangle_fan(igl::exterior_edges(F),cap); V = SV; } @@ -183,7 +183,7 @@ inline igl::WindingNumberTree::WindingNumberTree( V(parent.V), SV(), F(_F), - cap(triangle_fan(exterior_edges(_F))) + cap(triangle_fan(igl::exterior_edges(_F))) { } diff --git a/include/igl/barycenter.cpp b/include/igl/barycenter.cpp index 79738c822..2b5805078 100644 --- a/include/igl/barycenter.cpp +++ b/include/igl/barycenter.cpp @@ -37,4 +37,5 @@ template void igl::barycenter, Eigen::M template void igl::barycenter, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); template void igl::barycenter, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); template void igl::barycenter, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::barycenter, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); #endif diff --git a/include/igl/exterior_edges.h b/include/igl/exterior_edges.h index b003f86ee..2acea1955 100644 --- a/include/igl/exterior_edges.h +++ b/include/igl/exterior_edges.h @@ -17,7 +17,7 @@ namespace igl const Eigen::MatrixXi & F, Eigen::MatrixXi & E); // Inline version - Eigen::MatrixXi exterior_edges( const Eigen::MatrixXi & F); + IGL_INLINE Eigen::MatrixXi exterior_edges( const Eigen::MatrixXi & F); } #ifndef IGL_STATIC_LIBRARY # include "exterior_edges.cpp" diff --git a/include/igl/facet_components.cpp b/include/igl/facet_components.cpp index ff2a78ce8..e3a00d6f4 100644 --- a/include/igl/facet_components.cpp +++ b/include/igl/facet_components.cpp @@ -18,10 +18,12 @@ IGL_INLINE void igl::facet_components( template < typename TTIndex, - typename DerivedC> + typename DerivedC, + typename Derivedcounts> IGL_INLINE void igl::facet_components( const std::vector > > & TT, - Eigen::PlainObjectBase & C) + Eigen::PlainObjectBase & C, + Eigen::PlainObjectBase & counts) { using namespace std; using namespace igl; @@ -30,12 +32,14 @@ IGL_INLINE void igl::facet_components( C.resize(m,1); vector seen(m,false); Index id = 0; + vector vcounts; for(Index g = 0;g Q; Q.push(g); while(!Q.empty()) @@ -47,6 +51,7 @@ IGL_INLINE void igl::facet_components( continue; } seen[f] = true; + vcounts[id]++; C(f,0) = id; // Face f's neighbor lists opposite opposite each corner for(const auto & c : TT[f]) @@ -63,9 +68,17 @@ IGL_INLINE void igl::facet_components( } id++; } + assert(id == vcounts.size()); + const size_t ncc = vcounts.size(); + assert(C.maxCoeff()+1 == ncc); + counts.resize(ncc,1); + for(size_t i = 0;i >(std::vector >, std::allocator > > >, std::allocator >, std::allocator > > > > > const&, Eigen::PlainObjectBase >&); +template void igl::facet_components, Eigen::Matrix >(std::vector >, std::allocator > > >, std::allocator >, std::allocator > > > > > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); #endif diff --git a/include/igl/facet_components.h b/include/igl/facet_components.h index 254486f97..96ea5db03 100644 --- a/include/igl/facet_components.h +++ b/include/igl/facet_components.h @@ -22,10 +22,12 @@ namespace igl // C #F list of connected component ids template < typename TTIndex, - typename DerivedC> + typename DerivedC, + typename Derivedcounts> IGL_INLINE void facet_components( const std::vector > > & TT, - Eigen::PlainObjectBase & C); + Eigen::PlainObjectBase & C, + Eigen::PlainObjectBase & counts); } #ifndef IGL_STATIC_LIBRARY # include "facet_components.cpp" diff --git a/include/igl/outer_hull.cpp b/include/igl/outer_hull.cpp index 63856799c..6b7fea2e1 100644 --- a/include/igl/outer_hull.cpp +++ b/include/igl/outer_hull.cpp @@ -1,9 +1,10 @@ #include "outer_hull.h" #include "outer_facet.h" #include "facet_components.h" - +#include "winding_number.h" #include "triangle_triangle_adjacency.h" #include "unique_edge_map.h" +#include "barycenter.h" #include "per_face_normals.h" #include "all_edges.h" #include "colon.h" @@ -33,10 +34,10 @@ IGL_INLINE void igl::outer_hull( using namespace igl; typedef typename DerivedF::Index Index; Matrix C; + typedef Matrix MatrixXV; typedef Matrix MatrixXF; typedef Matrix MatrixXG; typedef Matrix MatrixXJ; - typedef Matrix MatrixXflip; const Index m = F.rows(); typedef Matrix MatrixX2I; @@ -48,8 +49,10 @@ IGL_INLINE void igl::outer_hull( vector > > TT,_1; triangle_triangle_adjacency(E,EMAP,uE2E,false,TT,_1); - facet_components(TT,C); - const Index ncc = C.maxCoeff()+1; + VectorXI counts; + facet_components(TT,C,counts); + assert(C.maxCoeff()+1 == counts.rows()); + const size_t ncc = counts.rows(); G.resize(0,F.cols()); J.resize(0,1); flip.setConstant(m,1,false); @@ -64,162 +67,229 @@ IGL_INLINE void igl::outer_hull( vector EH(3*m,false); vector vG(ncc); vector vJ(ncc); - - // Total size of G (and J) - size_t nG = 0; - // This is O( (n+m) * nnc) ! - for(Index id = 0;id vIM(ncc); + for(size_t id = 0;id g(ncc,0); + // place order of each face in its respective component + for(Index f = 0;f Q; + Q.push(f+0*m); + Q.push(f+1*m); + Q.push(f+2*m); + flip[f] = f_flip; + while(!Q.empty()) { - if(C(f) == id) + // face-edge + const int e = Q.front(); + Q.pop(); + // face + const int f = e%m; + // corner + const int c = e/m; + // Should never see edge again... + if(EH[e] == true) { - num_id++; + continue; } - } - //MatrixXF Fc(num_id,F.cols()); - MatrixXJ IM(num_id,1); - if(C.maxCoeff() == 0) - { - assert(num_id == F.rows()); - //Fc = F; - IM = MatrixXJ::LinSpaced(F.rows(),0,F.rows()-1); - }else - { - int g = 0; - for(Index f = 0;f Q; - Q.push(f+0*m); - Q.push(f+1*m); - Q.push(f+2*m); - flip[f] = f_flip; - int FHcount = 0; - while(!Q.empty()) + // find overlapping face-edges + const auto & neighbors = uE2E[EMAP(e)]; + const auto & fN = (flip[f]?-1.:1.)*N.row(f); + // source of edge according to f + const int fs = flip[f]?F(f,(c+2)%3):F(f,(c+1)%3); + // destination of edge according to f + const int fd = flip[f]?F(f,(c+1)%3):F(f,(c+2)%3); + const auto & eV = (V.row(fd)-V.row(fs)).normalized(); + // Loop over and find max dihedral angle + typename DerivedV::Scalar max_di = -1; + int max_ne = -1; + typename Eigen::Matrix< typename DerivedV::Scalar, 1, 3> max_nN; + for(const auto & ne : neighbors) { - // face-edge - const int e = Q.front(); - Q.pop(); - // face - const int f = e%m; - // corner - const int c = e/m; - // Should never see edge again... - if(EH[e] == true) + const int nf = ne%m; + if(nf == f) { continue; } - EH[e] = true; - // first time seeing face - if(!FH[f]) + const int nc = ne/m; + // are faces consistently oriented + //const int ns = F(nf,(nc+1)%3); + const int nd = F(nf,(nc+2)%3); + const bool cons = (flip[f]?fd:fs) == nd; + const auto & nN = (cons? (flip[f]?-1:1.) : (flip[f]?1.:-1.) )*N.row(nf); + const auto & ndi = M_PI - atan2( fN.cross(nN).dot(eV), fN.dot(nN)); + if(ndi>=max_di) { - FH[f] = true; - FHcount++; - } - // find overlapping face-edges - const auto & neighbors = uE2E[EMAP(e)]; - const auto & fN = (flip[f]?-1.:1.)*N.row(f); - // source of edge according to f - const int fs = flip[f]?F(f,(c+2)%3):F(f,(c+1)%3); - // destination of edge according to f - const int fd = flip[f]?F(f,(c+1)%3):F(f,(c+2)%3); - const auto & eV = (V.row(fd)-V.row(fs)).normalized(); - // Loop over and find max dihedral angle - typename DerivedV::Scalar max_di = -1; - int max_ne = -1; - typename Eigen::Matrix< typename DerivedV::Scalar, 1, 3> max_nN; - for(const auto & ne : neighbors) - { - const int nf = ne%m; - if(nf == f) - { - continue; - } - const int nc = ne/m; - // are faces consistently oriented - //const int ns = F(nf,(nc+1)%3); - const int nd = F(nf,(nc+2)%3); - const bool cons = (flip[f]?fd:fs) == nd; - const auto & nN = (cons? (flip[f]?-1:1.) : (flip[f]?1.:-1.) )*N.row(nf); - const auto & ndi = M_PI - atan2( fN.cross(nN).dot(eV), fN.dot(nN)); - if(ndi>=max_di) - { - max_ne = ne; - max_di = ndi; - max_nN = nN; - } - } - if(max_ne>=0) - { - const int nf = max_ne%m; - const int nc = max_ne/m; - const int nd = F(nf,(nc+2)%3); - const bool cons = (flip[f]?fd:fs) == nd; - flip[nf] = (cons ? flip[f] : !flip[f]); - const int ne1 = nf+((nc+1)%3)*m; - const int ne2 = nf+((nc+2)%3)*m; - if(!EH[ne1]) - { - Q.push(ne1); - } - if(!EH[ne2]) - { - Q.push(ne2); - } + max_ne = ne; + max_di = ndi; + max_nN = nN; } } - + if(max_ne>=0) { - vG[id].resize(FHcount,3); - vJ[id].resize(FHcount,1); - nG += FHcount; - size_t h = 0; - for(int i = 0;i & V, + const MatrixXV & BC, + const MatrixXG & A, + const MatrixXJ & AJ, + const MatrixXG & B)->bool + { + const auto & bounding_box = []( + const Eigen::PlainObjectBase & V, + const MatrixXG & F)-> + MatrixXV + { + MatrixXV BB(2,3); + BB<< + 1e26,1e26,1e26, + -1e26,-1e26,-1e26; + const size_t m = F.rows(); + for(size_t f = 0;f0 || + (ABB.row(0)-BBB.row(1)).maxCoeff()>0 ) + { + // bounding boxes do not overlap + return false; + } + //////////////////////////////////////////////////////////////////////// + // POTENTIAL ROBUSTNESS WEAK AREA + //////////////////////////////////////////////////////////////////////// + // + // q could be so close (<~1e-16) to B that the winding number is not a robust way to + // determine inside/outsideness. We could try to find a _better_ q which is + // farther away, but couldn't they all be bad? + MatrixXV q = BC.row(AJ(1)); + // In a perfect world, it's enough to test a single point. + double w; + winding_number_3( + V.data(),V.rows(), + B.data(),B.rows(), + q.data(),1,&w); + return fabs(w)>0.5; + }; + + // Reject components which are completely inside other components + vector keep(ncc,true); + size_t nG = 0; + // This is O( ncc * ncc * m) + for(size_t id = 0;id, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); template void igl::sort, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); template void igl::sort, Eigen::Matrix >(Eigen::PlainObjectBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sort, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); #endif diff --git a/include/igl/unique_edge_map.h b/include/igl/unique_edge_map.h index 5cbb17e20..e3583e310 100644 --- a/include/igl/unique_edge_map.h +++ b/include/igl/unique_edge_map.h @@ -1,5 +1,5 @@ -#ifndef IGL_EXTERIOR_EDGES_H -#define IGL_EXTERIOR_EDGES_H +#ifndef IGL_UNIQUE_EDGE_MAP_H +#define IGL_UNIQUE_EDGE_MAP_H #include "igl_inline.h" #include #include diff --git a/include/igl/winding_number.h b/include/igl/winding_number.h index f3eb9e32f..7629f3b11 100644 --- a/include/igl/winding_number.h +++ b/include/igl/winding_number.h @@ -39,15 +39,15 @@ namespace igl const double * O, const int no, double * S); - // Only one evaluation origin - template - IGL_INLINE void winding_number_3( - const double * V, - const int n, - const DerivedF * F, - const int m, - const double * O, - double * S); + //// Only one evaluation origin + //template + //IGL_INLINE void winding_number_3( + // const double * V, + // const int n, + // const DerivedF * F, + // const int m, + // const double * O, + // double * S); // 2d template IGL_INLINE void winding_number_2(