heat_geodesics.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. // This file is part of libigl, a simple c++ geometry processing library.
  2. //
  3. // Copyright (C) 2018 Alec Jacobson <[email protected]>
  4. //
  5. // This Source Code Form is subject to the terms of the Mozilla Public License
  6. // v. 2.0. If a copy of the MPL was not distributed with this file, You can
  7. // obtain one at http://mozilla.org/MPL/2.0/.
  8. #include "heat_geodesics.h"
  9. #include "grad.h"
  10. #include "doublearea.h"
  11. #include "cotmatrix.h"
  12. #include "intrinsic_delaunay_cotmatrix.h"
  13. #include "massmatrix.h"
  14. #include "PlainVector.h"
  15. #include "massmatrix_intrinsic.h"
  16. #include "grad_intrinsic.h"
  17. #include "boundary_facets.h"
  18. #include "unique.h"
  19. #include "avg_edge_length.h"
  20. #include "PlainMatrix.h"
  21. template < typename DerivedV, typename DerivedF, typename Scalar >
  22. IGL_INLINE bool igl::heat_geodesics_precompute(
  23. const Eigen::MatrixBase<DerivedV> & V,
  24. const Eigen::MatrixBase<DerivedF> & F,
  25. HeatGeodesicsData<Scalar> & data)
  26. {
  27. // default t value
  28. const Scalar h = avg_edge_length(V,F);
  29. const Scalar t = h*h;
  30. return heat_geodesics_precompute(V,F,t,data);
  31. }
  32. template < typename DerivedV, typename DerivedF, typename Scalar >
  33. IGL_INLINE bool igl::heat_geodesics_precompute(
  34. const Eigen::MatrixBase<DerivedV> & V,
  35. const Eigen::MatrixBase<DerivedF> & F,
  36. const Scalar t,
  37. HeatGeodesicsData<Scalar> & data)
  38. {
  39. typedef Eigen::Matrix<Scalar,Eigen::Dynamic,1> VectorXS;
  40. Eigen::SparseMatrix<Scalar> L,M;
  41. Eigen::Matrix<Scalar,Eigen::Dynamic,3> l_intrinsic;
  42. PlainMatrix<DerivedF> F_intrinsic;
  43. VectorXS dblA;
  44. if(data.use_intrinsic_delaunay)
  45. {
  46. igl::intrinsic_delaunay_cotmatrix(V,F,L,l_intrinsic,F_intrinsic);
  47. igl::massmatrix_intrinsic(l_intrinsic,F_intrinsic,MASSMATRIX_TYPE_DEFAULT,M);
  48. igl::doublearea(l_intrinsic,0,dblA);
  49. igl::grad_intrinsic(l_intrinsic,F_intrinsic,data.Grad);
  50. }else
  51. {
  52. igl::cotmatrix(V,F,L);
  53. igl::massmatrix(V,F,MASSMATRIX_TYPE_DEFAULT,M);
  54. igl::doublearea(V,F,dblA);
  55. igl::grad(V,F,data.Grad);
  56. }
  57. // div
  58. assert(F.cols() == 3 && "Only triangles are supported");
  59. // number of gradient components
  60. data.ng = data.Grad.rows() / F.rows();
  61. assert(data.ng == 3 || data.ng == 2);
  62. data.Div = -0.25*data.Grad.transpose()*dblA.colwise().replicate(data.ng).asDiagonal();
  63. Eigen::SparseMatrix<Scalar> Q = M - t*L;
  64. Eigen::MatrixXi O;
  65. igl::boundary_facets(F,O);
  66. igl::unique(O,data.b);
  67. {
  68. Eigen::SparseMatrix<Scalar> _;
  69. if(!igl::min_quad_with_fixed_precompute(
  70. Q,Eigen::VectorXi(),_,true,data.Neumann))
  71. {
  72. return false;
  73. }
  74. // Only need if there's a boundary
  75. if(data.b.size()>0)
  76. {
  77. if(!igl::min_quad_with_fixed_precompute(Q,data.b,_,true,data.Dirichlet))
  78. {
  79. return false;
  80. }
  81. }
  82. const Eigen::Matrix<Scalar,1,Eigen::Dynamic> M_diag_tr = M.diagonal().transpose();
  83. const Eigen::SparseMatrix<Scalar> Aeq = M_diag_tr.sparseView();
  84. L *= -0.5;
  85. if(!igl::min_quad_with_fixed_precompute(
  86. L,Eigen::VectorXi(),Aeq,true,data.Poisson))
  87. {
  88. return false;
  89. }
  90. }
  91. return true;
  92. }
  93. template < typename Scalar, typename Derivedgamma, typename DerivedD>
  94. IGL_INLINE void igl::heat_geodesics_solve(
  95. const HeatGeodesicsData<Scalar> & data,
  96. const Eigen::MatrixBase<Derivedgamma> & gamma,
  97. Eigen::PlainObjectBase<DerivedD> & D)
  98. {
  99. // number of mesh vertices
  100. const int n = data.Grad.cols();
  101. // Set up delta at gamma
  102. DerivedD u0 = DerivedD::Zero(n,1);
  103. for(int g = 0;g<gamma.size();g++)
  104. {
  105. u0(gamma(g)) = 1;
  106. }
  107. // Neumann solution
  108. DerivedD u;
  109. igl::min_quad_with_fixed_solve(
  110. data.Neumann,u0,DerivedD(),DerivedD(),u);
  111. if(data.b.size()>0)
  112. {
  113. // Average Dirichelt and Neumann solutions
  114. DerivedD uD;
  115. igl::min_quad_with_fixed_solve(
  116. data.Dirichlet,u0,DerivedD::Zero(data.b.size()).eval(),DerivedD(),uD);
  117. u += uD;
  118. u *= 0.5;
  119. }
  120. DerivedD grad_u = data.Grad*u;
  121. const int m = data.Grad.rows()/data.ng;
  122. for(int i = 0;i<m;i++)
  123. {
  124. // It is very important to use a stable norm calculation here. If the
  125. // triangle is far from a source, then the floating point values in the
  126. // gradient can be _very_ small (e.g., 1e-300). The standard/naive norm
  127. // calculation will suffer from underflow. Dividing by the max value is more
  128. // stable. (Eigen implements this as stableNorm or blueNorm).
  129. Scalar norm = 0;
  130. Scalar ma = 0;
  131. for(int d = 0;d<data.ng;d++) {ma = std::max(ma,std::fabs(grad_u(d*m+i)));}
  132. for(int d = 0;d<data.ng;d++)
  133. {
  134. const Scalar gui = grad_u(d*m+i) / ma;
  135. norm += gui*gui;
  136. }
  137. norm = ma*sqrt(norm);
  138. // These are probably over kill; ma==0 should be enough
  139. if(ma == 0 || norm == 0 || norm!=norm)
  140. {
  141. for(int d = 0;d<data.ng;d++) { grad_u(d*m+i) = 0; }
  142. }else
  143. {
  144. for(int d = 0;d<data.ng;d++) { grad_u(d*m+i) /= norm; }
  145. }
  146. }
  147. const DerivedD div_X = -data.Div*grad_u;
  148. const DerivedD Beq = (DerivedD(1,1)<<0).finished();
  149. igl::min_quad_with_fixed_solve(data.Poisson,(-div_X).eval(),DerivedD(),Beq,D);
  150. DerivedD Dgamma = D(gamma.derived());
  151. D.array() -= Dgamma.mean();
  152. if(D.mean() < 0)
  153. {
  154. D = -D;
  155. }
  156. }
  157. #ifdef IGL_STATIC_LIBRARY
  158. // Explicit template instantiation
  159. template void igl::heat_geodesics_solve<double, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 1, 0, -1, 1> >(igl::HeatGeodesicsData<double> const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&);
  160. template bool igl::heat_geodesics_precompute<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, double>(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, double, igl::HeatGeodesicsData<double>&);
  161. template bool igl::heat_geodesics_precompute<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, double>(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, igl::HeatGeodesicsData<double>&);
  162. #endif