#include "outer_hull.h" #include "outer_facet.h" #include "sortrows.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 "writePLY.h" #include "sort_angles.h" #include #include #include #include #include #include //#define IGL_OUTER_HULL_DEBUG template < typename DerivedV, typename DerivedF, typename DerivedN, typename DerivedG, typename DerivedJ, typename Derivedflip> IGL_INLINE void igl::outer_hull( const Eigen::PlainObjectBase & V, const Eigen::PlainObjectBase & F, const Eigen::PlainObjectBase & N, Eigen::PlainObjectBase & G, Eigen::PlainObjectBase & J, Eigen::PlainObjectBase & flip) { std::cout.precision(20); //for (size_t i=0; i C; typedef Matrix MatrixXV; typedef Matrix MatrixXF; typedef Matrix MatrixXG; typedef Matrix MatrixXJ; typedef Matrix RowVector3N; const Index m = F.rows(); const auto & duplicate_simplex = [&F](const int f, const int g)->bool { return (F(f,0) == F(g,0) && F(f,1) == F(g,1) && F(f,2) == F(g,2)) || (F(f,1) == F(g,0) && F(f,2) == F(g,1) && F(f,0) == F(g,2)) || (F(f,2) == F(g,0) && F(f,0) == F(g,1) && F(f,1) == F(g,2)) || (F(f,0) == F(g,2) && F(f,1) == F(g,1) && F(f,2) == F(g,0)) || (F(f,1) == F(g,2) && F(f,2) == F(g,1) && F(f,0) == F(g,0)) || (F(f,2) == F(g,2) && F(f,0) == F(g,1) && F(f,1) == F(g,0)); }; #ifdef IGL_OUTER_HULL_DEBUG cout<<"outer hull..."< MatrixX2I; typedef Matrix VectorXI; typedef Matrix Vector3F; MatrixX2I E,uE; VectorXI EMAP; vector > uE2E; unique_edge_map(F,E,uE,EMAP,uE2E); #ifdef IGL_OUTER_HULL_DEBUG for (size_t ui=0; ui face-edge index, sorted CCW around edge according to normal // uE --> sorted order index // uE --> bool, whether needed to flip face to make "consistent" with unique // edge // Place order of each half-edge in its corresponding sorted list around edge VectorXI diIM(3*m); // Whether face's edge used for sorting is consistent with unique edge VectorXI dicons(3*m); // dihedral angles of faces around edge with face of edge in dicons vector > di(uE2E.size()); // For each list of face-edges incide on a unique edge for(size_t ui = 0;ui<(size_t)uE.rows();ui++) { // Base normal vector to orient against const auto fe0 = uE2E[ui][0]; const RowVector3N & eVp = N.row(fe0%m); MatrixXd di_I(uE2E[ui].size(),3); const typename DerivedF::Scalar o = F(fe0%m, fe0/m); const typename DerivedF::Scalar d = F(fe0%m,((fe0/m)+2)%3); const typename DerivedF::Scalar s = F(fe0%m,((fe0/m)+1)%3); // Edge vector auto eV = (V.row(d)-V.row(s)).normalized(); auto edge_len = (V.row(d) - V.row(s)).norm(); auto edge_valance = uE2E[ui].size(); auto eO = V.row(o) - V.row(s); assert(edge_valance % 2 == 0); const typename DerivedV::Scalar EPS = 1e-12; bool degenerated = !eV.allFinite() || edge_len < EPS; bool all_faces_are_degenerated_and_coplanar = false; #ifdef IGL_OUTER_HULL_DEBUG if (degenerated && edge_valance > 2) { cerr.precision(30); std::cerr << ui << ": " << (V.row(d) - V.row(s)).norm() << std::endl; std::cerr << "Edge valance: " << edge_valance << std::endl; std::cerr << V.row(d) << std::endl; std::cerr << V.row(s) << std::endl; } #endif if (degenerated) { const size_t num_adj_faces = uE2E[ui].size(); Eigen::Matrix normals(num_adj_faces, 3); for (size_t fei=0; fei 0) { eV /= length; break; } } } if (!eV.allFinite() || eV.norm() < EPS) { //cerr << "This is bad... all adj face normals are colinear" << std::endl; eV.setZero(); all_faces_are_degenerated_and_coplanar = true; } if (degenerated){ // Adjust edge direction. Vector3F in_face_vec = V.row(o) - V.row(s); Vector3F edge = eV; if (edge.cross(in_face_vec).dot(eVp) < 0) { #ifdef IGL_OUTER_HULL_DEBUG cerr << "Flipping edge..." << std::endl; #endif eV *= -1; } //cerr << "Resolved: " << eV << std::endl; } #ifdef IGL_OUTER_HULL_DEBUG if (degenerated) { cerr << "eV: " << eV << std::endl; } #endif vector cons(uE2E[ui].size()); // Loop over incident face edges for(size_t fei = 0;fei temp = uE2E[ui]; #ifdef IGL_OUTER_HULL_DEBUG std::cout.precision(20); //std::cout << "sorted" << std::endl; #endif for(size_t fei = 0;fei(0); } //MatrixXd s_di_I; //igl::sortrows(di_I,true,s_di_I,IM); //di[ui].resize(uE2E[ui].size()); //for(size_t i = 0;i temp = uE2E[ui]; //for(size_t fei = 0;fei > > TT,_1; triangle_triangle_adjacency(E,EMAP,uE2E,false,TT,_1); VectorXI counts; #ifdef IGL_OUTER_HULL_DEBUG cout<<"facet components..."< FH(m,false); vector EH(3*m,false); vector vG(ncc); vector vJ(ncc); vector vIM(ncc); size_t face_count = 0; 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; //std::cout << "face " << face_count++ << ": " << f << std::endl; //std::cout << "f " << F.row(f).array()+1 << std::endl; //cout<<"flip("< "<<(nf+1)<<", |"<< // di[EMAP(e)][diIM(e)]<<" - "< "<<(nf+1)<=max_di) // { // max_ne = ne; // max_di = ndi; // } //} ////cout<<(max_ne != max_ne_2)<<" =?= "< "<<(max_ne%m)+1<<" != "<<(max_ne_2%m)+1<<" ... "<=max_di) // { // max_ne = ne; // max_di = ndi; // } // } //} if(nfei >= 0) { max_ne = uE2E[EMAP(e)][nfei]; } if(max_ne>=0) { // face of neighbor const int nf = max_ne%m; #ifdef IGL_OUTER_HULL_DEBUG if(!FH[nf]) { // first time seeing face cout<<(f+1)<<" --> "<<(nf+1)< & 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-15) 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(0)); // In a perfect world, it's enough to test a single point. double w; // winding_number_3 expects colmajor const typename DerivedV::Scalar * Vdata; Vdata = V.data(); Matrix< typename DerivedV::Scalar, DerivedV::RowsAtCompileTime, DerivedV::ColsAtCompileTime, ColMajor> Vcol; if(DerivedV::IsRowMajor) { // copy to convert to colmajor Vcol = V; Vdata = Vcol.data(); } winding_number_3( Vdata,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 IGL_INLINE void igl::outer_hull( const Eigen::PlainObjectBase & V, const Eigen::PlainObjectBase & F, Eigen::PlainObjectBase & G, Eigen::PlainObjectBase & J, Eigen::PlainObjectBase & flip) { Eigen::Matrix N; per_face_normals_stable(V,F,N); return outer_hull(V,F,N,G,J,flip); } template < typename Kernel, typename DerivedV, typename DerivedF, typename DerivedN, typename DerivedG, typename DerivedJ, typename Derivedflip> IGL_INLINE void igl::outer_hull_exact( const Eigen::PlainObjectBase & V, const Eigen::PlainObjectBase & F, const Eigen::PlainObjectBase & N, Eigen::PlainObjectBase & G, Eigen::PlainObjectBase & J, Eigen::PlainObjectBase & flip) { std::cout.precision(20); //for (size_t i=0; i C; typedef Matrix MatrixXV; typedef Matrix MatrixXF; typedef Matrix MatrixXG; typedef Matrix MatrixXJ; typedef Matrix RowVector3N; const Index m = F.rows(); const auto & duplicate_simplex = [&F](const int f, const int g)->bool { return (F(f,0) == F(g,0) && F(f,1) == F(g,1) && F(f,2) == F(g,2)) || (F(f,1) == F(g,0) && F(f,2) == F(g,1) && F(f,0) == F(g,2)) || (F(f,2) == F(g,0) && F(f,0) == F(g,1) && F(f,1) == F(g,2)) || (F(f,0) == F(g,2) && F(f,1) == F(g,1) && F(f,2) == F(g,0)) || (F(f,1) == F(g,2) && F(f,2) == F(g,1) && F(f,0) == F(g,0)) || (F(f,2) == F(g,2) && F(f,0) == F(g,1) && F(f,1) == F(g,0)); }; #ifdef IGL_OUTER_HULL_DEBUG cout<<"outer hull..."< MatrixX2I; typedef Matrix VectorXI; typedef Matrix Vector3F; MatrixX2I E,uE; VectorXI EMAP; vector > uE2E; unique_edge_map(F,E,uE,EMAP,uE2E); #ifdef IGL_OUTER_HULL_DEBUG for (size_t ui=0; ui face-edge index, sorted CCW around edge according to normal // uE --> sorted order index // uE --> bool, whether needed to flip face to make "consistent" with unique // edge // Place order of each half-edge in its corresponding sorted list around edge VectorXI diIM(3*m); // Whether face's edge used for sorting is consistent with unique edge VectorXI dicons(3*m); // dihedral angles of faces around edge with face of edge in dicons vector > di(uE2E.size()); // For each list of face-edges incide on a unique edge for(size_t ui = 0;ui<(size_t)uE.rows();ui++) { // Base normal vector to orient against const auto fe0 = uE2E[ui][0]; const RowVector3N & eVp = N.row(fe0%m); MatrixXd di_I(uE2E[ui].size(),3); const typename DerivedF::Scalar o = F(fe0%m, fe0/m); const typename DerivedF::Scalar d = F(fe0%m,((fe0/m)+2)%3); const typename DerivedF::Scalar s = F(fe0%m,((fe0/m)+1)%3); // Edge vector typename Kernel::Vector_3 exact_eV( V(d, 0) - V(s, 0), V(d, 1) - V(s, 1), V(d, 2) - V(s, 2)); auto sq_length = exact_eV.squared_length(); if (sq_length > 0.0) { exact_eV = exact_eV / sq_length; } RowVector3N eV( CGAL::to_double(exact_eV[0]), CGAL::to_double(exact_eV[1]), CGAL::to_double(exact_eV[2])); if (sq_length > 0.0) { eV.normalize(); } vector cons(uE2E[ui].size()); // Loop over incident face edges for(size_t fei = 0;fei temp = uE2E[ui]; #ifdef IGL_OUTER_HULL_DEBUG std::cout.precision(20); //std::cout << "sorted" << std::endl; #endif for(size_t fei = 0;fei(0); } } vector > > TT,_1; triangle_triangle_adjacency(E,EMAP,uE2E,false,TT,_1); VectorXI counts; #ifdef IGL_OUTER_HULL_DEBUG cout<<"facet components..."< FH(m,false); vector EH(3*m,false); vector vG(ncc); vector vJ(ncc); vector vIM(ncc); size_t face_count = 0; 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; //std::cout << "face " << face_count++ << ": " << f << std::endl; //std::cout << "f " << F.row(f).array()+1 << std::endl; //cout<<"flip("<= 0) { max_ne = uE2E[EMAP(e)][nfei]; } if(max_ne>=0) { // face of neighbor const int nf = max_ne%m; #ifdef IGL_OUTER_HULL_DEBUG if(!FH[nf]) { // first time seeing face cout<<(f+1)<<" --> "<<(nf+1)< & 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-15) 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? Matrix q( CGAL::to_double(BC(AJ(0), 0)), CGAL::to_double(BC(AJ(0), 1)), CGAL::to_double(BC(AJ(0), 2))); // In a perfect world, it's enough to test a single point. double w; // winding_number_3 expects colmajor double* Vdata; Matrix Vcol(V.rows(), V.cols()); for (size_t i=0; i0.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::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); template void igl::outer_hull, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); template void igl::outer_hull, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); #endif