AABB.cpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. #include <test_common.h>
  2. #include <igl/AABB.h>
  3. #include <igl/EPS.h>
  4. #include <igl/avg_edge_length.h>
  5. #include <igl/barycenter.h>
  6. #include <igl/colon.h>
  7. #include <igl/placeholders.h>
  8. #include <igl/get_seconds.h>
  9. #include <igl/point_mesh_squared_distance.h>
  10. #include <igl/point_simplex_squared_distance.h>
  11. #include <igl/per_face_normals.h>
  12. #include <igl/barycenter.h>
  13. #include <igl/randperm.h>
  14. #include <igl/read_triangle_mesh.h>
  15. #include <iostream>
  16. TEST_CASE("AABB: find_2d", "[igl]")
  17. {
  18. Eigen::MatrixXd V(6,2);
  19. V <<
  20. 0,0,
  21. 1,0,
  22. 0,1,
  23. 2,1,
  24. 2,2,
  25. 1,2;
  26. Eigen::MatrixXi F(4,3);
  27. F<<
  28. 2,0,1,
  29. 2,1,5,
  30. 5,3,4,
  31. 5,1,3;
  32. igl::AABB<Eigen::MatrixXd,2> tree;
  33. tree.init(V,F);
  34. Eigen::RowVector2d q(0.5,0.5);
  35. std::vector<int> r = tree.find(V,F,q);
  36. REQUIRE(r.size() == 2);
  37. REQUIRE(r[0] == 0);
  38. REQUIRE(r[1] == 1);
  39. }
  40. TEST_CASE("AABB: find_3d", "[igl]")
  41. {
  42. Eigen::MatrixXd V(7,3);
  43. V << 0,0,1,
  44. 1,0,1,
  45. 0,1,1,
  46. 2,1,1,
  47. 2,2,1,
  48. 1,2,1,
  49. 0,0,0;
  50. Eigen::MatrixXi F(4,4);
  51. F <<
  52. 0,1,2,6,
  53. 1,3,2,6,
  54. 3,4,5,6,
  55. 3,5,1,6;
  56. igl::AABB<Eigen::MatrixXd,3> tree;
  57. tree.init(V,F);
  58. Eigen::RowVector3d q(0.5,0.5,1.0);
  59. std::vector<int> r = tree.find(V,F,q);
  60. REQUIRE(r.size() == 2);
  61. REQUIRE(r[0] == 0);
  62. REQUIRE(r[1] == 1);
  63. }
  64. TEST_CASE("AABB: insert", "[igl]")
  65. {
  66. igl::AABB<Eigen::MatrixXd, 3> * tree = new igl::AABB<Eigen::MatrixXd, 3>();
  67. for(int i = 0;i<3;i++)
  68. {
  69. igl::AABB<Eigen::MatrixXd, 3> * leaf = new igl::AABB<Eigen::MatrixXd, 3>();
  70. leaf->m_box = Eigen::AlignedBox<double, 3>(
  71. Eigen::RowVector3d(-i,-i,-i),
  72. Eigen::RowVector3d(i,i,i));
  73. auto * ret = tree->insert(leaf);
  74. tree = ret->root();
  75. REQUIRE(leaf->is_leaf());
  76. }
  77. REQUIRE(tree->is_root());
  78. delete tree;
  79. }
  80. TEST_CASE("AABB: dynamic", "[igl]")
  81. {
  82. const int MAX_RUNS = 10;
  83. const auto test_case = [&MAX_RUNS](const std::string &param)
  84. {
  85. IGL_TICTOC_LAMBDA;
  86. Eigen::MatrixXd V;
  87. Eigen::MatrixXi F;
  88. // Load example mesh: GetParam() will be name of mesh file
  89. igl::read_triangle_mesh(test_common::data_path(param), V, F);
  90. // Make into soup
  91. V = V(Eigen::Map<Eigen::VectorXi>(F.data(),F.size()), igl::placeholders::all).eval();
  92. F = Eigen::Map<Eigen::MatrixXi>(igl::colon<int>(0,V.rows()-1).data(),V.rows()/3,3).eval();
  93. Eigen::MatrixXd BC;
  94. igl::barycenter(V,F,BC);
  95. Eigen::VectorXi I_gt = igl::colon<int>(0,F.rows()-1);
  96. Eigen::VectorXd sqrD_gt = Eigen::VectorXd::Zero(F.rows());
  97. Eigen::VectorXd sqrD;
  98. Eigen::VectorXi I;
  99. Eigen::MatrixXd C;
  100. igl::AABB<Eigen::MatrixXd, 3> * tree = new igl::AABB<Eigen::MatrixXd, 3>();
  101. tree->init(V,F);
  102. tictoc();
  103. tree->squared_distance(V,F,BC,sqrD,I,C);
  104. const double t0 = tictoc();
  105. test_common::assert_eq(I,I_gt);
  106. test_common::assert_near(sqrD,sqrD_gt,1e-15);
  107. REQUIRE(tree->size() == 2*F.rows()-1);
  108. const double pad = igl::EPS<double>();
  109. const double h = igl::avg_edge_length(V,F);
  110. const auto leaves = tree->gather_leaves(F.rows());
  111. for(auto * leaf : leaves)
  112. {
  113. REQUIRE(leaf);
  114. REQUIRE(leaf->is_leaf());
  115. }
  116. // This is slow. Only test on small meshes
  117. if(F.rows() < 4000)
  118. {
  119. // Gather list of pointers to leaves and pad by ε to force rebuild
  120. tree = tree->pad(leaves,pad,2);
  121. REQUIRE(tree->is_root());
  122. REQUIRE(tree == tree->root());
  123. for(auto * leaf : leaves)
  124. {
  125. REQUIRE(leaf);
  126. REQUIRE(leaf->is_leaf());
  127. }
  128. tictoc();
  129. tree->squared_distance(V,F,BC,sqrD,I,C);
  130. const double t1 = tictoc();
  131. REQUIRE(t1 < 10*t0);
  132. test_common::assert_eq(I,I_gt);
  133. test_common::assert_near(sqrD,sqrD_gt,1e-15);
  134. REQUIRE(tree->size() == 2*F.rows()-1);
  135. }
  136. #ifndef NDEBUG
  137. if(F.rows()>10000)
  138. {
  139. std::cerr<<"#ifndef NDEBUG: Skipping timing test."<<std::endl;
  140. delete tree;
  141. return;
  142. }
  143. #endif
  144. Eigen::VectorXi RV;
  145. Eigen::VectorXi RF;
  146. // Perturb a small subset of the triangles
  147. {
  148. {
  149. igl::randperm(F.rows(),RF);
  150. RF = RF.topRows(std::min(12,(int)F.rows())).eval();
  151. RV.resize(RF.size()*3);
  152. RV << RF, RF.array()+F.rows(), RF.array()+2*F.rows();
  153. }
  154. Eigen::MatrixXd TF = 0.1*h*Eigen::MatrixXd::Random(RF.size(),3);
  155. Eigen::MatrixXd TV(RV.rows(),3);
  156. TV<<TF,TF,TF;
  157. V(RV,igl::placeholders::all) += TV;
  158. igl::barycenter(V,F,BC);
  159. }
  160. const int qi = RF(0);
  161. double t_dynamic_tree;
  162. {
  163. tictoc();
  164. // update those perturbed triangles
  165. for(int i = 0;i<RF.size();i++)
  166. {
  167. tree = leaves[RF(i)]->update_primitive(V,F,pad)->root();
  168. }
  169. const double t_refit = tictoc();
  170. for(int r = 0;r<MAX_RUNS;r++)
  171. {
  172. tree->squared_distance(V,F,Eigen::MatrixXd(BC.row(qi)),sqrD,I,C);
  173. }
  174. const double t_query = tictoc()/MAX_RUNS;
  175. REQUIRE(I(0) == qi);
  176. t_dynamic_tree = t_refit+t_query;
  177. }
  178. double t_static_tree;
  179. {
  180. tictoc();
  181. for(int r = 0;r<MAX_RUNS;r++)
  182. {
  183. igl::point_mesh_squared_distance(
  184. Eigen::MatrixXd(BC.row(qi)),V,F,sqrD,I,C);
  185. }
  186. t_static_tree = tictoc()/MAX_RUNS;
  187. REQUIRE(I(0) == qi);
  188. }
  189. double t_brute_force;
  190. {
  191. tictoc();
  192. double min_dist;
  193. int min_i;
  194. for(int r = 0;r<MAX_RUNS;r++)
  195. {
  196. min_dist = std::numeric_limits<double>::infinity();
  197. min_i = -1;
  198. Eigen::RowVector3d q = Eigen::RowVector3d(BC.row(qi));
  199. for(int i = 0;i<F.rows();i++)
  200. {
  201. Eigen::RowVector3d c;
  202. double d;
  203. igl::point_simplex_squared_distance<3>(q,V,F,i,d,c);
  204. if(d < min_dist)
  205. {
  206. min_dist = d;
  207. min_i = i;
  208. }
  209. }
  210. }
  211. t_brute_force = tictoc()/MAX_RUNS;
  212. REQUIRE(min_i == qi);
  213. }
  214. // Only compare speeds on large meshes
  215. if(F.rows() > 300)
  216. {
  217. REQUIRE(t_dynamic_tree < t_static_tree);
  218. REQUIRE(t_dynamic_tree < t_brute_force);
  219. }
  220. delete tree;
  221. };
  222. test_common::run_test_cases(test_common::all_meshes(), test_case);
  223. }
  224. TEST_CASE("AABB: intersect", "[igl]")
  225. {
  226. Eigen::MatrixXd V(4,3);
  227. V << 0,0,1,
  228. 1,0,1,
  229. 0,1,1,
  230. 0,0,0;
  231. Eigen::MatrixXi F(4,3);
  232. F <<
  233. 0,1,2,
  234. 0,2,3,
  235. 0,3,1,
  236. 2,1,3;
  237. Eigen::MatrixXd BC;
  238. igl::barycenter(V,F,BC);
  239. Eigen::MatrixXd N;
  240. igl::per_face_normals(V,F,N);
  241. Eigen::MatrixXd origin = BC+N;
  242. Eigen::MatrixXd dir = -N;
  243. igl::AABB<Eigen::MatrixXd,3> tree;
  244. tree.init(V,F);
  245. Eigen::VectorXi I;
  246. Eigen::VectorXd T;
  247. Eigen::MatrixXd UV;
  248. double min_t = 0;
  249. tree.intersect_ray(V,F,origin,dir,min_t,I,T,UV);
  250. std::vector<std::vector<igl::Hit<double>>> hits;
  251. tree.intersect_ray(V,F,origin,dir,hits);
  252. }
  253. TEST_CASE("AABB: intersect_tet", "[igl]")
  254. {
  255. // V = [
  256. // 0 0 0
  257. // 1 0 0
  258. // 0 1 0
  259. // 0 0 1
  260. //];
  261. //Ele = [
  262. // 3 2 1
  263. // 2 4 1
  264. // 4 3 1
  265. // 3 4 2
  266. //];
  267. //origin_i = [
  268. // 0.2 0.2 -1
  269. //];
  270. //dir_i = [
  271. // 0.05 0.05 1.25
  272. //];
  273. Eigen::MatrixXd V(4,3);
  274. V << 0,0,0,
  275. 1,0,0,
  276. 0,1,0,
  277. 0,0,1;
  278. Eigen::MatrixXi Ele(4,3);
  279. Ele <<
  280. 2,1,0,
  281. 1,3,0,
  282. 3,2,0,
  283. 2,3,1;
  284. Eigen::MatrixXd origin(1,3);
  285. origin << 0.2,0.2,-1;
  286. Eigen::MatrixXd dir(1,3);
  287. dir << 0.05,0.05,1.25;
  288. igl::AABB<Eigen::MatrixXd,3> tree;
  289. tree.init(V,Ele);
  290. Eigen::VectorXi I;
  291. Eigen::VectorXd T;
  292. Eigen::MatrixXd UV;
  293. double min_t = std::numeric_limits<double>::infinity();
  294. tree.intersect_ray(V,Ele,origin,dir,min_t,I,T,UV);
  295. REQUIRE(I(0) == 0);
  296. REQUIRE(T(0) == Approx(0.8));
  297. REQUIRE(UV(0) == Approx(0.24));
  298. REQUIRE(UV(1) == Approx(0.52));
  299. }