bool key_down(igl::Viewer& viewer, unsigned char key, int modifier) { if (key == 'E') { extend_arrows = !extend_arrows; } if (key <'1' || key >'8') return false; viewer.data.clear(); viewer.core.show_lines = false; viewer.core.show_texture = false; if (key == '1') { // Cross field viewer.data.set_mesh(V, F); viewer.data.add_edges(extend_arrows ? B - global_scale*X1 : B, B + global_scale*X1 ,Eigen::RowVector3d(1,0,0)); viewer.data.add_edges(extend_arrows ? B - global_scale*X2 : B, B + global_scale*X2 ,Eigen::RowVector3d(0,0,1)); } if (key == '2') { // Bisector field viewer.data.set_mesh(V, F); viewer.data.add_edges(extend_arrows ? B - global_scale*BIS1 : B, B + global_scale*BIS1 ,Eigen::RowVector3d(1,0,0)); viewer.data.add_edges(extend_arrows ? B - global_scale*BIS2 : B, B + global_scale*BIS2 ,Eigen::RowVector3d(0,0,1)); } if (key == '3') { // Bisector field combed viewer.data.set_mesh(V, F); viewer.data.add_edges(extend_arrows ? B - global_scale*BIS1_combed : B, B + global_scale*BIS1_combed ,Eigen::RowVector3d(1,0,0)); viewer.data.add_edges(extend_arrows ? B - global_scale*BIS2_combed : B, B + global_scale*BIS2_combed ,Eigen::RowVector3d(0,0,1)); } if (key == '4') { // Singularities and cuts viewer.data.set_mesh(V, F); // Plot cuts int l_count = Seams.sum(); Eigen::MatrixXd P1(l_count,3); Eigen::MatrixXd P2(l_count,3); for (unsigned i=0; i<Seams.rows(); ++i) { for (unsigned j=0; j<Seams.cols(); ++j) { if (Seams(i,j) != 0) { P1.row(l_count-1) = V.row(F(i,j)); P2.row(l_count-1) = V.row(F(i,(j+1)%3)); l_count--; } } } viewer.data.add_edges(P1, P2, Eigen::RowVector3d(1, 0, 0)); // Plot the singularities as colored dots (red for negative, blue for positive) for (unsigned i=0; i<singularityIndex.size();++i) { if (singularityIndex(i) < 2 && singularityIndex(i) > 0) viewer.data.add_points(V.row(i),Eigen::RowVector3d(1,0,0)); else if (singularityIndex(i) > 2) viewer.data.add_points(V.row(i),Eigen::RowVector3d(0,1,0)); } } if (key == '5') { // Singularities and cuts, original field // Singularities and cuts viewer.data.set_mesh(V, F); viewer.data.add_edges(extend_arrows ? B - global_scale*X1_combed : B, B + global_scale*X1_combed ,Eigen::RowVector3d(1,0,0)); viewer.data.add_edges(extend_arrows ? B - global_scale*X2_combed : B, B + global_scale*X2_combed ,Eigen::RowVector3d(0,0,1)); // Plot cuts int l_count = Seams.sum(); Eigen::MatrixXd P1(l_count,3); Eigen::MatrixXd P2(l_count,3); for (unsigned i=0; i<Seams.rows(); ++i) { for (unsigned j=0; j<Seams.cols(); ++j) { if (Seams(i,j) != 0) { P1.row(l_count-1) = V.row(F(i,j)); P2.row(l_count-1) = V.row(F(i,(j+1)%3)); l_count--; } } } viewer.data.add_edges(P1, P2, Eigen::RowVector3d(1, 0, 0)); // Plot the singularities as colored dots (red for negative, blue for positive) for (unsigned i=0; i<singularityIndex.size();++i) { if (singularityIndex(i) < 2 && singularityIndex(i) > 0) viewer.data.add_points(V.row(i),Eigen::RowVector3d(1,0,0)); else if (singularityIndex(i) > 2) viewer.data.add_points(V.row(i),Eigen::RowVector3d(0,1,0)); } } if (key == '6') { // Global parametrization UV viewer.data.set_mesh(UV, FUV); viewer.data.set_uv(UV); viewer.core.show_lines = true; } if (key == '7') { // Global parametrization in 3D viewer.data.set_mesh(V, F); viewer.data.set_uv(UV,FUV); viewer.core.show_texture = true; } if (key == '8') { // Global parametrization in 3D with seams viewer.data.set_mesh(V, F); viewer.data.set_uv(UV_seams,FUV_seams); viewer.core.show_texture = true; } viewer.data.set_colors(Eigen::RowVector3d(1,1,1)); // 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 bool igl::copyleft::boolean::mesh_boolean( const Eigen::PlainObjectBase<DerivedVA> & VA, const Eigen::PlainObjectBase<DerivedFA> & FA, const Eigen::PlainObjectBase<DerivedVB> & VB, const Eigen::PlainObjectBase<DerivedFB> & FB, const WindingNumberOp& wind_num_op, const KeepFunc& keep, const ResolveFunc& resolve_fun, Eigen::PlainObjectBase<DerivedVC > & VC, Eigen::PlainObjectBase<DerivedFC > & FC, Eigen::PlainObjectBase<DerivedJ > & J) { #ifdef MESH_BOOLEAN_TIMING const auto & tictoc = []() -> double { static double t_start = igl::get_seconds(); double diff = igl::get_seconds()-t_start; t_start += diff; return diff; }; const auto log_time = [&](const std::string& label) -> void { std::cout << "mesh_boolean." << label << ": " << tictoc() << std::endl; }; tictoc(); #endif typedef typename DerivedVC::Scalar Scalar; //typedef typename DerivedFC::Scalar Index; typedef CGAL::Epeck Kernel; typedef Kernel::FT ExactScalar; typedef Eigen::Matrix<Scalar,Eigen::Dynamic,3> MatrixX3S; //typedef Eigen::Matrix<Index,Eigen::Dynamic,Eigen::Dynamic> MatrixXI; typedef Eigen::Matrix<typename DerivedJ::Scalar,Eigen::Dynamic,1> VectorXJ; // Generate combined mesh. typedef Eigen::Matrix< ExactScalar, Eigen::Dynamic, Eigen::Dynamic, DerivedVC::IsRowMajor> MatrixXES; MatrixXES V; DerivedFC F; VectorXJ CJ; { DerivedVA VV(VA.rows() + VB.rows(), 3); DerivedFC FF(FA.rows() + FB.rows(), 3); VV << VA, VB; FF << FA, FB.array() + VA.rows(); //// Handle annoying empty cases //if(VA.size()>0) //{ // VV<<VA; //} //if(VB.size()>0) //{ // VV<<VB; //} //if(FA.size()>0) //{ // FF<<FA; //} //if(FB.size()>0) //{ // FF<<FB.array()+VA.rows(); //} resolve_fun(VV, FF, V, F, CJ); } #ifdef MESH_BOOLEAN_TIMING log_time("resolve_self_intersection"); #endif // Compute winding numbers on each side of each facet. const size_t num_faces = F.rows(); Eigen::MatrixXi W; Eigen::VectorXi labels(num_faces); std::transform(CJ.data(), CJ.data()+CJ.size(), labels.data(), [&](int i) { return i<FA.rows() ? 0:1; }); bool valid = true; if (num_faces > 0) { valid = valid & igl::copyleft::cgal::propagate_winding_numbers(V, F, labels, W); } else { W.resize(0, 4); } assert((size_t)W.rows() == num_faces); if (W.cols() == 2) { assert(FB.rows() == 0); Eigen::MatrixXi W_tmp(num_faces, 4); W_tmp << W, Eigen::MatrixXi::Zero(num_faces, 2); W = W_tmp; } else { assert(W.cols() == 4); } #ifdef MESH_BOOLEAN_TIMING log_time("propagate_input_winding_number"); #endif // Compute resulting winding number. Eigen::MatrixXi Wr(num_faces, 2); for (size_t i=0; i<num_faces; i++) { Eigen::MatrixXi w_out(1,2), w_in(1,2); w_out << W(i,0), W(i,2); w_in << W(i,1), W(i,3); Wr(i,0) = wind_num_op(w_out); Wr(i,1) = wind_num_op(w_in); } #ifdef MESH_BOOLEAN_TIMING log_time("compute_output_winding_number"); #endif // Extract boundary separating inside from outside. auto index_to_signed_index = [&](size_t i, bool ori) -> int { return (i+1)*(ori?1:-1); }; //auto signed_index_to_index = [&](int i) -> size_t { // return abs(i) - 1; //}; std::vector<int> selected; for(size_t i=0; i<num_faces; i++) { auto should_keep = keep(Wr(i,0), Wr(i,1)); if (should_keep > 0) { selected.push_back(index_to_signed_index(i, true)); } else if (should_keep < 0) { selected.push_back(index_to_signed_index(i, false)); } } const size_t num_selected = selected.size(); DerivedFC kept_faces(num_selected, 3); DerivedJ kept_face_indices(num_selected, 1); for (size_t i=0; i<num_selected; i++) { size_t idx = abs(selected[i]) - 1; if (selected[i] > 0) { kept_faces.row(i) = F.row(idx); } else { kept_faces.row(i) = F.row(idx).reverse(); } kept_face_indices(i, 0) = CJ[idx]; } #ifdef MESH_BOOLEAN_TIMING log_time("extract_output"); #endif // Finally, remove duplicated faces and unreferenced vertices. { DerivedFC G; DerivedJ JJ; igl::resolve_duplicated_faces(kept_faces, G, JJ); igl::slice(kept_face_indices, JJ, 1, J); #ifdef DOUBLE_CHECK_EXACT_OUTPUT { // Sanity check on exact output. igl::copyleft::cgal::RemeshSelfIntersectionsParam params; params.detect_only = true; params.first_only = true; MatrixXES dummy_VV; DerivedFC dummy_FF, dummy_IF; Eigen::VectorXi dummy_J, dummy_IM; igl::copyleft::cgal::SelfIntersectMesh< Kernel, MatrixXES, DerivedFC, MatrixXES, DerivedFC, DerivedFC, Eigen::VectorXi, Eigen::VectorXi > checker(V, G, params, dummy_VV, dummy_FF, dummy_IF, dummy_J, dummy_IM); if (checker.count != 0) { throw "Self-intersection not fully resolved."; } } #endif MatrixX3S Vs(V.rows(), V.cols()); for (size_t i=0; i<(size_t)V.rows(); i++) { for (size_t j=0; j<(size_t)V.cols(); j++) { igl::copyleft::cgal::assign_scalar(V(i,j), Vs(i,j)); } } Eigen::VectorXi newIM; igl::remove_unreferenced(Vs,G,VC,FC,newIM); } #ifdef MESH_BOOLEAN_TIMING log_time("clean_up"); #endif return valid; }
IGL_INLINE void igl::copyleft::boolean::mesh_boolean( const Eigen::PlainObjectBase<DerivedVA> & VA, const Eigen::PlainObjectBase<DerivedFA> & FA, const Eigen::PlainObjectBase<DerivedVB> & VB, const Eigen::PlainObjectBase<DerivedFB> & FB, const WindingNumberOp& wind_num_op, const KeepFunc& keep, const ResolveFunc& resolve_fun, Eigen::PlainObjectBase<DerivedVC > & VC, Eigen::PlainObjectBase<DerivedFC > & FC, Eigen::PlainObjectBase<DerivedJ > & J) { typedef typename DerivedVC::Scalar Scalar; //typedef typename DerivedFC::Scalar Index; typedef CGAL::Epeck Kernel; typedef Kernel::FT ExactScalar; typedef Eigen::Matrix<Scalar,Eigen::Dynamic,3> MatrixX3S; //typedef Eigen::Matrix<Index,Eigen::Dynamic,Eigen::Dynamic> MatrixXI; typedef Eigen::Matrix<typename DerivedJ::Scalar,Eigen::Dynamic,1> VectorXJ; // Generate combined mesh. typedef Eigen::Matrix< ExactScalar, Eigen::Dynamic, Eigen::Dynamic, DerivedVC::IsRowMajor> MatrixXES; MatrixXES V; DerivedFC F; VectorXJ CJ; { DerivedVA VV(VA.rows() + VB.rows(), 3); DerivedFC FF(FA.rows() + FB.rows(), 3); VV << VA, VB; FF << FA, FB.array() + VA.rows(); //// Handle annoying empty cases //if(VA.size()>0) //{ // VV<<VA; //} //if(VB.size()>0) //{ // VV<<VB; //} //if(FA.size()>0) //{ // FF<<FA; //} //if(FB.size()>0) //{ // FF<<FB.array()+VA.rows(); //} resolve_fun(VV, FF, V, F, CJ); } // Compute winding numbers on each side of each facet. const size_t num_faces = F.rows(); Eigen::MatrixXi W; Eigen::VectorXi labels(num_faces); std::transform(CJ.data(), CJ.data()+CJ.size(), labels.data(), [&](int i) { return i<FA.rows() ? 0:1; }); igl::copyleft::cgal::propagate_winding_numbers(V, F, labels, W); assert((size_t)W.rows() == num_faces); if (W.cols() == 2) { assert(FB.rows() == 0); Eigen::MatrixXi W_tmp(num_faces, 4); W_tmp << W, Eigen::MatrixXi::Zero(num_faces, 2); W = W_tmp; } else { assert(W.cols() == 4); } // Compute resulting winding number. Eigen::MatrixXi Wr(num_faces, 2); for (size_t i=0; i<num_faces; i++) { Eigen::MatrixXi w_out(1,2), w_in(1,2); w_out << W(i,0), W(i,2); w_in << W(i,1), W(i,3); Wr(i,0) = wind_num_op(w_out); Wr(i,1) = wind_num_op(w_in); } // Extract boundary separating inside from outside. auto index_to_signed_index = [&](size_t i, bool ori) -> int{ return (i+1)*(ori?1:-1); }; //auto signed_index_to_index = [&](int i) -> size_t { // return abs(i) - 1; //}; std::vector<int> selected; for(size_t i=0; i<num_faces; i++) { auto should_keep = keep(Wr(i,0), Wr(i,1)); if (should_keep > 0) { selected.push_back(index_to_signed_index(i, true)); } else if (should_keep < 0) { selected.push_back(index_to_signed_index(i, false)); } } const size_t num_selected = selected.size(); DerivedFC kept_faces(num_selected, 3); DerivedJ kept_face_indices(num_selected, 1); for (size_t i=0; i<num_selected; i++) { size_t idx = abs(selected[i]) - 1; if (selected[i] > 0) { kept_faces.row(i) = F.row(idx); } else { kept_faces.row(i) = F.row(idx).reverse(); } kept_face_indices(i, 0) = CJ[idx]; } // Finally, remove duplicated faces and unreferenced vertices. { DerivedFC G; DerivedJ JJ; igl::resolve_duplicated_faces(kept_faces, G, JJ); igl::slice(kept_face_indices, JJ, 1, J); MatrixX3S Vs(V.rows(), V.cols()); for (size_t i=0; i<(size_t)V.rows(); i++) { for (size_t j=0; j<(size_t)V.cols(); j++) { igl::copyleft::cgal::assign_scalar(V(i,j), Vs(i,j)); } } Eigen::VectorXi newIM; igl::remove_unreferenced(Vs,G,VC,FC,newIM); } }
void fromIntEigenMatrix_16(const Eigen::MatrixXi& from_mat, int **&to_mat) { fromIntEigenMatrix_16(from_mat, to_mat, from_mat.rows(), from_mat.cols()); }
IGL_INLINE bool igl::arap_dof_precomputation( const Eigen::MatrixXd & V, const Eigen::MatrixXi & F, const LbsMatrixType & M, const Eigen::Matrix<int,Eigen::Dynamic,1> & G, ArapDOFData<LbsMatrixType, SSCALAR> & data) { using namespace Eigen; typedef Matrix<SSCALAR, Dynamic, Dynamic> MatrixXS; // number of mesh (domain) vertices int n = V.rows(); // cache problem size data.n = n; // dimension of mesh data.dim = V.cols(); assert(data.dim == M.rows()/n); assert(data.dim*n == M.rows()); if(data.dim == 3) { // Check if z-coordinate is all zeros if(V.col(2).minCoeff() == 0 && V.col(2).maxCoeff() == 0) { data.effective_dim = 2; } }else { data.effective_dim = data.dim; } // Number of handles data.m = M.cols()/data.dim/(data.dim+1); assert(data.m*data.dim*(data.dim+1) == M.cols()); //assert(m == C.rows()); //printf("n=%d; dim=%d; m=%d;\n",n,data.dim,data.m); // Build cotangent laplacian SparseMatrix<double> Lcot; //printf("cotmatrix()\n"); cotmatrix(V,F,Lcot); // Discrete laplacian (should be minus matlab version) SparseMatrix<double> Lapl = -2.0*Lcot; #ifdef EXTREME_VERBOSE cout<<"LaplIJV=["<<endl;print_ijv(Lapl,1);cout<<endl<<"];"<< endl<<"Lapl=sparse(LaplIJV(:,1),LaplIJV(:,2),LaplIJV(:,3),"<< Lapl.rows()<<","<<Lapl.cols()<<");"<<endl; #endif // Get group sum scatter matrix, when applied sums all entries of the same // group according to G SparseMatrix<double> G_sum; if(G.size() == 0) { speye(n,G_sum); }else { // groups are defined per vertex, convert to per face using mode Eigen::Matrix<int,Eigen::Dynamic,1> GG; if(data.energy == ARAP_ENERGY_TYPE_ELEMENTS) { MatrixXi GF(F.rows(),F.cols()); for(int j = 0;j<F.cols();j++) { Matrix<int,Eigen::Dynamic,1> GFj; slice(G,F.col(j),GFj); GF.col(j) = GFj; } mode<int>(GF,2,GG); }else { GG=G; } //printf("group_sum_matrix()\n"); group_sum_matrix(GG,G_sum); } #ifdef EXTREME_VERBOSE cout<<"G_sumIJV=["<<endl;print_ijv(G_sum,1);cout<<endl<<"];"<< endl<<"G_sum=sparse(G_sumIJV(:,1),G_sumIJV(:,2),G_sumIJV(:,3),"<< G_sum.rows()<<","<<G_sum.cols()<<");"<<endl; #endif // Get covariance scatter matrix, when applied collects the covariance matrices // used to fit rotations to during optimization SparseMatrix<double> CSM; //printf("covariance_scatter_matrix()\n"); covariance_scatter_matrix(V,F,data.energy,CSM); #ifdef EXTREME_VERBOSE cout<<"CSMIJV=["<<endl;print_ijv(CSM,1);cout<<endl<<"];"<< endl<<"CSM=sparse(CSMIJV(:,1),CSMIJV(:,2),CSMIJV(:,3),"<< CSM.rows()<<","<<CSM.cols()<<");"<<endl; #endif // Build the covariance matrix "constructor". This is a set of *scatter* // matrices that when multiplied on the right by column of the transformation // matrix entries (the degrees of freedom) L, we get a stack of dim by 1 // covariance matrix column, with a column in the stack for each rotation // *group*. The output is a list of matrices because we construct each column // in the stack of covariance matrices with an independent matrix-vector // multiplication. // // We want to build S which is a stack of dim by dim covariance matrices. // Thus S is dim*g by dim, where dim is the number of dimensions and g is the // number of groups. We can precompute dim matrices CSM_M such that column i // in S is computed as S(:,i) = CSM_M{i} * L, where L is a column of the // skinning transformation matrix values. To be clear, the covariance matrix // for group k is then given as the dim by dim matrix pulled from the stack: // S((k-1)*dim + 1:dim,:) // Apply group sum to each dimension's block of covariance scatter matrix SparseMatrix<double> G_sum_dim; repdiag(G_sum,data.dim,G_sum_dim); CSM = G_sum_dim * CSM; #ifdef EXTREME_VERBOSE cout<<"CSMIJV=["<<endl;print_ijv(CSM,1);cout<<endl<<"];"<< endl<<"CSM=sparse(CSMIJV(:,1),CSMIJV(:,2),CSMIJV(:,3),"<< CSM.rows()<<","<<CSM.cols()<<");"<<endl; #endif //printf("CSM_M()\n"); // Precompute CSM times M for each dimension data.CSM_M.resize(data.dim); #ifdef EXTREME_VERBOSE cout<<"data.CSM_M = cell("<<data.dim<<",1);"<<endl; #endif // span of integers from 0 to n-1 Eigen::Matrix<int,Eigen::Dynamic,1> span_n(n); for(int i = 0;i<n;i++) { span_n(i) = i; } // span of integers from 0 to M.cols()-1 Eigen::Matrix<int,Eigen::Dynamic,1> span_mlbs_cols(M.cols()); for(int i = 0;i<M.cols();i++) { span_mlbs_cols(i) = i; } // number of groups int k = CSM.rows()/data.dim; for(int i = 0;i<data.dim;i++) { //printf("CSM_M(): Mi\n"); LbsMatrixType M_i; //printf("CSM_M(): slice\n"); slice(M,(span_n.array()+i*n).matrix(),span_mlbs_cols,M_i); LbsMatrixType M_i_dim; data.CSM_M[i].resize(k*data.dim,data.m*data.dim*(data.dim+1)); assert(data.CSM_M[i].cols() == M.cols()); for(int j = 0;j<data.dim;j++) { SparseMatrix<double> CSMj; //printf("CSM_M(): slice\n"); slice( CSM, colon<int>(j*k,(j+1)*k-1), colon<int>(j*n,(j+1)*n-1), CSMj); assert(CSMj.rows() == k); assert(CSMj.cols() == n); LbsMatrixType CSMjM_i = CSMj * M_i; if(is_sparse(CSMjM_i)) { // Convert to full MatrixXd CSMjM_ifull; //printf("CSM_M(): full\n"); full(CSMjM_i,CSMjM_ifull); // printf("CSM_M[%d]: %d %d\n",i,data.CSM_M[i].rows(),data.CSM_M[i].cols()); // printf("CSM_M[%d].block(%d*%d=%d,0,%d,%d): %d %d\n",i,j,k,CSMjM_i.rows(),CSMjM_i.cols(), // data.CSM_M[i].block(j*k,0,CSMjM_i.rows(),CSMjM_i.cols()).rows(), // data.CSM_M[i].block(j*k,0,CSMjM_i.rows(),CSMjM_i.cols()).cols()); // printf("CSM_MjMi: %d %d\n",i,CSMjM_i.rows(),CSMjM_i.cols()); // printf("CSM_MjM_ifull: %d %d\n",i,CSMjM_ifull.rows(),CSMjM_ifull.cols()); data.CSM_M[i].block(j*k,0,CSMjM_i.rows(),CSMjM_i.cols()) = CSMjM_ifull; }else { data.CSM_M[i].block(j*k,0,CSMjM_i.rows(),CSMjM_i.cols()) = CSMjM_i; } } #ifdef EXTREME_VERBOSE cout<<"CSM_Mi=["<<endl<<data.CSM_M[i]<<endl<<"];"<<endl; #endif } // precompute arap_rhs matrix //printf("arap_rhs()\n"); SparseMatrix<double> K; arap_rhs(V,F,V.cols(),data.energy,K); //#ifdef EXTREME_VERBOSE // cout<<"KIJV=["<<endl;print_ijv(K,1);cout<<endl<<"];"<< // endl<<"K=sparse(KIJV(:,1),KIJV(:,2),KIJV(:,3),"<< // K.rows()<<","<<K.cols()<<");"<<endl; //#endif // Precompute left muliplication by M and right multiplication by G_sum SparseMatrix<double> G_sumT = G_sum.transpose(); SparseMatrix<double> G_sumT_dim_dim; repdiag(G_sumT,data.dim*data.dim,G_sumT_dim_dim); LbsMatrixType MT = M.transpose(); // If this is a bottle neck then consider reordering matrix multiplication data.M_KG = -4.0 * (MT * (K * G_sumT_dim_dim)); //#ifdef EXTREME_VERBOSE // cout<<"data.M_KGIJV=["<<endl;print_ijv(data.M_KG,1);cout<<endl<<"];"<< // endl<<"data.M_KG=sparse(data.M_KGIJV(:,1),data.M_KGIJV(:,2),data.M_KGIJV(:,3),"<< // data.M_KG.rows()<<","<<data.M_KG.cols()<<");"<<endl; //#endif // Precompute system matrix //printf("A()\n"); SparseMatrix<double> A; repdiag(Lapl,data.dim,A); data.Q = MT * (A * M); //#ifdef EXTREME_VERBOSE // cout<<"QIJV=["<<endl;print_ijv(data.Q,1);cout<<endl<<"];"<< // endl<<"Q=sparse(QIJV(:,1),QIJV(:,2),QIJV(:,3),"<< // data.Q.rows()<<","<<data.Q.cols()<<");"<<endl; //#endif // Always do dynamics precomputation so we can hot-switch //if(data.with_dynamics) //{ // Build cotangent laplacian SparseMatrix<double> Mass; //printf("massmatrix()\n"); massmatrix(V,F,(F.cols()>3?MASSMATRIX_TYPE_BARYCENTRIC:MASSMATRIX_TYPE_VORONOI),Mass); //cout<<"MIJV=["<<endl;print_ijv(Mass,1);cout<<endl<<"];"<< // endl<<"M=sparse(MIJV(:,1),MIJV(:,2),MIJV(:,3),"<< // Mass.rows()<<","<<Mass.cols()<<");"<<endl; //speye(data.n,Mass); SparseMatrix<double> Mass_rep; repdiag(Mass,data.dim,Mass_rep); // Multiply either side by weights matrix (should be dense) data.Mass_tilde = MT * Mass_rep * M; MatrixXd ones(data.dim*data.n,data.dim); for(int i = 0;i<data.n;i++) { for(int d = 0;d<data.dim;d++) { ones(i+d*data.n,d) = 1; } } data.fgrav = MT * (Mass_rep * ones); data.fext = MatrixXS::Zero(MT.rows(),1); //data.fgrav = MT * (ones); //} // This may/should be superfluous //printf("is_symmetric()\n"); if(!is_symmetric(data.Q)) { //printf("Fixing symmetry...\n"); // "Fix" symmetry LbsMatrixType QT = data.Q.transpose(); LbsMatrixType Q_copy = data.Q; data.Q = 0.5*(Q_copy+QT); // Check that ^^^ this really worked. It doesn't always //assert(is_symmetric(*Q)); } //printf("arap_dof_precomputation() succeeded... so far...\n"); verbose("Number of handles: %i\n", data.m); return true; }