// This file is part of libigl, a simple c++ geometry processing library. // // Copyright (C) 2018 Alec Jacobson // // This Source Code Form is subject to the terms of the Mozilla Public License // v. 2.0. If a copy of the MPL was not distributed with this file, You can // obtain one at http://mozilla.org/MPL/2.0/. #include "heat_geodesics.h" #include "grad.h" #include "doublearea.h" #include "cotmatrix.h" #include "intrinsic_delaunay_cotmatrix.h" #include "massmatrix.h" #include "massmatrix_intrinsic.h" #include "grad_intrinsic.h" #include "boundary_facets.h" #include "unique.h" #include "slice.h" #include "avg_edge_length.h" template < typename DerivedV, typename DerivedF, typename Scalar > IGL_INLINE bool igl::heat_geodesics_precompute( const Eigen::MatrixBase & V, const Eigen::MatrixBase & F, HeatGeodesicsData & data) { // default t value const Scalar h = avg_edge_length(V,F); const Scalar t = h*h; return heat_geodesics_precompute(V,F,t,data); } template < typename DerivedV, typename DerivedF, typename Scalar > IGL_INLINE bool igl::heat_geodesics_precompute( const Eigen::MatrixBase & V, const Eigen::MatrixBase & F, const Scalar t, HeatGeodesicsData & data) { typedef Eigen::Matrix VectorXS; typedef Eigen::Matrix MatrixXS; Eigen::SparseMatrix L,M; Eigen::Matrix l_intrinsic; DerivedF F_intrinsic; VectorXS dblA; if(data.use_intrinsic_delaunay) { igl::intrinsic_delaunay_cotmatrix(V,F,L,l_intrinsic,F_intrinsic); igl::massmatrix_intrinsic(l_intrinsic,F_intrinsic,MASSMATRIX_TYPE_DEFAULT,M); igl::doublearea(l_intrinsic,0,dblA); igl::grad_intrinsic(l_intrinsic,F_intrinsic,data.Grad); }else { igl::cotmatrix(V,F,L); igl::massmatrix(V,F,MASSMATRIX_TYPE_DEFAULT,M); igl::doublearea(V,F,dblA); igl::grad(V,F,data.Grad); } // div assert(F.cols() == 3 && "Only triangles are supported"); // number of gradient components data.ng = data.Grad.rows() / F.rows(); assert(data.ng == 3 || data.ng == 2); data.Div = -0.25*data.Grad.transpose()*dblA.colwise().replicate(data.ng).asDiagonal(); Eigen::SparseMatrix Q = M - t*L; Eigen::MatrixXi O; igl::boundary_facets(F,O); igl::unique(O,data.b); { Eigen::SparseMatrix _; if(!igl::min_quad_with_fixed_precompute( Q,Eigen::VectorXi(),_,true,data.Neumann)) { return false; } // Only need if there's a boundary if(data.b.size()>0) { if(!igl::min_quad_with_fixed_precompute(Q,data.b,_,true,data.Dirichlet)) { return false; } } const Eigen::SparseMatrix Aeq = M.diagonal().transpose().sparseView(); L *= -0.5; if(!igl::min_quad_with_fixed_precompute( L,Eigen::VectorXi(),Aeq,true,data.Poisson)) { return false; } } return true; } template < typename Scalar, typename Derivedgamma, typename DerivedD> IGL_INLINE void igl::heat_geodesics_solve( const HeatGeodesicsData & data, const Eigen::MatrixBase & gamma, Eigen::PlainObjectBase & D) { // number of mesh vertices const int n = data.Grad.cols(); // Set up delta at gamma DerivedD u0 = DerivedD::Zero(n,1); for(int g = 0;g0) { // Average Dirichelt and Neumann solutions DerivedD uD; igl::min_quad_with_fixed_solve( data.Dirichlet,u0,DerivedD::Zero(data.b.size()).eval(),DerivedD(),uD); u += uD; u *= 0.5; } DerivedD grad_u = data.Grad*u; const int m = data.Grad.rows()/data.ng; for(int i = 0;i, Eigen::Matrix >(igl::HeatGeodesicsData const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); template bool igl::heat_geodesics_precompute, Eigen::Matrix, double>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, double, igl::HeatGeodesicsData&); template bool igl::heat_geodesics_precompute, Eigen::Matrix, double>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::HeatGeodesicsData&); #endif