GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: test/convex.cpp Lines: 91 91 100.0 %
Date: 2024-02-09 12:57:42 Branches: 341 670 50.9 %

Line Branch Exec Source
1
/*
2
 * Software License Agreement (BSD License)
3
 *
4
 *  Copyright (c) 2019, LAAS-CNRS
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
35
/** \author Joseph Mirabel */
36
37
#define BOOST_TEST_MODULE FCL_GEOMETRIC_SHAPES
38
#include <boost/test/included/unit_test.hpp>
39
40
#include <hpp/fcl/shape/convex.h>
41
#include <hpp/fcl/collision.h>
42
#include <hpp/fcl/distance.h>
43
44
#include "utility.h"
45
46
using namespace hpp::fcl;
47
48
2
Convex<Quadrilateral> buildBox(FCL_REAL l, FCL_REAL w, FCL_REAL d) {
49

18
  Vec3f* pts = new Vec3f[8];
50
2
  pts[0] = Vec3f(l, w, d);
51
2
  pts[1] = Vec3f(l, w, -d);
52
2
  pts[2] = Vec3f(l, -w, d);
53
2
  pts[3] = Vec3f(l, -w, -d);
54
2
  pts[4] = Vec3f(-l, w, d);
55
2
  pts[5] = Vec3f(-l, w, -d);
56
2
  pts[6] = Vec3f(-l, -w, d);
57
2
  pts[7] = Vec3f(-l, -w, -d);
58
59
14
  Quadrilateral* polygons = new Quadrilateral[6];
60
2
  polygons[0].set(0, 2, 3, 1);  // x+ side
61
2
  polygons[1].set(2, 6, 7, 3);  // y- side
62
2
  polygons[2].set(4, 5, 7, 6);  // x- side
63
2
  polygons[3].set(0, 1, 5, 4);  // y+ side
64
2
  polygons[4].set(1, 3, 7, 5);  // z- side
65
2
  polygons[5].set(0, 2, 6, 4);  // z+ side
66
67
  return Convex<Quadrilateral>(true,
68
                               pts,  // points
69
                               8,    // num points
70
                               polygons,
71
                               6  // number of polygons
72
2
  );
73
}
74
75
















