IGL_INLINE void igl::boundary_loop( const Eigen::MatrixXd& V, const Eigen::MatrixXi& F, Eigen::VectorXi& b) { std::vector<int> bnd; bnd.clear(); std::vector<bool> isVisited(V.rows(),false); // Actually mesh only needs to be manifold near boundary, so this is // over zealous (see gptoolbox's outline_loop for a more general // (and probably faster) implementation) assert(is_edge_manifold(V,F) && "Mesh must be manifold"); Eigen::MatrixXi TT,TTi; std::vector<std::vector<int> > VF, VFi; igl::triangle_triangle_adjacency(V,F,TT,TTi); igl::vertex_triangle_adjacency(V,F,VF,VFi); // Extract one boundary edge bool done = false; for (int i = 0; i < TT.rows() && !done; i++) { for (int j = 0; j < TT.cols(); j++) { if (TT(i,j) < 0) { int idx1, idx2; idx1 = F(i,j); idx2 = F(i,(j+1) % F.cols()); bnd.push_back(idx1); bnd.push_back(idx2); isVisited[idx1] = true; isVisited[idx2] = true; done = true; break; } } } // Traverse boundary while(1) { bool changed = false; int lastV; lastV = bnd[bnd.size()-1]; for (int i = 0; i < (int)VF[lastV].size(); i++) { int curr_neighbor = VF[lastV][i]; if (TT.row(curr_neighbor).minCoeff() < 0.) // Face contains boundary edge { int idx_lastV_in_face; if (F(curr_neighbor,0) == lastV) idx_lastV_in_face = 0; if (F(curr_neighbor,1) == lastV) idx_lastV_in_face = 1; if (F(curr_neighbor,2) == lastV) idx_lastV_in_face = 2; int idx_prev = (idx_lastV_in_face + F.cols()-1) % F.cols(); int idx_next = (idx_lastV_in_face + 1) % F.cols(); bool isPrevVisited = isVisited[F(curr_neighbor,idx_prev)]; bool isNextVisited = isVisited[F(curr_neighbor,idx_next)]; bool gotBndEdge = false; int next_bnd_vertex; if (!isNextVisited && TT(curr_neighbor,idx_lastV_in_face) < 0) { next_bnd_vertex = idx_next; gotBndEdge = true; } else if (!isPrevVisited && TT(curr_neighbor,(idx_lastV_in_face+2) % F.cols()) < 0) { next_bnd_vertex = idx_prev; gotBndEdge = true; } if (gotBndEdge) { changed = true; bnd.push_back(F(curr_neighbor,next_bnd_vertex)); isVisited[F(curr_neighbor,next_bnd_vertex)] = true; break; } } } if (!changed) break; } b.resize(bnd.size()); for(unsigned i=0;i<bnd.size();++i) b(i) = bnd[i]; }
void igl::crouzeix_raviart_cotmatrix( const Eigen::MatrixBase<DerivedV> & V, const Eigen::MatrixBase<DerivedF> & F, const Eigen::MatrixBase<DerivedE> & E, const Eigen::MatrixBase<DerivedEMAP> & EMAP, Eigen::SparseMatrix<LT> & L) { // number of rows const int m = F.rows(); // Element simplex size const int ss = F.cols(); // Mesh should be edge-manifold assert(F.cols() != 3 || is_edge_manifold(F)); typedef Eigen::Matrix<LT,Eigen::Dynamic,Eigen::Dynamic> MatrixXS; MatrixXS C; cotmatrix_entries(V,F,C); Eigen::MatrixXi F2E(m,ss); { int k =0; for(int c = 0;c<ss;c++) { for(int f = 0;f<m;f++) { F2E(f,c) = k++; } } } // number of entries inserted per facet const int k = ss*(ss-1)*2; std::vector<Eigen::Triplet<LT> > LIJV;LIJV.reserve(k*m); Eigen::VectorXi LI(k),LJ(k),LV(k); // Compensation factor to match scales in matlab version double factor = 2.0; switch(ss) { default: assert(false && "unsupported simplex size"); case 3: factor = 4.0; LI<<0,1,2,1,2,0,0,1,2,1,2,0; LJ<<1,2,0,0,1,2,0,1,2,1,2,0; LV<<2,0,1,2,0,1,2,0,1,2,0,1; break; case 4: factor *= -1.0; LI<<0,3,3,3,1,2,1,0,1,2,2,0,0,3,3,3,1,2,1,0,1,2,2,0; LJ<<1,0,1,2,2,0,0,3,3,3,1,2,0,3,3,3,1,2,1,0,1,2,2,0; LV<<2,3,4,5,0,1,2,3,4,5,0,1,2,3,4,5,0,1,2,3,4,5,0,1; break; } for(int f=0;f<m;f++) { for(int c = 0;c<k;c++) { LIJV.emplace_back( EMAP(F2E(f,LI(c))), EMAP(F2E(f,LJ(c))), (c<(k/2)?-1.:1.) * factor *C(f,LV(c))); } } L.resize(E.rows(),E.rows()); L.setFromTriplets(LIJV.begin(),LIJV.end()); }