IGL_INLINE void igl::sortrows( const Eigen::PlainObjectBase<DerivedX>& X, const bool ascending, Eigen::PlainObjectBase<DerivedX>& Y, Eigen::PlainObjectBase<DerivedIX>& IX) { using namespace std; using namespace Eigen; using namespace igl; // Resize output Y.resize(X.rows(),X.cols()); IX.resize(X.rows(),1); for(int i = 0;i<X.rows();i++) { IX(i) = i; } std::sort( IX.data(), IX.data()+IX.size(), igl::IndexRowLessThan<const Eigen::PlainObjectBase<DerivedX> & >(X)); // if not ascending then reverse if(!ascending) { std::reverse(IX.data(),IX.data()+IX.size()); } for(int i = 0;i<X.rows();i++) { Y.row(i) = X.row(IX(i)); } }
IGL_INLINE void igl::setdiff( const Eigen::PlainObjectBase<DerivedA> & A, const Eigen::PlainObjectBase<DerivedB> & B, Eigen::PlainObjectBase<DerivedC> & C, Eigen::PlainObjectBase<DerivedIA> & IA) { using namespace Eigen; using namespace std; // boring base cases if(A.size() == 0) { C.resize(0,1); IA.resize(0,1); } if(B.size() == 0) { C.resize(A.size(),1); copy(A.data(),A.data()+A.size(),C.data()); IA = igl::colon<typename DerivedIA::Scalar>(0,C.size()-1); } // Get rid of any duplicates typedef Matrix<typename DerivedA::Scalar,Dynamic,1> VectorA; typedef Matrix<typename DerivedB::Scalar,Dynamic,1> VectorB; VectorA uA; VectorB uB; typedef PlainObjectBase<DerivedIA> IAType; IAType uIA,uIuA,uIB,uIuB; unique(A,uA,uIA,uIuA); unique(B,uB,uIB,uIuB); // Sort both VectorA sA; VectorB sB; IAType sIA,sIB; sort(uA,1,true,sA,sIA); sort(uB,1,true,sB,sIB); vector<typename DerivedB::Scalar> vC; vector<typename DerivedIA::Scalar> vIA; int bi = 0; // loop over sA bool past = false; for(int a = 0;a<sA.size();a++) { while(!past && sA(a)>sB(bi)) { bi++; past = bi>=sB.size(); } if(past || sA(a)<sB(bi)) { // then sA(a) did not appear in sB vC.push_back(sA(a)); vIA.push_back(uIA(sIA(a))); } } list_to_matrix(vC,C); list_to_matrix(vIA,IA); }
IGL_INLINE void igl::randperm( const int n, Eigen::PlainObjectBase<DerivedI> & I) { Eigen::VectorXi II; igl::colon(0,1,n-1,II); I = II; std::random_shuffle(I.data(),I.data()+n); }
IGL_INLINE int igl::unproject_to_zero_plane( const Eigen::PlainObjectBase<Derivedwin> & win, Eigen::PlainObjectBase<Derivedobj> & obj) { return unproject_to_zero_plane(win(0),win(1), &obj.data()[0], &obj.data()[1], &obj.data()[2]); }
void clamp(Eigen::PlainObjectBase<Derived>& m, typename Derived::Scalar lower, typename Derived::Scalar upper) { typedef typename Eigen::PlainObjectBase<Derived>::Index idx_t; for (idx_t i = 0; i < m.outerSize(); ++i) { for (idx_t j = 0; j < m.innerSize(); ++j) { int offset = i * m.innerSize() + j; if (m.data()[offset] < lower) m.data()[offset] = lower; if (m.data()[offset] > upper) m.data()[offset] = upper; } } }
IGL_INLINE void igl::mod( const Eigen::PlainObjectBase<DerivedA> & A, const int base, Eigen::PlainObjectBase<DerivedB> & B) { B.resize(A.rows(),A.cols()); for(int i = 0;i<B.size();i++) { *(B.data()+i) = (*(A.data()+i))%base; } }
IGL_INLINE void igl::opengl::vertex_array( const Eigen::PlainObjectBase<DerivedV> & V, const Eigen::PlainObjectBase<DerivedF> & F, GLuint & va_id, GLuint & ab_id, GLuint & eab_id) { // Inputs should be in RowMajor storage. If not, we have no choice but to // create a copy. if(!(V.Options & Eigen::RowMajor)) { Eigen::Matrix< typename DerivedV::Scalar, DerivedV::RowsAtCompileTime, DerivedV::ColsAtCompileTime, Eigen::RowMajor> VR = V; return vertex_array(VR,F,va_id,ab_id,eab_id); } if(!(F.Options & Eigen::RowMajor)) { Eigen::Matrix< typename DerivedF::Scalar, DerivedF::RowsAtCompileTime, DerivedF::ColsAtCompileTime, Eigen::RowMajor> FR = F; return vertex_array(V,FR,va_id,ab_id,eab_id); } // Generate and attach buffers to vertex array glGenVertexArrays(1, &va_id); glGenBuffers(1, &ab_id); glGenBuffers(1, &eab_id); glBindVertexArray(va_id); glBindBuffer(GL_ARRAY_BUFFER, ab_id); const auto size_VScalar = sizeof(typename DerivedV::Scalar); const auto size_FScalar = sizeof(typename DerivedF::Scalar); glBufferData(GL_ARRAY_BUFFER,size_VScalar*V.size(),V.data(),GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eab_id); assert(sizeof(GLuint) == size_FScalar && "F type does not match GLuint"); glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)*F.size(), F.data(), GL_STATIC_DRAW); glVertexAttribPointer( 0, V.cols(), size_VScalar==sizeof(float)?GL_FLOAT:GL_DOUBLE, GL_FALSE, V.cols()*size_VScalar, (GLvoid*)0); glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); }
IGL_INLINE void igl::matlab::parse_rhs_double( const mxArray *prhs[], Eigen::PlainObjectBase<DerivedV> & V) { using namespace std; using namespace Eigen; // set number of mesh vertices const int n = mxGetM(prhs[0]); // set vertex position pointers double * Vp = mxGetPr(prhs[0]); const int dim = mxGetN(prhs[0]); typedef typename DerivedV::Scalar Scalar; Matrix<Scalar, DerivedV::ColsAtCompileTime, DerivedV::RowsAtCompileTime, RowMajor> VT; Scalar * V_data; if(DerivedV::IsRowMajor) { VT.resize(dim,n); V_data = VT.data(); }else { V.resize(n,dim); V_data = V.data(); } copy(Vp,Vp+n*dim,V_data); if(DerivedV::IsRowMajor) { V = VT.transpose(); } }
IGL_INLINE void igl::outer_vertex( const Eigen::PlainObjectBase<DerivedV> & V, const Eigen::PlainObjectBase<DerivedF> & F, const Eigen::PlainObjectBase<DerivedI> & I, IndexType & v_index, Eigen::PlainObjectBase<DerivedA> & A) { // Algorithm: // Find an outer vertex (i.e. vertex reachable from infinity) // Return the vertex with the largest X value. // If there is a tie, pick the one with largest Y value. // If there is still a tie, pick the one with the largest Z value. // If there is still a tie, then there are duplicated vertices within the // mesh, which violates the precondition. const size_t INVALID = std::numeric_limits<size_t>::max(); const size_t num_selected_faces = I.rows(); std::vector<size_t> candidate_faces; size_t outer_vid = INVALID; typename DerivedV::Scalar outer_val = 0; for (size_t i=0; i<num_selected_faces; i++) { size_t f = I(i); for (size_t j=0; j<3; j++) { auto v = F(f, j); auto vx = V(v, 0); if (outer_vid == INVALID || vx > outer_val) { outer_val = vx; outer_vid = v; candidate_faces = {f}; } else if (v == outer_vid) { candidate_faces.push_back(f); } else if (vx == outer_val) { // Break tie. auto vy = V(v,1); auto vz = V(v, 2); auto outer_y = V(outer_vid, 1); auto outer_z = V(outer_vid, 2); assert(!(vy == outer_y && vz == outer_z)); bool replace = (vy > outer_y) || ((vy == outer_y) && (vz > outer_z)); if (replace) { outer_val = vx; outer_vid = v; candidate_faces = {f}; } } } } assert(outer_vid != INVALID); assert(candidate_faces.size() > 0); v_index = outer_vid; A.resize(candidate_faces.size()); std::copy(candidate_faces.begin(), candidate_faces.end(), A.data()); }
IGL_INLINE void igl::parse_rhs_double( const mxArray *prhs[], Eigen::PlainObjectBase<DerivedV> & V) { using namespace std; // set number of mesh vertices const int n = mxGetM(prhs[0]); // set vertex position pointers double * Vp = mxGetPr(prhs[0]); const int dim = mxGetN(prhs[0]); V.resize(n,dim); copy(Vp,Vp+n*dim,&V.data()[0]); }
IGL_INLINE void igl::components( const Eigen::SparseMatrix<AScalar> & A, Eigen::PlainObjectBase<DerivedC> & C) { assert(A.rows() == A.cols()); using namespace Eigen; // THIS IS DENSE: //boost::adjacency_matrix<boost::undirectedS> bA(A.rows()); boost::adjacency_list<boost::vecS,boost::vecS,boost::undirectedS> bA(A.rows()); for(int j=0; j<A.outerSize();j++) { // Iterate over inside for(typename SparseMatrix<AScalar>::InnerIterator it (A,j); it; ++it) { if(0 != it.value()) { boost::add_edge(it.row(),it.col(),bA); } } } C.resize(A.rows(),1); boost::connected_components(bA,C.data()); }
IGL_INLINE void igl::resolve_duplicated_faces( const Eigen::PlainObjectBase<DerivedF1>& F1, Eigen::PlainObjectBase<DerivedF2>& F2, Eigen::PlainObjectBase<DerivedJ>& J) { //typedef typename DerivedF1::Scalar Index; Eigen::VectorXi IA,IC; DerivedF1 uF; igl::unique_simplices(F1,uF,IA,IC); const size_t num_faces = F1.rows(); const size_t num_unique_faces = uF.rows(); assert((size_t) IA.rows() == num_unique_faces); // faces on top of each unique face std::vector<std::vector<int> > uF2F(num_unique_faces); // signed counts Eigen::VectorXi counts = Eigen::VectorXi::Zero(num_unique_faces); Eigen::VectorXi ucounts = Eigen::VectorXi::Zero(num_unique_faces); // loop over all faces for (size_t i=0; i<num_faces; i++) { const size_t ui = IC(i); const bool consistent = (F1(i,0) == uF(ui, 0) && F1(i,1) == uF(ui, 1) && F1(i,2) == uF(ui, 2)) || (F1(i,0) == uF(ui, 1) && F1(i,1) == uF(ui, 2) && F1(i,2) == uF(ui, 0)) || (F1(i,0) == uF(ui, 2) && F1(i,1) == uF(ui, 0) && F1(i,2) == uF(ui, 1)); uF2F[ui].push_back(int(i+1) * (consistent?1:-1)); counts(ui) += consistent ? 1:-1; ucounts(ui)++; } std::vector<size_t> kept_faces; for (size_t i=0; i<num_unique_faces; i++) { if (ucounts[i] == 1) { kept_faces.push_back(abs(uF2F[i][0])-1); continue; } if (counts[i] == 1) { bool found = false; for (auto fid : uF2F[i]) { if (fid > 0) { kept_faces.push_back(abs(fid)-1); found = true; break; } } assert(found); } else if (counts[i] == -1) { bool found = false; for (auto fid : uF2F[i]) { if (fid < 0) { kept_faces.push_back(abs(fid)-1); found = true; break; } } assert(found); } else { assert(counts[i] == 0); } } const size_t num_kept = kept_faces.size(); J.resize(num_kept, 1); std::copy(kept_faces.begin(), kept_faces.end(), J.data()); igl::slice(F1, J, 1, F2); }
IGL_INLINE void igl::outer_hull( const Eigen::PlainObjectBase<DerivedV> & V, const Eigen::PlainObjectBase<DerivedF> & F, const Eigen::PlainObjectBase<DerivedN> & N, Eigen::PlainObjectBase<DerivedG> & G, Eigen::PlainObjectBase<DerivedJ> & J, Eigen::PlainObjectBase<Derivedflip> & flip) { using namespace Eigen; using namespace std; using namespace igl; typedef typename DerivedF::Index Index; Matrix<Index,DerivedF::RowsAtCompileTime,1> C; typedef Matrix<typename DerivedV::Scalar,Dynamic,DerivedV::ColsAtCompileTime> MatrixXV; typedef Matrix<typename DerivedF::Scalar,Dynamic,DerivedF::ColsAtCompileTime> MatrixXF; typedef Matrix<typename DerivedG::Scalar,Dynamic,DerivedG::ColsAtCompileTime> MatrixXG; typedef Matrix<typename DerivedJ::Scalar,Dynamic,DerivedJ::ColsAtCompileTime> MatrixXJ; typedef Matrix<typename DerivedN::Scalar,1,3> RowVector3N; const Index m = F.rows(); #ifdef IGL_OUTER_HULL_DEBUG cout<<"outer hull..."<<endl; #endif #ifdef IGL_OUTER_HULL_DEBUG cout<<"edge map..."<<endl; #endif typedef Matrix<typename DerivedF::Scalar,Dynamic,2> MatrixX2I; typedef Matrix<typename DerivedF::Index,Dynamic,1> VectorXI; MatrixX2I E,uE; VectorXI EMAP; vector<vector<typename DerivedF::Index> > uE2E; unique_edge_map(F,E,uE,EMAP,uE2E); // TODO: // uE --> 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 VectorXI diIM(3*m); VectorXI dicons(3*m); vector<vector<typename DerivedV::Scalar> > 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); di[ui].resize(uE2E[ui].size()); 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 const auto & eV = (V.row(d)-V.row(s)).normalized(); vector<bool> cons(uE2E[ui].size()); // Loop over incident face edges for(size_t fei = 0;fei<uE2E[ui].size();fei++) { const auto & fe = uE2E[ui][fei]; const auto f = fe % m; const auto c = fe / m; // source should match destination to be consistent cons[fei] = (d == F(f,(c+1)%3)); assert( cons[fei] || (d == F(f,(c+2)%3))); assert(!cons[fei] || (s == F(f,(c+2)%3))); assert(!cons[fei] || (d == F(f,(c+1)%3))); // Angle between n and f const RowVector3N & n = N.row(f); di[ui][fei] = M_PI - atan2( eVp.cross(n).dot(eV), eVp.dot(n)); if(!cons[fei]) { di[ui][fei] = di[ui][fei] + M_PI; if(di[ui][fei]>2.*M_PI) { di[ui][fei] = di[ui][fei] - 2.*M_PI; } } } vector<size_t> IM; igl::sort(di[ui],true,di[ui],IM); // copy old list vector<typename DerivedF::Index> temp = uE2E[ui]; for(size_t fei = 0;fei<uE2E[ui].size();fei++) { uE2E[ui][fei] = temp[IM[fei]]; const auto & fe = uE2E[ui][fei]; diIM(fe) = fei; dicons(fe) = cons[IM[fei]]; } } vector<vector<vector<Index > > > TT,_1; triangle_triangle_adjacency(E,EMAP,uE2E,false,TT,_1); VectorXI counts; #ifdef IGL_OUTER_HULL_DEBUG cout<<"facet components..."<<endl; #endif 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); #ifdef IGL_OUTER_HULL_DEBUG cout<<"reindex..."<<endl; #endif // H contains list of faces on outer hull; vector<bool> FH(m,false); vector<bool> EH(3*m,false); vector<MatrixXG> vG(ncc); vector<MatrixXJ> vJ(ncc); vector<MatrixXJ> vIM(ncc); for(size_t id = 0;id<ncc;id++) { vIM[id].resize(counts[id],1); } // current index into each IM vector<size_t> g(ncc,0); // place order of each face in its respective component for(Index f = 0;f<m;f++) { vIM[C(f)](g[C(f)]++) = f; } #ifdef IGL_OUTER_HULL_DEBUG cout<<"barycenters..."<<endl; #endif // assumes that "resolve" has handled any coplanar cases correctly and nearly // coplanar cases can be sorted based on barycenter. MatrixXV BC; barycenter(V,F,BC); #ifdef IGL_OUTER_HULL_DEBUG cout<<"loop over CCs (="<<ncc<<")..."<<endl; #endif for(Index id = 0;id<(Index)ncc;id++) { auto & IM = vIM[id]; // starting face that's guaranteed to be on the outer hull and in this // component int f; bool f_flip; #ifdef IGL_OUTER_HULL_DEBUG cout<<"outer facet..."<<endl; #endif outer_facet(V,F,N,IM,f,f_flip); #ifdef IGL_OUTER_HULL_DEBUG cout<<"outer facet: "<<f<<endl; #endif int FHcount = 0; // Q contains list of face edges to continue traversing upong queue<int> Q; Q.push(f+0*m); Q.push(f+1*m); Q.push(f+2*m); flip(f) = f_flip; //cout<<"flip("<<f<<") = "<<(flip(f)?"true":"false")<<endl; #ifdef IGL_OUTER_HULL_DEBUG cout<<"BFS..."<<endl; #endif while(!Q.empty()) { // 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) { continue; } EH[e] = true; // first time seeing face if(!FH[f]) { FH[f] = true; FHcount++; } // find overlapping face-edges const auto & neighbors = uE2E[EMAP(e)]; // normal after possible flipping 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); // Edge vector according to f's (flipped) orientation. const auto & eV = (V.row(fd)-V.row(fs)).normalized(); // edge valence const size_t val = uE2E[EMAP(e)].size(); //#warning "EXPERIMENTAL, DO NOT USE" //// THIS IS WRONG! The first face is---after sorting---no longer the face //// used for orienting the sort. //const auto ui = EMAP(e); //const auto fe0 = uE2E[ui][0]; //const auto es = F(fe0%m,((fe0/m)+1)%3); const int e_cons = (dicons(e) ? 1: -1); const int nfei = (diIM(e) + val + e_cons*(flip(f)?-1:1))%val; const int max_ne_2 = uE2E[EMAP(e)][nfei]; int max_ne = -1; //// Loop over and find max dihedral angle //typename DerivedV::Scalar max_di = -1; //for(const auto & ne : neighbors) //{ // const int nf = ne%m; // if(nf == f) // { // continue; // } // // Corner of neighbor // const int nc = ne/m; // // Is neighbor oriented consistently with (flipped) f? // //const int ns = F(nf,(nc+1)%3); // const int nd = F(nf,(nc+2)%3); // const bool cons = (flip(f)?fd:fs) == nd; // // Normal after possibly flipping to match flip or orientation of f // const auto & nN = (cons? (flip(f)?-1:1.) : (flip(f)?1.:-1.) )*N.row(nf); // // Angle between n and f // const auto & ndi = M_PI - atan2( fN.cross(nN).dot(eV), fN.dot(nN)); // if(ndi>=max_di) // { // max_ne = ne; // max_di = ndi; // } //} ////cout<<(max_ne != max_ne_2)<<" =?= "<<e_cons<<endl; //if(max_ne != max_ne_2) //{ // cout<<(f+1)<<" ---> "<<(max_ne%m)+1<<" != "<<(max_ne_2%m)+1<<" ... "<<e_cons<<" "<<flip(f)<<endl; // typename DerivedV::Scalar max_di = -1; // for(size_t nei = 0;nei<neighbors.size();nei++) // { // const auto & ne = neighbors[nei]; // const int nf = ne%m; // if(nf == f) // { // cout<<" "<<(ne%m)+1<<":\t"<<0<<"\t"<<di[EMAP[e]][nei]<<" "<<diIM(ne)<<endl; // continue; // } // // Corner of neighbor // const int nc = ne/m; // // Is neighbor oriented consistently with (flipped) f? // //const int ns = F(nf,(nc+1)%3); // const int nd = F(nf,(nc+2)%3); // const bool cons = (flip(f)?fd:fs) == nd; // // Normal after possibly flipping to match flip or orientation of f // const auto & nN = (cons? (flip(f)?-1:1.) : (flip(f)?1.:-1.) )*N.row(nf); // // Angle between n and f // const auto & ndi = M_PI - atan2( fN.cross(nN).dot(eV), fN.dot(nN)); // cout<<" "<<(ne%m)+1<<":\t"<<ndi<<"\t"<<di[EMAP[e]][nei]<<" "<<diIM(ne)<<endl; // if(ndi>=max_di) // { // max_ne = ne; // max_di = ndi; // } // } //} max_ne = max_ne_2; if(max_ne>=0) { // face of neighbor const int nf = max_ne%m; // corner of neighbor 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)); //cout<<"flip("<<nf<<") = "<<(flip(nf)?"true":"false")<<endl; 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); } } } { vG[id].resize(FHcount,3); vJ[id].resize(FHcount,1); //nG += FHcount; size_t h = 0; assert(counts(id) == IM.rows()); for(int i = 0;i<counts(id);i++) { const size_t f = IM(i); //if(f_flip) //{ // flip(f) = !flip(f); //} if(FH[f]) { vG[id].row(h) = (flip(f)?F.row(f).reverse().eval():F.row(f)); vJ[id](h,0) = f; h++; } } assert((int)h == FHcount); } } // Is A inside B? Assuming A and B are consistently oriented but closed and // non-intersecting. const auto & is_component_inside_other = []( const Eigen::PlainObjectBase<DerivedV> & V, const MatrixXV & BC, const MatrixXG & A, const MatrixXJ & AJ, const MatrixXG & B)->bool { const auto & bounding_box = []( const Eigen::PlainObjectBase<DerivedV> & 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;f<m;f++) { for(size_t c = 0;c<3;c++) { const auto & vfc = V.row(F(f,c)); BB.row(0) = BB.row(0).array().min(vfc.array()).eval(); BB.row(1) = BB.row(1).array().max(vfc.array()).eval(); } } return BB; }; // A lot of the time we're dealing with unrelated, distant components: cull // them. MatrixXV ABB = bounding_box(V,A); MatrixXV BBB = bounding_box(V,B); if( (BBB.row(0)-ABB.row(1)).maxCoeff()>0 || (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(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<bool> keep(ncc,true); size_t nG = 0; // This is O( ncc * ncc * m) for(size_t id = 0;id<ncc;id++) { for(size_t oid = 0;oid<ncc;oid++) { if(id == oid) { continue; } const bool inside = is_component_inside_other(V,BC,vG[id],vJ[id],vG[oid]); #ifdef IGL_OUTER_HULL_DEBUG cout<<id<<" is inside "<<oid<<" ? "<<inside<<endl; #endif keep[id] = keep[id] && !inside; } if(keep[id]) { nG += vJ[id].rows(); } } // collect G and J across components G.resize(nG,3); J.resize(nG,1); { size_t off = 0; for(Index id = 0;id<(Index)ncc;id++) { if(keep[id]) { assert(vG[id].rows() == vJ[id].rows()); G.block(off,0,vG[id].rows(),vG[id].cols()) = vG[id]; J.block(off,0,vJ[id].rows(),vJ[id].cols()) = vJ[id]; off += vG[id].rows(); } } } }
IGL_INLINE void igl::copyleft::cgal::snap_rounding( const Eigen::PlainObjectBase<DerivedV> & V, const Eigen::PlainObjectBase<DerivedE> & E, Eigen::PlainObjectBase<DerivedVI> & VI, Eigen::PlainObjectBase<DerivedEI> & EI, Eigen::PlainObjectBase<DerivedJ> & J) { using namespace Eigen; using namespace igl; using namespace igl::copyleft::cgal; using namespace std; // Exact scalar type typedef CGAL::Epeck Kernel; typedef Kernel::FT EScalar; typedef CGAL::Segment_2<Kernel> Segment_2; typedef CGAL::Point_2<Kernel> Point_2; typedef CGAL::Vector_2<Kernel> Vector_2; typedef Matrix<EScalar,Dynamic,Dynamic> MatrixXE; // Convert vertex positions to exact kernel MatrixXE VE; { VectorXi IM; resolve_intersections(V,E,VE,EI,J,IM); for_each( EI.data(), EI.data()+EI.size(), [&IM](typename DerivedEI::Scalar& i){i=IM(i);}); VectorXi _; remove_unreferenced( MatrixXE(VE), DerivedEI(EI), VE,EI,_); } // find all hot pixels //// southwest and north east corners //const RowVector2i SW( // round(VE.col(0).minCoeff()), // round(VE.col(1).minCoeff())); //const RowVector2i NE( // round(VE.col(0).maxCoeff()), // round(VE.col(1).maxCoeff())); // https://github.com/CGAL/cgal/issues/548 // Round an exact scalar to the nearest integer. A priori can't just round // double. Suppose e=0.5+ε but double(e)<0.5 // // Inputs: // e exact number // Outputs: // i closest integer const auto & round = [](const EScalar & e)->int { const double d = CGAL::to_double(e); // get an integer that's near the closest int int i = std::round(d); EScalar di_sqr = CGAL::square((e-EScalar(i))); const auto & search = [&i,&di_sqr,&e](const int dir) { while(true) { const int j = i+dir; const EScalar dj_sqr = CGAL::square((e-EScalar(j))); if(dj_sqr < di_sqr) { i = j; di_sqr = dj_sqr; }else { break; } } }; // Try to increase/decrease int search(1); search(-1); return i; }; vector<Point_2> hot; for(int i = 0;i<VE.rows();i++) { hot.emplace_back(round(VE(i,0)),round(VE(i,1))); } { std::vector<size_t> _1,_2; igl::unique(vector<Point_2>(hot),hot,_1,_2); } // find all segments intersecting hot pixels // split edge at closest point to hot pixel center vector<vector<Point_2>> steiner(EI.rows()); // initialize each segment with endpoints for(int i = 0;i<EI.rows();i++) { steiner[i].emplace_back(VE(EI(i,0),0),VE(EI(i,0),1)); steiner[i].emplace_back(VE(EI(i,1),0),VE(EI(i,1),1)); } // silly O(n²) implementation for(const Point_2 & h : hot) { // North, East, South, West Segment_2 wall[4] = { {h+Vector_2(-0.5, 0.5),h+Vector_2( 0.5, 0.5)}, {h+Vector_2( 0.5, 0.5),h+Vector_2( 0.5,-0.5)}, {h+Vector_2( 0.5,-0.5),h+Vector_2(-0.5,-0.5)}, {h+Vector_2(-0.5,-0.5),h+Vector_2(-0.5, 0.5)} }; // consider all segments for(int i = 0;i<EI.rows();i++) { // endpoints const Point_2 s(VE(EI(i,0),0),VE(EI(i,0),1)); const Point_2 d(VE(EI(i,1),0),VE(EI(i,1),1)); // if either end-point is in h's pixel then ignore const Point_2 rs(round(s.x()),round(s.y())); const Point_2 rd(round(d.x()),round(d.y())); if(h == rs || h == rd) { continue; } // otherwise check for intersections with walls consider all walls const Segment_2 si(s,d); vector<Point_2> hits; for(int j = 0;j<4;j++) { const Segment_2 & sj = wall[j]; if(CGAL::do_intersect(si,sj)) { CGAL::Object result = CGAL::intersection(si,sj); if(const Point_2 * p = CGAL::object_cast<Point_2 >(&result)) { hits.push_back(*p); }else if(const Segment_2 * s = CGAL::object_cast<Segment_2 >(&result)) { // add both endpoints hits.push_back(s->vertex(0)); hits.push_back(s->vertex(1)); } } } if(hits.size() == 0) { continue; } // centroid of hits Vector_2 cen(0,0); for(const Point_2 & hit : hits) { cen = Vector_2(cen.x()+hit.x(), cen.y()+hit.y()); } cen = Vector_2(cen.x()/EScalar(hits.size()),cen.y()/EScalar(hits.size())); const Point_2 rcen(round(cen.x()),round(cen.y())); // after all of that, don't add as a steiner unless it's going to round // to h if(rcen == h) { steiner[i].emplace_back(cen.x(),cen.y()); } } } { DerivedJ prevJ = J; VectorXi IM; subdivide_segments(MatrixXE(VE),MatrixXi(EI),steiner,VE,EI,J,IM); for_each(J.data(),J.data()+J.size(),[&prevJ](typename DerivedJ::Scalar & j){j=prevJ(j);}); for_each( EI.data(), EI.data()+EI.size(), [&IM](typename DerivedEI::Scalar& i){i=IM(i);}); VectorXi _; remove_unreferenced( MatrixXE(VE), DerivedEI(EI), VE,EI,_); } VI.resize(VE.rows(),VE.cols()); for(int i = 0;i<VE.rows();i++) { for(int j = 0;j<VE.cols();j++) { VI(i,j) = round(CGAL::to_double(VE(i,j))); } } }
IGL_INLINE void igl::outer_edge( const Eigen::PlainObjectBase<DerivedV> & V, const Eigen::PlainObjectBase<DerivedF> & F, const Eigen::PlainObjectBase<DerivedI> & I, IndexType & v1, IndexType & v2, Eigen::PlainObjectBase<DerivedA> & A) { // Algorithm: // Find an outer vertex first. // Find the incident edge with largest slope when projected onto XY plane. // If there is still a tie, break it using the projected slope onto ZX plane. // If there is still a tie, then there are multiple overlapping edges, // which violates the precondition. typedef typename DerivedV::Scalar Scalar; typedef typename DerivedV::Index Index; typedef typename Eigen::Matrix<Scalar, 3, 1> ScalarArray3; typedef typename Eigen::Matrix<typename DerivedF::Scalar, 3, 1> IndexArray3; const size_t INVALID = std::numeric_limits<size_t>::max(); Index outer_vid; Eigen::Matrix<Index,Eigen::Dynamic,1> candidate_faces; outer_vertex(V, F, I, outer_vid, candidate_faces); const ScalarArray3& outer_v = V.row(outer_vid); assert(candidate_faces.size() > 0); auto get_vertex_index = [&](const IndexArray3& f, Index vid) -> Index { if (f[0] == vid) return 0; if (f[1] == vid) return 1; if (f[2] == vid) return 2; assert(false); return -1; }; Scalar outer_slope_YX = 0; Scalar outer_slope_ZX = 0; size_t outer_opp_vid = INVALID; bool infinite_slope_detected = false; std::vector<Index> incident_faces; auto check_and_update_outer_edge = [&](Index opp_vid, Index fid) { if (opp_vid == outer_opp_vid) { incident_faces.push_back(fid); return; } const ScalarArray3 opp_v = V.row(opp_vid); if (!infinite_slope_detected && outer_v[0] != opp_v[0]) { // Finite slope const ScalarArray3 diff = opp_v - outer_v; const Scalar slope_YX = diff[1] / diff[0]; const Scalar slope_ZX = diff[2] / diff[0]; if (outer_opp_vid == INVALID || slope_YX > outer_slope_YX || (slope_YX == outer_slope_YX && slope_ZX > outer_slope_ZX)) { outer_opp_vid = opp_vid; outer_slope_YX = slope_YX; outer_slope_ZX = slope_ZX; incident_faces = {fid}; } } else if (!infinite_slope_detected) { // Infinite slope outer_opp_vid = opp_vid; infinite_slope_detected = true; incident_faces = {fid}; } }; const auto num_candidate_faces = candidate_faces.size(); for (size_t i=0; i<num_candidate_faces; i++) { const Index fid = candidate_faces(i); const IndexArray3& f = F.row(fid); size_t id = get_vertex_index(f, outer_vid); Index next_vid = f((id+1)%3); Index prev_vid = f((id+2)%3); check_and_update_outer_edge(next_vid, fid); check_and_update_outer_edge(prev_vid, fid); } v1 = outer_vid; v2 = outer_opp_vid; A.resize(incident_faces.size()); std::copy(incident_faces.begin(), incident_faces.end(), A.data()); }
IGL_INLINE void igl::ismember( const Eigen::MatrixBase<DerivedA> & A, const Eigen::MatrixBase<DerivedB> & B, Eigen::PlainObjectBase<DerivedIA> & IA, Eigen::PlainObjectBase<DerivedLOCB> & LOCB) { using namespace Eigen; using namespace std; IA.resizeLike(A); IA.setConstant(false); LOCB.resizeLike(A); LOCB.setConstant(-1); // boring base cases if(A.size() == 0) { return; } if(B.size() == 0) { return; } // Get rid of any duplicates typedef Matrix<typename DerivedA::Scalar,Dynamic,1> VectorA; typedef Matrix<typename DerivedB::Scalar,Dynamic,1> VectorB; const VectorA vA(Eigen::Map<const VectorA>(DerivedA(A).data(), A.cols()*A.rows(),1)); const VectorB vB(Eigen::Map<const VectorB>(DerivedB(B).data(), B.cols()*B.rows(),1)); VectorA uA; VectorB uB; Eigen::Matrix<typename DerivedA::Index,Dynamic,1> uIA,uIuA,uIB,uIuB; unique(vA,uA,uIA,uIuA); unique(vB,uB,uIB,uIuB); // Sort both VectorA sA; VectorB sB; Eigen::Matrix<typename DerivedA::Index,Dynamic,1> sIA,sIB; sort(uA,1,true,sA,sIA); sort(uB,1,true,sB,sIB); Eigen::Matrix<bool,Eigen::Dynamic,1> uF = Eigen::Matrix<bool,Eigen::Dynamic,1>::Zero(sA.size(),1); Eigen::Matrix<typename DerivedLOCB::Scalar, Eigen::Dynamic,1> uLOCB = Eigen::Matrix<typename DerivedLOCB::Scalar,Eigen::Dynamic,1>:: Constant(sA.size(),1,-1); { int bi = 0; // loop over sA bool past = false; for(int a = 0;a<sA.size();a++) { while(!past && sA(a)>sB(bi)) { bi++; past = bi>=sB.size(); } if(!past && sA(a)==sB(bi)) { uF(sIA(a)) = true; uLOCB(sIA(a)) = uIB(sIB(bi)); } } } Map< Matrix<typename DerivedIA::Scalar,Dynamic,1> > vIA(IA.data(),IA.cols()*IA.rows(),1); Map< Matrix<typename DerivedLOCB::Scalar,Dynamic,1> > vLOCB(LOCB.data(),LOCB.cols()*LOCB.rows(),1); for(int a = 0;a<A.size();a++) { vIA(a) = uF(uIuA(a)); vLOCB(a) = uLOCB(uIuA(a)); } }