4
BOOST_AUTO_TEST_CASE(convex) {
76
2
  FCL_REAL l = 1, w = 1, d = 1;
77
4
  Convex<Quadrilateral> box(buildBox(l, w, d));
78
79
  // Check neighbors
80
18
  for (int i = 0; i < 8; ++i) {
81


16
    BOOST_CHECK_EQUAL(box.neighbors[i].count(), 3);
82
  }
83


2
  BOOST_CHECK_EQUAL(box.neighbors[0][0], 1);
84


2
  BOOST_CHECK_EQUAL(box.neighbors[0][1], 2);
85


2
  BOOST_CHECK_EQUAL(box.neighbors[0][2], 4);
86
87


2
  BOOST_CHECK_EQUAL(box.neighbors[1][0], 0);
88


2
  BOOST_CHECK_EQUAL(box.neighbors[1][1], 3);
89


2
  BOOST_CHECK_EQUAL(box.neighbors[1][2], 5);
90
91


2
  BOOST_CHECK_EQUAL(box.neighbors[2][0], 0);
92


2
  BOOST_CHECK_EQUAL(box.neighbors[2][1], 3);
93


2
  BOOST_CHECK_EQUAL(box.neighbors[2][2], 6);
94
95


2
  BOOST_CHECK_EQUAL(box.neighbors[3][0], 1);
96


2
  BOOST_CHECK_EQUAL(box.neighbors[3][1], 2);
97


2
  BOOST_CHECK_EQUAL(box.neighbors[3][2], 7);
98
99


2
  BOOST_CHECK_EQUAL(box.neighbors[4][0], 0);
100


2
  BOOST_CHECK_EQUAL(box.neighbors[4][1], 5);
101


2
  BOOST_CHECK_EQUAL(box.neighbors[4][2], 6);
102
103


2
  BOOST_CHECK_EQUAL(box.neighbors[5][0], 1);
104


2
  BOOST_CHECK_EQUAL(box.neighbors[5][1], 4);
105


2
  BOOST_CHECK_EQUAL(box.neighbors[5][2], 7);
106
107


2
  BOOST_CHECK_EQUAL(box.neighbors[6][0], 2);
108


2
  BOOST_CHECK_EQUAL(box.neighbors[6][1], 4);
109


2
  BOOST_CHECK_EQUAL(box.neighbors[6][2], 7);
110
111


2
  BOOST_CHECK_EQUAL(box.neighbors[7][0], 3);
112


2
  BOOST_CHECK_EQUAL(box.neighbors[7][1], 5);
113


2
  BOOST_CHECK_EQUAL(box.neighbors[7][2], 6);
114
2
}
115
116
template <typename Sa, typename Sb>
117
1002
void compareShapeIntersection(const Sa& sa, const Sb& sb,
118
                              const Transform3f& tf1, const Transform3f& tf2,
119
                              FCL_REAL tol = 1e-9) {
120
1002
  CollisionRequest request(CONTACT | DISTANCE_LOWER_BOUND, 1);
121

2004
  CollisionResult resA, resB;
122
123
1002
  collide(&sa, tf1, &sa, tf2, request, resA);
124
1002
  collide(&sb, tf1, &sb, tf2, request, resB);
125
126


1002
  BOOST_CHECK_EQUAL(resA.isCollision(), resB.isCollision());
127


1002
  BOOST_CHECK_EQUAL(resA.numContacts(), resB.numContacts());
128
129

1002
  if (resA.isCollision() && resB.isCollision()) {
130


9
    Contact cA = resA.getContact(0), cB = resB.getContact(0);
131
132







9
    BOOST_TEST_MESSAGE(tf1 << '\n'
133
                           << cA.pos.format(pyfmt) << '\n'
134
                           << '\n'
135
                           << tf2 << '\n'
136
                           << cB.pos.format(pyfmt) << '\n');
137
    // Only warnings because there are still some bugs.
138



9
    BOOST_WARN_SMALL((cA.pos - cB.pos).squaredNorm(), tol);
139



9
    BOOST_WARN_SMALL((cA.normal - cB.normal).squaredNorm(), tol);
140
  } else {
141



993
    BOOST_CHECK_CLOSE(resA.distance_lower_bound, resB.distance_lower_bound,
142
                      tol);  // distances should be same
143
  }
144
1002
}
145
146
template <typename Sa, typename Sb>
147
1002
void compareShapeDistance(const Sa& sa, const Sb& sb, const Transform3f& tf1,
148
                          const Transform3f& tf2, FCL_REAL tol = 1e-9) {
149
1002
  DistanceRequest request(true);
150

1002
  DistanceResult resA, resB;
151
152
1002
  distance(&sa, tf1, &sa, tf2, request, resA);
153
1002
  distance(&sb, tf1, &sb, tf2, request, resB);
154
155













1002
  BOOST_TEST_MESSAGE(tf1 << '\n'
156
                         << resA.normal.format(pyfmt) << '\n'
157
                         << resA.nearest_points[0].format(pyfmt) << '\n'
158
                         << resA.nearest_points[1].format(pyfmt) << '\n'
159
                         << '\n'
160
                         << tf2 << '\n'
161
                         << resB.normal.format(pyfmt) << '\n'
162
                         << resB.nearest_points[0].format(pyfmt) << '\n'
163
                         << resB.nearest_points[1].format(pyfmt) << '\n');
164
  // TODO in one case, there is a mismatch between the distances and I cannot
165
  // say which one is correct. To visualize the case, use script
166
  // test/geometric_shapes.py
167



1002
  BOOST_WARN_CLOSE(resA.min_distance, resB.min_distance, tol);
168
  // BOOST_CHECK_CLOSE(resA.min_distance, resB.min_distance, tol);
169
170
  // Only warnings because there are still some bugs.
171



1002
  BOOST_WARN_SMALL((resA.normal - resA.normal).squaredNorm(), tol);
172



1002
  BOOST_WARN_SMALL(
173
      (resA.nearest_points[0] - resB.nearest_points[0]).squaredNorm(), tol);
174



1002
  BOOST_WARN_SMALL(
175
      (resA.nearest_points[1] - resB.nearest_points[1]).squaredNorm(), tol);
176
1002
}
177
178
















