normal_and_nearest_points.cpp
Go to the documentation of this file.
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2022-2024, INRIA
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of Willow Garage, Inc. nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  */
34 
37 #define BOOST_TEST_MODULE COAL_NORMAL_AND_NEAREST_POINTS
38 #include <boost/test/included/unit_test.hpp>
39 
40 #include "coal/fwd.hh"
42 #include "coal/collision_data.h"
43 #include "coal/BV/OBBRSS.h"
44 #include "coal/BVH/BVH_model.h"
46 
47 #include "utility.h"
48 
49 using namespace coal;
50 typedef Eigen::Vector2d Vec2d;
51 
52 // This test suite is designed to operate on any pair of primitive shapes:
53 // spheres, capsules, boxes, ellipsoids, cones, cylinders, planes, halfspaces,
54 // convex meshes. Do not use this file for BVH, octree etc. It would not make
55 // sense.
56 
57 // This test is designed to check if the normal and the nearest points
58 // are properly handled as defined in DistanceResult::normal.
59 // Regardless of wether or not the two shapes are in intersection, regardless of
60 // whether `collide` or `distance` is called:
61 // --> we denote `dist` the (signed) distance that separates the two shapes
62 // --> we denote `p1` and `p2` their nearest_points (witness points)
63 // --> the `normal` should always point from shape 1 to shape 2, i.e we should
64 // always have: | normal = sign(dist) * (p2 - p1).normalized()
65 // | p2 = p1 + dist * normal
66 // Thus:
67 // --> if o1 and o2 are not in collision, translating o2 by vector
68 // `-(dist + eps) * normal` should bring them in collision (eps > 0).
69 // --> if o1 and o2 are in collision, translating o2 by vector
70 // `-(dist - eps)) * normal` should separate them (eps > 0).
71 // --> finally, if abs(dist) > 0, we should have:
72 // normal = sign(dist) * (p2 - p1).normalized()
73 // p2 = p1 + dist * normal
74 template <typename ShapeType1, typename ShapeType2>
76  const ShapeType1& o1, const ShapeType2& o2,
77  size_t gjk_max_iterations = GJK_DEFAULT_MAX_ITERATIONS,
78  CoalScalar gjk_tolerance = GJK_DEFAULT_TOLERANCE,
79  size_t epa_max_iterations = EPA_DEFAULT_MAX_ITERATIONS,
80  CoalScalar epa_tolerance = EPA_DEFAULT_TOLERANCE) {
81 // Generate random poses for o2
82 #ifndef NDEBUG // if debug mode
83  std::size_t n = 10;
84 #else
85  size_t n = 1000;
86 #endif
87  // We want to make sure we generate poses that are in collision
88  // so we take a relatively small extent for the random poses
89  CoalScalar extents[] = {-1.5, -1.5, -1.5, 1.5, 1.5, 1.5};
90  std::vector<Transform3s> transforms;
91  generateRandomTransforms(extents, transforms, n);
93 
94  CollisionRequest colreq;
95  colreq.distance_upper_bound = std::numeric_limits<CoalScalar>::max();
96  // For strictly convex shapes, the default tolerance of EPA is way too low.
97  // Because EPA is basically trying to fit a polytope to a strictly convex
98  // surface, it might take it a lot of iterations to converge to a low
99  // tolerance. A solution is to increase the number of iterations and the
100  // tolerance (and/or increase the number of faces and vertices EPA is allowed
101  // to work with).
102  colreq.gjk_max_iterations = gjk_max_iterations;
103  colreq.gjk_tolerance = gjk_tolerance;
104  colreq.epa_max_iterations = epa_max_iterations;
105  colreq.epa_tolerance = epa_tolerance;
106  DistanceRequest distreq;
107  distreq.gjk_max_iterations = gjk_max_iterations;
108  distreq.gjk_tolerance = gjk_tolerance;
109  distreq.epa_max_iterations = epa_max_iterations;
110  distreq.epa_tolerance = epa_tolerance;
111 
112  for (size_t i = 0; i < n; i++) {
113  // Run both `distance` and `collide`.
114  // Since CollisionRequest::distance_lower_bound is set to infinity,
115  // these functions should agree on the results regardless of collision or
116  // not.
117  Transform3s tf2 = transforms[i];
118  CollisionResult colres;
119  DistanceResult distres;
120  size_t col = collide(&o1, tf1, &o2, tf2, colreq, colres);
121  CoalScalar dist = distance(&o1, tf1, &o2, tf2, distreq, distres);
122 
123  const CoalScalar dummy_precision(
125  if (col) {
126  BOOST_CHECK(dist <= 0.);
127  BOOST_CHECK_CLOSE(dist, distres.min_distance, dummy_precision);
128  Contact contact = colres.getContact(0);
129  BOOST_CHECK_CLOSE(dist, contact.penetration_depth, dummy_precision);
130 
131  Vec3s cp1 = contact.nearest_points[0];
132  EIGEN_VECTOR_IS_APPROX(cp1, distres.nearest_points[0], dummy_precision);
133 
134  Vec3s cp2 = contact.nearest_points[1];
135  EIGEN_VECTOR_IS_APPROX(cp2, distres.nearest_points[1], dummy_precision);
136  BOOST_CHECK_CLOSE(contact.penetration_depth, -(cp2 - cp1).norm(),
137  epa_tolerance);
138  EIGEN_VECTOR_IS_APPROX(cp1, cp2 - dist * distres.normal, epa_tolerance);
139 
140  Vec3s separation_vector = contact.penetration_depth * contact.normal;
141  EIGEN_VECTOR_IS_APPROX(separation_vector, cp2 - cp1, epa_tolerance);
142 
143  if (dist < 0) {
144  EIGEN_VECTOR_IS_APPROX(contact.normal, -(cp2 - cp1).normalized(),
145  epa_tolerance);
146  }
147 
148  // Separate the shapes
149  Transform3s new_tf1 = tf1;
150  CoalScalar eps = 1e-2;
151  new_tf1.setTranslation(tf1.getTranslation() + separation_vector -
152  eps * contact.normal);
153  CollisionResult new_colres;
154  DistanceResult new_distres;
155  size_t new_col = collide(&o1, new_tf1, &o2, tf2, colreq, new_colres);
156  CoalScalar new_dist =
157  distance(&o1, new_tf1, &o2, tf2, distreq, new_distres);
158  BOOST_CHECK(new_dist > 0);
159  BOOST_CHECK(!new_col);
160  BOOST_CHECK(!new_colres.isCollision());
161  BOOST_CHECK_CLOSE(new_colres.distance_lower_bound, new_dist,
162  epa_tolerance);
163  Vec3s new_cp1 = new_distres.nearest_points[0];
164  Vec3s new_cp2 = new_distres.nearest_points[1];
165  BOOST_CHECK_CLOSE(new_dist, (new_cp1 - new_cp2).norm(), epa_tolerance);
166  EIGEN_VECTOR_IS_APPROX(new_cp1, new_cp2 - new_dist * new_distres.normal,
167  epa_tolerance);
168 
169  Vec3s new_separation_vector = new_dist * new_distres.normal;
170  EIGEN_VECTOR_IS_APPROX(new_separation_vector, new_cp2 - new_cp1,
171  epa_tolerance);
172 
173  if (new_dist > 0) {
174  EIGEN_VECTOR_IS_APPROX(new_distres.normal,
175  (new_cp2 - new_cp1).normalized(), gjk_tolerance);
176  }
177  } else {
178  BOOST_CHECK(dist >= 0.);
179  BOOST_CHECK_CLOSE(distres.min_distance, dist, dummy_precision);
180  BOOST_CHECK_CLOSE(dist, colres.distance_lower_bound, dummy_precision);
181 
182  Vec3s cp1 = distres.nearest_points[0];
183  Vec3s cp2 = distres.nearest_points[1];
184  BOOST_CHECK_CLOSE(dist, (cp1 - cp2).norm(), gjk_tolerance);
185  EIGEN_VECTOR_IS_APPROX(cp1, cp2 - dist * distres.normal, gjk_tolerance);
186 
187  Vec3s separation_vector = dist * distres.normal;
188  EIGEN_VECTOR_IS_APPROX(separation_vector, cp2 - cp1, gjk_tolerance);
189 
190  if (dist > 0) {
191  EIGEN_VECTOR_IS_APPROX(distres.normal, (cp2 - cp1).normalized(),
192  gjk_tolerance);
193  }
194 
195  // Bring the shapes in collision
196  // We actually can't guarantee that the shapes will be in collision.
197  // Suppose you have two disjoing cones, which witness points are the tips
198  // of the cones.
199  // If you translate one of the cones by the separation vector and it
200  // happens to be parallel to the axis of the cone, the two shapes will
201  // still be disjoint.
202  CoalScalar eps = 1e-2;
203  Transform3s new_tf1 = tf1;
204  new_tf1.setTranslation(tf1.getTranslation() + separation_vector +
205  eps * distres.normal);
206  CollisionResult new_colres;
207  DistanceResult new_distres;
208  collide(&o1, new_tf1, &o2, tf2, colreq, new_colres);
209  CoalScalar new_dist =
210  distance(&o1, new_tf1, &o2, tf2, distreq, new_distres);
211  BOOST_CHECK(new_dist < dist);
212  BOOST_CHECK_CLOSE(new_colres.distance_lower_bound, new_dist,
213  dummy_precision);
214  // tolerance
215  if (new_colres.isCollision()) {
216  Contact contact = new_colres.getContact(0);
217  Vec3s new_cp1 = contact.nearest_points[0];
218  EIGEN_VECTOR_IS_APPROX(new_cp1, new_distres.nearest_points[0],
219  dummy_precision);
220 
221  Vec3s new_cp2 = contact.nearest_points[1];
222  EIGEN_VECTOR_IS_APPROX(new_cp2, new_distres.nearest_points[1],
223  dummy_precision);
224  BOOST_CHECK_CLOSE(contact.penetration_depth,
225  -(new_cp2 - new_cp1).norm(), epa_tolerance);
226  EIGEN_VECTOR_IS_APPROX(new_cp1, new_cp2 - new_dist * new_distres.normal,
227  epa_tolerance);
228 
229  Vec3s new_separation_vector =
230  contact.penetration_depth * contact.normal;
231  EIGEN_VECTOR_IS_APPROX(new_separation_vector, new_cp2 - new_cp1,
232  epa_tolerance);
233 
234  if (new_dist < 0) {
236  contact.normal, -(new_cp2 - new_cp1).normalized(), epa_tolerance);
237  }
238  }
239  }
240  }
241 }
242 
243 template <int VecSize>
244 Eigen::Matrix<CoalScalar, VecSize, 1> generateRandomVector(CoalScalar min,
245  CoalScalar max) {
246  typedef Eigen::Matrix<CoalScalar, VecSize, 1> VecType;
247  // Generate a random vector in the [min, max] range
248  VecType v = VecType::Random() * (max - min) * 0.5 +
249  VecType::Ones() * (max + min) * 0.5;
250  return v;
251 }
252 
254  CoalScalar r =
255  static_cast<CoalScalar>(rand()) / static_cast<CoalScalar>(RAND_MAX);
256  r = 2 * r - 1.0;
257  return r * (max - min) * 0.5 + (max + min) * 0.5;
258 }
259 
260 BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_sphere_sphere) {
261  for (size_t i = 0; i < 10; ++i) {
262  Vec2d radii = generateRandomVector<2>(0.05, 1.0);
263  shared_ptr<Sphere> o1(new Sphere(radii(0)));
264  shared_ptr<Sphere> o2(new Sphere(radii(1)));
265 
266  test_normal_and_nearest_points(*o1.get(), *o2.get());
267  }
268 }
269 
270 BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_sphere_capsule) {
271  for (size_t i = 0; i < 10; ++i) {
272  Vec2d radii = generateRandomVector<2>(0.05, 1.0);
273  CoalScalar h = generateRandomNumber(0.15, 1.0);
274  shared_ptr<Sphere> o1(new Sphere(radii(0)));
275  shared_ptr<Capsule> o2(new Capsule(radii(1), h));
276 
277  test_normal_and_nearest_points(*o1.get(), *o2.get());
278  test_normal_and_nearest_points(*o2.get(), *o1.get());
279  }
280 }
281 
282 BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_sphere_box) {
283  for (size_t i = 0; i < 10; ++i) {
284  shared_ptr<Box> o1(new Box(generateRandomVector<3>(0.05, 1.0)));
285  shared_ptr<Sphere> o2(new Sphere(generateRandomNumber(0.05, 1.0)));
286 
287  test_normal_and_nearest_points(*o1.get(), *o2.get());
288  test_normal_and_nearest_points(*o2.get(), *o1.get());
289  }
290 }
291 
292 BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_mesh_mesh) {
293  for (size_t i = 0; i < 10; ++i) {
295  Ellipsoid(generateRandomVector<3>(0.05, 1.0)));
296  shared_ptr<Convex<Triangle>> o1(new Convex<Triangle>(
297  o1_.points, o1_.num_points, o1_.polygons, o1_.num_polygons));
299  Ellipsoid(generateRandomVector<3>(0.05, 1.0)));
300  shared_ptr<Convex<Triangle>> o2(new Convex<Triangle>(
301  o2_.points, o2_.num_points, o2_.polygons, o2_.num_polygons));
302 
303  size_t gjk_max_iterations = GJK_DEFAULT_MAX_ITERATIONS;
304  CoalScalar gjk_tolerance = GJK_DEFAULT_TOLERANCE;
305  size_t epa_max_iterations = EPA_DEFAULT_MAX_ITERATIONS;
306  CoalScalar epa_tolerance = EPA_DEFAULT_TOLERANCE;
307  test_normal_and_nearest_points(*o1.get(), *o2.get(), gjk_max_iterations,
308  gjk_tolerance, epa_max_iterations,
309  epa_tolerance);
310  }
311 }
312 
313 BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_mesh_box) {
315  Ellipsoid(generateRandomVector<3>(0.05, 1.0)));
316  shared_ptr<Convex<Triangle>> o1(new Convex<Triangle>(
317  o1_.points, o1_.num_points, o1_.polygons, o1_.num_polygons));
318  shared_ptr<Box> o2(new Box(generateRandomVector<3>(0.05, 1.0)));
319 
320  test_normal_and_nearest_points(*o1.get(), *o2.get());
321  test_normal_and_nearest_points(*o2.get(), *o1.get());
322 }
323 
324 BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_mesh_ellipsoid) {
325  for (size_t i = 0; i < 10; ++i) {
327  Ellipsoid(generateRandomVector<3>(0.05, 1.0)));
328  shared_ptr<Convex<Triangle>> o1(new Convex<Triangle>(
329  o1_.points, o1_.num_points, o1_.polygons, o1_.num_polygons));
330  shared_ptr<Ellipsoid> o2(new Ellipsoid(generateRandomVector<3>(0.05, 1.0)));
331 
332  CoalScalar gjk_tolerance = 1e-6;
333  // With EPA's tolerance set at 1e-3, the precision on the normal, contact
334  // points and penetration depth is on the order of the milimeter. However,
335  // EPA (currently) cannot converge to lower tolerances on strictly convex
336  // shapes in a reasonable amount of iterations.
337  CoalScalar epa_tolerance = 1e-3;
338  test_normal_and_nearest_points(*o1.get(), *o2.get(),
339  GJK_DEFAULT_MAX_ITERATIONS, gjk_tolerance,
340  EPA_DEFAULT_MAX_ITERATIONS, epa_tolerance);
341  test_normal_and_nearest_points(*o2.get(), *o1.get(),
342  GJK_DEFAULT_MAX_ITERATIONS, gjk_tolerance,
343  EPA_DEFAULT_MAX_ITERATIONS, epa_tolerance);
344  }
345 }
346 
347 BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_ellipsoid_ellipsoid) {
348  for (size_t i = 0; i < 10; ++i) {
349  shared_ptr<Ellipsoid> o1(new Ellipsoid(generateRandomVector<3>(0.05, 1.0)));
350  shared_ptr<Ellipsoid> o2(new Ellipsoid(generateRandomVector<3>(0.05, 1.0)));
351 
352  size_t gjk_max_iterations = GJK_DEFAULT_MAX_ITERATIONS;
353  CoalScalar gjk_tolerance = 1e-6;
354  // With EPA's tolerance set at 1e-3, the precision on the normal, contact
355  // points and penetration depth is on the order of the milimeter. However,
356  // EPA (currently) cannot converge to lower tolerances on strictly convex
357  // shapes in a reasonable amount of iterations.
358  size_t epa_max_iterations = 250;
359  CoalScalar epa_tolerance = 1e-3;
360  // For EPA on ellipsoids, we need to increase the number of iterations in
361  // this test. This is simply because this test checks **a lot** of cases and
362  // it can generate some of the worst cases for EPA. We don't want to
363  // increase the tolerance too much because otherwise the test would not
364  // work.
365  test_normal_and_nearest_points(*o1.get(), *o2.get(), gjk_max_iterations,
366  gjk_tolerance, epa_max_iterations,
367  epa_tolerance);
368  }
369 }
370 
371 BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_box_plane) {
372  for (size_t i = 0; i < 10; ++i) {
373  shared_ptr<Box> o1(new Box(generateRandomVector<3>(0.05, 1.0)));
374  CoalScalar offset = generateRandomNumber(-0.5, 0.5);
375  Vec3s n = Vec3s::Random();
376  n.normalize();
377  shared_ptr<Plane> o2(new Plane(n, offset));
378 
379  test_normal_and_nearest_points(*o1.get(), *o2.get());
380  test_normal_and_nearest_points(*o2.get(), *o1.get());
381  }
382 }
383 
384 BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_box_halfspace) {
385  for (size_t i = 0; i < 10; ++i) {
386  shared_ptr<Box> o1(new Box(generateRandomVector<3>(0.05, 1.0)));
387  CoalScalar offset = 0.1;
388  Vec3s n = Vec3s::Random();
389  n.normalize();
390  shared_ptr<Halfspace> o2(new Halfspace(n, offset));
391 
392  test_normal_and_nearest_points(*o1.get(), *o2.get());
393  test_normal_and_nearest_points(*o2.get(), *o1.get());
394  }
395 }
396 
397 BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_capsule_halfspace) {
398  for (size_t i = 0; i < 10; ++i) {
399  CoalScalar r = generateRandomNumber(0.05, 1.0);
400  CoalScalar h = generateRandomNumber(0.15, 1.0);
401  shared_ptr<Capsule> o1(new Capsule(r, h));
402  CoalScalar offset = generateRandomNumber(-0.5, 0.5);
403  Vec3s n = Vec3s::Random();
404  n.normalize();
405  shared_ptr<Halfspace> o2(new Halfspace(n, offset));
406 
407  test_normal_and_nearest_points(*o1.get(), *o2.get());
408  test_normal_and_nearest_points(*o2.get(), *o1.get());
409  }
410 }
411 
412 BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_sphere_halfspace) {
413  for (size_t i = 0; i < 10; ++i) {
414  shared_ptr<Sphere> o1(new Sphere(generateRandomNumber(0.05, 1.0)));
415  CoalScalar offset = generateRandomNumber(-0.5, 0.5);
416  Vec3s n = Vec3s::Random();
417  n.normalize();
418  shared_ptr<Halfspace> o2(new Halfspace(n, offset));
419 
420  test_normal_and_nearest_points(*o1.get(), *o2.get());
421  test_normal_and_nearest_points(*o2.get(), *o1.get());
422  }
423 }
424 
425 BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_sphere_plane) {
426  for (size_t i = 0; i < 10; ++i) {
427  shared_ptr<Sphere> o1(new Sphere(generateRandomNumber(0.05, 1.0)));
428  CoalScalar offset = generateRandomNumber(-0.5, 0.5);
429  Vec3s n = Vec3s::Random();
430  n.normalize();
431  shared_ptr<Plane> o2(new Plane(n, offset));
432 
433  test_normal_and_nearest_points(*o1.get(), *o2.get());
434  test_normal_and_nearest_points(*o2.get(), *o1.get());
435  }
436 }
437 
438 BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_mesh_halfspace) {
439  for (size_t i = 0; i < 10; ++i) {
441  Ellipsoid(generateRandomVector<3>(0.05, 1.0)));
442  shared_ptr<Convex<Triangle>> o1(new Convex<Triangle>(
443  o1_.points, o1_.num_points, o1_.polygons, o1_.num_polygons));
444  CoalScalar offset = generateRandomNumber(-0.5, 0.5);
445  Vec3s n = Vec3s::Random();
446  n.normalize();
447  shared_ptr<Halfspace> o2(new Halfspace(n, offset));
448 
449  test_normal_and_nearest_points(*o1.get(), *o2.get());
450  test_normal_and_nearest_points(*o2.get(), *o1.get());
451  }
452 }
453 
454 BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_cone_cylinder) {
455  for (size_t i = 0; i < 10; ++i) {
456  Vec2d r = generateRandomVector<2>(0.05, 1.0);
457  Vec2d h = generateRandomVector<2>(0.15, 1.0);
458  shared_ptr<Cone> o1(new Cone(r(0), h(0)));
459  shared_ptr<Cylinder> o2(new Cylinder(r(1), h(1)));
460 
461  size_t gjk_max_iterations = GJK_DEFAULT_MAX_ITERATIONS;
462  CoalScalar gjk_tolerance = 1e-6;
463  size_t epa_max_iterations = 250;
464  CoalScalar epa_tolerance = 1e-3;
465  test_normal_and_nearest_points(*o1.get(), *o2.get(), gjk_max_iterations,
466  gjk_tolerance, epa_max_iterations,
467  epa_tolerance);
468  test_normal_and_nearest_points(*o2.get(), *o1.get(), gjk_max_iterations,
469  gjk_tolerance, epa_max_iterations,
470  epa_tolerance);
471  }
472 }
473 
474 BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_cylinder_ellipsoid) {
475  for (size_t i = 0; i < 10; ++i) {
476  shared_ptr<Ellipsoid> o1(new Ellipsoid(generateRandomVector<3>(0.05, 1.0)));
477  Vec2d r = generateRandomVector<2>(0.05, 1.0);
478  Vec2d h = generateRandomVector<2>(0.15, 1.0);
479  shared_ptr<Cylinder> o2(new Cylinder(r(1), h(1)));
480 
481  size_t gjk_max_iterations = GJK_DEFAULT_MAX_ITERATIONS;
482  CoalScalar gjk_tolerance = 1e-6;
483  size_t epa_max_iterations = 250;
484  CoalScalar epa_tolerance = 1e-3;
485  test_normal_and_nearest_points(*o1.get(), *o2.get(), gjk_max_iterations,
486  gjk_tolerance, epa_max_iterations,
487  epa_tolerance);
488  test_normal_and_nearest_points(*o2.get(), *o1.get(), gjk_max_iterations,
489  gjk_tolerance, epa_max_iterations,
490  epa_tolerance);
491  }
492 }
493 
494 BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_cone_halfspace) {
495  for (size_t i = 0; i < 10; ++i) {
496  CoalScalar r = generateRandomNumber(0.05, 1.0);
497  CoalScalar h = generateRandomNumber(0.15, 1.0);
498  shared_ptr<Cone> o1(new Cone(r, h));
499  CoalScalar offset = generateRandomNumber(-0.5, 0.5);
500  Vec3s n = Vec3s::Random();
501  n.normalize();
502  shared_ptr<Halfspace> o2(new Halfspace(n, offset));
503 
504  test_normal_and_nearest_points(*o1.get(), *o2.get());
505  test_normal_and_nearest_points(*o2.get(), *o1.get());
506  }
507 }
508 
509 BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_cylinder_halfspace) {
510  for (size_t i = 0; i < 10; ++i) {
511  CoalScalar r = generateRandomNumber(0.05, 1.0);
512  CoalScalar h = generateRandomNumber(0.15, 1.0);
513  shared_ptr<Cylinder> o1(new Cylinder(r, h));
514  CoalScalar offset = generateRandomNumber(-0.5, 0.5);
515  Vec3s n = Vec3s::Random();
516  n.normalize();
517  shared_ptr<Halfspace> o2(new Halfspace(n, offset));
518 
519  test_normal_and_nearest_points(*o1.get(), *o2.get());
520  test_normal_and_nearest_points(*o2.get(), *o1.get());
521  }
522 }
523 
524 BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_cone_plane) {
525  for (size_t i = 0; i < 10; ++i) {
526  CoalScalar r = generateRandomNumber(0.05, 1.0);
527  CoalScalar h = generateRandomNumber(0.15, 1.0);
528  shared_ptr<Cone> o1(new Cone(r, h));
529  CoalScalar offset = generateRandomNumber(-0.5, 0.5);
530  Vec3s n = Vec3s::Random();
531  n.normalize();
532  shared_ptr<Plane> o2(new Plane(n, offset));
533 
534  test_normal_and_nearest_points(*o1.get(), *o2.get());
535  test_normal_and_nearest_points(*o2.get(), *o1.get());
536  }
537 }
538 
539 BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_cylinder_plane) {
540  for (size_t i = 0; i < 10; ++i) {
541  CoalScalar r = generateRandomNumber(0.05, 1.0);
542  CoalScalar h = generateRandomNumber(0.15, 1.0);
543  shared_ptr<Cylinder> o1(new Cylinder(r, h));
544  CoalScalar offset = generateRandomNumber(-0.5, 0.5);
545  Vec3s n = Vec3s::Random();
546  n.normalize();
547  shared_ptr<Plane> o2(new Plane(n, offset));
548 
549  test_normal_and_nearest_points(*o1.get(), *o2.get());
550  test_normal_and_nearest_points(*o2.get(), *o1.get());
551  }
552 }
553 
554 BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_capsule_plane) {
555  for (size_t i = 0; i < 10; ++i) {
556  CoalScalar r = generateRandomNumber(0.05, 1.0);
557  CoalScalar h = generateRandomNumber(0.15, 1.0);
558  shared_ptr<Capsule> o1(new Capsule(r, h));
559  CoalScalar offset = generateRandomNumber(-0.5, 0.5);
560  Vec3s n = Vec3s::Random();
561  n.normalize();
562  shared_ptr<Plane> o2(new Plane(n, offset));
563 
564  test_normal_and_nearest_points(*o1.get(), *o2.get());
565  test_normal_and_nearest_points(*o2.get(), *o1.get());
566  }
567 }
568 
569 BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_capsule_capsule) {
570  for (size_t i = 0; i < 10; ++i) {
571  Vec2d r = generateRandomVector<2>(0.05, 1.0);
572  Vec2d h = generateRandomVector<2>(0.15, 1.0);
573  shared_ptr<Capsule> o1(new Capsule(r(0), h(0)));
574  shared_ptr<Capsule> o2(new Capsule(r(1), h(1)));
575 
576  test_normal_and_nearest_points(*o1.get(), *o2.get());
577  test_normal_and_nearest_points(*o2.get(), *o1.get());
578  }
579 }
580 
581 BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_sphere_cylinder) {
582  for (size_t i = 0; i < 10; ++i) {
583  Vec2d r = generateRandomVector<2>(0.05, 1.0);
584  CoalScalar h = generateRandomNumber(0.15, 1.0);
585  shared_ptr<Sphere> o1(new Sphere(r(0)));
586  shared_ptr<Cylinder> o2(new Cylinder(r(1), h));
587 
588  test_normal_and_nearest_points(*o1.get(), *o2.get());
589  test_normal_and_nearest_points(*o2.get(), *o1.get());
590  }
591 }
592 
593 BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_ellipsoid_halfspace) {
594  for (size_t i = 0; i < 10; ++i) {
595  CoalScalar offset = generateRandomNumber(0.15, 1.0);
596  Vec3s n = Vec3s::Random();
597  n.normalize();
598  shared_ptr<Ellipsoid> o1(new Ellipsoid(generateRandomVector<3>(0.05, 1.0)));
599  shared_ptr<Halfspace> o2(new Halfspace(n, offset));
600 
601  test_normal_and_nearest_points(*o1.get(), *o2.get());
602  test_normal_and_nearest_points(*o2.get(), *o1.get());
603  }
604 }
605 
606 BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_ellipsoid_plane) {
607  for (size_t i = 0; i < 10; ++i) {
608  CoalScalar offset = generateRandomNumber(0.15, 1.0);
609  Vec3s n = Vec3s::Random();
610  n.normalize();
611  shared_ptr<Ellipsoid> o1(new Ellipsoid(generateRandomVector<3>(0.05, 1.0)));
612  shared_ptr<Plane> o2(new Plane(n, offset));
613 
614  test_normal_and_nearest_points(*o1.get(), *o2.get());
615  test_normal_and_nearest_points(*o2.get(), *o1.get());
616  }
617 }
618 
620  const Halfspace& o2) {
621  // Generate random poses for o2
622 #ifndef NDEBUG // if debug mode
623  std::size_t n = 1;
624 #else
625  size_t n = 10000;
626 #endif
627  CoalScalar extents[] = {-2., -2., -2., 2., 2., 2.};
628  std::vector<Transform3s> transforms;
629  generateRandomTransforms(extents, transforms, n);
631  transforms[0] = Transform3s::Identity();
632 
633  CollisionRequest colreq;
634  colreq.distance_upper_bound = std::numeric_limits<CoalScalar>::max();
635  colreq.num_max_contacts = 100;
636  CollisionResult colres;
637  DistanceRequest distreq;
638  DistanceResult distres;
639 
640  for (size_t i = 0; i < n; i++) {
641  Transform3s tf2 = transforms[i];
642  colres.clear();
643  distres.clear();
644  size_t col = collide(&o1, tf1, &o2, tf2, colreq, colres);
645  CoalScalar dist = distance(&o1, tf1, &o2, tf2, distreq, distres);
646 
647  if (col) {
648  BOOST_CHECK(dist <= 0.);
649  BOOST_CHECK_CLOSE(dist, distres.min_distance, 1e-6);
650  for (size_t c = 0; c < colres.numContacts(); c++) {
651  Contact contact = colres.getContact(c);
652  BOOST_CHECK(contact.penetration_depth <= 0);
653  BOOST_CHECK(contact.penetration_depth >= colres.distance_lower_bound);
654 
655  Vec3s cp1 = contact.nearest_points[0];
656  Vec3s cp2 = contact.nearest_points[1];
657  BOOST_CHECK_CLOSE(contact.penetration_depth, -(cp2 - cp1).norm(), 1e-6);
659  cp1, cp2 - contact.penetration_depth * contact.normal, 1e-6);
660 
661  Vec3s separation_vector = contact.penetration_depth * contact.normal;
662  EIGEN_VECTOR_IS_APPROX(separation_vector, cp2 - cp1, 1e-6);
663 
664  if (dist < 0) {
665  EIGEN_VECTOR_IS_APPROX(contact.normal, -(cp2 - cp1).normalized(),
666  1e-6);
667  }
668  }
669  } else {
670  BOOST_CHECK(dist >= 0.);
671  BOOST_CHECK_CLOSE(distres.min_distance, dist, 1e-6);
672  BOOST_CHECK_CLOSE(dist, colres.distance_lower_bound, 1e-6);
673  }
674  }
675 }
676 
678  const BVHModel<OBBRSS>& o2) {
680 }
681 
682 BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_bvh_halfspace) {
683  Box* box_ptr = new coal::Box(1, 1, 1);
684  coal::CollisionGeometryPtr_t b1(box_ptr);
686  generateBVHModel(o1, *box_ptr, Transform3s());
687  o1.buildConvexRepresentation(false);
688 
689  CoalScalar offset = 0.1;
690  Vec3s n = Vec3s::Random();
691  n.normalize();
692  shared_ptr<Halfspace> o2(new Halfspace(n, offset));
693 
694  test_normal_and_nearest_points(o1, *o2.get());
695  test_normal_and_nearest_points(*o2.get(), o1);
696 }
coal::Convex::num_polygons
unsigned int num_polygons
Definition: coal/shape/convex.h:102
test_normal_and_nearest_points
void test_normal_and_nearest_points(const ShapeType1 &o1, const ShapeType2 &o2, size_t gjk_max_iterations=GJK_DEFAULT_MAX_ITERATIONS, CoalScalar gjk_tolerance=GJK_DEFAULT_TOLERANCE, size_t epa_max_iterations=EPA_DEFAULT_MAX_ITERATIONS, CoalScalar epa_tolerance=EPA_DEFAULT_TOLERANCE)
Definition: normal_and_nearest_points.cpp:75
coal::QueryRequest::gjk_tolerance
CoalScalar gjk_tolerance
tolerance for the GJK algorithm. Note: This tolerance determines the precision on the estimated dista...
Definition: coal/collision_data.h:192
coal::Vec3s
Eigen::Matrix< CoalScalar, 3, 1 > Vec3s
Definition: coal/data_types.h:77
coal::BVHModelBase::buildConvexRepresentation
void buildConvexRepresentation(bool share_memory)
Build this Convex<Triangle> representation of this model. The result is stored in attribute convex.
Definition: BVH_model.cpp:128
coal::QueryRequest::epa_tolerance
CoalScalar epa_tolerance
tolerance for EPA. Note: This tolerance determines the precision on the estimated distance between tw...
Definition: coal/collision_data.h:211
generateRandomNumber
CoalScalar generateRandomNumber(CoalScalar min, CoalScalar max)
Definition: normal_and_nearest_points.cpp:253
coal::GJK_DEFAULT_TOLERANCE
constexpr CoalScalar GJK_DEFAULT_TOLERANCE
Definition: coal/narrowphase/narrowphase_defaults.h:47
coal::CollisionResult::isCollision
bool isCollision() const
return binary collision result
Definition: coal/collision_data.h:443
Vec2d
Eigen::Vector2d Vec2d
Definition: normal_and_nearest_points.cpp:50
coal::Contact::normal
Vec3s normal
contact normal, pointing from o1 to o2. The normal defined as the normalized separation vector: norma...
Definition: coal/collision_data.h:88
coal::Halfspace
Half Space: this is equivalent to the Plane in ODE. A Half space has a priviledged direction: the dir...
Definition: coal/shape/geometric_shapes.h:892
gjk.tf1
tuple tf1
Definition: test/scripts/gjk.py:27
coal::Capsule
Capsule It is where is the distance between the point x and the capsule segment AB,...
Definition: coal/shape/geometric_shapes.h:383
collision_data.h
coal
Main namespace.
Definition: coal/broadphase/broadphase_bruteforce.h:44
coal::DistanceResult::normal
Vec3s normal
normal.
Definition: coal/collision_data.h:1061
utility.h
octree.r
r
Definition: octree.py:9
coal::Ellipsoid
Ellipsoid centered at point zero.
Definition: coal/shape/geometric_shapes.h:305
coal::distance
COAL_DLLAPI CoalScalar distance(const Matrix3s &R0, const Vec3s &T0, const kIOS &b1, const kIOS &b2, Vec3s *P=NULL, Vec3s *Q=NULL)
Approximate distance between two kIOS bounding volumes.
Definition: kIOS.cpp:180
eps
const CoalScalar eps
Definition: obb.cpp:93
coal::Contact::penetration_depth
CoalScalar penetration_depth
penetration depth
Definition: coal/collision_data.h:105
coal::Plane
Infinite plane. A plane can be viewed as two half spaces; it has no priviledged direction....
Definition: coal/shape/geometric_shapes.h:983
coal::CollisionResult::distance_lower_bound
CoalScalar distance_lower_bound
Definition: coal/collision_data.h:401
coal::Box
Center at zero point, axis aligned box.
Definition: coal/shape/geometric_shapes.h:166
coal::constructPolytopeFromEllipsoid
Convex< Triangle > constructPolytopeFromEllipsoid(const Ellipsoid &ellipsoid)
We give an ellipsoid as input. The output is a 20 faces polytope which vertices belong to the origina...
Definition: utility.cpp:501
coal::Sphere
Center at zero point sphere.
Definition: coal/shape/geometric_shapes.h:240
EIGEN_VECTOR_IS_APPROX
#define EIGEN_VECTOR_IS_APPROX(Va, Vb, precision)
Definition: utility.h:59
coal::Transform3s
Simple transform class used locally by InterpMotion.
Definition: coal/math/transform.h:55
coal::QueryRequest::gjk_max_iterations
size_t gjk_max_iterations
maximum iteration for the GJK algorithm
Definition: coal/collision_data.h:186
coal::Transform3s::setTranslation
void setTranslation(const Eigen::MatrixBase< Derived > &T_)
set transform from translation
Definition: coal/math/transform.h:143
coal::DistanceResult::min_distance
CoalScalar min_distance
minimum distance between two objects. If two objects are in collision and DistanceRequest::enable_sig...
Definition: coal/collision_data.h:1058
BVH_model.h
coal::DistanceRequest
request to the distance computation
Definition: coal/collision_data.h:985
coal::EPA_DEFAULT_MAX_ITERATIONS
constexpr size_t EPA_DEFAULT_MAX_ITERATIONS
Definition: coal/narrowphase/narrowphase_defaults.h:59
coal::CollisionResult::clear
void clear()
clear the results obtained
Definition: coal/collision_data.h:483
c
c
coal::Contact::nearest_points
std::array< Vec3s, 2 > nearest_points
nearest points associated to this contact.
Definition: coal/collision_data.h:99
fwd.hh
coal::CollisionResult
collision result
Definition: coal/collision_data.h:390
coal::CollisionRequest
request to the collision algorithm
Definition: coal/collision_data.h:311
coal::Transform3s::Identity
static Transform3s Identity()
Definition: coal/math/transform.h:68
coal::QueryRequest::epa_max_iterations
size_t epa_max_iterations
max number of iterations for EPA
Definition: coal/collision_data.h:204
coal::DistanceResult
distance result
Definition: coal/collision_data.h:1051
gjk.tf2
tuple tf2
Definition: test/scripts/gjk.py:36
coal::Convex
Convex polytope.
Definition: coal/serialization/collision_object.h:51
coal::Cylinder
Cylinder along Z axis. The cylinder is defined at its centroid.
Definition: coal/shape/geometric_shapes.h:560
coal::Contact
Contact information returned by collision.
Definition: coal/collision_data.h:58
coal::GJK_DEFAULT_MAX_ITERATIONS
constexpr size_t GJK_DEFAULT_MAX_ITERATIONS
GJK.
Definition: coal/narrowphase/narrowphase_defaults.h:46
coal::generateRandomTransforms
void generateRandomTransforms(CoalScalar extents[6], std::vector< Transform3s > &transforms, std::size_t n)
Generate n random transforms whose translations are constrained by extents.
Definition: utility.cpp:226
extents
CoalScalar extents[6]
Definition: test/geometric_shapes.cpp:53
generateRandomVector
Eigen::Matrix< CoalScalar, VecSize, 1 > generateRandomVector(CoalScalar min, CoalScalar max)
Definition: normal_and_nearest_points.cpp:244
epsilon
static CoalScalar epsilon
Definition: simple.cpp:12
coal::Cone
Cone The base of the cone is at and the top is at .
Definition: coal/shape/geometric_shapes.h:467
coal::EPA_DEFAULT_TOLERANCE
constexpr CoalScalar EPA_DEFAULT_TOLERANCE
Definition: coal/narrowphase/narrowphase_defaults.h:60
coal::BVHModel
A class describing the bounding hierarchy of a mesh model or a point cloud model (which is viewed as ...
Definition: coal/BVH/BVH_model.h:314
coal::CollisionRequest::num_max_contacts
size_t num_max_contacts
The maximum number of contacts that can be returned.
Definition: coal/collision_data.h:313
OBBRSS.h
coal::Convex::polygons
std::shared_ptr< std::vector< PolygonT > > polygons
An array of PolygonT object. PolygonT should contains a list of vertices for each polygon,...
Definition: coal/shape/convex.h:101
geometric_shapes.h
coal::CollisionResult::getContact
const Contact & getContact(size_t i) const
get the i-th contact calculated
Definition: coal/collision_data.h:449
geometric_shape_to_BVH_model.h
coal::collide
COAL_DLLAPI std::size_t collide(const CollisionObject *o1, const CollisionObject *o2, const CollisionRequest &request, CollisionResult &result)
Main collision interface: given two collision objects, and the requirements for contacts,...
Definition: src/collision.cpp:61
BOOST_AUTO_TEST_CASE
BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_sphere_sphere)
Definition: normal_and_nearest_points.cpp:260
coal::CoalScalar
double CoalScalar
Definition: coal/data_types.h:76
coal::DistanceResult::clear
void clear()
clear the result
Definition: coal/collision_data.h:1139
coal::DistanceResult::nearest_points
std::array< Vec3s, 2 > nearest_points
nearest points. See CollisionResult::nearest_points.
Definition: coal/collision_data.h:1065
coal::CollisionRequest::distance_upper_bound
CoalScalar distance_upper_bound
Distance above which GJK solver makes an early stopping. GJK stops searching for the closest points w...
Definition: coal/collision_data.h:340
coal::generateBVHModel
void generateBVHModel(BVHModel< BV > &model, const Box &shape, const Transform3s &pose)
Generate BVH model from box.
Definition: coal/shape/geometric_shape_to_BVH_model.h:49
coal::CollisionGeometryPtr_t
shared_ptr< CollisionGeometry > CollisionGeometryPtr_t
Definition: include/coal/fwd.hh:134
obb.v
list v
Definition: obb.py:48
coal::CollisionResult::numContacts
size_t numContacts() const
number of contacts found
Definition: coal/collision_data.h:446


hpp-fcl
Author(s):
autogenerated on Sat Nov 23 2024 03:44:58