pseudonormal_test.cpp 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. // This file is part of libigl, a simple c++ geometry processing library.
  2. //
  3. // Copyright (C) 2014 Alec Jacobson <alecjacobson@gmail.com>
  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 "pseudonormal_test.h"
  9. #include "barycentric_coordinates.h"
  10. #include "doublearea.h"
  11. #include "project_to_line_segment.h"
  12. #include <cassert>
  13. IGL_INLINE void igl::pseudonormal_test(
  14. const Eigen::MatrixXd & V,
  15. const Eigen::MatrixXi & F,
  16. const Eigen::MatrixXd & FN,
  17. const Eigen::MatrixXd & VN,
  18. const Eigen::MatrixXd & EN,
  19. const Eigen::VectorXi & EMAP,
  20. const Eigen::RowVector3d & q,
  21. const int f,
  22. const Eigen::RowVector3d & c,
  23. double & s,
  24. Eigen::RowVector3d & n)
  25. {
  26. using namespace Eigen;
  27. const auto & qc = q-c;
  28. RowVector3d b;
  29. // Using barycentric coorindates to determine whether close to a vertex/edge
  30. // seems prone to error when dealing with nearly degenerate triangles: Even
  31. // the barycenter (1/3,1/3,1/3) can be made arbitrarily close to an
  32. // edge/vertex
  33. //
  34. const RowVector3d A = V.row(F(f,0));
  35. const RowVector3d B = V.row(F(f,1));
  36. const RowVector3d C = V.row(F(f,2));
  37. const double area = [&A,&B,&C]()
  38. {
  39. Matrix<double,1,1> area;
  40. doublearea(A,B,C,area);
  41. return area(0);
  42. }();
  43. // These were chosen arbitrarily. In a floating point scenario, I'm not sure
  44. // the best way to determine if c is on a vertex/edge or in the middle of the
  45. // face: specifically, I'm worrying about degenerate triangles where
  46. // barycentric coordinates are error-prone.
  47. const double MIN_DOUBLE_AREA = 1e-4;
  48. const double epsilon = 1e-12;
  49. if(area>MIN_DOUBLE_AREA)
  50. {
  51. barycentric_coordinates( c,A,B,C,b);
  52. // Determine which normal to use
  53. const int type = (b.array()<=epsilon).cast<int>().sum();
  54. switch(type)
  55. {
  56. case 2:
  57. // Find vertex
  58. for(int x = 0;x<3;x++)
  59. {
  60. if(b(x)>epsilon)
  61. {
  62. n = VN.row(F(f,x));
  63. break;
  64. }
  65. }
  66. break;
  67. case 1:
  68. // Find edge
  69. for(int x = 0;x<3;x++)
  70. {
  71. if(b(x)<=epsilon)
  72. {
  73. n = EN.row(EMAP(F.rows()*x+f));
  74. break;
  75. }
  76. }
  77. break;
  78. default:
  79. assert(false && "all barycentric coords zero.");
  80. case 0:
  81. n = FN.row(f);
  82. break;
  83. }
  84. }else
  85. {
  86. // Check each vertex
  87. bool found = false;
  88. for(int v = 0;v<3 && !found;v++)
  89. {
  90. if( (c-V.row(F(f,v))).norm() < epsilon)
  91. {
  92. found = true;
  93. n = VN.row(F(f,v));
  94. }
  95. }
  96. // Check each edge
  97. for(int e = 0;e<3 && !found;e++)
  98. {
  99. const RowVector3d s = V.row(F(f,(e+1)%3));
  100. const RowVector3d d = V.row(F(f,(e+2)%3));
  101. Matrix<double,1,1> sqr_d_j_x(1,1);
  102. Matrix<double,1,1> t(1,1);
  103. project_to_line_segment(c,s,d,t,sqr_d_j_x);
  104. if(sqrt(sqr_d_j_x(0)) < epsilon)
  105. {
  106. n = EN.row(EMAP(F.rows()*e+f));
  107. found = true;
  108. }
  109. }
  110. // Finally just use face
  111. if(!found)
  112. {
  113. n = FN.row(f);
  114. }
  115. }
  116. s = (qc.dot(n) >= 0 ? 1. : -1.);
  117. }
  118. IGL_INLINE void igl::pseudonormal_test(
  119. const Eigen::MatrixXd & V,
  120. const Eigen::MatrixXi & E,
  121. const Eigen::MatrixXd & EN,
  122. const Eigen::MatrixXd & VN,
  123. const Eigen::RowVector2d & q,
  124. const int e,
  125. const Eigen::RowVector2d & c,
  126. double & s,
  127. Eigen::RowVector2d & n)
  128. {
  129. using namespace Eigen;
  130. const auto & qc = q-c;
  131. const double len = (V.row(E(e,1))-V.row(E(e,0))).norm();
  132. // barycentric coordinates
  133. RowVector2d b((c-V.row(E(e,1))).norm()/len,(c-V.row(E(e,0))).norm()/len);
  134. // Determine which normal to use
  135. const double epsilon = 1e-12;
  136. const int type = (b.array()<=epsilon).cast<int>().sum();
  137. switch(type)
  138. {
  139. case 1:
  140. // Find vertex
  141. for(int x = 0;x<2;x++)
  142. {
  143. if(b(x)>epsilon)
  144. {
  145. n = VN.row(E(e,x));
  146. break;
  147. }
  148. }
  149. break;
  150. default:
  151. assert(false && "all barycentric coords zero.");
  152. case 0:
  153. n = EN.row(e);
  154. break;
  155. }
  156. s = (qc.dot(n) >= 0 ? 1. : -1.);
  157. }