int NumExactMatches(const Eigen::MatrixXi& M, const Eigen::MatrixXi& m, int& best_score, int& best_r, int& best_c) { const int border = std::min((int)std::min(m.rows(),m.cols())-2, 2); best_score = std::numeric_limits<int>::max(); const Eigen::Vector2i rcmax( 2*border + M.rows() - m.rows(), 2*border + M.cols() - m.cols()); int num_zeros = 0; for(int r=-border; r < rcmax(0); ++r ) { for(int c=-border; c < rcmax(1); ++c) { const int hd = HammingDistance(M, m, r,c ); if(hd < best_score) { best_score = hd; best_r = r; best_c = c; } if(hd==0) { ++num_zeros; } } } return num_zeros; }
TEST(CSGTree, extrusion) { Eigen::MatrixXd V; Eigen::MatrixXi F; test_common::load_mesh("extrusion.obj", V, F); igl::copyleft::cgal::CSGTree tree(V, F); igl::copyleft::cgal::CSGTree inter(tree, tree, "i"); // returns error Eigen::MatrixXd V2 = inter.cast_V<Eigen::MatrixXd>(); Eigen::MatrixXi F2 = inter.F(); ASSERT_EQ(V.rows(), V2.rows()); ASSERT_EQ(F.rows(), F2.rows()); }
void PointProjector::project(Eigen::MatrixXi &indexImage, Eigen::MatrixXf &depthImage, const HomogeneousPoint3fVector &points) const { depthImage.resize(indexImage.rows(), indexImage.cols()); depthImage.fill(std::numeric_limits<float>::max()); indexImage.fill(-1); const HomogeneousPoint3f* point = &points[0]; for (size_t i=0; i<points.size(); i++, point++){ int x, y; float d; if (!project(x, y, d, *point) || x<0 || x>=indexImage.rows() || y<0 || y>=indexImage.cols() ) continue; float& otherDistance=depthImage(x,y); int& otherIndex=indexImage(x,y); if (otherDistance>d) { otherDistance = d; otherIndex = i; } } }
IGL_INLINE void igl::ViewerData::set_edges( const Eigen::MatrixXd& P, const Eigen::MatrixXi& E, const Eigen::MatrixXd& C) { using namespace Eigen; lines.resize(E.rows(),9); assert(C.cols() == 3); for(int e = 0;e<E.rows();e++) { RowVector3d color; if(C.size() == 3) { color<<C; }else if(C.rows() == E.rows()) { color<<C.row(e); } lines.row(e)<< P.row(E(e,0)), P.row(E(e,1)), color; } dirty |= DIRTY_OVERLAY_LINES; }
void optimize_index_buffer( const Eigen::MatrixXi& F, const bool silent, Eigen::MatrixXi& OF) { const int numTris = F.rows(); std::vector<int> triBuf; for (int t=0; t<numTris; t++) { triBuf.push_back(F(t,0)); triBuf.push_back(F(t,1)); triBuf.push_back(F(t,2)); } VertexCache vertex_cache; int misses = vertex_cache.GetCacheMissCount(&triBuf[0], numTris); if (!silent) { printf("*** Before optimization ***\n"); printf("Cache misses\t: %d\n", misses); printf("ACMR\t\t: %f\n", (float)misses / (float)numTris); } VertexCacheOptimizer vco; if(!silent) { printf("Optimizing ... \n"); } // vco.draw_list is the new order VertexCacheOptimizer::Result res = vco.Optimize(&triBuf[0], numTris); if (res) { printf("Error in vertex cache optimization...\n"); } misses = vertex_cache.GetCacheMissCount(&triBuf[0], numTris); if (!silent) { printf("*** After optimization ***\n"); printf("Cache misses\t: %d\n", misses); printf("ACMR\t\t: %f\n", (float)misses / (float)numTris); } for (int t=0; t<numTris; t++) { OF(t,0) = triBuf[3*t]; OF(t,1) = triBuf[3*t + 1]; OF(t,2) = triBuf[3*t + 2]; } }
TEST(copyleft_cgal_peel_outer_hull_layers, TwoCubes) { Eigen::MatrixXd V; Eigen::MatrixXi F; test_common::load_mesh("two-boxes-bad-self-union.ply", V, F); ASSERT_EQ(486, V.rows()); ASSERT_EQ(708, F.rows()); typedef CGAL::Exact_predicates_exact_constructions_kernel K; typedef K::FT Scalar; typedef Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> MatrixXe; MatrixXe Vs; Eigen::MatrixXi Fs, IF; Eigen::VectorXi J, IM; igl::copyleft::cgal::RemeshSelfIntersectionsParam param; igl::copyleft::cgal::remesh_self_intersections(V, F, param, Vs, Fs, IF, J, IM); std::for_each(Fs.data(),Fs.data()+Fs.size(), [&IM](int & a){ a=IM(a); }); MatrixXe Vt; Eigen::MatrixXi Ft; igl::remove_unreferenced(Vs,Fs,Vt,Ft,IM); const size_t num_faces = Ft.rows(); Eigen::VectorXi I, flipped; size_t num_peels = igl::copyleft::cgal::peel_outer_hull_layers(Vt, Ft, I, flipped); Eigen::MatrixXd vertices(Vt.rows(), Vt.cols()); std::transform(Vt.data(), Vt.data() + Vt.rows() * Vt.cols(), vertices.data(), [](Scalar v) { return CGAL::to_double(v); }); igl::writeOBJ("debug.obj", vertices, Ft); ASSERT_EQ(num_faces, I.rows()); ASSERT_EQ(0, I.minCoeff()); ASSERT_EQ(1, I.maxCoeff()); }
IGL_INLINE void igl::adjacency_matrix( const Eigen::MatrixXi & F, Eigen::SparseMatrix<T>& A) { using namespace std; using namespace Eigen; typedef Triplet<T> IJV; vector<IJV > ijv; ijv.reserve(F.size()*2); // Loop over faces for(int i = 0;i<F.rows();i++) { // Loop over this face for(int j = 0;j<F.cols();j++) { // Get indices of edge: s --> d int s = F(i,j); int d = F(i,(j+1)%F.cols()); ijv.push_back(IJV(s,d,1)); ijv.push_back(IJV(d,s,1)); } } const int n = F.maxCoeff()+1; A.resize(n,n); switch(F.cols()) { case 3: A.reserve(6*(F.maxCoeff()+1)); break; case 4: A.reserve(26*(F.maxCoeff()+1)); break; } A.setFromTriplets(ijv.begin(),ijv.end()); // Force all non-zeros to be one // Iterate over outside for(int k=0; k<A.outerSize(); ++k) { // Iterate over inside for(typename Eigen::SparseMatrix<T>::InnerIterator it (A,k); it; ++it) { assert(it.value() != 0); A.coeffRef(it.row(),it.col()) = 1; } } }
IGL_INLINE void igl::PolyVectorFieldFinder<DerivedV, DerivedF>::getGeneralCoeffConstraints(const Eigen::VectorXi &isConstrained, const Eigen::Matrix<typename DerivedV::Scalar, Eigen::Dynamic, Eigen::Dynamic> &cfW, int k, Eigen::Matrix<std::complex<typename DerivedV::Scalar>, Eigen::Dynamic,1> &Ck) { int numConstrained = isConstrained.sum(); Ck.resize(numConstrained,1); int n = cfW.cols()/3; Eigen::MatrixXi allCombs; { Eigen::VectorXi V = Eigen::VectorXi::LinSpaced(n,0,n-1); igl::nchoosek(V,k+1,allCombs); } int ind = 0; for (int fi = 0; fi <numF; ++fi) { const Eigen::Matrix<typename DerivedV::Scalar, 1, 3> &b1 = B1.row(fi); const Eigen::Matrix<typename DerivedV::Scalar, 1, 3> &b2 = B2.row(fi); if(isConstrained[fi]) { std::complex<typename DerivedV::Scalar> ck(0); for (int j = 0; j < allCombs.rows(); ++j) { std::complex<typename DerivedV::Scalar> tk(1.); //collect products for (int i = 0; i < allCombs.cols(); ++i) { int index = allCombs(j,i); const Eigen::Matrix<typename DerivedV::Scalar, 1, 3> &w = cfW.block(fi,3*index,1,3); typename DerivedV::Scalar w0 = w.dot(b1); typename DerivedV::Scalar w1 = w.dot(b2); std::complex<typename DerivedV::Scalar> u(w0,w1); tk*= u*u; } //collect sum ck += tk; } Ck(ind) = ck; ind ++; } } }
IGL_INLINE void igl::n_polyvector(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, const Eigen::VectorXi& b, const Eigen::MatrixXd& bc, Eigen::MatrixXd &output) { Eigen::VectorXi isConstrained = Eigen::VectorXi::Constant(F.rows(),0); Eigen::MatrixXd cfW = Eigen::MatrixXd::Constant(F.rows(),bc.cols(),0); for(unsigned i=0; i<b.size();++i) { isConstrained(b(i)) = 1; cfW.row(b(i)) << bc.row(i); } if (b.size() == F.rows()) { output = cfW; return; } int n = cfW.cols()/3; igl::PolyVectorFieldFinder<Eigen::MatrixXd, Eigen::MatrixXi> pvff(V,F,n); pvff.solve(isConstrained, cfW, output); }
IGL_INLINE void igl::triangle_fan( const Eigen::MatrixXi & E, Eigen::MatrixXi & cap) { using namespace std; using namespace Eigen; // Handle lame base case if(E.size() == 0) { cap.resize(0,E.cols()+1); return; } // "Triangulate" aka "close" the E trivially with facets // Note: in 2D we need to know if E endpoints are incoming or // outgoing (left or right). Thus this will not work. assert(E.cols() == 2); // Arbitrary starting vertex //int s = E(int(((double)rand() / RAND_MAX)*E.rows()),0); int s = E(rand()%E.rows(),0); vector<vector<int> > lcap; for(int i = 0;i<E.rows();i++) { // Skip edges incident on s (they would be zero-area) if(E(i,0) == s || E(i,1) == s) { continue; } vector<int> e(3); e[0] = s; e[1] = E(i,0); e[2] = E(i,1); lcap.push_back(e); } list_to_matrix(lcap,cap); }
int main(int argc, char * argv[]) { using namespace Eigen; using namespace igl; using namespace std; // init mesh string filename = "../shared/decimated-knight.obj"; if(argc < 2) { cerr<<"Usage:"<<endl<<" ./example input.obj"<<endl; cout<<endl<<"Opening default mesh..."<<endl; }else { // Read and prepare mesh filename = argv[1]; } if(!read_triangle_mesh(filename,V,F)) { return 1; } // Compute normals, centroid, colors, bounding box diagonal per_face_normals(V,F,N); normalize_row_lengths(N,N); mean = V.colwise().mean(); C.resize(F.rows(),3); init_C(); bbd = (V.colwise().maxCoeff() - V.colwise().minCoeff()).maxCoeff(); // Init embree ei.init(V.cast<float>(),F.cast<int>()); // Init glut glutInit(&argc,argv); glutInitDisplayString( "rgba depth double samples>=8 "); glutInitWindowSize(glutGet(GLUT_SCREEN_WIDTH)/2.0,glutGet(GLUT_SCREEN_HEIGHT)); glutCreateWindow("embree"); glutDisplayFunc(display); glutReshapeFunc(reshape); glutKeyboardFunc(key); glutMouseFunc(mouse); glutMotionFunc(mouse_drag); glutPassiveMotionFunc(mouse_move); glutMainLoop(); return 0; }
int ComputationGraph::matrix(Eigen::MatrixXi matrix) { std::vector<int> output; for (int i = 0; i < matrix.rows(); i++) { for (int j = 0; j < matrix.cols(); j++) { output.push_back(matrix(i, j)); } } nodes.push_back({ -1, {}, output }); return nodes.size() - 1; }
IGL_INLINE std::vector<int> igl::circulation( const int e, const bool ccw, const Eigen::MatrixXi & F, const Eigen::MatrixXi & E, const Eigen::VectorXi & EMAP, const Eigen::MatrixXi & EF, const Eigen::MatrixXi & EI) { // prepare output std::vector<int> N; N.reserve(6); const int m = F.rows(); const auto & step = [&]( const int e, const int ff, int & ne, int & nf) { assert((EF(e,1) == ff || EF(e,0) == ff) && "e should touch ff"); //const int fside = EF(e,1)==ff?1:0; const int nside = EF(e,0)==ff?1:0; const int nv = EI(e,nside); // get next face nf = EF(e,nside); // get next edge const int dir = ccw?-1:1; ne = EMAP(nf+m*((nv+dir+3)%3)); }; // Always start with first face (ccw in step will be sure to turn right // direction) const int f0 = EF(e,0); int fi = f0; int ei = e; while(true) { step(ei,fi,ei,fi); N.push_back(fi); // back to start? if(fi == f0) { assert(ei == e); break; } } return N; }
void drawCuts(igl::viewer::Viewer& viewer, const Eigen::MatrixXi &cuts) { int maxCutNum = cuts.sum(); Eigen::MatrixXd start(maxCutNum,3); Eigen::MatrixXd end(maxCutNum,3); int ind = 0; for (unsigned int i=0;i<F.rows();i++) for (int j=0;j<3;j++) if (cuts(i,j)) { start.row(ind) = V.row(F(i,j)); end.row(ind) = V.row(F(i,(j+1)%3)); ind++; } viewer.data.add_edges(start, end , Eigen::RowVector3d(1.,0,1.)); }
// Plots the mesh with an N-RoSy field and its singularities on top // The constrained faces (b) are colored in red. void plot_mesh_nrosy( igl::viewer::Viewer& viewer, Eigen::MatrixXd& V, Eigen::MatrixXi& F, int N, Eigen::MatrixXd& PD1, Eigen::VectorXd& S, Eigen::VectorXi& b) { using namespace Eigen; using namespace std; // Clear the mesh viewer.data.clear(); viewer.data.set_mesh(V,F); // Expand the representative vectors in the full vector set and plot them as lines double avg = igl::avg_edge_length(V, F); MatrixXd Y; representative_to_nrosy(V, F, PD1, N, Y); MatrixXd B; igl::barycenter(V,F,B); MatrixXd Be(B.rows()*N,3); for(unsigned i=0; i<B.rows(); ++i) for(unsigned j=0; j<N; ++j) Be.row(i*N+j) = B.row(i); viewer.data.add_edges(Be,Be+Y*(avg/2),RowVector3d(0,0,1)); // Plot the singularities as colored dots (red for negative, blue for positive) for (unsigned i=0; i<S.size(); ++i) { if (S(i) < -0.001) viewer.data.add_points(V.row(i),RowVector3d(1,0,0)); else if (S(i) > 0.001) viewer.data.add_points(V.row(i),RowVector3d(0,1,0)); } // Highlight in red the constrained faces MatrixXd C = MatrixXd::Constant(F.rows(),3,1); for (unsigned i=0; i<b.size(); ++i) C.row(b(i)) << 1, 0, 0; viewer.data.set_colors(C); }
int main(int argc, char * argv[]) { using namespace Eigen; using namespace igl; using namespace std; // init mesh string filename = "/usr/local/igl/libigl/examples/shared/decimated-knight.obj" ; if(argc > 1) { filename = argv[1]; } if(!read_triangle_mesh(filename,V,F)) { return 1; } // Compute normals, centroid, colors, bounding box diagonal per_face_normals(V,F,N); normalize_row_lengths(N,N); mean = V.colwise().mean(); C.resize(F.rows(),3); init_C(); bbd = (V.colwise().maxCoeff() - V.colwise().minCoeff()).maxCoeff(); // Init viewports init_viewports(); // Init glut glutInit(&argc,argv); glutInitDisplayString( "rgba depth double samples>=8 "); glutInitWindowSize(glutGet(GLUT_SCREEN_WIDTH)/2.0,glutGet(GLUT_SCREEN_HEIGHT)); glutCreateWindow("multi-viewport"); glutDisplayFunc(display); glutReshapeFunc(reshape); glutKeyboardFunc(key); glutMouseFunc(mouse); glutMotionFunc(mouse_drag); glutPassiveMotionFunc(mouse_move); glutMainLoop(); return 0; }
IGL_INLINE void igl::forward_kinematics( const Eigen::MatrixXd & C, const Eigen::MatrixXi & BE, const Eigen::VectorXi & P, const std::vector< Eigen::Quaterniond,Eigen::aligned_allocator<Eigen::Quaterniond> > & dQ, std::vector< Eigen::Quaterniond,Eigen::aligned_allocator<Eigen::Quaterniond> > & vQ, std::vector<Eigen::Vector3d> & vT) { using namespace std; using namespace Eigen; const int m = BE.rows(); assert(m == P.rows()); assert(m == (int)dQ.size()); vector<bool> computed(m,false); vQ.resize(m); vT.resize(m); function<void (int) > fk_helper = [&] (int b) { if(!computed[b]) { if(P(b) < 0) { // base case for roots vQ[b] = dQ[b]; const Vector3d r = C.row(BE(b,0)).transpose(); vT[b] = r-dQ[b]*r; }else { // Otherwise first compute parent's const int p = P(b); fk_helper(p); vQ[b] = vQ[p] * dQ[b]; const Vector3d r = C.row(BE(b,0)).transpose(); vT[b] = vT[p] - vQ[b]*r + vQ[p]*r; } computed[b] = true; } }; for(int b = 0;b<m;b++) { fk_helper(b); } }
/* Train the classifier with the dataset of breastcancer patients from the LibSVM Libary */ void TrainSVMClassifier_BreastCancerDataSet_shouldReturnTrue() { /* Declarating an featurematrixdataset, the first matrix of the matrixpair is the trainingmatrix and the second one is the testmatrix.*/ std::pair<MatrixDoubleType,MatrixDoubleType> matrixDouble; matrixDouble = convertCSVToMatrix<double>(GetTestDataFilePath("Classification/FeaturematrixBreastcancer.csv"),';',0.5,true); m_TrainingMatrixX = matrixDouble.first; m_TestXPredict = matrixDouble.second; /* The declaration of the labelmatrixdataset is equivalent to the declaration of the featurematrixdataset.*/ std::pair<MatrixIntType,MatrixIntType> matrixInt; matrixInt = convertCSVToMatrix<int>(GetTestDataFilePath("Classification/LabelmatrixBreastcancer.csv"),';',0.5,false); m_TrainingLabelMatrixY = matrixInt.first; m_TestYPredict = matrixInt.second; /* Setting of the SVM-Parameters*/ classifier = mitk::LibSVMClassifier::New(); classifier->SetGamma(1/(double)(m_TrainingMatrixX.cols())); classifier->SetSvmType(0); classifier->SetKernelType(2); /* Train the classifier, by giving trainingdataset for the labels and features. The result in an colunmvector of the labels.*/ classifier->Train(m_TrainingMatrixX,m_TrainingLabelMatrixY); Eigen::MatrixXi classes = classifier->Predict(m_TestXPredict); /* Testing the matching between the calculated colunmvector and the result of the SVM */ unsigned int maxrows = classes.rows(); bool isYPredictVector = false; int count = 0; for(int i= 0; i < maxrows; i++){ if(classes(i,0) == m_TestYPredict(i,0)){ isYPredictVector = true; count++; } } MITK_INFO << 100*count/(double)(maxrows) << "%"; MITK_TEST_CONDITION(isIntervall<int>(m_TestYPredict,classes,75,100),"Testvalue is in range."); }
IGL_INLINE void igl::viewer::ViewerCore::get_scale_and_shift_to_fit_mesh( const Eigen::MatrixXd& V, const Eigen::MatrixXi& F, float& zoom, Eigen::Vector3f& shift) { if (V.rows() == 0) return; Eigen::MatrixXd BC; if (F.rows() <= 1) { BC = V; } else { igl::barycenter(V,F,BC); } return get_scale_and_shift_to_fit_mesh(BC,zoom,shift); }
IGL_INLINE bool igl::copyleft::progressive_hulls( const Eigen::MatrixXd & V, const Eigen::MatrixXi & F, const size_t max_m, Eigen::MatrixXd & U, Eigen::MatrixXi & G, Eigen::VectorXi & J) { int m = F.rows(); Eigen::VectorXi I; return decimate( V, F, progressive_hulls_cost_and_placement, max_faces_stopping_condition(m,max_m), U, G, J, I); }
void triangle_tree( const Eigen::MatrixXd & V, const Eigen::MatrixXi & F, TriTree & tree, TriangleList & tlist) { assert(F.cols() == 3); tlist.clear(); // Loop over facets for(int f = 0;f<F.rows();f++) { Point3 a(V(F(f,0),0), V(F(f,0),1), V(F(f,0),2)); Point3 b(V(F(f,1),0), V(F(f,1),1), V(F(f,1),2)); Point3 c(V(F(f,2),0), V(F(f,2),1), V(F(f,2),2)); tlist.push_back(Triangle3( a,b,c)); } // constructs AABB tree tree.clear(); tree.insert(tlist.begin(),tlist.end()); }
int main(int argc, char *argv[]) { // Mesh with per-face color Eigen::MatrixXd V, C; Eigen::MatrixXi F; // Load a mesh in OFF format igl::readOFF(TUTORIAL_SHARED_PATH "/fertility.off", V, F); // Initialize white C = Eigen::MatrixXd::Constant(F.rows(),3,1); igl::viewer::Viewer viewer; viewer.callback_mouse_down = [&V,&F,&C](igl::viewer::Viewer& viewer, int, int)->bool { int fid; Eigen::Vector3f bc; // Cast a ray in the view direction starting from the mouse position double x = viewer.current_mouse_x; double y = viewer.core.viewport(3) - viewer.current_mouse_y; if(igl::unproject_onto_mesh(Eigen::Vector2f(x,y), viewer.core.view * viewer.core.model, viewer.core.proj, viewer.core.viewport, V, F, fid, bc)) { // paint hit red C.row(fid)<<1,0,0; viewer.data.set_colors(C); return true; } return false; }; std::cout<<R"(Usage: [click] Pick face on shape )"; // Show mesh viewer.data.set_mesh(V, F); viewer.data.set_colors(C); viewer.core.show_lines = false; viewer.launch(); }
// Helpers that draws the most common meshes IGL_INLINE void igl::ViewerData::set_mesh(const Eigen::MatrixXd& _V, const Eigen::MatrixXi& _F) { using namespace std; Eigen::MatrixXd V_temp; // If V only has two columns, pad with a column of zeros if (_V.cols() == 2) { V_temp = Eigen::MatrixXd::Zero(_V.rows(),3); V_temp.block(0,0,_V.rows(),2) = _V; } else V_temp = _V; if (V.rows() == 0 && F.rows() == 0) { clear(); V = V_temp; F = _F; compute_normals(); uniform_colors(Eigen::Vector3d(51.0/255.0,43.0/255.0,33.3/255.0), Eigen::Vector3d(255.0/255.0,228.0/255.0,58.0/255.0), Eigen::Vector3d(255.0/255.0,235.0/255.0,80.0/255.0)); grid_texture(); } else { if (_V.rows() == V.rows() && _F.rows() == F.rows()) { V = V_temp; F = _F; } else cerr << "ERROR (set_mesh): The new mesh has a different number of vertices/faces. Please clear the mesh before plotting."; } dirty |= DIRTY_FACE | DIRTY_POSITION; }
// IF THIS IS EVER TEMPLATED BE SURE THAT V IS COLMAJOR IGL_INLINE void igl::winding_number( const Eigen::MatrixXd & V, const Eigen::MatrixXi & F, const Eigen::MatrixXd & O, Eigen::VectorXd & W) { using namespace Eigen; // make room for output W.resize(O.rows(),1); switch(F.cols()) { case 2: return winding_number_2( V.data(), V.rows(), F.data(), F.rows(), O.data(), O.rows(), W.data()); case 3: { WindingNumberAABB<Vector3d> hier(V,F); hier.grow(); // loop over origins const int no = O.rows(); # pragma omp parallel for if (no>IGL_WINDING_NUMBER_OMP_MIN_VALUE) for(int o = 0; o<no; o++) { Vector3d p = O.row(o); W(o) = hier.winding_number(p); } break; } default: assert(false && "Bad simplex size"); break; } }
void shortest_edge_and_midpoint6(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, RowVectorXd &p) { // circulation angle sum p = 0.5 * (V.row(E(e, 0)) + V.row(E(e, 1))); const vector<int> c1 = igl::circulation(e, false, F, E, EMAP, EF, EI); const vector<int> c2 = igl::circulation(e, true, F, E, EMAP, EF, EI); unordered_set<int> circ; circ.insert(c1.begin(), c1.end()); circ.insert(c2.begin(), c2.end()); cost = 0.0; for (int face : circ) { for (int j = 0; j < 3; j++) { int edge = EMAP(face + j * F.rows()); cost += acos(normals.row(EF(edge, 0)).dot(normals.row(EF(edge, 1)))); } } }
IGL_INLINE bool igl::tetgen::mesh_with_skeleton( const Eigen::MatrixXd & V, const Eigen::MatrixXi & F, const Eigen::MatrixXd & C, const Eigen::VectorXi & /*P*/, const Eigen::MatrixXi & BE, const Eigen::MatrixXi & CE, const int samples_per_bone, const std::string & tetgen_flags, Eigen::MatrixXd & VV, Eigen::MatrixXi & TT, Eigen::MatrixXi & FF) { using namespace Eigen; using namespace igl; using namespace std; const string eff_tetgen_flags = (tetgen_flags.length() == 0?DEFAULT_TETGEN_FLAGS:tetgen_flags); // Collect all edges that need samples: MatrixXi BECE = cat(1,BE,CE); MatrixXd S; // Sample each edge with 10 samples. (Choice of 10 doesn't seem to matter so // much, but could under some circumstances) sample_edges(C,BECE,samples_per_bone,S); // Vertices we'll constrain tet mesh to meet MatrixXd VS = cat(1,V,S); // Use tetgen to mesh the interior of surface, this assumes surface: // * has no holes // * has no non-manifold edges or vertices // * has consistent orientation // * has no self-intersections // * has no 0-volume pieces //writeOBJ("mesh_with_skeleton.obj",VS,F); cerr<<"tetgen begin()"<<endl; int status = tetrahedralize( VS,F,eff_tetgen_flags,VV,TT,FF); cerr<<"tetgen end()"<<endl; if(FF.rows() != F.rows()) { // Issue a warning if the surface has changed cerr<<"mesh_with_skeleton: Warning: boundary faces != input faces"<<endl; } if(status != 0) { cerr<< "***************************************************************"<<endl<< "***************************************************************"<<endl<< "***************************************************************"<<endl<< "***************************************************************"<<endl<< "* mesh_with_skeleton: tetgen failed. Just meshing convex hull *"<<endl<< "***************************************************************"<<endl<< "***************************************************************"<<endl<< "***************************************************************"<<endl<< "***************************************************************"<<endl; // If meshing convex hull then use more regular mesh status = tetrahedralize(VS,F,"q1.414",VV,TT,FF); // I suppose this will fail if the skeleton is outside the mesh assert(FF.maxCoeff() < VV.rows()); if(status != 0) { cerr<<"mesh_with_skeleton: tetgen failed again."<<endl; return false; } } return true; }
bool key_down(igl::viewer::Viewer& viewer, unsigned char key, int modifier) { using namespace std; using namespace Eigen; if (key <'1' || key >'6') return false; viewer.data.clear(); viewer.core.show_lines = false; viewer.core.show_texture = false; if (key == '1') { // Frame field constraints viewer.data.set_mesh(V, F); MatrixXd F1_t = MatrixXd::Zero(FF1.rows(),FF1.cols()); MatrixXd F2_t = MatrixXd::Zero(FF2.rows(),FF2.cols()); // Highlight in red the constrained faces MatrixXd C = MatrixXd::Constant(F.rows(),3,1); for (unsigned i=0; i<b.size();++i) { C.row(b(i)) << 1, 0, 0; F1_t.row(b(i)) = bc1.row(i); F2_t.row(b(i)) = bc2.row(i); } viewer.data.set_colors(C); MatrixXd C1,C2; VectorXd K1 = F1_t.rowwise().norm(); VectorXd K2 = F2_t.rowwise().norm(); igl::jet(K1,true,C1); igl::jet(K2,true,C2); viewer.data.add_edges(B - global_scale*F1_t, B + global_scale*F1_t ,C1); viewer.data.add_edges(B - global_scale*F2_t, B + global_scale*F2_t ,C2); } if (key == '2') { // Frame field viewer.data.set_mesh(V, F); MatrixXd C1,C2; VectorXd K1 = FF1.rowwise().norm(); VectorXd K2 = FF2.rowwise().norm(); igl::jet(K1,true,C1); igl::jet(K2,true,C2); viewer.data.add_edges(B - global_scale*FF1, B + global_scale*FF1 ,C1); viewer.data.add_edges(B - global_scale*FF2, B + global_scale*FF2 ,C2); // Highlight in red the constrained faces MatrixXd C = MatrixXd::Constant(F.rows(),3,1); for (unsigned i=0; i<b.size();++i) C.row(b(i)) << 1, 0, 0; viewer.data.set_colors(C); } if (key == '3') { // Deformed with frame field viewer.data.set_mesh(V_deformed, F); viewer.data.add_edges(B_deformed - global_scale*FF1_deformed, B_deformed + global_scale*FF1_deformed ,Eigen::RowVector3d(1,0,0)); viewer.data.add_edges(B_deformed - global_scale*FF2_deformed, B_deformed + global_scale*FF2_deformed ,Eigen::RowVector3d(0,0,1)); viewer.data.set_colors(RowVector3d(1,1,1)); } if (key == '4') { // Deformed with cross field viewer.data.set_mesh(V_deformed, F); viewer.data.add_edges(B_deformed - global_scale*X1_deformed, B_deformed + global_scale*X1_deformed ,Eigen::RowVector3d(0,0,1)); viewer.data.add_edges(B_deformed - global_scale*X2_deformed, B_deformed + global_scale*X2_deformed ,Eigen::RowVector3d(0,0,1)); viewer.data.set_colors(RowVector3d(1,1,1)); } if (key == '5') { // Deformed with quad texture viewer.data.set_mesh(V_deformed, F); viewer.data.set_uv(V_uv,F_uv); viewer.data.set_colors(RowVector3d(1,1,1)); viewer.core.show_texture = true; } if (key == '6') { // Deformed with quad texture viewer.data.set_mesh(V, F); viewer.data.set_uv(V_uv,F_uv); viewer.data.set_colors(RowVector3d(1,1,1)); viewer.core.show_texture = true; } // Replace the standard texture with an integer shift invariant texture Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> texture_R, texture_G, texture_B; line_texture(texture_R, texture_G, texture_B); viewer.data.set_texture(texture_R, texture_B, texture_G); viewer.core.align_camera_center(viewer.data.V,viewer.data.F); return false; }
IGL_INLINE void igl::triangle::triangulate( const Eigen::MatrixXd& V, const Eigen::MatrixXi& E, const Eigen::MatrixXd& H, const std::string flags, Eigen::MatrixXd& V2, Eigen::MatrixXi& F2) { using namespace std; using namespace Eigen; // Prepare the flags string full_flags = flags + "pzB"; // Prepare the input struct triangulateio in; assert(V.cols() == 2); in.numberofpoints = V.rows(); in.pointlist = (double*)calloc(V.rows()*2,sizeof(double)); for (unsigned i=0;i<V.rows();++i) for (unsigned j=0;j<2;++j) in.pointlist[i*2+j] = V(i,j); in.numberofpointattributes = 0; in.pointmarkerlist = (int*)calloc(V.rows(),sizeof(int)); for (unsigned i=0;i<V.rows();++i) in.pointmarkerlist[i] = 1; in.trianglelist = NULL; in.numberoftriangles = 0; in.numberofcorners = 0; in.numberoftriangleattributes = 0; in.triangleattributelist = NULL; in.numberofsegments = E.rows(); in.segmentlist = (int*)calloc(E.rows()*2,sizeof(int)); for (unsigned i=0;i<E.rows();++i) for (unsigned j=0;j<2;++j) in.segmentlist[i*2+j] = E(i,j); in.segmentmarkerlist = (int*)calloc(E.rows(),sizeof(int)); for (unsigned i=0;i<E.rows();++i) in.segmentmarkerlist[i] = 1; in.numberofholes = H.rows(); in.holelist = (double*)calloc(H.rows()*2,sizeof(double)); for (unsigned i=0;i<H.rows();++i) for (unsigned j=0;j<2;++j) in.holelist[i*2+j] = H(i,j); in.numberofregions = 0; // Prepare the output struct triangulateio out; out.pointlist = NULL; out.trianglelist = NULL; out.segmentlist = NULL; // Call triangle ::triangulate(const_cast<char*>(full_flags.c_str()), &in, &out, 0); // Return the mesh V2.resize(out.numberofpoints,2); for (unsigned i=0;i<V2.rows();++i) for (unsigned j=0;j<2;++j) V2(i,j) = out.pointlist[i*2+j]; F2.resize(out.numberoftriangles,3); for (unsigned i=0;i<F2.rows();++i) for (unsigned j=0;j<3;++j) F2(i,j) = out.trianglelist[i*3+j]; // Cleanup in free(in.pointlist); free(in.pointmarkerlist); free(in.segmentlist); free(in.segmentmarkerlist); free(in.holelist); // Cleanup out free(out.pointlist); free(out.trianglelist); free(out.segmentlist); }
IGL_INLINE bool igl::collapse_edge( 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, Eigen::MatrixXd & V, Eigen::MatrixXi & F, Eigen::MatrixXi & E, Eigen::VectorXi & EMAP, Eigen::MatrixXi & EF, Eigen::MatrixXi & EI, std::set<std::pair<double,int> > & Q, std::vector<std::set<std::pair<double,int> >::iterator > & Qit, Eigen::MatrixXd & C, int & e, int & e1, int & e2, int & f1, int & f2) { using namespace Eigen; if(Q.empty()) { // no edges to collapse return false; } std::pair<double,int> p = *(Q.begin()); if(p.first == std::numeric_limits<double>::infinity()) { // min cost edge is infinite cost return false; } Q.erase(Q.begin()); e = p.second; Qit[e] = Q.end(); 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); if(collapsed) { // Erase the two, other collapsed edges Q.erase(Qit[e1]); Qit[e1] = Q.end(); Q.erase(Qit[e2]); Qit[e2] = Q.end(); // update local neighbors // loop over original face neighbors for(auto n : N) { if(F(n,0) != IGL_COLLAPSE_EDGE_NULL || F(n,1) != IGL_COLLAPSE_EDGE_NULL || F(n,2) != IGL_COLLAPSE_EDGE_NULL) { for(int v = 0;v<3;v++) { // get edge id const int ei = EMAP(v*F.rows()+n); // erase old entry Q.erase(Qit[ei]); // compute cost and potential placement double cost; RowVectorXd place; cost_and_placement(ei,V,F,E,EMAP,EF,EI,cost,place); // Replace in queue Qit[ei] = Q.insert(std::pair<double,int>(cost,ei)).first; C.row(ei) = place; } } } }else { // reinsert with infinite weight (the provided cost function must **not** // have given this un-collapsable edge inf cost already) p.first = std::numeric_limits<double>::infinity(); Qit[e] = Q.insert(p).first; } return collapsed; }
IGL_INLINE bool igl::collapse_edge( const int e, const Eigen::RowVectorXd & p, Eigen::MatrixXd & V, Eigen::MatrixXi & F, Eigen::MatrixXi & E, Eigen::VectorXi & EMAP, Eigen::MatrixXi & EF, Eigen::MatrixXi & EI, int & a_e1, int & a_e2, int & a_f1, int & a_f2) { // Assign this to 0 rather than, say, -1 so that deleted elements will get // draw as degenerate elements at vertex 0 (which should always exist and // never get collapsed to anything else since it is the smallest index) using namespace Eigen; using namespace std; const int eflip = E(e,0)>E(e,1); // source and destination const int s = eflip?E(e,1):E(e,0); const int d = eflip?E(e,0):E(e,1); if(!edge_collapse_is_valid(e,F,E,EMAP,EF,EI)) { return false; } // Important to grab neighbors of d before monkeying with edges const std::vector<int> nV2Fd = circulation(e,!eflip,F,E,EMAP,EF,EI); // The following implementation strongly relies on s<d assert(s<d && "s should be less than d"); // move source and destination to midpoint V.row(s) = p; V.row(d) = p; // Helper function to replace edge and associate information with NULL const auto & kill_edge = [&E,&EI,&EF](const int e) { E(e,0) = IGL_COLLAPSE_EDGE_NULL; E(e,1) = IGL_COLLAPSE_EDGE_NULL; EF(e,0) = IGL_COLLAPSE_EDGE_NULL; EF(e,1) = IGL_COLLAPSE_EDGE_NULL; EI(e,0) = IGL_COLLAPSE_EDGE_NULL; EI(e,1) = IGL_COLLAPSE_EDGE_NULL; }; // update edge info // for each flap const int m = F.rows(); for(int side = 0;side<2;side++) { const int f = EF(e,side); const int v = EI(e,side); const int sign = (eflip==0?1:-1)*(1-2*side); // next edge emanating from d const int e1 = EMAP(f+m*((v+sign*1+3)%3)); // prev edge pointing to s const int e2 = EMAP(f+m*((v+sign*2+3)%3)); assert(E(e1,0) == d || E(e1,1) == d); assert(E(e2,0) == s || E(e2,1) == s); // face adjacent to f on e1, also incident on d const bool flip1 = EF(e1,1)==f; const int f1 = flip1 ? EF(e1,0) : EF(e1,1); assert(f1!=f); assert(F(f1,0)==d || F(f1,1)==d || F(f1,2) == d); // across from which vertex of f1 does e1 appear? const int v1 = flip1 ? EI(e1,0) : EI(e1,1); // Kill e1 kill_edge(e1); // Kill f F(f,0) = IGL_COLLAPSE_EDGE_NULL; F(f,1) = IGL_COLLAPSE_EDGE_NULL; F(f,2) = IGL_COLLAPSE_EDGE_NULL; // map f1's edge on e1 to e2 assert(EMAP(f1+m*v1) == e1); EMAP(f1+m*v1) = e2; // side opposite f2, the face adjacent to f on e2, also incident on s const int opp2 = (EF(e2,0)==f?0:1); assert(EF(e2,opp2) == f); EF(e2,opp2) = f1; EI(e2,opp2) = v1; // remap e2 from d to s E(e2,0) = E(e2,0)==d ? s : E(e2,0); E(e2,1) = E(e2,1)==d ? s : E(e2,1); if(side==0) { a_e1 = e1; a_f1 = f; }else { a_e2 = e1; a_f2 = f; } } // finally, reindex faces and edges incident on d. Do this last so asserts // make sense. // // Could actually skip first and last, since those are always the two // collpased faces. for(auto f : nV2Fd) { for(int v = 0;v<3;v++) { if(F(f,v) == d) { const int flip1 = (EF(EMAP(f+m*((v+1)%3)),0)==f)?1:0; const int flip2 = (EF(EMAP(f+m*((v+2)%3)),0)==f)?0:1; assert( E(EMAP(f+m*((v+1)%3)),flip1) == d || E(EMAP(f+m*((v+1)%3)),flip1) == s); E(EMAP(f+m*((v+1)%3)),flip1) = s; assert( E(EMAP(f+m*((v+2)%3)),flip2) == d || E(EMAP(f+m*((v+2)%3)),flip2) == s); E(EMAP(f+m*((v+2)%3)),flip2) = s; F(f,v) = s; break; } } } // Finally, "remove" this edge and its information kill_edge(e); return true; }