GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
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 |
Generated by: GCOVR (Version 4.2) |