SphereMeshWedge.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. // This file is part of libigl, a simple c++ geometry processing library.
  2. //
  3. // Copyright (C) 2025 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 "SphereMeshWedge.h"
  9. #include "round_cone_signed_distance.h"
  10. #include "sign.h"
  11. #include <Eigen/QR>
  12. #include <Eigen/Geometry>
  13. template <typename Scalar>
  14. IGL_INLINE igl::SphereMeshWedge<Scalar>::SphereMeshWedge(
  15. const RowVector3S & V0,
  16. const RowVector3S & V1,
  17. const RowVector3S & V2,
  18. const Scalar r0,
  19. const Scalar r1,
  20. const Scalar r2)
  21. {
  22. // Internal copy
  23. V.row(0) = V0;
  24. V.row(1) = V1;
  25. V.row(2) = V2;
  26. r(0) = r0;
  27. r(1) = r1;
  28. r(2) = r2;
  29. flavor = FULL;
  30. // By default use full
  31. EV.row(0) = V.row(2) - V.row(1);
  32. EV.row(1) = V.row(0) - V.row(2);
  33. EV.row(2) = V.row(1) - V.row(0);
  34. l = EV.rowwise().norm();
  35. l2 = l.array().square();
  36. rr << r(1) - r(2), r(2) - r(0), r(0) - r(1);
  37. a2 = l2.array() - rr.array().square();
  38. il2 = 1.0/l2.array();
  39. /////////////////////////////////////////////
  40. /// BIG_VERTEX ?
  41. /////////////////////////////////////////////
  42. {
  43. r.maxCoeff(&max_i);
  44. int j = (max_i+1)%3;
  45. int k = (max_i+2)%3;
  46. if((l(k) + r(j) < r(max_i)) && (l(j) + r(k) < r(max_i)))
  47. {
  48. flavor = BIG_VERTEX;
  49. }
  50. }
  51. /////////////////////////////////////////////
  52. /// BIG_EDGE ?
  53. /////////////////////////////////////////////
  54. if(flavor == FULL)
  55. {
  56. // Case where one edge's roundCone containes the others
  57. for(int e = 0;e<3;e++)
  58. {
  59. const int i = (e+1)%3;
  60. const int j = (e+2)%3;
  61. const int k = (e+3)%3;
  62. const Scalar s =
  63. igl::round_cone_signed_distance(V.row(k),V.row(i),V.row(j),r(i),r(j));
  64. if(-s > r(k))
  65. {
  66. flavor = BIG_EDGE;
  67. max_i = i;
  68. break;
  69. }
  70. }
  71. }
  72. if(flavor == FULL && !compute_planes())
  73. {
  74. flavor = NO_TRIANGLE;
  75. }
  76. }
  77. template <typename Scalar>
  78. IGL_INLINE Scalar igl::SphereMeshWedge<Scalar>::operator()(const RowVector3S & p) const
  79. {
  80. if(flavor == BIG_VERTEX)
  81. {
  82. // Case 0: Vertex i
  83. return (p - V.row(max_i)).norm() - r(max_i);
  84. }
  85. if(flavor == BIG_EDGE)
  86. {
  87. const int i = max_i;
  88. const int j = (i+1)%3;
  89. // Case 1: Edge e
  90. return this->round_cone_signed_distance(p,i,j);
  91. }
  92. Scalar s = std::numeric_limits<Scalar>::infinity();
  93. if(flavor == FULL)
  94. {
  95. // This is possibly the bottleneck and could be turned into precomputed
  96. // plane equations.
  97. // signed distance to triangle plane (this is immediately recomputed later in
  98. // sdSkewedExtrudedTriangle...)
  99. const auto plane_sdf = [](
  100. const RowVector3S & p,
  101. const Eigen::RowVector4d & plane)
  102. {
  103. return plane.head<3>().dot(p) + plane(3);
  104. };
  105. Scalar d0 = plane_sdf(p, planes.row(0));
  106. Scalar planes_s = -std::abs(d0);
  107. // Reflect if necessary so that q is always on negative side of plane
  108. RowVector3S q = p - (d0 - planes_s) * planes.row(0).template head<3>();
  109. // Other planes (for negative side slab, by symmetry)
  110. for(int i = 1;i<planes.rows();i++)
  111. {
  112. planes_s = std::max(planes_s,plane_sdf(q, planes.row(i)));
  113. }
  114. // This produces correct interior distance
  115. if(planes_s <= 0)
  116. {
  117. s = std::min(s,planes_s);
  118. }else
  119. {
  120. const auto & nor = planes.row(1).template head<3>();
  121. const RowVector3S q0 = q - T.row(0);
  122. const RowVector3S q1 = q - T.row(1);
  123. const RowVector3S q2 = q - T.row(2);
  124. if(!(sign(C.row(0).dot(q0)) +
  125. sign(C.row(1).dot(q1)) +
  126. sign(C.row(2).dot(q2))<2.0))
  127. {
  128. s = std::min(s,planes_s);
  129. }
  130. }
  131. //s = std::min(s,sdSkewedExtrudedTriangle(q,V,T));
  132. //s = std::min(s,sdSkewedExtrudedTriangle(p,B,V));
  133. }
  134. assert(flavor == FULL || flavor == NO_TRIANGLE);
  135. for(int e = 0;e<3;e++)
  136. {
  137. const int i = (e+1)%3;
  138. const int j = (e+2)%3;
  139. s = std::min(s,this->round_cone_signed_distance(p,i,j));
  140. }
  141. return s;
  142. }
  143. template <typename Scalar>
  144. IGL_INLINE bool igl::SphereMeshWedge<Scalar>::compute_planes()
  145. {
  146. // Non-degenerate case
  147. const RowVector3S & a = V.row(0);
  148. const RowVector3S & b = V.row(1);
  149. const RowVector3S & c = V.row(2);
  150. const Scalar & ra = r(0);
  151. const Scalar & rb = r(1);
  152. const Scalar & rc = r(2);
  153. Eigen::Matrix<Scalar,2,3,Eigen::RowMajor> A;
  154. A<<
  155. b-a,
  156. c-a;
  157. const Eigen::Vector2d d(rb-ra,rc-ra);
  158. const RowVector3S N = (A.row(0).cross(A.row(1))).normalized();
  159. //const Eigen::CompleteOrthogonalDecomposition<decltype(A)> cod(A);
  160. const RowVector3S n0 = A.completeOrthogonalDecomposition().solve(d);
  161. const Scalar qA = N.squaredNorm();
  162. // qB is zeros by construction. We could delete all terms involving qB
  163. // It's not even clear if keeping them would lead to more accurate results.
  164. const Scalar qB = 2 * N.dot(n0);
  165. const Scalar qC = n0.squaredNorm() - 1;
  166. const Scalar qD = qB*qB - 4*qA*qC;
  167. if(qD<0) { return false; }
  168. Scalar t_sol_1 = (-qB + std::sqrt(qD)) / (2*qA);
  169. RowVector3S n1 = -(t_sol_1 * N + n0);
  170. T = V + r * n1;
  171. const auto plane_equation = [](
  172. const RowVector3S & a,
  173. const RowVector3S & b,
  174. const RowVector3S & c)->Eigen::RowVector4d
  175. {
  176. RowVector3S n = (b-a).cross(c-a).normalized();
  177. n.normalize();
  178. Scalar d = -n.dot(a);
  179. return Eigen::RowVector4d(n(0),n(1),n(2),d);
  180. };
  181. planes.row(0) = plane_equation(V.row(0),V.row(1),V.row(2));
  182. planes.row(1) = plane_equation(T.row(2),T.row(1),T.row(0));
  183. planes.row(2) = plane_equation(V.row(1),V.row(0),T.row(0));
  184. planes.row(3) = plane_equation(V.row(2),V.row(1),T.row(1));
  185. planes.row(4) = plane_equation(V.row(0),V.row(2),T.row(2));
  186. // Determine if the closest point is on the face.
  187. const RowVector3S v10 = T.row(1) - T.row(0);
  188. const RowVector3S v21 = T.row(2) - T.row(1);
  189. const RowVector3S v02 = T.row(0) - T.row(2);
  190. const auto & nor = planes.row(1).template head<3>();
  191. const RowVector3S c10 = v10.cross(nor);
  192. const RowVector3S c21 = v21.cross(nor);
  193. const RowVector3S c02 = v02.cross(nor);
  194. C<<c10,c21,c02;
  195. return true;
  196. }
  197. template <typename Scalar>
  198. IGL_INLINE Scalar igl::SphereMeshWedge<Scalar>::round_cone_signed_distance(const RowVector3S & p, const int i, const int j) const
  199. {
  200. const int e = (j+1)%3;
  201. return igl::round_cone_signed_distance(
  202. p, V.row(i), r(i), r(j), EV.row(e), l2(e), rr(e), a2(e), il2(e));
  203. }
  204. #ifdef IGL_STATIC_LIBRARY
  205. /// Explicit template instantiation
  206. template class igl::SphereMeshWedge<double>;
  207. #endif