void test3() { #ifdef MAKE_TEST3 std::cout << "Multidimensional test:" << std::endl; // Construct data for the IVP double T = 1; // Many dimensions int n = 5; // Multidimensional rhs Eigen::VectorXd y0(2*n); for(int i = 0; i < n; ++i) { y0(i)=(i+1.)/n; y0(i+n)=-1; } // Multidimensional rhs auto f = [n] (Eigen::VectorXd y) { Eigen::VectorXd fy(2*n); Eigen::VectorXd g(n); g(0) = y(0)*(y(1)+y(0)); g(n-1) = y(n-1)*(y(n-1)+y(n-2)); for(int i = 1; i < n-1; ++i) { g(i) = y(i)*(y(i-1)+y(i+1)); } Eigen::SparseMatrix<double> C(n,n); C.reserve(3); for(int i = 0; i < n; ++i) { C.insert(i,i) = 2; if(i < n-1) C.insert(i,i+1) = -1; if(i >= 1) C.insert(i,i-1) = -1; } C.makeCompressed(); fy.head(n) = y.head(n); Eigen::SparseLU< Eigen::SparseMatrix<double> > solver; solver.analyzePattern(C); solver.compute(C); fy.tail(n) = solver.solve(g); return fy; }; // Constructor: ode45<Eigen::VectorXd> O(f); // Setup options O.options.do_statistics = true; // Solve auto sol = O.solve(y0, T); // Print info O.print(); std::cout << "T = " << sol.back().second << std::endl; std::cout << "y(T) = " << std::endl << sol.back().first << std::endl; #endif }
NatCSI(const std::vector<double> & t, const std::vector<double> & y) : t(t), y(y), h(t.size()-1), c(t.size()) { // Size check assert( ( t.size() == y.size() ) && "Error: mismatched size of t and y!"); // m is the number of conditions (t goes from t_0 to t_n) // WARNING: m = n+1 m = t.size(); // Vector containing increments (from the right) for(int i = 0; i < (m - 1); ++i) { h(i) = t[i+1] - t[i]; // Check that t is sorted assert( ( h(i) > 0 ) && "Error: array t must be sorted!"); } // System matrix and rhs as in 3.5.9, we remove first and last row (known by natural contition) Eigen::SparseMatrix<double> A(m,m); Eigen::VectorXd b(m); // WARNING: sparse reserve space A.reserve(3); // Fill in natural conditions 3.5.10 for matrix A.coeffRef(0,0) = 2 / h(0); A.coeffRef(0,1) = 1 / h(0); A.coeffRef(m-1,m-2) = 1 / h(m-2); A.coeffRef(m-1,m-1) = 2 / h(m-2); // Reuse computation for rhs double bold = (y[1] - y[0]) / (h(0)*h(0)); b(0) = 3*bold; // Fill in natural conditions 3.5.10 // Fill matrix A and rhs b for(int i = 1; i < m-1; ++i) { // PRecompute b_i double hinv = 1./h(i); // Fill in a A.coeffRef(i,i-1) = hinv; A.coeffRef(i,i) = 2./h(i) + 2./h(i-1); A.coeffRef(i,i+1) = hinv; // Reuse computation for rhs b double bnew = (y[i+1] - y[i]) / (h(i)*h(i)); b(i) = 3. * (bnew + bold); bold = bnew; } b(m-1) = 3*bold; // Fill in natural conditions 3.5.10 // Compress the matrix A.makeCompressed(); std::cout << A; // Factorize A and solve system A*c(1:end) = b Eigen::SparseLU<Eigen::SparseMatrix<double>> lu; lu.compute(A); c = lu.solve(b); }
void testEigen(int m, int n, int nnz, std::vector<int>& rows, std::vector<int>& cols, std::vector<double>& values, double* matB){ double start, stop, time_to_solve, time_to_build; double tol=1e-9; Eigen::SparseMatrix<double> A; std::vector< Eigen::Triplet<double> > trips; trips.reserve(m * n); for (int k = 0; k < nnz; k++){ double _val = values[k]; int i = rows[k]; int j = cols[k]; if (fabs(_val) > tol){ trips.push_back(Eigen::Triplet<double>(i-1,j-1,_val)); } } //NOTE: setFromTriples() accumulates contributions to the same (i,j)! A.resize(m, n); start = second(); A.setFromTriplets(trips.begin(), trips.end()); stop = second(); time_to_build = stop - start; Eigen::SparseLU< Eigen::SparseMatrix<double>, Eigen::COLAMDOrdering<int> > solverLU; Eigen::VectorXd b; b.resize(m); for (int i = 0; i < m; i++ ) b(i) = matB[i]; printf("\nProcessing in Eigen using LU...\n"); start = second(); solverLU.compute(A); Eigen::VectorXd X = solverLU.solve(b); stop = second(); time_to_solve = stop - start; Eigen::VectorXd ax = A * X; Eigen::VectorXd bMinusAx = b - ax; double h_r[m]; for (int i=0; i<m; i++) h_r[i]=bMinusAx(i); double r_inf = vec_norminf(m, h_r); printf("(Eigen) |b - A*x| = %E \n", r_inf); printf("(Eigen) Time to build(sec): %f\n", time_to_build); printf("(Eigen) Time (sec): %f\n", time_to_solve); }
//************************************ // Method: SolvePFC // FullName: PFCal::PFC::SolvePFC // Access: public // Returns: PF_RESULT // Qualifier: 潮流计算模板函数,构造各种方法求解线性方程组 //************************************ PFVoid PFC::Solve(){ PFDouble opml(0.0); Eigen::SparseLU<PFCore::PFSMatrixXD, PFCore::PFCOLAMDOrdering> solver; PFCIter = 0; //0. 初始化计算矩阵及向量大小 this->_InitPFCalMatrix(); do { //1. 求解功率失配量 cout << "QG DISPATCH..." << endl; _MakeDPQVector(); //2. 如果达到收敛条件,退出 cout << "MAXDISPATCH\t" << PFCIter << "\t" << dPFCPQ.cwiseAbs().maxCoeff() << endl; if (dPFCPQ.cwiseAbs().maxCoeff() < PFCEpsm){ this->PFCResult = PF_RESULT_CONVERG; return; } //3. 计算雅可比阵 _MakeJacoMatrix(); //4. 计算电压偏差 solver.compute(PFCJaco); if (solver.info() != Eigen::Success){ cout << "Solving<stage_1> Failed!" << endl; this->PFCResult = PF_RESULT_DIVERGE_FAILURE; return; } dPFCVA = solver.solve(dPFCPQ); if (solver.info() != Eigen::Success){ cout << "Solving<stage_2> Failed!" << endl; this->PFCResult = PF_RESULT_DIVERGE_FAILURE; return; } //5. 检测是否出现NAN if(dPFCVA.hasNaN()){ this->PFCResult = PF_RESULT_DIVERGE_NAN; return; } //6. 更新状态量 cout << "VOL DISPATCH..." << endl; opml = _CalOptimalMultiplier(); dPFCVA *= opml; _UpdateSysState(); //7. 迭代次数增加 ++PFCIter; } while (PFCIter <= PFCMaxIter); //迭代次数越限 if (PFCIter > PFCMaxIter){ this->PFCResult = PF_RESULT_DIVERGE_OVER_ITER; return; } this->PFCResult = PF_RESULT_DIVERGE; return; }
Tvector<T> Tsparse_matrix<T>::solve( const Tvector<T>& b ) const { assert( ROWS == b.size() ); // Check dimensions are compatible // Convert Tvector to an Eigen matrix Eigen::Matrix<T, -1, 1> B; B.resize(b.size(),1); for (std::size_t i=0; i<b.size(); ++i) { B(i,0) = b.CONTAINER[i]; } // Convert Tsparse_matrix to an Eigen::SparseMatrix Eigen::SparseMatrix<T> A(ROWS,COLS); // Declare Eigen sparse matrix std::vector<Eigen::Triplet<T>> triplet_list; // Declare list of triplets for ( std::size_t i=0; i<ROWS; ++i ) { if ( !S_MATRIX[i].isempty() ) // Check that the row is not empty { std::vector<std::size_t> index; std::vector<T> element; index = S_MATRIX[i].index_list(); element = S_MATRIX[i].element_list(); std::size_t J = index.size(); for ( std::size_t j=0; j<J; ++j ) { triplet_list.push_back( Eigen::Triplet<T>( i, index[j], element[j] )); } } } A.setFromTriplets(triplet_list.begin(), triplet_list.end()); // Setup and solve the system Eigen::SparseLU< Eigen::SparseMatrix<T> > solverA; //Eigen::BiCGSTAB<Eigen::SparseMatrix<T>> solverA; //solverA.analyzePattern(A); //solverA.factorize(A); solverA.compute(A); Eigen::Matrix<T, -1, 1> X; X.resize(b.size(),1); X = solverA.solve(B); // Convert back to a Tvector Tvector<T> x(COLS,0.0); for (std::size_t i=0; i<COLS; ++i) { x.CONTAINER[i] = X(i,0); } return x; }
int main() { // Construct data for RK order 4 MatrixXd A = MatrixXd::Zero(4,4); A(1,0) = .5; A(2,1) = .5; A(3,2) = 1; VectorXd b(4); b << 1./6, 1./3, 1./3, 1./6; // Construct data for the IVP double T = 1; int n = 5; VectorXd y0(2*n); for(int i = 0; i < n; ++i) { y0(i)=(i+1.)/n; y0(i+n)=-1; } auto f = [n] (VectorXd y) { VectorXd fy(2*n); VectorXd g(n); g(0) = y(0)*(y(1)+y(0)); g(n-1) = y(n-1)*(y(n-1)+y(n-2)); for(int i = 1; i < n-1; ++i) { g(i) = y(i)*(y(i-1)+y(i+1)); } Eigen::SparseMatrix<double> C(n,n); C.reserve(3); for(int i = 0; i < n; ++i) { C.insert(i,i) = 2; if(i < n-1) C.insert(i,i+1) = -1; if(i >= 1) C.insert(i,i-1) = -1; } C.makeCompressed(); fy.head(n) = y.head(n); Eigen::SparseLU< Eigen::SparseMatrix<double> > solver; solver.analyzePattern(C); solver.compute(C); fy.tail(n) = solver.solve(g); return fy; }; errors(f, T, y0, A, b); }
void ReliefGeneration::CompressHeightField(std::vector<double>& heightField, int resX, int resY) { double bbScale = 2; double alpha = bbScale * 300.0; double threthold = bbScale * 0.05; int vertNum = (resX + 1) * (resY + 1); std::vector<MagicMath::Vector3> DeltaVector(vertNum); for (int xid = 0; xid < resX; xid++) { for (int yid = 0; yid < resY; yid++) { int index = xid * (resY + 1) + yid; MagicMath::Vector3 deltaT(0, 0, 0); deltaT[0] = heightField.at(index + resY + 1) - heightField.at(index); deltaT[1] = heightField.at(index + 1) - heightField.at(index); double deltaMag = deltaT.Normalise(); if (deltaMag > threthold) { deltaMag = 0; } else { deltaMag = log(1 + alpha * deltaMag) / alpha; } DeltaVector.at(index) = deltaT * deltaMag; } } std::vector<double> LaplaceField(vertNum); for (int xid = 1; xid < resX; xid++) { for (int yid = 1; yid < resY; yid++) { int index = xid * (resY + 1) + yid; LaplaceField.at(index) = DeltaVector.at(index)[0] - DeltaVector.at(index - resY - 1)[0] + DeltaVector.at(index)[1] - DeltaVector.at(index - 1)[1]; } } DebugLog << "Relief: Construct Matrix" << std::endl; std::vector< Eigen::Triplet<double> > tripletList; Eigen::VectorXd b(vertNum, 1); for (int xid = 0; xid < resX + 1; xid++) { for (int yid = 0; yid < resY + 1; yid++) { int index = xid * (resY + 1) + yid; if (xid == 0 || xid == resX || yid == 0 || yid == resY) { tripletList.push_back( Eigen::Triplet<double>(index, index, 1) ); b(index) = 0; } else { tripletList.push_back( Eigen::Triplet<double>(index, index, -4.0) ); tripletList.push_back( Eigen::Triplet<double>(index, index + 1, 1.0) ); tripletList.push_back( Eigen::Triplet<double>(index, index - 1, 1.0) ); tripletList.push_back( Eigen::Triplet<double>(index, index + resY + 1, 1.0) ); tripletList.push_back( Eigen::Triplet<double>(index, index - resY - 1, 1.0) ); b(index) = LaplaceField.at(index); } } } DebugLog << "Relief: Solve Matrix" << std::endl; Eigen::SparseMatrix<double, Eigen::ColMajor> matA(vertNum,vertNum); matA.setFromTriplets(tripletList.begin(), tripletList.end()); Eigen::SparseLU<Eigen::SparseMatrix<double, Eigen::ColMajor> > solver; solver.compute(matA); if(solver.info()!= Eigen::Success) { DebugLog << "Relief: SuperLU Failed" << std::endl; } Eigen::VectorXd res = solver.solve(b); //Copy results for (int i = 0; i < vertNum; i++) { heightField.at(i) = res(i); } }
IGL_INLINE void igl::PolyVectorFieldFinder<DerivedV, DerivedF>:: minQuadWithKnownMini(const Eigen::SparseMatrix<std::complex<typename DerivedV::Scalar> > &Q, const Eigen::SparseMatrix<std::complex<typename DerivedV::Scalar> > &f, const Eigen::VectorXi isConstrained, const Eigen::Matrix<std::complex<typename DerivedV::Scalar>, Eigen::Dynamic, 1> &xknown, Eigen::Matrix<std::complex<typename DerivedV::Scalar>, Eigen::Dynamic, 1> &x) { int N = Q.rows(); int nc = xknown.rows(); Eigen::VectorXi known; known.setZero(nc,1); Eigen::VectorXi unknown; unknown.setZero(N-nc,1); int indk = 0, indu = 0; for (int i = 0; i<N; ++i) if (isConstrained[i]) { known[indk] = i; indk++; } else { unknown[indu] = i; indu++; } Eigen::SparseMatrix<std::complex<typename DerivedV::Scalar>> Quu, Quk; igl::slice(Q,unknown, unknown, Quu); igl::slice(Q,unknown, known, Quk); std::vector<typename Eigen::Triplet<std::complex<typename DerivedV::Scalar> > > tripletList; Eigen::SparseMatrix<std::complex<typename DerivedV::Scalar> > fu(N-nc,1); igl::slice(f,unknown, Eigen::VectorXi::Zero(1,1), fu); Eigen::SparseMatrix<std::complex<typename DerivedV::Scalar> > rhs = (Quk*xknown).sparseView()+.5*fu; Eigen::SparseLU< Eigen::SparseMatrix<std::complex<typename DerivedV::Scalar>>> solver; solver.compute(-Quu); if(solver.info()!=Eigen::Success) { std::cerr<<"Decomposition failed!"<<std::endl; return; } Eigen::SparseMatrix<std::complex<typename DerivedV::Scalar>> b = solver.solve(rhs); if(solver.info()!=Eigen::Success) { std::cerr<<"Solving failed!"<<std::endl; return; } indk = 0, indu = 0; x.setZero(N,1); for (int i = 0; i<N; ++i) if (isConstrained[i]) x[i] = xknown[indk++]; else x[i] = b.coeff(indu++,0); }
void smooth(VectorField<float> &VF, const vector<int> &v_indices, const int n_unknowns ) { typedef float T; int nUnknowns = n_unknowns; if (nUnknowns == 0) for (const auto flag : v_indices) if (flag < 0) nUnknowns++; //2. build linear system const int channels = VF.VF.cols(); std::vector< Eigen::Triplet<float> > lhsTriplets; lhsTriplets.reserve(nUnknowns * 5); Eigen::Matrix<T, -1, -1> rhs(nUnknowns, channels); //Eigen::MatrixXf rhs(nUnknowns, channels); rhs.setZero(); for (int i = 0; i < v_indices.size(); i++) { const int varible_id = v_indices[i]; if (varible_id == -1) continue; vector<int> neightbors = VF.VV[i]; #if 0 vector<int> weights = Vf.VVW[i]; T weight = 0; for (int j = 0; j < weights.size(); j++) weight += weights[j]; #else //uniform weights vector<T> weights(neightbors.size(), -1); T weight = neightbors.size(); #endif for (int j = 0; j < neightbors.size(); j++) { int neighbor_v = neightbors[j]; if (v_indices[neighbor_v] == -1) { //boundary rhs.row(varible_id) -= weights[j] * VF.VF.row(neighbor_v); } else { lhsTriplets.push_back(Eigen::Triplet<float>( varible_id, v_indices[neighbor_v], weights[j])); } } lhsTriplets.push_back(Eigen::Triplet<float>( varible_id, varible_id, weight)); // Add f to rhs. //rhs.row(pid) += VF.VF.row(); } //3. Solve the sparse linear system of equations Eigen::SparseMatrix<T> A(nUnknowns, nUnknowns); A.setFromTriplets(lhsTriplets.begin(), lhsTriplets.end()); Eigen::SparseLU< Eigen::SparseMatrix<T> > solver; solver.analyzePattern(A); solver.factorize(A); Eigen::Matrix<T, -1, -1>result(nUnknowns, channels); // Eigen::MatrixXf result(nUnknowns, channels); for (int c = 0; c < channels; ++c) result.col(c) = solver.solve(rhs.col(c)); //4. Copy results back for (int i = 0; i < v_indices.size(); i++) { const int varible_id = v_indices[i]; if (varible_id == -1) continue; VF.VF.row(i) = result.row(varible_id); } }
void poisson_blend(mve::FloatImage::ConstPtr src, mve::ByteImage::ConstPtr mask, mve::FloatImage::Ptr dest, float alpha) { assert(src->width() == mask->width() && mask->width() == dest->width()); assert(src->height() == mask->height() && mask->height() == dest->height()); assert(src->channels() == 3 && dest->channels() == 3); assert(mask->channels() == 1); assert(valid_mask(mask)); const int n = dest->get_pixel_amount(); const int width = dest->width(); const int height = dest->height(); const int channels = dest->channels(); mve::Image<int>::Ptr indices = mve::Image<int>::create(width, height, 1); indices->fill(-1); int index = 0; for (int i = 0; i < n; ++i) { if (mask->at(i) != 0) { indices->at(i) = index; index++; } } const int nnz = index; std::vector<math::Vec3f> coefficients_b; coefficients_b.resize(nnz); std::vector<Eigen::Triplet<float, int> > coefficients_A; coefficients_A.reserve(nnz); //TODO better estimate... for (int i = 0; i < n; ++i) { const int row = indices->at(i); if (mask->at(i) == 126 || mask->at(i) == 128) { Eigen::Triplet<float, int> t(row, row, 1.0f); coefficients_A.push_back(t); coefficients_b[row] = math::Vec3f(&dest->at(i, 0)); } if (mask->at(i) == 255) { const int i01 = indices->at(i - width); const int i10 = indices->at(i - 1); const int i11 = indices->at(i); const int i12 = indices->at(i + 1); const int i21 = indices->at(i + width); /* All neighbours should be eighter border conditions or part of the optimization. */ assert(i01 != -1 && i10 != -1 && i11 != -1 && i12 != -1 && i21 != -1); Eigen::Triplet<float, int> t01(row, i01, 1.0f); Eigen::Triplet<float, int> t10(row, i10, 1.0f); Eigen::Triplet<float, int> t11(row, i11, -4.0f); Eigen::Triplet<float, int> t12(row, i12, 1.0f); Eigen::Triplet<float, int> t21(row, i21, 1.0f); Eigen::Triplet<float, int> triplets[] = {t01, t10, t11, t12, t21}; coefficients_A.insert(coefficients_A.end(), triplets, triplets + 5); math::Vec3f l_d = simple_laplacian(i, dest); math::Vec3f l_s = simple_laplacian(i, src); coefficients_b[row] = (alpha * l_s + (1.0f - alpha) * l_d); } } SpMat A(nnz, nnz); A.setFromTriplets(coefficients_A.begin(), coefficients_A.end()); Eigen::SparseLU<SpMat, Eigen::COLAMDOrdering<int> > solver; solver.compute(A); for (int channel = 0; channel < channels; ++channel) { Eigen::VectorXf b(nnz); for (std::size_t i = 0; i < coefficients_b.size(); ++i) b[i] = coefficients_b[i][channel]; Eigen::VectorXf x(n); x = solver.solve(b); for (int i = 0; i < n; ++i) { int index = indices->at(i); if (index != -1) dest->at(i, channel) = x[index]; } } }
void MeshDenoisingViaL0Minimization::solveVertices(TriMesh &mesh, Eigen::MatrixXd &initial_vertices_matrix, std::vector< std::vector<TriMesh::VertexHandle> > &edge_vertex_handle, std::vector< std::vector<double> > &coef, std::vector<TriMesh::Point> &delta, double alpha, double beta) { Eigen::MatrixXd right_term = initial_vertices_matrix; Eigen::SparseMatrix<double> coef_matrix((int)mesh.n_vertices(), (int)mesh.n_vertices()); std::vector< Eigen::Triplet<double> > triple; triple.clear(); std::map<TriMesh::VertexHandle, double> vertex_coef; std::set<TriMesh::EdgeHandle> edge_handle;; for(TriMesh::VertexIter v_it = mesh.vertices_begin(); v_it != mesh.vertices_end(); v_it++) { edge_handle.clear(); vertex_coef.clear(); vertex_coef[*v_it] = 1.0; for(TriMesh::VertexFaceIter vf_it = mesh.vf_iter(*v_it); vf_it.is_valid(); vf_it++) { for(TriMesh::FaceEdgeIter fe_it = mesh.fe_iter(*vf_it); fe_it.is_valid(); fe_it++) { edge_handle.insert(*fe_it); } } TriMesh::Point right(0.0, 0.0, 0.0); for(std::set<TriMesh::EdgeHandle>::iterator s_it = edge_handle.begin(); s_it != edge_handle.end(); s_it++) { if(!mesh.is_boundary(*s_it)) { int index = (*s_it).idx(); TriMesh::VertexHandle v1 = edge_vertex_handle[index][0], v2 = edge_vertex_handle[index][1], v3 = edge_vertex_handle[index][2], v4 = edge_vertex_handle[index][3]; double coe1 = coef[index][0], coe2 = coef[index][1], coe3 = coef[index][2], coe4 = coef[index][3]; TriMesh::Point temp_delta = delta[index]; if(v1 == *v_it) { vertex_coef[v1] = vertex_coef[v1] + alpha + beta * coe1 * coe1; vertex_coef[v2] = vertex_coef[v2] - alpha + beta * coe1 * coe2; vertex_coef[v3] = vertex_coef[v3] + alpha + beta * coe1 * coe3; vertex_coef[v4] = vertex_coef[v4] - alpha + beta * coe1 * coe4; right += temp_delta * beta * coe1; } else if(v2 == *v_it) { vertex_coef[v1] = vertex_coef[v1] - alpha + beta * coe2 * coe1; vertex_coef[v2] = vertex_coef[v2] + alpha + beta * coe2 * coe2; vertex_coef[v3] = vertex_coef[v3] - alpha + beta * coe2 * coe3; vertex_coef[v4] = vertex_coef[v4] + alpha + beta * coe2 * coe4; right += temp_delta * beta * coe2; } else if(v3 == *v_it) { vertex_coef[v1] = vertex_coef[v1] + alpha + beta * coe3 * coe1; vertex_coef[v2] = vertex_coef[v2] - alpha + beta * coe3 * coe2; vertex_coef[v3] = vertex_coef[v3] + alpha + beta * coe3 * coe3; vertex_coef[v4] = vertex_coef[v4] - alpha + beta * coe3 * coe4; right += temp_delta * beta * coe3; } else if(v4 == *v_it) { vertex_coef[v1] = vertex_coef[v1] - alpha + beta * coe4 * coe1; vertex_coef[v2] = vertex_coef[v2] + alpha + beta * coe4 * coe2; vertex_coef[v3] = vertex_coef[v3] - alpha + beta * coe4 * coe3; vertex_coef[v4] = vertex_coef[v4] + alpha + beta * coe4 * coe4; right += temp_delta * beta * coe4; } } } right_term(v_it->idx(), 0) += right[0]; right_term(v_it->idx(), 1) += right[1]; right_term(v_it->idx(), 2) += right[2]; for(std::map<TriMesh::VertexHandle, double>::iterator m_it = vertex_coef.begin(); m_it != vertex_coef.end(); m_it++) { triple.push_back(Eigen::Triplet<double>(v_it->idx(), m_it->first.idx(), m_it->second)); } } coef_matrix.setFromTriplets(triple.begin(), triple.end()); Eigen::SparseLU<Eigen::SparseMatrix<double> > solver; solver.analyzePattern(coef_matrix); solver.factorize(coef_matrix); Eigen::MatrixXd vertices_term = solver.solve(right_term); for(TriMesh::VertexIter v_it = mesh.vertices_begin(); v_it != mesh.vertices_end(); v_it++) { int index = v_it->idx(); TriMesh::Point pt = TriMesh::Point(vertices_term(index,0), vertices_term(index,1), vertices_term(index,2)); mesh.set_point(*v_it, pt); } }
int main (int argc, char **argv) { GetPot cl (argc, argv); if (cl.search (2, "-h", "--help")) { std::cerr << help_text << std::endl; return 0; } const double a = cl.follow (0.0, "-a"); const double b = cl.follow (1.0, "-b"); const unsigned int nnodes = cl.follow (100, 2, "-n", "--nnodes"); const std::string diffusion = cl.follow ("1.0", 2, "-d", "--diffusion"); const std::string forcing = cl.follow ("1.0", 2, "-f", "--forcing"); coeff f_coeff (forcing); coeff a_coeff (diffusion); const std::string quadrature = cl.follow ("trapezoidal.so", 2, "-q", "--quadrature-rule"); std::function <void ()> r_r; void * handle = dlopen (quadrature.c_str (), RTLD_NOW); if (! handle) { std::cerr << "fem1d: cannot load dynamic object!" << std::endl; std::cerr << dlerror () << std::endl; return (-1); } void * sym = dlsym (handle, "register_rules"); if (! sym) { std::cerr << "fem1d: cannot load symbol!" << std::endl; std::cerr << dlerror () << std::endl; return (-1); } r_r = reinterpret_cast <void (*) ()> (sym); r_r (); auto & the_factory = quadrature_factory::instance (); auto integrate = the_factory.create ("trapezoidal"); if (! integrate) { std::cerr << "rule name unknown" << std::endl; return (-1); } mesh m (a, b, nnodes); Eigen::SparseMatrix<double> A(nnodes, nnodes); Eigen::Matrix2d mloc; mloc << 0, 0, 0, 0; for (unsigned int iel = 0; iel < m.nels; ++iel) { mloc << 0, 0, 0, 0; for (unsigned int inode = 0; inode < 2; ++inode) { auto igrad = [=, &m] (double x) -> double { return basisfungrad (x, m.nodes[m.elements[iel][0]], m.nodes[m.elements[iel][1]], inode == 0 ? 1.0 : 0.0, inode == 1 ? 1.0 : 0.0); }; for (unsigned int jnode = 0; jnode < 2; ++jnode) { auto jgrad = [=, &m] (double x) -> double { return basisfungrad (x, m.nodes[m.elements[iel][0]], m.nodes[m.elements[iel][1]], jnode == 0 ? 1.0 : 0.0, jnode == 1 ? 1.0 : 0.0); }; mloc(inode,jnode) += (*integrate) ([=, &m, &igrad, &jgrad, &a_coeff] (double x) -> double { return (igrad (x) * jgrad (x) * a_coeff (x)); }, m.nodes[m.elements[iel][0]], m.nodes[m.elements[iel][1]]); A.coeffRef(m.elements[iel][inode], m.elements[iel][jnode]) += mloc(inode,jnode); } } } Eigen::VectorXd f(nnodes); for (unsigned int ii = 0; ii < nnodes; ++ii) f(ii) = 0.0; Eigen::Vector2d vloc; for (unsigned int iel = 0; iel < m.nels; ++iel) { vloc << 0, 0; for (unsigned int inode = 0; inode < 2; ++inode) { auto ifun = [=, &m] (double x) -> double { return basisfun (x, m.nodes[m.elements[iel][0]], m.nodes[m.elements[iel][1]], inode == 0 ? 1.0 : 0.0, inode == 1 ? 1.0 : 0.0); }; vloc(inode) += (*integrate) ([=, &m, &ifun, &f_coeff] (double x) -> double { return (ifun (x) * f_coeff (x)); }, m.nodes[m.elements[iel][0]], m.nodes[m.elements[iel][1]]); f(m.elements[iel][inode]) += vloc(inode); } } f(0) = 0; f(nnodes - 1) = 0; A.coeffRef(0,0) = 1.0e10; A.coeffRef(nnodes-1,nnodes-1) = 1.0e10; for (unsigned int ii = 1; ii < nnodes; ++ii) { A.coeffRef(0, ii) = 0.0; A.coeffRef(nnodes-1, nnodes-1-ii) = 0.0; } Eigen::SparseLU<Eigen::SparseMatrix<double>> solver; A.makeCompressed (); solver.analyzePattern(A); solver.factorize(A); Eigen::VectorXd uh = solver.solve (f); for (unsigned int ii = 0; ii < nnodes; ++ii) std::cout << m.nodes[ii] << " " << uh(ii, 0) << std:: endl; return 0; };
bool fill_hole(std::vector<std::size_t> const & hole, UniGraph const & graph, mve::TriangleMesh::ConstPtr mesh, mve::MeshInfo const & mesh_info, std::vector<std::vector<VertexProjectionInfo> > * vertex_projection_infos, std::vector<TexturePatch::Ptr> * texture_patches) { mve::TriangleMesh::FaceList const & mesh_faces = mesh->get_faces(); mve::TriangleMesh::VertexList const & vertices = mesh->get_vertices(); std::map<std::size_t, std::set<std::size_t> > tmp; for (std::size_t const face_id : hole) { std::size_t const v0 = mesh_faces[face_id * 3]; std::size_t const v1 = mesh_faces[face_id * 3 + 1]; std::size_t const v2 = mesh_faces[face_id * 3 + 2]; tmp[v0].insert(face_id); tmp[v1].insert(face_id); tmp[v2].insert(face_id); } std::size_t const num_vertices = tmp.size(); /* Only fill small holes. */ if (num_vertices > MAX_HOLE_NUM_FACES) return false; /* Calculate 2D parameterization using the technique from libremesh/patch2d, * which was published as sourcecode accompanying the following paper: * * Isotropic Surface Remeshing * Simon Fuhrmann, Jens Ackermann, Thomas Kalbe, Michael Goesele */ std::size_t seed = -1; std::vector<bool> is_border(num_vertices, false); std::vector<std::vector<std::size_t> > adj_verts_via_border(num_vertices); /* Index structures to map from local <-> global vertex id. */ std::map<std::size_t, std::size_t> g2l; std::vector<std::size_t> l2g(num_vertices); /* Index structure to determine column in matrix/vector. */ std::vector<std::size_t> idx(num_vertices); std::size_t num_border_vertices = 0; bool disk_topology = true; std::map<std::size_t, std::set<std::size_t> >::iterator it = tmp.begin(); for (std::size_t j = 0; j < num_vertices; ++j, ++it) { std::size_t vertex_id = it->first; g2l[vertex_id] = j; l2g[j] = vertex_id; /* Check topology in original mesh. */ if (mesh_info[vertex_id].vclass != mve::MeshInfo::VERTEX_CLASS_SIMPLE) { /* Complex/Border vertex in original mesh */ disk_topology = false; break; } /* Check new topology and determine if vertex is now at the border. */ std::vector<std::size_t> const & adj_faces = mesh_info[vertex_id].faces; std::set<std::size_t> const & adj_hole_faces = it->second; std::vector<std::pair<std::size_t, std::size_t> > fan; for (std::size_t k = 0; k < adj_faces.size(); ++k) { std::size_t adj_face = adj_faces[k]; if (graph.get_label(adj_faces[k]) == 0 && adj_hole_faces.find(adj_face) != adj_hole_faces.end()) { std::size_t curr = adj_faces[k]; std::size_t next = adj_faces[(k + 1) % adj_faces.size()]; std::pair<std::size_t, std::size_t> pair(curr, next); fan.push_back(pair); } } std::size_t gaps = 0; for (std::size_t k = 0; k < fan.size(); k++) { std::size_t curr = fan[k].first; std::size_t next = fan[(k + 1) % fan.size()].first; if (fan[k].second != next) { ++gaps; for (std::size_t l = 0; l < 3; ++l) { if(mesh_faces[curr * 3 + l] == vertex_id) { std::size_t second = mesh_faces[curr * 3 + (l + 2) % 3]; adj_verts_via_border[j].push_back(second); } if(mesh_faces[next * 3 + l] == vertex_id) { std::size_t first = mesh_faces[next * 3 + (l + 1) % 3]; adj_verts_via_border[j].push_back(first); } } } } is_border[j] = gaps == 1; /* Check if vertex is now complex. */ if (gaps > 1) { /* Complex vertex in hole */ disk_topology = false; break; } if (is_border[j]) { idx[j] = num_border_vertices++; seed = vertex_id; } else { idx[j] = j - num_border_vertices; } } tmp.clear(); /* No disk or genus zero topology */ if (!disk_topology || num_border_vertices == 0) return false; std::vector<std::size_t> border; border.reserve(num_border_vertices); std::size_t prev = seed; std::size_t curr = seed; while (prev == seed || curr != seed) { std::size_t next = std::numeric_limits<std::size_t>::max(); std::vector<std::size_t> const & adj_verts = adj_verts_via_border[g2l[curr]]; for (std::size_t adj_vert : adj_verts) { assert(is_border[g2l[adj_vert]]); if (adj_vert != prev && adj_vert != curr) { next = adj_vert; break; } } if (next != std::numeric_limits<std::size_t>::max()) { prev = curr; curr = next; border.push_back(next); } else { /* No new border vertex */ border.clear(); break; } /* Loop within border */ if (border.size() > num_border_vertices) break; } if (border.size() != num_border_vertices) return false; float total_length = 0.0f; float total_projection_length = 0.0f; for (std::size_t j = 0; j < border.size(); ++j) { std::size_t vi0 = border[j]; std::size_t vi1 = border[(j + 1) % border.size()]; std::vector<VertexProjectionInfo> const & vpi0 = vertex_projection_infos->at(vi0); std::vector<VertexProjectionInfo> const & vpi1 = vertex_projection_infos->at(vi0); /* According to the previous checks (vertex class within the origial * mesh and boundary) there already has to be at least one projection * of each border vertex. */ assert(!vpi0.empty() && !vpi1.empty()); math::Vec2f vp0(0.0f), vp1(0.0f); for (VertexProjectionInfo const & info0 : vpi0) { for (VertexProjectionInfo const & info1 : vpi1) { if (info0.texture_patch_id == info1.texture_patch_id) { vp0 = info0.projection; vp1 = info1.projection; break; } } } total_projection_length += (vp0 - vp1).norm(); math::Vec3f const & v0 = vertices[vi0]; math::Vec3f const & v1 = vertices[vi1]; total_length += (v0 - v1).norm(); } float radius = total_projection_length / (2.0f * MATH_PI); if (total_length < std::numeric_limits<float>::epsilon()) return false; float length = 0.0f; std::vector<math::Vec2f> projections(num_vertices); for (std::size_t j = 0; j < border.size(); ++j) { float angle = 2.0f * MATH_PI * (length / total_length); projections[g2l[border[j]]] = math::Vec2f(std::cos(angle), std::sin(angle)); math::Vec3f const & v0 = vertices[border[j]]; math::Vec3f const & v1 = vertices[border[(j + 1) % border.size()]]; length += (v0 - v1).norm(); } typedef Eigen::Triplet<float, int> Triplet; std::vector<Triplet> coeff; std::size_t matrix_size = num_vertices - border.size(); Eigen::VectorXf xx(matrix_size), xy(matrix_size); if (matrix_size != 0) { Eigen::VectorXf bx(matrix_size); Eigen::VectorXf by(matrix_size); for (std::size_t j = 0; j < num_vertices; ++j) { if (is_border[j]) continue; std::size_t const vertex_id = l2g[j]; /* Calculate "Mean Value Coordinates" as proposed by Michael S. Floater */ std::map<std::size_t, float> weights; std::vector<std::size_t> const & adj_faces = mesh_info[vertex_id].faces; for (std::size_t adj_face : adj_faces) { std::size_t v0 = mesh_faces[adj_face * 3]; std::size_t v1 = mesh_faces[adj_face * 3 + 1]; std::size_t v2 = mesh_faces[adj_face * 3 + 2]; if (v1 == vertex_id) std::swap(v1, v0); if (v2 == vertex_id) std::swap(v2, v0); math::Vec3f v01 = vertices[v1] - vertices[v0]; float v01n = v01.norm(); math::Vec3f v02 = vertices[v2] - vertices[v0]; float v02n = v02.norm(); /* Ensure numerical stability */ if (v01n * v02n < std::numeric_limits<float>::epsilon()) return false; float alpha = std::acos(v01.dot(v02) / (v01n * v02n)); weights[g2l[v1]] += std::tan(alpha / 2.0f) / v01n; weights[g2l[v2]] += std::tan(alpha / 2.0f) / v02n; } std::map<std::size_t, float>::iterator it; float sum = 0.0f; for (it = weights.begin(); it != weights.end(); ++it) sum += it->second; assert(sum > 0.0f); for (it = weights.begin(); it != weights.end(); ++it) it->second /= sum; bx[idx[j]] = 0.0f; by[idx[j]] = 0.0f; for (it = weights.begin(); it != weights.end(); ++it) { if (is_border[it->first]) { std::size_t border_vertex_id = border[idx[it->first]]; bx[idx[j]] += projections[g2l[border_vertex_id]][0] * it->second; by[idx[j]] += projections[g2l[border_vertex_id]][1] * it->second; } else { coeff.push_back(Triplet(idx[j], idx[it->first], -it->second)); } } } for (std::size_t j = 0; j < matrix_size; ++j) { coeff.push_back(Triplet(j, j, 1.0f)); } typedef Eigen::SparseMatrix<float> SpMat; SpMat A(matrix_size, matrix_size); A.setFromTriplets(coeff.begin(), coeff.end()); Eigen::SparseLU<SpMat> solver; solver.analyzePattern(A); solver.factorize(A); xx = solver.solve(bx); xy = solver.solve(by); } float const max_hole_patch_size = MAX_HOLE_PATCH_SIZE; int image_size = std::min(std::floor(radius * 1.1f) * 2.0f, max_hole_patch_size); /* Ensure a minimum scale of one */ image_size += 2 * (1 + texture_patch_border); int scale = image_size / 2 - texture_patch_border; for (std::size_t j = 0, k = 0; j < num_vertices; ++j) { if (is_border[j]) { projections[j] = projections[j] * scale + image_size / 2; } else { projections[j] = math::Vec2f(xx[k], xy[k]) * scale + image_size / 2; ++k; } } mve::ByteImage::Ptr image = mve::ByteImage::create(image_size, image_size, 3); //DEBUG image->fill_color(*math::Vec4uc(0, 255, 0, 255)); std::vector<math::Vec2f> texcoords; texcoords.reserve(hole.size()); for (std::size_t const face_id : hole) { for (std::size_t j = 0; j < 3; ++j) { std::size_t const vertex_id = mesh_faces[face_id * 3 + j]; math::Vec2f const & projection = projections[g2l[vertex_id]]; texcoords.push_back(projection); } } TexturePatch::Ptr texture_patch = TexturePatch::create(0, hole, texcoords, image); std::size_t texture_patch_id; #pragma omp critical { texture_patches->push_back(texture_patch); texture_patch_id = texture_patches->size() - 1; } for (std::size_t j = 0; j < num_vertices; ++j) { std::size_t const vertex_id = l2g[j]; std::vector<std::size_t> const & adj_faces = mesh_info[vertex_id].faces; std::vector<std::size_t> faces; faces.reserve(adj_faces.size()); for (std::size_t adj_face : adj_faces) { if (graph.get_label(adj_face) == 0) { faces.push_back(adj_face); } } VertexProjectionInfo info = {texture_patch_id, projections[j], faces}; #pragma omp critical vertex_projection_infos->at(vertex_id).push_back(info); } return true; }
void generate_texture_patches(UniGraph const & graph, std::vector<TextureView> const & texture_views, mve::TriangleMesh::ConstPtr mesh, mve::VertexInfoList::ConstPtr vertex_infos, std::vector<std::vector<VertexProjectionInfo> > * vertex_projection_infos, std::vector<TexturePatch> * texture_patches) { util::WallTimer timer; mve::TriangleMesh::FaceList const & mesh_faces = mesh->get_faces(); mve::TriangleMesh::VertexList const & vertices = mesh->get_vertices(); vertex_projection_infos->resize(vertices.size()); std::size_t num_patches = 0; std::cout << "\tRunning... " << std::flush; #pragma omp parallel for schedule(dynamic) for (std::size_t i = 0; i < texture_views.size(); ++i) { std::vector<std::vector<std::size_t> > subgraphs; int const label = i + 1; graph.get_subgraphs(label, &subgraphs); std::list<TexturePatchCandidate> candidates; for (std::size_t j = 0; j < subgraphs.size(); ++j) { candidates.push_back(generate_candidate(label, texture_views[i], subgraphs[j], mesh)); } /* Merge candidates which contain the same image content. */ std::list<TexturePatchCandidate>::iterator it, sit; for (it = candidates.begin(); it != candidates.end(); ++it) { for (sit = candidates.begin(); sit != candidates.end();) { Rect<int> bounding_box = sit->bounding_box; if (it != sit && bounding_box.is_inside(&it->bounding_box)) { TexturePatch::Faces & faces = it->texture_patch.get_faces(); TexturePatch::Faces & ofaces = sit->texture_patch.get_faces(); faces.insert(faces.end(), ofaces.begin(), ofaces.end()); TexturePatch::Texcoords & texcoords = it->texture_patch.get_texcoords(); TexturePatch::Texcoords & otexcoords = sit->texture_patch.get_texcoords(); math::Vec2f offset; offset[0] = sit->bounding_box.min_x - it->bounding_box.min_x; offset[1] = sit->bounding_box.min_y - it->bounding_box.min_y; for (std::size_t i = 0; i < otexcoords.size(); ++i) { texcoords.push_back(otexcoords[i] + offset); } sit = candidates.erase(sit); } else { ++sit; } } } it = candidates.begin(); for (; it != candidates.end(); ++it) { std::size_t texture_patch_id; #pragma omp critical { texture_patches->push_back(it->texture_patch); texture_patch_id = num_patches++; } std::vector<std::size_t> const & faces = it->texture_patch.get_faces(); std::vector<math::Vec2f> const & texcoords = it->texture_patch.get_texcoords(); for (std::size_t i = 0; i < faces.size(); ++i) { std::size_t const face_id = faces[i]; std::size_t const face_pos = face_id * 3; for (std::size_t j = 0; j < 3; ++j) { std::size_t const vertex_id = mesh_faces[face_pos + j]; math::Vec2f const projection = texcoords[i * 3 + j]; VertexProjectionInfo info = {texture_patch_id, projection, {face_id}}; #pragma omp critical vertex_projection_infos->at(vertex_id).push_back(info); } } } } merge_vertex_projection_infos(vertex_projection_infos); std::size_t num_holes = 0; std::size_t num_hole_faces = 0; //if (!settings.skip_hole_filling) { { std::vector<std::vector<std::size_t> > subgraphs; graph.get_subgraphs(0, &subgraphs); #pragma omp parallel for schedule(dynamic) for (std::size_t i = 0; i < subgraphs.size(); ++i) { std::vector<std::size_t> const & subgraph = subgraphs[i]; std::map<std::size_t, std::set<std::size_t> > tmp; for (std::size_t const face_id : subgraph) { std::size_t const v0 = mesh_faces[face_id * 3]; std::size_t const v1 = mesh_faces[face_id * 3 + 1]; std::size_t const v2 = mesh_faces[face_id * 3 + 2]; tmp[v0].insert(face_id); tmp[v1].insert(face_id); tmp[v2].insert(face_id); } std::size_t const num_vertices = tmp.size(); /* Only fill small holes. */ if (num_vertices > 100) { //std::cerr << "Hole to large" << std::endl; continue; } /* Calculate 2D parameterization using the technique from libremesh/patch2d, * which was published as sourcecode accompanying the following paper: * * Isotropic Surface Remeshing * Simon Fuhrmann, Jens Ackermann, Thomas Kalbe, Michael Goesele */ std::size_t seed = -1; std::vector<bool> is_border(num_vertices, false); std::vector<std::vector<std::size_t> > adj_verts_via_border(num_vertices); /* Index structures to map from local <-> global vertex id. */ std::map<std::size_t, std::size_t> g2l; std::vector<std::size_t> l2g(num_vertices); /* Index structure to determine column in matrix/vector. */ std::vector<std::size_t> idx(num_vertices); std::size_t num_border_vertices = 0; bool disk_topology = true; std::map<std::size_t, std::set<std::size_t> >::iterator it = tmp.begin(); for (std::size_t j = 0; j < num_vertices; ++j, ++it) { std::size_t vertex_id = it->first; g2l[vertex_id] = j; l2g[j] = vertex_id; /* Check topology in original mesh. */ if (vertex_infos->at(vertex_id).vclass != mve::VERTEX_CLASS_SIMPLE) { //std::cerr << "Complex/Border vertex in original mesh" << std::endl; disk_topology = false; break; } /* Check new topology and determine if vertex is now at the border. */ std::vector<std::size_t> const & adj_faces = vertex_infos->at(vertex_id).faces; std::set<std::size_t> const & adj_hole_faces = it->second; std::vector<std::pair<std::size_t, std::size_t> > fan; for (std::size_t k = 0; k < adj_faces.size(); ++k) { std::size_t adj_face = adj_faces[k]; if (graph.get_label(adj_faces[k]) == 0 && adj_hole_faces.find(adj_face) != adj_hole_faces.end()) { std::size_t curr = adj_faces[k]; std::size_t next = adj_faces[(k + 1) % adj_faces.size()]; std::pair<std::size_t, std::size_t> pair(curr, next); fan.push_back(pair); } } std::size_t gaps = 0; for (std::size_t k = 0; k < fan.size(); k++) { std::size_t curr = fan[k].first; std::size_t next = fan[(k + 1) % fan.size()].first; if (fan[k].second != next) { ++gaps; for (std::size_t l = 0; l < 3; ++l) { if(mesh_faces[curr * 3 + l] == vertex_id) { std::size_t second = mesh_faces[curr * 3 + (l + 2) % 3]; adj_verts_via_border[j].push_back(second); } if(mesh_faces[next * 3 + l] == vertex_id) { std::size_t first = mesh_faces[next * 3 + (l + 1) % 3]; adj_verts_via_border[j].push_back(first); } } } } is_border[j] = gaps == 1; /* Check if vertex is now complex. */ if (gaps > 1) { //std::cerr << "Complex vertex in hole" << std::endl; disk_topology = false; break; } if (is_border[j]) { idx[j] = num_border_vertices++; seed = vertex_id; } else { idx[j] = j - num_border_vertices; } } tmp.clear(); if (!disk_topology) continue; if (num_border_vertices == 0) { //std::cerr << "Genus zero topology" << std::endl; continue; } std::vector<std::size_t> border; border.reserve(num_border_vertices); std::size_t prev = seed; std::size_t curr = seed; while (prev == seed || curr != seed) { std::size_t next = std::numeric_limits<std::size_t>::max(); std::vector<std::size_t> const & adj_verts = adj_verts_via_border[g2l[curr]]; for (std::size_t adj_vert : adj_verts) { assert(is_border[g2l[adj_vert]]); if (adj_vert != prev && adj_vert != curr) { next = adj_vert; break; } } if (next != std::numeric_limits<std::size_t>::max()) { prev = curr; curr = next; border.push_back(next); } else { //std::cerr << "No new border vertex" << std::endl; border.clear(); break; } if (border.size() > num_border_vertices) { //std::cerr << "Loop within border" << std::endl; break; } } if (border.size() != num_border_vertices) { continue; } float total_length = 0.0f; float total_projection_length = 0.0f; for (std::size_t j = 0; j < border.size(); ++j) { std::size_t vi0 = border[j]; std::size_t vi1 = border[(j + 1) % border.size()]; std::vector<VertexProjectionInfo> const & vpi0 = vertex_projection_infos->at(vi0); std::vector<VertexProjectionInfo> const & vpi1 = vertex_projection_infos->at(vi0); /* According to the previous checks (vertex class within the origial * mesh and boundary) there already has to be at least one projection * of each border vertex. */ assert(!vpi0.empty() && !vpi1.empty()); math::Vec2f vp0(0.0f), vp1(0.0f); for (VertexProjectionInfo const & info0 : vpi0) { for (VertexProjectionInfo const & info1 : vpi1) { if (info0.texture_patch_id == info1.texture_patch_id) { vp0 = info0.projection; vp1 = info1.projection; break; } } } total_projection_length += (vp0 - vp1).norm(); math::Vec3f const & v0 = vertices[vi0]; math::Vec3f const & v1 = vertices[vi1]; total_length += (v0 - v1).norm(); } float radius = total_projection_length / (2.0f * MATH_PI); float length = 0.0f; std::vector<math::Vec2f> projections(num_vertices); for (std::size_t j = 0; j < border.size(); ++j) { float angle = 2.0f * MATH_PI * (length / total_length); projections[g2l[border[j]]] = math::Vec2f(std::cos(angle), std::sin(angle)); math::Vec3f const & v0 = vertices[border[j]]; math::Vec3f const & v1 = vertices[border[(j + 1) % border.size()]]; length += (v0 - v1).norm(); } typedef Eigen::Triplet<float, int> Triplet; std::vector<Triplet> coeff; std::size_t matrix_size = num_vertices - border.size(); Eigen::VectorXf xx(matrix_size), xy(matrix_size); if (matrix_size != 0) { Eigen::VectorXf bx(matrix_size); Eigen::VectorXf by(matrix_size); for (std::size_t j = 0; j < num_vertices; ++j) { if (is_border[j]) continue; std::size_t const vertex_id = l2g[j]; /* Calculate "Mean Value Coordinates" as proposed by Michael S. Floater */ std::map<std::size_t, float> weights; std::vector<std::size_t> const & adj_faces = vertex_infos->at(vertex_id).faces; for (std::size_t adj_face : adj_faces) { std::size_t v0 = mesh_faces[adj_face * 3]; std::size_t v1 = mesh_faces[adj_face * 3 + 1]; std::size_t v2 = mesh_faces[adj_face * 3 + 2]; if (v1 == vertex_id) std::swap(v1, v0); if (v2 == vertex_id) std::swap(v2, v0); math::Vec3f v01 = vertices[v1] - vertices[v0]; float v01n = v01.norm(); math::Vec3f v02 = vertices[v2] - vertices[v0]; float v02n = v02.norm(); float alpha = std::acos(v01.dot(v02) / (v01n * v02n)); weights[g2l[v1]] += std::tan(alpha / 2.0f) / v01n; weights[g2l[v2]] += std::tan(alpha / 2.0f) / v02n; } std::map<std::size_t, float>::iterator it; float sum = 0.0f; for (it = weights.begin(); it != weights.end(); ++it) sum += it->second; for (it = weights.begin(); it != weights.end(); ++it) it->second /= sum; bx[idx[j]] = 0.0f; by[idx[j]] = 0.0f; for (it = weights.begin(); it != weights.end(); ++it) { if (is_border[it->first]) { std::size_t border_vertex_id = border[idx[it->first]]; bx[idx[j]] += projections[g2l[border_vertex_id]][0] * it->second; by[idx[j]] += projections[g2l[border_vertex_id]][1] * it->second; } else { coeff.push_back(Triplet(idx[j], idx[it->first], -it->second)); } } } for (std::size_t j = 0; j < matrix_size; ++j) { coeff.push_back(Triplet(j, j, 1.0f)); } typedef Eigen::SparseMatrix<float> SpMat; SpMat A(matrix_size, matrix_size); A.setFromTriplets(coeff.begin(), coeff.end()); Eigen::SparseLU<SpMat> solver; solver.analyzePattern(A); solver.factorize(A); xx = solver.solve(bx); xy = solver.solve(by); } int image_size = std::floor(radius * 1.1f) * 2 + 4; int scale = image_size / 2 - texture_patch_border; for (std::size_t j = 0, k = 0; j < num_vertices; ++j) { if (!is_border[j]) { projections[j] = math::Vec2f(xx[k], xy[k]) * scale + image_size / 2; ++k; } else { projections[j] = projections[j] * scale + image_size / 2; } } mve::ByteImage::Ptr image = mve::ByteImage::create(image_size, image_size, 3); //DEBUG image->fill_color(*math::Vec4uc(0, 255, 0, 255)); std::vector<math::Vec2f> texcoords; texcoords.reserve(subgraph.size()); for (std::size_t const face_id : subgraph) { for (std::size_t j = 0; j < 3; ++j) { std::size_t const vertex_id = mesh_faces[face_id * 3 + j]; math::Vec2f const & projection = projections[g2l[vertex_id]]; texcoords.push_back(projection); } } TexturePatch texture_patch(0, subgraph, texcoords, image); std::size_t texture_patch_id; #pragma omp critical { texture_patches->push_back(texture_patch); texture_patch_id = num_patches++; num_hole_faces += subgraph.size(); ++num_holes; } for (std::size_t j = 0; j < num_vertices; ++j) { std::size_t const vertex_id = l2g[j]; std::vector<std::size_t> const & adj_faces = vertex_infos->at(vertex_id).faces; std::vector<std::size_t> faces; faces.reserve(adj_faces.size()); for (std::size_t adj_face : adj_faces) { if (graph.get_label(adj_face) == 0) { faces.push_back(adj_face); } } VertexProjectionInfo info = {texture_patch_id, projections[j], faces}; #pragma omp critical vertex_projection_infos->at(vertex_id).push_back(info); } } } merge_vertex_projection_infos(vertex_projection_infos); std::cout << "done. (Took " << timer.get_elapsed_sec() << "s)" << std::endl; std::cout << "\t" << num_patches << " texture patches." << std::endl; std::cout << "\t" << num_holes << " holes (" << num_hole_faces << " faces)." << std::endl; }