hfields.cpp
Go to the documentation of this file.
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2021, 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 Open Source Robotics Foundation 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_HEIGHT_FIELDS
38 #include <boost/test/included/unit_test.hpp>
39 #include <boost/filesystem.hpp>
40 
41 #include "fcl_resources/config.h"
42 
43 #include "coal/collision.h"
44 #include "coal/hfield.h"
45 #include "coal/math/transform.h"
49 
50 #include "coal/collision.h"
52 
53 #include "utility.h"
54 #include <iostream>
55 
56 using namespace coal;
57 
58 template <typename BV>
59 void test_constant_hfields(const Eigen::DenseIndex nx,
60  const Eigen::DenseIndex ny,
61  const CoalScalar min_altitude,
62  const CoalScalar max_altitude) {
63  const CoalScalar x_dim = 1., y_dim = 2.;
64  const MatrixXs heights = MatrixXs::Constant(ny, nx, max_altitude);
65 
66  HeightField<BV> hfield(x_dim, y_dim, heights, min_altitude);
67 
68  BOOST_CHECK(hfield.getXDim() == x_dim);
69  BOOST_CHECK(hfield.getYDim() == y_dim);
70 
71  const VecXs& x_grid = hfield.getXGrid();
72  BOOST_CHECK(x_grid[0] == -x_dim / 2.);
73  BOOST_CHECK(x_grid[nx - 1] == x_dim / 2.);
74 
75  const VecXs& y_grid = hfield.getYGrid();
76  BOOST_CHECK(y_grid[0] == y_dim / 2.);
77  BOOST_CHECK(y_grid[ny - 1] == -y_dim / 2.);
78 
79  // Test local AABB
80  hfield.computeLocalAABB();
81 
82  for (Eigen::DenseIndex i = 0; i < nx; ++i) {
83  for (Eigen::DenseIndex j = 0; j < ny; ++j) {
84  Vec3s point(x_grid[i], y_grid[j], heights(j, i));
85  BOOST_CHECK(hfield.aabb_local.contain(point));
86  }
87  }
88 
89  // Test clone
90  {
91  HeightField<BV>* hfield_clone = hfield.clone();
92  hfield_clone->computeLocalAABB();
93  BOOST_CHECK(*hfield_clone == hfield);
94 
95  delete hfield_clone;
96  }
97 
98  // Build equivalent object
99  const Box equivalent_box(x_dim, y_dim, max_altitude - min_altitude);
100  const Transform3s box_placement(
101  Matrix3s::Identity(), Vec3s(0., 0., (max_altitude + min_altitude) / 2.));
102 
103  // Test collision
104  const Sphere sphere(1.);
105  static const Transform3s IdTransform;
106 
107  const Box box(Vec3s::Ones());
108 
110 
111  // No collision case
112  {
113  const CoalScalar eps_no_collision = +0.1 * (max_altitude - min_altitude);
114  M_sphere.setTranslation(
115  Vec3s(0., 0., max_altitude + sphere.radius + eps_no_collision));
116  M_box.setTranslation(
117  Vec3s(0., 0., max_altitude + box.halfSide[2] + eps_no_collision));
118  CollisionRequest request;
119 
120  CollisionResult result;
121  collide(&hfield, IdTransform, &sphere, M_sphere, request, result);
122 
123  BOOST_CHECK(!result.isCollision());
124 
125  CollisionResult result_check_sphere;
126  collide(&equivalent_box, IdTransform * box_placement, &sphere, M_sphere,
127  request, result_check_sphere);
128 
129  BOOST_CHECK(!result_check_sphere.isCollision());
130 
131  CollisionResult result_check_box;
132  collide(&equivalent_box, IdTransform * box_placement, &box, M_box, request,
133  result_check_box);
134 
135  BOOST_CHECK(!result_check_box.isCollision());
136  }
137 
138  // Collision case
139  {
140  const CoalScalar eps_collision = -0.1 * (max_altitude - min_altitude);
141  M_sphere.setTranslation(
142  Vec3s(0., 0., max_altitude + sphere.radius + eps_collision));
143  M_box.setTranslation(
144  Vec3s(0., 0., max_altitude + box.halfSide[2] + eps_collision));
146  request; //(CONTACT | DISTANCE_LOWER_BOUND, (size_t)((nx-1)*(ny-1)));
147 
148  CollisionResult result;
149  collide(&hfield, IdTransform, &sphere, M_sphere, request, result);
150 
151  BOOST_CHECK(result.isCollision());
152 
153  CollisionResult result_check;
154  collide(&equivalent_box, IdTransform * box_placement, &sphere, M_sphere,
155  request, result_check);
156 
157  BOOST_CHECK(result_check.isCollision());
158 
159  CollisionResult result_check_box;
160  collide(&equivalent_box, IdTransform * box_placement, &box, M_box, request,
161  result_check_box);
162 
163  BOOST_CHECK(result_check_box.isCollision());
164  }
165 
166  // Update height
167  hfield.updateHeights(
168  MatrixXs::Constant(ny, nx, max_altitude / 2.)); // We change nothing
169 
170  // No collision case
171  {
172  const CoalScalar eps_no_collision = +0.1 * (max_altitude - min_altitude);
173  M_sphere.setTranslation(
174  Vec3s(0., 0., max_altitude + sphere.radius + eps_no_collision));
175  M_box.setTranslation(
176  Vec3s(0., 0., max_altitude + box.halfSide[2] + eps_no_collision));
177  CollisionRequest request;
178 
179  CollisionResult result;
180  collide(&hfield, IdTransform, &sphere, M_sphere, request, result);
181 
182  BOOST_CHECK(!result.isCollision());
183 
184  CollisionResult result_check_sphere;
185  collide(&equivalent_box, IdTransform * box_placement, &sphere, M_sphere,
186  request, result_check_sphere);
187 
188  BOOST_CHECK(!result_check_sphere.isCollision());
189 
190  CollisionResult result_check_box;
191  collide(&equivalent_box, IdTransform * box_placement, &box, M_box, request,
192  result_check_box);
193 
194  BOOST_CHECK(!result_check_box.isCollision());
195  }
196 
197  // Collision case
198  {
199  const CoalScalar eps_collision = -0.1 * (max_altitude - min_altitude);
200  M_sphere.setTranslation(
201  Vec3s(0., 0., max_altitude + sphere.radius + eps_collision));
202  M_box.setTranslation(
203  Vec3s(0., 0., max_altitude + box.halfSide[2] + eps_collision));
205  request; //(CONTACT | DISTANCE_LOWER_BOUND, (size_t)((nx-1)*(ny-1)));
206 
207  CollisionResult result;
208  collide(&hfield, IdTransform, &sphere, M_sphere, request, result);
209 
210  BOOST_CHECK(!result.isCollision());
211 
212  CollisionResult result_check;
213  collide(&equivalent_box, IdTransform * box_placement, &sphere, M_sphere,
214  request, result_check);
215 
216  BOOST_CHECK(result_check.isCollision());
217 
218  CollisionResult result_check_box;
219  collide(&equivalent_box, IdTransform * box_placement, &box, M_box, request,
220  result_check_box);
221 
222  BOOST_CHECK(result_check_box.isCollision());
223  }
224 
225  // Restore height
226  hfield.updateHeights(
227  MatrixXs::Constant(ny, nx, max_altitude)); // We change nothing
228 
229  // Collision case
230  {
231  const CoalScalar eps_collision = -0.1 * (max_altitude - min_altitude);
232  M_sphere.setTranslation(
233  Vec3s(0., 0., max_altitude + sphere.radius + eps_collision));
234  M_box.setTranslation(
235  Vec3s(0., 0., max_altitude + box.halfSide[2] + eps_collision));
237  request; //(CONTACT | DISTANCE_LOWER_BOUND, (size_t)((nx-1)*(ny-1)));
238 
239  CollisionResult result;
240  collide(&hfield, IdTransform, &sphere, M_sphere, request, result);
241 
242  BOOST_CHECK(result.isCollision());
243 
244  CollisionResult result_check;
245  collide(&equivalent_box, IdTransform * box_placement, &sphere, M_sphere,
246  request, result_check);
247 
248  BOOST_CHECK(result_check.isCollision());
249 
250  CollisionResult result_check_box;
251  collide(&equivalent_box, IdTransform * box_placement, &box, M_box, request,
252  result_check_box);
253 
254  BOOST_CHECK(result_check_box.isCollision());
255  }
256 }
257 
258 BOOST_AUTO_TEST_CASE(building_constant_hfields) {
259  const CoalScalar max_altitude = 1., min_altitude = 0.;
260 
261  test_constant_hfields<OBBRSS>(2, 2, min_altitude,
262  max_altitude); // Simple case
263  test_constant_hfields<OBBRSS>(20, 2, min_altitude, max_altitude);
264  test_constant_hfields<OBBRSS>(100, 100, min_altitude, max_altitude);
265  // test_constant_hfields<OBBRSS>(1000,1000,min_altitude,max_altitude);
266 
267  test_constant_hfields<AABB>(2, 2, min_altitude, max_altitude); // Simple case
268  test_constant_hfields<AABB>(20, 2, min_altitude, max_altitude);
269  test_constant_hfields<AABB>(100, 100, min_altitude, max_altitude);
270 }
271 
272 template <typename BV>
273 void test_negative_security_margin(const Eigen::DenseIndex nx,
274  const Eigen::DenseIndex ny,
275  const CoalScalar min_altitude,
276  const CoalScalar max_altitude) {
277  const CoalScalar x_dim = 1., y_dim = 2.;
278  const MatrixXs heights = MatrixXs::Constant(ny, nx, max_altitude);
279 
280  HeightField<BV> hfield(x_dim, y_dim, heights, min_altitude);
281 
282  // Build equivalent object
283  const Box equivalent_box(x_dim, y_dim, max_altitude - min_altitude);
284  const Transform3s box_placement(
285  Matrix3s::Identity(), Vec3s(0., 0., (max_altitude + min_altitude) / 2.));
286 
287  // Test collision
288  const Sphere sphere(1.);
289  static const Transform3s IdTransform;
290 
291  const Box box(Vec3s::Ones());
292 
294 
295  // No collision case
296  {
297  const CoalScalar eps_no_collision = +0.1 * (max_altitude - min_altitude);
298  M_sphere.setTranslation(
299  Vec3s(0., 0., max_altitude + sphere.radius + eps_no_collision));
300  M_box.setTranslation(
301  Vec3s(0., 0., max_altitude + box.halfSide[2] + eps_no_collision));
302  CollisionRequest request;
303 
304  CollisionResult result;
305  collide(&hfield, IdTransform, &sphere, M_sphere, request, result);
306 
307  BOOST_CHECK(!result.isCollision());
308 
309  CollisionResult result_check_sphere;
310  collide(&equivalent_box, IdTransform * box_placement, &sphere, M_sphere,
311  request, result_check_sphere);
312 
313  BOOST_CHECK(!result_check_sphere.isCollision());
314 
315  CollisionResult result_check_box;
316  collide(&equivalent_box, IdTransform * box_placement, &box, M_box, request,
317  result_check_box);
318 
319  BOOST_CHECK(!result_check_box.isCollision());
320  }
321 
322  // Collision case - positive security_margin
323  {
324  const CoalScalar eps_no_collision = +0.1 * (max_altitude - min_altitude);
325  M_sphere.setTranslation(
326  Vec3s(0., 0., max_altitude + sphere.radius + eps_no_collision));
327  M_box.setTranslation(
328  Vec3s(0., 0., max_altitude + box.halfSide[2] + eps_no_collision));
329  CollisionRequest request;
330  request.security_margin = eps_no_collision + 1e-6;
331 
332  CollisionResult result;
333  collide(&hfield, IdTransform, &sphere, M_sphere, request, result);
334 
335  BOOST_CHECK(result.isCollision());
336 
337  CollisionResult result_check_sphere;
338  collide(&equivalent_box, IdTransform * box_placement, &sphere, M_sphere,
339  request, result_check_sphere);
340 
341  BOOST_CHECK(result_check_sphere.isCollision());
342 
343  CollisionResult result_check_box;
344  collide(&equivalent_box, IdTransform * box_placement, &box, M_box, request,
345  result_check_box);
346 
347  BOOST_CHECK(result_check_box.isCollision());
348  }
349 
350  // Collision case
351  {
352  const CoalScalar eps_no_collision = -0.1 * (max_altitude - min_altitude);
353  M_sphere.setTranslation(
354  Vec3s(0., 0., max_altitude + sphere.radius + eps_no_collision));
355  M_box.setTranslation(
356  Vec3s(0., 0., max_altitude + box.halfSide[2] + eps_no_collision));
357  CollisionRequest request;
358 
359  CollisionResult result;
360  collide(&hfield, IdTransform, &sphere, M_sphere, request, result);
361 
362  BOOST_CHECK(result.isCollision());
363 
364  CollisionResult result_check_sphere;
365  collide(&equivalent_box, IdTransform * box_placement, &sphere, M_sphere,
366  request, result_check_sphere);
367 
368  BOOST_CHECK(result_check_sphere.isCollision());
369 
370  CollisionResult result_check_box;
371  collide(&equivalent_box, IdTransform * box_placement, &box, M_box, request,
372  result_check_box);
373 
374  BOOST_CHECK(result_check_box.isCollision());
375  }
376 
377  // No collision case - negative security_margin
378  {
379  const CoalScalar eps_no_collision = -0.1 * (max_altitude - min_altitude);
380  M_sphere.setTranslation(
381  Vec3s(0., 0., max_altitude + sphere.radius + eps_no_collision));
382  M_box.setTranslation(
383  Vec3s(0., 0., max_altitude + box.halfSide[2] + eps_no_collision));
384  CollisionRequest request;
385  request.security_margin = eps_no_collision - 1e-4;
386 
387  CollisionResult result;
388  collide(&hfield, IdTransform, &sphere, M_sphere, request, result);
389 
390  BOOST_CHECK(!result.isCollision());
391 
392  CollisionResult result_check_sphere;
393  collide(&equivalent_box, IdTransform * box_placement, &sphere, M_sphere,
394  request, result_check_sphere);
395 
396  BOOST_CHECK(!result_check_sphere.isCollision());
397 
398  CollisionResult result_check_box;
399  collide(&equivalent_box, IdTransform * box_placement, &box, M_box, request,
400  result_check_box);
401 
402  BOOST_CHECK(!result_check_box.isCollision());
403  }
404 }
405 
406 BOOST_AUTO_TEST_CASE(negative_security_margin) {
407  const CoalScalar max_altitude = 1., min_altitude = 0.;
408 
409  // test_negative_security_margin<OBBRSS>(100, 100, min_altitude,
410  // max_altitude);
411  test_negative_security_margin<AABB>(100, 100, min_altitude, max_altitude);
412 }
413 
414 BOOST_AUTO_TEST_CASE(hfield_with_square_hole) {
415  const Eigen::DenseIndex nx = 100, ny = 100;
416 
417  typedef AABB BV;
418  const MatrixXs X =
419  Eigen::RowVectorXd::LinSpaced(nx, -1., 1.).replicate(ny, 1);
420  const MatrixXs Y = Eigen::VectorXd::LinSpaced(ny, 1., -1.).replicate(1, nx);
421 
422  const CoalScalar dim_square = 0.5;
423 
424  const Eigen::Array<bool, Eigen::Dynamic, Eigen::Dynamic> hole =
425  (X.array().abs() < dim_square) && (Y.array().abs() < dim_square);
426 
427  const MatrixXs heights =
428  MatrixXs::Ones(ny, nx) - hole.cast<double>().matrix();
429 
430  const HeightField<BV> hfield(2., 2., heights, -10.);
431 
432  Sphere sphere(0.48);
433  const Transform3s sphere_pos(Vec3s(0., 0., 0.5));
434  const Transform3s hfield_pos;
435 
436  const CollisionRequest request;
437 
438  CollisionResult result;
439  {
440  collide(&hfield, hfield_pos, &sphere, sphere_pos, request, result);
441 
442  BOOST_CHECK(!result.isCollision());
443  }
444 
445  sphere.radius = 0.51;
446 
447  {
448  CollisionResult result;
449  const Sphere sphere2(0.51);
450  collide(&hfield, hfield_pos, &sphere2, sphere_pos, request, result);
451 
452  BOOST_CHECK(result.isCollision());
453  }
454 }
455 
456 BOOST_AUTO_TEST_CASE(hfield_with_circular_hole) {
457  const Eigen::DenseIndex nx = 100, ny = 100;
458 
459  // typedef OBBRSS BV; TODO(jcarpent): OBBRSS does not work (compile in Debug
460  // mode), as the overlap of OBBRSS is not satisfactory yet.
461  typedef AABB BV;
462  const MatrixXs X =
463  Eigen::RowVectorXd::LinSpaced(nx, -1., 1.).replicate(ny, 1);
464  const MatrixXs Y = Eigen::VectorXd::LinSpaced(ny, 1., -1.).replicate(1, nx);
465 
466  const CoalScalar dim_hole = 1;
467 
468  const Eigen::Array<bool, Eigen::Dynamic, Eigen::Dynamic> hole =
469  (X.array().square() + Y.array().square() <= dim_hole);
470 
471  const MatrixXs heights =
472  MatrixXs::Ones(ny, nx) - hole.cast<double>().matrix();
473 
474  const HeightField<BV> hfield(2., 2., heights, -10.);
475 
476  BOOST_CHECK(hfield.getXGrid()[0] == -1.);
477  BOOST_CHECK(hfield.getXGrid()[nx - 1] == +1.);
478 
479  BOOST_CHECK(hfield.getYGrid()[0] == +1.);
480  BOOST_CHECK(hfield.getYGrid()[ny - 1] == -1.);
481 
482  Sphere sphere(0.975);
483  const Transform3s sphere_pos(Vec3s(0., 0., 1.));
484  const Transform3s hfield_pos;
485 
486  const CoalScalar thresholds[3] = {0., 0.01, -0.005};
487 
488  for (int i = 0; i < 3; ++i) {
489  CollisionResult result;
490  CollisionRequest request;
491  request.security_margin = thresholds[i];
492  collide(&hfield, hfield_pos, &sphere, sphere_pos, request, result);
493 
494  BOOST_CHECK(!result.isCollision());
495  }
496 
497  // Increase the size of the sphere to force the collision
498  sphere.radius = 1.01;
499  for (int i = 0; i < 3; ++i) {
500  CollisionResult result;
501  CollisionRequest request;
502  request.security_margin = thresholds[i];
503  collide(&hfield, hfield_pos, &sphere, sphere_pos, request, result);
504 
505  BOOST_CHECK(result.isCollision());
506  }
507 
508  {
509  CollisionResult result;
510  CollisionRequest request;
511  request.security_margin = -0.02;
512  collide(&hfield, hfield_pos, &sphere, sphere_pos, request, result);
513 
514  BOOST_CHECK(!result.isCollision());
515  }
516 }
517 
518 bool isApprox(const CoalScalar v1, const CoalScalar v2,
519  const CoalScalar tol = 1e-6) {
520  return std::fabs(v1 - v2) <= tol;
521 }
522 
524  const std::vector<Vec3s>& points) {
525  const Vec3s pointA = points[triangle[0]];
526  const Vec3s pointB = points[triangle[1]];
527  const Vec3s pointC = points[triangle[2]];
528 
529  return (pointB - pointA).cross(pointC - pointA).normalized();
530 }
531 
532 BOOST_AUTO_TEST_CASE(test_hfield_bin_face_normal_orientation) {
533  const CoalScalar sphere_radius = 1.;
534  Sphere sphere(sphere_radius);
535  MatrixXs altitutes(2, 2);
536  CoalScalar altitude_value = 1.;
537  altitutes.fill(altitude_value);
538 
539  typedef AABB BV;
540  HeightField<BV> hfield(1., 1., altitutes, 0.);
541 
542  const HeightField<BV>::BVS& nodes = hfield.getNodes();
543  BOOST_CHECK(nodes.size() == 1);
544  const HeightField<BV>::Node& node = nodes[0];
545 
546  typedef HFNodeBase::FaceOrientation FaceOrientation;
547  BOOST_CHECK((node.contact_active_faces & FaceOrientation::BOTTOM) ==
548  int(FaceOrientation::BOTTOM));
549  BOOST_CHECK((node.contact_active_faces & FaceOrientation::TOP) ==
550  int(FaceOrientation::TOP));
551  BOOST_CHECK((node.contact_active_faces & FaceOrientation::NORTH) ==
552  int(FaceOrientation::NORTH));
553  BOOST_CHECK((node.contact_active_faces & FaceOrientation::SOUTH) ==
554  int(FaceOrientation::SOUTH));
555  BOOST_CHECK((node.contact_active_faces & FaceOrientation::EAST) ==
556  int(FaceOrientation::EAST));
557  BOOST_CHECK((node.contact_active_faces & FaceOrientation::WEST) ==
558  int(FaceOrientation::WEST));
559 
560  Convex<Triangle> convex1, convex2;
561  int convex1_active_faces, convex2_active_faces;
562  details::buildConvexTriangles(node, hfield, convex1, convex1_active_faces,
563  convex2, convex2_active_faces);
564 
565  // Check face normals for convex1
566  {
567  const std::vector<Vec3s>& points = *(convex1.points);
568  // BOTTOM
569  {
570  const Triangle& triangle = (*(convex1.polygons))[0];
571 
572  BOOST_CHECK(
573  computeFaceNormal(triangle, points).isApprox(-Vec3s::UnitZ()));
574  }
575 
576  // TOP
577  {
578  const Triangle& triangle = (*(convex1.polygons))[1];
579 
580  BOOST_CHECK(computeFaceNormal(triangle, points).isApprox(Vec3s::UnitZ()));
581  }
582 
583  // WEST sides
584  {
585  const Triangle& triangle1 = (*(convex1.polygons))[2];
586  const Triangle& triangle2 = (*(convex1.polygons))[3];
587 
588  BOOST_CHECK(
589  computeFaceNormal(triangle1, points).isApprox(-Vec3s::UnitX()));
590  BOOST_CHECK(
591  computeFaceNormal(triangle2, points).isApprox(-Vec3s::UnitX()));
592  }
593 
594  // SOUTH-EAST sides
595  {
596  const Vec3s south_east_normal = Vec3s(1., -1., 0).normalized();
597 
598  const Triangle& triangle1 = (*(convex1.polygons))[4];
599  const Triangle& triangle2 = (*(convex1.polygons))[5];
600 
601  BOOST_CHECK(
602  computeFaceNormal(triangle1, points).isApprox(south_east_normal));
603  BOOST_CHECK(
604  computeFaceNormal(triangle2, points).isApprox(south_east_normal));
605  }
606 
607  // NORTH sides
608  {
609  const Triangle& triangle1 = (*(convex1.polygons))[6];
610  const Triangle& triangle2 = (*(convex1.polygons))[7];
611 
612  std::cout << "computeFaceNormal(triangle1,points): "
613  << computeFaceNormal(triangle1, points).transpose()
614  << std::endl;
615  BOOST_CHECK(
616  computeFaceNormal(triangle1, points).isApprox(Vec3s::UnitY()));
617  BOOST_CHECK(
618  computeFaceNormal(triangle2, points).isApprox(Vec3s::UnitY()));
619  }
620  }
621 
622  // Check face normals for convex2
623  {
624  const std::vector<Vec3s>& points = *(convex2.points);
625 
626  // BOTTOM
627  {
628  const Triangle& triangle = (*(convex2.polygons))[0];
629 
630  BOOST_CHECK(
631  computeFaceNormal(triangle, points).isApprox(-Vec3s::UnitZ()));
632  }
633 
634  // TOP
635  {
636  const Triangle& triangle = (*(convex2.polygons))[1];
637 
638  BOOST_CHECK(computeFaceNormal(triangle, points).isApprox(Vec3s::UnitZ()));
639  }
640 
641  // SOUTH sides
642  {
643  const Triangle& triangle1 = (*(convex2.polygons))[2];
644  const Triangle& triangle2 = (*(convex2.polygons))[3];
645 
646  BOOST_CHECK(
647  computeFaceNormal(triangle1, points).isApprox(-Vec3s::UnitY()));
648  BOOST_CHECK(
649  computeFaceNormal(triangle2, points).isApprox(-Vec3s::UnitY()));
650  }
651 
652  // NORTH-WEST sides
653  {
654  const Vec3s north_west_normal = Vec3s(-1., 1., 0).normalized();
655 
656  const Triangle& triangle1 = (*(convex2.polygons))[4];
657  const Triangle& triangle2 = (*(convex2.polygons))[5];
658 
659  BOOST_CHECK(
660  computeFaceNormal(triangle1, points).isApprox(north_west_normal));
661  BOOST_CHECK(
662  computeFaceNormal(triangle2, points).isApprox(north_west_normal));
663  }
664 
665  // EAST sides
666  {
667  const Triangle& triangle1 = (*(convex2.polygons))[6];
668  const Triangle& triangle2 = (*(convex2.polygons))[7];
669 
670  BOOST_CHECK(
671  computeFaceNormal(triangle1, points).isApprox(Vec3s::UnitX()));
672  BOOST_CHECK(
673  computeFaceNormal(triangle2, points).isApprox(Vec3s::UnitX()));
674  }
675  }
676 }
677 
678 BOOST_AUTO_TEST_CASE(test_hfield_bin_active_faces) {
679  typedef HFNodeBase::FaceOrientation FaceOrientation;
680  const CoalScalar sphere_radius = 1.;
681  Sphere sphere(sphere_radius);
682  MatrixXs altitutes(3, 3);
683  CoalScalar altitude_value = 1.;
684  altitutes.fill(altitude_value);
685 
686  typedef AABB BV;
687  HeightField<BV> hfield(1., 1., altitutes, 0.);
688 
689  const HeightField<BV>::BVS& nodes = hfield.getNodes();
690  BOOST_CHECK(nodes.size() == 7);
691 
692  for (const auto& node : nodes) {
693  if (node.isLeaf()) {
694  BOOST_CHECK((node.contact_active_faces & FaceOrientation::BOTTOM) ==
695  int(FaceOrientation::BOTTOM));
696  BOOST_CHECK((node.contact_active_faces & FaceOrientation::TOP) ==
697  int(FaceOrientation::TOP));
698 
699  if (node.x_id == 0)
700  BOOST_CHECK((node.contact_active_faces & FaceOrientation::WEST) ==
701  int(FaceOrientation::WEST));
702  if (node.y_id == 0)
703  BOOST_CHECK((node.contact_active_faces & FaceOrientation::NORTH) ==
704  int(FaceOrientation::NORTH));
705 
706  if (node.x_id == 1)
707  BOOST_CHECK((node.contact_active_faces & FaceOrientation::EAST) ==
708  int(FaceOrientation::EAST));
709  if (node.y_id == 1)
710  BOOST_CHECK((node.contact_active_faces & FaceOrientation::SOUTH) ==
711  int(FaceOrientation::SOUTH));
712  }
713  }
714 }
715 
716 BOOST_AUTO_TEST_CASE(test_hfield_single_bin) {
717  const CoalScalar sphere_radius = 1.;
718  Sphere sphere(sphere_radius);
719  MatrixXs altitutes(2, 2);
720  CoalScalar altitude_value = 1.;
721  altitutes.fill(altitude_value);
722 
723  typedef AABB BV;
724  HeightField<BV> hfield(1., 1., altitutes, 0.);
725 
726  const HeightField<BV>::BVS& nodes = hfield.getNodes();
727  BOOST_CHECK(nodes.size() == 1);
728 
729  // Collision from the TOP
730  {
731  const Transform3s sphere_pos(Vec3s(0., 0., 2.));
732  const Transform3s hfield_pos;
733 
734  CollisionResult result;
735  CollisionRequest request;
736  request.security_margin = -0.005;
737  collide(&hfield, hfield_pos, &sphere, sphere_pos, request, result);
738 
739  BOOST_CHECK(!result.isCollision());
740  BOOST_CHECK(
741  isApprox(result.distance_lower_bound, -request.security_margin));
742  }
743 
744  // Same, but with a positive margin.
745  {
746  const Transform3s sphere_pos(Vec3s(0., 0., 2.));
747  const Transform3s hfield_pos;
748 
749  CollisionResult result;
750  CollisionRequest request;
751  request.security_margin = +0.005;
752  collide(&hfield, hfield_pos, &sphere, sphere_pos, request, result);
753 
754  BOOST_CHECK(result.isCollision());
755  if (result.isCollision()) {
756  const Contact& contact = result.getContact(0);
757  BOOST_CHECK(contact.normal.isApprox(Vec3s::UnitZ()));
758  std::cout << "contact.penetration_depth: " << contact.penetration_depth
759  << std::endl;
760  BOOST_CHECK(isApprox(contact.penetration_depth, 0.));
761  }
762  }
763 
764  // Collision from the BOTTOM
765  {
766  const Transform3s sphere_pos(Vec3s(0., 0., -1.));
767  const Transform3s hfield_pos;
768 
769  CollisionResult result;
770  CollisionRequest request;
771  request.security_margin = -0.005;
772  collide(&hfield, hfield_pos, &sphere, sphere_pos, request, result);
773 
774  BOOST_CHECK(!result.isCollision());
775  }
776 
777  {
778  const Transform3s sphere_pos(Vec3s(0., 0., -1.));
779  const Transform3s hfield_pos;
780 
781  CollisionResult result;
782  CollisionRequest request;
783  request.security_margin = +0.005;
784  collide(&hfield, hfield_pos, &sphere, sphere_pos, request, result);
785 
786  BOOST_CHECK(result.isCollision());
787  {
788  const Contact& contact = result.getContact(0);
789  BOOST_CHECK(contact.normal.isApprox(-Vec3s::UnitZ()));
790  std::cout << "contact.penetration_depth: " << contact.penetration_depth
791  << std::endl;
792  BOOST_CHECK(isApprox(contact.penetration_depth, 0.));
793  }
794  }
795 
796  // Collision from the WEST
797  {
798  const Transform3s sphere_pos(
799  Vec3s(hfield.getXGrid()[0] - sphere_radius, 0., 0.5));
800  const Transform3s hfield_pos;
801 
802  CollisionResult result;
803  CollisionRequest request;
804  request.security_margin = -0.005;
805  collide(&hfield, hfield_pos, &sphere, sphere_pos, request, result);
806 
807  BOOST_CHECK(!result.isCollision());
808  }
809 
810  {
811  const Transform3s sphere_pos(
812  Vec3s(hfield.getXGrid()[0] - sphere_radius, 0., 0.5));
813  const Transform3s hfield_pos;
814 
815  CollisionResult result;
816  CollisionRequest request;
817  request.security_margin = +0.005;
818  collide(&hfield, hfield_pos, &sphere, sphere_pos, request, result);
819 
820  BOOST_CHECK(result.isCollision());
821  if (result.isCollision()) {
822  const Contact& contact = result.getContact(0);
823  BOOST_CHECK(contact.normal.isApprox(-Vec3s::UnitX()));
824  BOOST_CHECK(isApprox(contact.penetration_depth, 0.));
825  }
826  }
827 
828  // Collision from the EAST
829  {
830  const Transform3s sphere_pos(
831  Vec3s(hfield.getXGrid()[1] + sphere_radius, 0., 0.5));
832  const Transform3s hfield_pos;
833 
834  CollisionResult result;
835  CollisionRequest request;
836  request.security_margin = -0.005;
837  collide(&hfield, hfield_pos, &sphere, sphere_pos, request, result);
838 
839  BOOST_CHECK(!result.isCollision());
840  }
841 
842  {
843  const Transform3s sphere_pos(
844  Vec3s(hfield.getXGrid()[1] + sphere_radius, 0., 0.5));
845  const Transform3s hfield_pos;
846 
847  CollisionResult result;
848  CollisionRequest request;
849  request.security_margin = +0.005;
850  collide(&hfield, hfield_pos, &sphere, sphere_pos, request, result);
851 
852  BOOST_CHECK(result.isCollision());
853 
854  if (result.isCollision()) {
855  const Contact& contact = result.getContact(0);
856  BOOST_CHECK(contact.normal.isApprox(Vec3s::UnitX()));
857  BOOST_CHECK(isApprox(contact.penetration_depth, 0.));
858  }
859  }
860 
861  // Collision from the NORTH
862  {
863  const Transform3s sphere_pos(
864  Vec3s(0., hfield.getYGrid()[0] + sphere_radius, 0.5));
865  const Transform3s hfield_pos;
866 
867  CollisionResult result;
868  CollisionRequest request;
869  request.security_margin = -0.005;
870  collide(&hfield, hfield_pos, &sphere, sphere_pos, request, result);
871 
872  BOOST_CHECK(!result.isCollision());
873  }
874 
875  {
876  const Transform3s sphere_pos(
877  Vec3s(0., hfield.getYGrid()[0] + sphere_radius, 0.5));
878  const Transform3s hfield_pos;
879 
880  CollisionResult result;
881  CollisionRequest request;
882  request.security_margin = +0.005;
883  collide(&hfield, hfield_pos, &sphere, sphere_pos, request, result);
884 
885  BOOST_CHECK(result.isCollision());
886 
887  if (result.isCollision()) {
888  const Contact& contact = result.getContact(0);
889  BOOST_CHECK(contact.normal.isApprox(Vec3s::UnitY()));
890  BOOST_CHECK(isApprox(contact.penetration_depth, 0.));
891  }
892  }
893 
894  // Collision from the SOUTH
895  {
896  const Transform3s sphere_pos(
897  Vec3s(0., hfield.getYGrid()[1] - sphere_radius, 0.5));
898  const Transform3s hfield_pos;
899 
900  CollisionResult result;
901  CollisionRequest request;
902  request.security_margin = -0.005;
903  collide(&hfield, hfield_pos, &sphere, sphere_pos, request, result);
904 
905  BOOST_CHECK(!result.isCollision());
906  }
907 
908  {
909  const Transform3s sphere_pos(
910  Vec3s(0., hfield.getYGrid()[1] - sphere_radius, 0.5));
911  const Transform3s hfield_pos;
912 
913  CollisionResult result;
914  CollisionRequest request;
915  request.security_margin = +0.005;
916  collide(&hfield, hfield_pos, &sphere, sphere_pos, request, result);
917 
918  BOOST_CHECK(result.isCollision());
919 
920  if (result.isCollision()) {
921  const Contact& contact = result.getContact(0);
922  BOOST_CHECK(contact.normal.isApprox(-Vec3s::UnitY()));
923  BOOST_CHECK(isApprox(contact.penetration_depth, 0.));
924  }
925  }
926 }
coal::HeightField::getXGrid
const VecXs & getXGrid() const
Returns a const reference of the grid along the X direction.
Definition: coal/hfield.h:252
collision.h
coal::Vec3s
Eigen::Matrix< CoalScalar, 3, 1 > Vec3s
Definition: coal/data_types.h:77
collision_manager.box
box
Definition: collision_manager.py:11
coal::CollisionResult::isCollision
bool isCollision() const
return binary collision result
Definition: coal/collision_data.h:443
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
traversal_node_hfield_shape.h
collision_manager.sphere
sphere
Definition: collision_manager.py:4
collision_manager.M_sphere
M_sphere
Definition: collision_manager.py:7
loader.h
coal::CollisionRequest::security_margin
CoalScalar security_margin
Distance below which objects are considered in collision. See Collision.
Definition: coal/collision_data.h:328
coal
Main namespace.
Definition: coal/broadphase/broadphase_bruteforce.h:44
utility.h
isApprox
bool isApprox(const CoalScalar v1, const CoalScalar v2, const CoalScalar tol=1e-6)
Definition: hfields.cpp:518
coal::HeightField::getYDim
CoalScalar getYDim() const
Returns the dimension of the Height Field along the Y direction.
Definition: coal/hfield.h:262
coal::Contact::penetration_depth
CoalScalar penetration_depth
penetration depth
Definition: coal/collision_data.h:105
coal::HeightField::updateHeights
void updateHeights(const MatrixXs &new_heights)
Update Height Field height.
Definition: coal/hfield.h:290
coal::AABB
A class describing the AABB collision structure, which is a box in 3D space determined by two diagona...
Definition: coal/BV/AABB.h:55
coal::HeightField::computeLocalAABB
void computeLocalAABB()
Compute the AABB for the HeightField, used for broad-phase collision.
Definition: coal/hfield.h:278
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::Sphere
Center at zero point sphere.
Definition: coal/shape/geometric_shapes.h:240
coal::Transform3s
Simple transform class used locally by InterpMotion.
Definition: coal/math/transform.h:55
coal::HeightField::clone
virtual HeightField< BV > * clone() const
Clone *this into a new CollisionGeometry.
Definition: coal/hfield.h:269
coal::UnitZ
const Vec3s UnitZ
Definition: utility.cpp:90
transform.h
coal::VecXs
Eigen::Matrix< CoalScalar, Eigen::Dynamic, 1 > VecXs
Definition: coal/data_types.h:80
coal::HeightField::BVS
std::vector< Node, Eigen::aligned_allocator< Node > > BVS
Definition: coal/hfield.h:209
coal::HeightField::getNodes
const BVS & getNodes() const
Definition: coal/hfield.h:271
coal::CollisionGeometry::aabb_local
AABB aabb_local
AABB in local coordinate, used for tight AABB when only translation transform.
Definition: coal/collision_object.h:158
BOOST_AUTO_TEST_CASE
BOOST_AUTO_TEST_CASE(building_constant_hfields)
Definition: hfields.cpp:258
coal::CollisionResult
collision result
Definition: coal/collision_data.h:390
coal::CollisionRequest
request to the collision algorithm
Definition: coal/collision_data.h:311
coal::Convex
Convex polytope.
Definition: coal/serialization/collision_object.h:51
coal::UnitX
const Vec3s UnitX
Definition: utility.cpp:88
test_constant_hfields
void test_constant_hfields(const Eigen::DenseIndex nx, const Eigen::DenseIndex ny, const CoalScalar min_altitude, const CoalScalar max_altitude)
Definition: hfields.cpp:59
coal::Contact
Contact information returned by collision.
Definition: coal/collision_data.h:58
coal::AABB::contain
bool contain(const Vec3s &p) const
Check whether the AABB contains a point.
Definition: coal/BV/AABB.h:102
coal::HeightField::getXDim
CoalScalar getXDim() const
Returns the dimension of the Height Field along the X direction.
Definition: coal/hfield.h:260
assimp.h
coal::HeightField
Data structure depicting a height field given by the base grid dimensions and the elevation along the...
Definition: coal/hfield.h:202
coal::MatrixXs
Eigen::Matrix< CoalScalar, Eigen::Dynamic, Eigen::Dynamic > MatrixXs
Definition: coal/data_types.h:86
coal::HFNodeBase::FaceOrientation
FaceOrientation
Definition: coal/hfield.h:56
coal::HeightField::getYGrid
const VecXs & getYGrid() const
Returns a const reference of the grid along the Y direction.
Definition: coal/hfield.h:254
hfield.h
collision_manager.M_box
M_box
Definition: collision_manager.py:14
coal::HFNodeBase::contact_active_faces
int contact_active_faces
Definition: coal/hfield.h:75
computeFaceNormal
Vec3s computeFaceNormal(const Triangle &triangle, const std::vector< Vec3s > &points)
Definition: hfields.cpp:523
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::HFNode
Definition: coal/hfield.h:128
coal::CollisionResult::getContact
const Contact & getContact(size_t i) const
get the i-th contact calculated
Definition: coal/collision_data.h:449
Y
Y
test_negative_security_margin
void test_negative_security_margin(const Eigen::DenseIndex nx, const Eigen::DenseIndex ny, const CoalScalar min_altitude, const CoalScalar max_altitude)
Definition: hfields.cpp:273
coal::UnitY
const Vec3s UnitY
Definition: utility.cpp:89
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
coal::Triangle
Triangle with 3 indices for points.
Definition: coal/data_types.h:111
coal::CoalScalar
double CoalScalar
Definition: coal/data_types.h:76
X


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