kelvinlets.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. // This file is part of libigl, a simple c++ geometry processing library.
  2. //
  3. // Copyright (C) 2013 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. #include "kelvinlets.h"
  8. #include "PI.h"
  9. #include "parallel_for.h"
  10. namespace igl {
  11. // Performs the deformation of a single point based on the regularized
  12. // kelvinlets
  13. //
  14. // Inputs:
  15. // dt delta time used to calculate brush tip displacement
  16. // x dim-vector of point to be deformed
  17. // x0 dim-vector of brush tip
  18. // f dim-vector of brush force (translation)
  19. // F dim by dim matrix of brush force matrix (linear)
  20. // kp parameters for the kelvinlet brush like brush radius, scale etc
  21. // Returns:
  22. // X dim-vector of the new point x gets displaced to post deformation
  23. template<typename Derivedx,
  24. typename Derivedx0,
  25. typename Derivedf,
  26. typename DerivedF,
  27. typename Scalar>
  28. IGL_INLINE auto kelvinlet_evaluator(const Scalar dt,
  29. const Eigen::MatrixBase<Derivedx>& x,
  30. const Eigen::MatrixBase<Derivedx0>& x0,
  31. const Eigen::MatrixBase<Derivedf>& f,
  32. const Eigen::MatrixBase<DerivedF>& F,
  33. const igl::KelvinletParams<Scalar>& kp)
  34. -> Eigen::Matrix<Scalar, 3, 1>
  35. {
  36. static constexpr double POISSON_RATIO = 0.5;
  37. static constexpr double SHEAR_MODULUS = 1;
  38. static constexpr double a = 1 / (4 * igl::PI * SHEAR_MODULUS);
  39. static constexpr double b = a / (4 * (1 - POISSON_RATIO));
  40. static constexpr double c = 2 / (3 * a - 2 * b);
  41. const auto linearVelocity = f / c / kp.epsilon;
  42. const auto originAdjusted = x0 + linearVelocity * dt;
  43. const auto r = x - originAdjusted;
  44. const auto r_norm_sq = r.squaredNorm();
  45. std::function<Eigen::Matrix<Scalar, 3, 1>(const Scalar&)> kelvinlet;
  46. switch (kp.brushType) {
  47. case igl::BrushType::GRAB: {
  48. // Regularized Kelvinlets: Formula (6)
  49. kelvinlet = [&r, &f, &r_norm_sq](const Scalar& epsilon) {
  50. const auto r_epsilon = sqrt(r_norm_sq + epsilon * epsilon);
  51. const auto r_epsilon_3 = r_epsilon * r_epsilon * r_epsilon;
  52. auto t1 = ((a - b) / r_epsilon) * f;
  53. auto t2 = ((b / r_epsilon_3) * r * r.transpose()) * f;
  54. auto t3 = ((a * epsilon * epsilon) / (2 * r_epsilon_3)) * f;
  55. return t1 + t2 + t3;
  56. };
  57. break;
  58. }
  59. case igl::BrushType::TWIST: {
  60. // Regularized Kelvinlets: Formula (15)
  61. kelvinlet = [&r, &F, &r_norm_sq](const Scalar& epsilon) {
  62. const auto r_epsilon = sqrt(r_norm_sq + epsilon * epsilon);
  63. const auto r_epsilon_3 = r_epsilon * r_epsilon * r_epsilon;
  64. return -a *
  65. (1 / (r_epsilon_3) +
  66. 3 * epsilon * epsilon /
  67. (2 * r_epsilon_3 * r_epsilon * r_epsilon)) *
  68. F * r;
  69. };
  70. break;
  71. }
  72. case igl::BrushType::SCALE: {
  73. // Regularized Kelvinlets: Formula (16)
  74. kelvinlet = [&r, &F, &r_norm_sq](const Scalar& epsilon) {
  75. static constexpr auto b_compressible = a / 4; // assumes poisson ratio 0
  76. const auto r_epsilon = sqrt(r_norm_sq + epsilon * epsilon);
  77. const auto r_epsilon_3 = r_epsilon * r_epsilon * r_epsilon;
  78. auto coeff =
  79. (2 * b_compressible - a) *
  80. (1 / (r_epsilon_3) +
  81. 3 * (epsilon * epsilon) / (2 * r_epsilon_3 * r_epsilon * r_epsilon));
  82. return coeff * F * r;
  83. };
  84. break;
  85. }
  86. case igl::BrushType::PINCH: {
  87. // Regularized Kelvinlets: Formula (17)
  88. kelvinlet = [&r, &F, &r_norm_sq, &kp](const Scalar& epsilon) {
  89. const auto r_epsilon = sqrt(r_norm_sq + kp.epsilon * kp.epsilon);
  90. const auto r_epsilon_3 = r_epsilon * r_epsilon * r_epsilon;
  91. auto t1 = ((2 * b - a) / r_epsilon_3) * F * r;
  92. auto t2_coeff = 3 / (2 * r_epsilon * r_epsilon * r_epsilon_3);
  93. auto t2 = t2_coeff * (2 * b * (r.transpose().dot(F * r)) * r +
  94. a * epsilon * epsilon * epsilon * F * r);
  95. return t1 - t2;
  96. };
  97. break;
  98. }
  99. }
  100. if (kp.scale == 1) {
  101. return kelvinlet(kp.ep[0]);
  102. } else if (kp.scale == 2) {
  103. // Regularized Kelvinlets: Formula (8)
  104. return (kelvinlet(kp.ep[0]) - kelvinlet(kp.ep[1])) * 10;
  105. }
  106. // Regularized Kelvinlets: Formula (10)
  107. return (kp.w[0] * kelvinlet(kp.ep[0]) + kp.w[1] * kelvinlet(kp.ep[1]) +
  108. kp.w[2] * kelvinlet(kp.ep[2])) *
  109. 20;
  110. };
  111. // Implements the Bogacki-Shrampine ODE Solver
  112. // https://en.wikipedia.org/wiki/Bogacki%E2%80%93Shampine_method
  113. //
  114. // It calculates the second and third order approximations which can be used to
  115. // estimate the error in the integration step
  116. //
  117. // Inputs:
  118. // t starting time
  119. // dt delta time used to calculate brush tip displacement
  120. // x dim-vector of point to be deformed
  121. // x0 dim-vector of brush tip
  122. // f dim-vector of brush force (translation)
  123. // F dim by dim matrix of brush force matrix (linear)
  124. // kp parameters for the kelvinlet brush like brush radius, scale etc
  125. // Outputs:
  126. // result dim vector holding the third order approximation result
  127. // error The euclidean distance between the second and third order
  128. // approximations
  129. template<typename Scalar,
  130. typename Derivedx,
  131. typename Derivedx0,
  132. typename Derivedf,
  133. typename DerivedF>
  134. IGL_INLINE void integrate(const Scalar t,
  135. const Scalar dt,
  136. const Eigen::MatrixBase<Derivedx>& x,
  137. const Eigen::MatrixBase<Derivedx0>& x0,
  138. const Eigen::MatrixBase<Derivedf>& f,
  139. const Eigen::MatrixBase<DerivedF>& F,
  140. const igl::KelvinletParams<Scalar>& kp,
  141. Eigen::MatrixBase<Derivedx>& result,
  142. Scalar& error)
  143. {
  144. constexpr Scalar a1 = 0;
  145. constexpr Scalar a2 = 1 / 2.0f;
  146. constexpr Scalar a3 = 3 / 4.0f;
  147. constexpr Scalar a4 = 1.0f;
  148. constexpr Scalar b21 = 1 / 2.0f;
  149. constexpr Scalar b31 = 0;
  150. constexpr Scalar b32 = 3 / 4.0f;
  151. constexpr Scalar b41 = 2 / 9.0f;
  152. constexpr Scalar b42 = 1 / 3.0f;
  153. constexpr Scalar b43 = 4 / 9.0f;
  154. constexpr Scalar c1 = 2 / 9.0f; // third order answer
  155. constexpr Scalar c2 = 1 / 3.0f;
  156. constexpr Scalar c3 = 4 / 9.0f;
  157. constexpr Scalar d1 = 7 / 24.0f; // second order answer
  158. constexpr Scalar d2 = 1 / 4.0f;
  159. constexpr Scalar d3 = 1 / 3.0f;
  160. constexpr Scalar d4 = 1 / 8.0f;
  161. auto k1 = dt * kelvinlet_evaluator(t + dt * a1, x, x0, f, F, kp);
  162. auto k2 = dt * kelvinlet_evaluator(t + dt * a2, x + k1 * b21, x0, f, F, kp);
  163. auto k3 = dt * kelvinlet_evaluator(
  164. t + dt * a3, x + k1 * b31 + k2 * b32, x0, f, F, kp);
  165. auto k4 =
  166. dt * kelvinlet_evaluator(
  167. t + dt * a4, x + k1 * b41 + k2 * b42 + k3 * b43, x0, f, F, kp);
  168. auto r1 = x + k1 * d1 + k2 * d2 + k3 * d3 + k4 * d4;
  169. auto r2 = x + k1 * c1 + k2 * c2 + k3 * c3;
  170. result = r2;
  171. error = (r2 - r1).norm() / dt;
  172. };
  173. template<typename DerivedV,
  174. typename Derivedx0,
  175. typename Derivedf,
  176. typename DerivedF,
  177. typename DerivedU>
  178. IGL_INLINE void kelvinlets(
  179. const Eigen::MatrixBase<DerivedV>& V,
  180. const Eigen::MatrixBase<Derivedx0>& x0,
  181. const Eigen::MatrixBase<Derivedf>& f,
  182. const Eigen::MatrixBase<DerivedF>& F,
  183. const KelvinletParams<typename DerivedV::Scalar>& params,
  184. Eigen::PlainObjectBase<DerivedU>& U)
  185. {
  186. using Scalar = typename DerivedV::Scalar;
  187. constexpr auto max_error = 0.001f;
  188. constexpr Scalar safety = 0.9;
  189. const auto calc_displacement = [&](const int index) {
  190. Scalar dt = 0.1;
  191. Scalar t = 0;
  192. Eigen::Matrix<Scalar, 3, 1> x = V.row(index).transpose();
  193. decltype(x) result;
  194. Scalar error;
  195. // taking smaller steps seems to prevents weird inside-out artifacts in the
  196. // final result. This implementation used an adaptive time step solver to
  197. // numerically integrate the ODEs
  198. while (t < 1) {
  199. dt = std::min(dt, 1 - t);
  200. integrate(t, dt, x, x0, f, F, params, result, error);
  201. auto new_dt = dt * safety * std::pow(max_error / error, 1 / 3.0);
  202. if (error <= max_error || dt <= 0.001) {
  203. x = result;
  204. t += dt;
  205. dt = new_dt;
  206. } else {
  207. dt = std::max(abs(new_dt - dt) < 0.001 ? dt / 2.f : new_dt, 0.001);
  208. }
  209. }
  210. U.row(index) = x.transpose();
  211. };
  212. const int n = V.rows();
  213. U.resize(n, V.cols());
  214. igl::parallel_for(n, calc_displacement, 1000);
  215. }
  216. }
  217. #ifdef IGL_STATIC_LIBRARY
  218. template void igl::kelvinlets<Eigen::Matrix<double, -1, -1, 0, -1, -1>,
  219. Eigen::Matrix<double, 3, 1, 0, 3, 1>,
  220. Eigen::Matrix<double, 3, 1, 0, 3, 1>,
  221. Eigen::Matrix<double, 3, 3, 0, 3, 3>,
  222. Eigen::Matrix<double, -1, -1, 0, -1, -1>>(
  223. Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1>> const&,
  224. Eigen::MatrixBase<Eigen::Matrix<double, 3, 1, 0, 3, 1>> const&,
  225. Eigen::MatrixBase<Eigen::Matrix<double, 3, 1, 0, 3, 1>> const&,
  226. Eigen::MatrixBase<Eigen::Matrix<double, 3, 3, 0, 3, 3>> const&,
  227. igl::KelvinletParams<double> const&,
  228. Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1>>&);
  229. #endif