4
BOOST_AUTO_TEST_CASE(compare_convex_box) {
179
2
  FCL_REAL extents[6] = {0, 0, 0, 10, 10, 10};
180
2
  FCL_REAL l = 1, w = 1, d = 1, eps = 1e-4;
181
4
  Box box(l * 2, w * 2, d * 2);
182
4
  Convex<Quadrilateral> convex_box(buildBox(l, w, d));
183
184
2
  Transform3f tf1;
185
2
  Transform3f tf2;
186
187

2
  tf2.setTranslation(Vec3f(3, 0, 0));
188
2
  compareShapeIntersection(box, convex_box, tf1, tf2, eps);
189
2
  compareShapeDistance(box, convex_box, tf1, tf2, eps);
190
191

2
  tf2.setTranslation(Vec3f(0, 0, 0));
192
2
  compareShapeIntersection(box, convex_box, tf1, tf2, eps);
193
2
  compareShapeDistance(box, convex_box, tf1, tf2, eps);
194
195
2002
  for (int i = 0; i < 1000; ++i) {
196
2000
    generateRandomTransform(extents, tf2);
197
2000
    compareShapeIntersection(box, convex_box, tf1, tf2, eps);
198
2000
    compareShapeDistance(box, convex_box, tf1, tf2, eps);
199
  }
200
2
}
201
202
#ifdef HPP_FCL_HAS_QHULL
203
BOOST_AUTO_TEST_CASE(convex_hull_throw) {
204
  std::vector<Vec3f> points({
205
      Vec3f(1, 1, 1),
206
      Vec3f(0, 0, 0),
207
      Vec3f(1, 0, 0),
208
  });
209
210
  BOOST_CHECK_THROW(ConvexBase::convexHull(points.data(), 0, false, NULL),
211
                    std::invalid_argument);
212
  BOOST_CHECK_THROW(ConvexBase::convexHull(points.data(), 1, false, NULL),
213
                    std::invalid_argument);
214
  BOOST_CHECK_THROW(ConvexBase::convexHull(points.data(), 2, false, NULL),
215
                    std::invalid_argument);
216
  BOOST_CHECK_THROW(ConvexBase::convexHull(points.data(), 3, false, NULL),
217
                    std::invalid_argument);
218
}
219
220
BOOST_AUTO_TEST_CASE(convex_hull_quad) {
221
  std::vector<Vec3f> points({
222
      Vec3f(1, 1, 1),
223
      Vec3f(0, 0, 0),
224
      Vec3f(1, 0, 0),
225
      Vec3f(0, 0, 1),
226
  });
227
228
  ConvexBase* convexHull = ConvexBase::convexHull(
229
      points.data(), (unsigned int)points.size(), false, NULL);
230
231
  BOOST_REQUIRE_EQUAL(convexHull->num_points, 4);
232
  BOOST_CHECK_EQUAL(convexHull->neighbors[0].count(), 3);
233
  BOOST_CHECK_EQUAL(convexHull->neighbors[1].count(), 3);
234
  BOOST_CHECK_EQUAL(convexHull->neighbors[2].count(), 3);
235
  delete convexHull;
236
}
237
238
BOOST_AUTO_TEST_CASE(convex_hull_box_like) {
239
  std::vector<Vec3f> points({
240
      Vec3f(1, 1, 1),
241
      Vec3f(1, 1, -1),
242
      Vec3f(1, -1, 1),
243
      Vec3f(1, -1, -1),
244
      Vec3f(-1, 1, 1),
245
      Vec3f(-1, 1, -1),
246
      Vec3f(-1, -1, 1),
247
      Vec3f(-1, -1, -1),
248
      Vec3f(0, 0, 0),
249
      Vec3f(0, 0, 0.99),
250
  });
251
252
  ConvexBase* convexHull = ConvexBase::convexHull(
253
      points.data(), (unsigned int)points.size(), false, NULL);
254
255
  BOOST_REQUIRE_EQUAL(8, convexHull->num_points);
256
  for (int i = 0; i < 8; ++i) {
257
    BOOST_CHECK(convexHull->points[i].cwiseAbs() == Vec3f(1, 1, 1));
258
    BOOST_CHECK_EQUAL(convexHull->neighbors[i].count(), 3);
259
  }
260
  delete convexHull;
261
262
  convexHull = ConvexBase::convexHull(points.data(),
263
                                      (unsigned int)points.size(), true, NULL);
264
  Convex<Triangle>* convex_tri = dynamic_cast<Convex<Triangle>*>(convexHull);
265
  BOOST_CHECK(convex_tri != NULL);
266
267
  BOOST_REQUIRE_EQUAL(8, convexHull->num_points);
268
  for (int i = 0; i < 8; ++i) {
269
    BOOST_CHECK(convexHull->points[i].cwiseAbs() == Vec3f(1, 1, 1));
270
    BOOST_CHECK(convexHull->neighbors[i].count() >= 3);
271
    BOOST_CHECK(convexHull->neighbors[i].count() <= 6);
272
  }
273
  delete convexHull;
274
}
275
#endif