GCC Code Coverage Report


Directory: ./
File: include/hpp/fcl/collision_data.h
Date: 2024-11-17 18:53:04
Exec Total Coverage
Lines: 313 378 82.8%
Branches: 167 468 35.7%

Line Branch Exec Source
1 /*
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2011-2014, Willow Garage, Inc.
5 * Copyright (c) 2014-2015, Open Source Robotics Foundation
6 * Copyright (c) 2024, INRIA
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * * Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following
17 * disclaimer in the documentation and/or other materials provided
18 * with the distribution.
19 * * Neither the name of Open Source Robotics Foundation nor the names of its
20 * contributors may be used to endorse or promote products derived
21 * from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 */
36
37 /** \author Jia Pan */
38
39 #ifndef HPP_FCL_COLLISION_DATA_H
40 #define HPP_FCL_COLLISION_DATA_H
41
42 #include <vector>
43 #include <array>
44 #include <set>
45 #include <limits>
46 #include <numeric>
47
48 #include "hpp/fcl/collision_object.h"
49 #include "hpp/fcl/config.hh"
50 #include "hpp/fcl/data_types.h"
51 #include "hpp/fcl/timings.h"
52 #include "hpp/fcl/narrowphase/narrowphase_defaults.h"
53 #include "hpp/fcl/logging.h"
54
55 namespace hpp {
56 namespace fcl {
57
58 /// @brief Contact information returned by collision
59 struct HPP_FCL_DLLAPI Contact {
60 /// @brief collision object 1
61 const CollisionGeometry* o1;
62
63 /// @brief collision object 2
64 const CollisionGeometry* o2;
65
66 /// @brief contact primitive in object 1
67 /// if object 1 is mesh or point cloud, it is the triangle or point id
68 /// if object 1 is geometry shape, it is NONE (-1),
69 /// if object 1 is octree, it is the id of the cell
70 int b1;
71
72 /// @brief contact primitive in object 2
73 /// if object 2 is mesh or point cloud, it is the triangle or point id
74 /// if object 2 is geometry shape, it is NONE (-1),
75 /// if object 2 is octree, it is the id of the cell
76 int b2;
77
78 /// @brief contact normal, pointing from o1 to o2.
79 /// The normal defined as the normalized separation vector:
80 /// normal = (p2 - p1) / dist(o1, o2), where p1 = nearest_points[0]
81 /// belongs to o1 and p2 = nearest_points[1] belongs to o2 and dist(o1, o2) is
82 /// the **signed** distance between o1 and o2. The normal always points from
83 /// o1 to o2.
84 /// @note The separation vector is the smallest vector such that if o1 is
85 /// translated by it, o1 and o2 are in touching contact (they share at least
86 /// one contact point but have a zero intersection volume). If the shapes
87 /// overlap, dist(o1, o2) = -((p2-p1).norm()). Otherwise, dist(o1, o2) =
88 /// (p2-p1).norm().
89 Vec3f normal;
90
91 /// @brief nearest points associated to this contact.
92 /// @note Also referred as "witness points" in other collision libraries.
93 /// The points p1 = nearest_points[0] and p2 = nearest_points[1] verify the
94 /// property that dist(o1, o2) * (p1 - p2) is the separation vector between o1
95 /// and o2, with dist(o1, o2) being the **signed** distance separating o1 from
96 /// o2. See \ref DistanceResult::normal for the definition of the separation
97 /// vector. If o1 and o2 have multiple contacts, the nearest_points are
98 /// associated with the contact which has the greatest penetration depth.
99 /// TODO (louis): rename `nearest_points` to `witness_points`.
100 std::array<Vec3f, 2> nearest_points;
101
102 /// @brief contact position, in world space
103 Vec3f pos;
104
105 /// @brief penetration depth
106 FCL_REAL penetration_depth;
107
108 /// @brief invalid contact primitive information
109 static const int NONE = -1;
110
111 /// @brief Default constructor
112 2480 Contact() : o1(NULL), o2(NULL), b1(NONE), b2(NONE) {
113 2480 penetration_depth = (std::numeric_limits<FCL_REAL>::max)();
114 4960 nearest_points[0] = nearest_points[1] = normal = pos =
115
5/10
✓ Branch 2 taken 2480 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2480 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 2480 times.
✗ Branch 9 not taken.
✓ Branch 12 taken 2480 times.
✗ Branch 13 not taken.
✓ Branch 16 taken 2480 times.
✗ Branch 17 not taken.
2480 Vec3f::Constant(std::numeric_limits<FCL_REAL>::quiet_NaN());
116 2480 }
117
118 Contact(const CollisionGeometry* o1_, const CollisionGeometry* o2_, int b1_,
119 int b2_)
120 : o1(o1_), o2(o2_), b1(b1_), b2(b2_) {
121 penetration_depth = (std::numeric_limits<FCL_REAL>::max)();
122 nearest_points[0] = nearest_points[1] = normal = pos =
123 Vec3f::Constant(std::numeric_limits<FCL_REAL>::quiet_NaN());
124 }
125
126 17 Contact(const CollisionGeometry* o1_, const CollisionGeometry* o2_, int b1_,
127 int b2_, const Vec3f& pos_, const Vec3f& normal_, FCL_REAL depth_)
128 17 : o1(o1_),
129 17 o2(o2_),
130 17 b1(b1_),
131 17 b2(b2_),
132 17 normal(normal_),
133
5/10
✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 17 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 17 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 17 times.
✗ Branch 14 not taken.
34 nearest_points{pos_ - (depth_ * normal_ / 2),
134
3/6
✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 17 times.
✗ Branch 8 not taken.
17 pos_ + (depth_ * normal_ / 2)},
135 17 pos(pos_),
136 17 penetration_depth(depth_) {}
137
138 28905 Contact(const CollisionGeometry* o1_, const CollisionGeometry* o2_, int b1_,
139 int b2_, const Vec3f& p1, const Vec3f& p2, const Vec3f& normal_,
140 FCL_REAL depth_)
141 28905 : o1(o1_),
142 28905 o2(o2_),
143 28905 b1(b1_),
144 28905 b2(b2_),
145 28905 normal(normal_),
146 28905 nearest_points{p1, p2},
147
3/6
✓ Branch 1 taken 28905 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 28905 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 28905 times.
✗ Branch 8 not taken.
28905 pos((p1 + p2) / 2),
148 28905 penetration_depth(depth_) {}
149
150 bool operator<(const Contact& other) const {
151 if (b1 == other.b1) return b2 < other.b2;
152 return b1 < other.b1;
153 }
154
155 42 bool operator==(const Contact& other) const {
156
2/4
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 42 times.
✗ Branch 3 not taken.
42 return o1 == other.o1 && o2 == other.o2 && b1 == other.b1 &&
157
3/6
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 42 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 42 times.
✗ Branch 7 not taken.
42 b2 == other.b2 && normal == other.normal && pos == other.pos &&
158
1/2
✓ Branch 3 taken 42 times.
✗ Branch 4 not taken.
42 nearest_points[0] == other.nearest_points[0] &&
159
2/4
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
✓ Branch 5 taken 42 times.
✗ Branch 6 not taken.
126 nearest_points[1] == other.nearest_points[1] &&
160
1/2
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
84 penetration_depth == other.penetration_depth;
161 }
162
163 bool operator!=(const Contact& other) const { return !(*this == other); }
164
165 FCL_REAL getDistanceToCollision(const CollisionRequest& request) const;
166 };
167
168 struct QueryResult;
169
170 /// @brief base class for all query requests
171 struct HPP_FCL_DLLAPI QueryRequest {
172 // @brief Initial guess to use for the GJK algorithm
173 GJKInitialGuess gjk_initial_guess;
174
175 /// @brief whether enable gjk initial guess
176 /// @Deprecated Use gjk_initial_guess instead
177 HPP_FCL_DEPRECATED_MESSAGE(Use gjk_initial_guess instead)
178 bool enable_cached_gjk_guess;
179
180 /// @brief the gjk initial guess set by user
181 mutable Vec3f cached_gjk_guess;
182
183 /// @brief the support function initial guess set by user
184 mutable support_func_guess_t cached_support_func_guess;
185
186 /// @brief maximum iteration for the GJK algorithm
187 size_t gjk_max_iterations;
188
189 /// @brief tolerance for the GJK algorithm.
190 /// Note: This tolerance determines the precision on the estimated distance
191 /// between two geometries which are not in collision.
192 /// It is recommended to not set this tolerance to less than 1e-6.
193 FCL_REAL gjk_tolerance;
194
195 /// @brief whether to enable the Nesterov accleration of GJK
196 GJKVariant gjk_variant;
197
198 /// @brief convergence criterion used to stop GJK
199 GJKConvergenceCriterion gjk_convergence_criterion;
200
201 /// @brief convergence criterion used to stop GJK
202 GJKConvergenceCriterionType gjk_convergence_criterion_type;
203
204 /// @brief max number of iterations for EPA
205 size_t epa_max_iterations;
206
207 /// @brief tolerance for EPA.
208 /// Note: This tolerance determines the precision on the estimated distance
209 /// between two geometries which are in collision.
210 /// It is recommended to not set this tolerance to less than 1e-6.
211 /// Also, setting EPA's tolerance to less than GJK's is not recommended.
212 FCL_REAL epa_tolerance;
213
214 /// @brief enable timings when performing collision/distance request
215 bool enable_timings;
216
217 /// @brief threshold below which a collision is considered.
218 FCL_REAL collision_distance_threshold;
219
220 HPP_FCL_COMPILER_DIAGNOSTIC_PUSH
221 HPP_FCL_COMPILER_DIAGNOSTIC_IGNORED_DEPRECECATED_DECLARATIONS
222 /// @brief Default constructor.
223 99041 QueryRequest()
224 99041 : gjk_initial_guess(GJKInitialGuess::DefaultGuess),
225 99041 enable_cached_gjk_guess(false),
226
1/2
✓ Branch 1 taken 99041 times.
✗ Branch 2 not taken.
99041 cached_gjk_guess(1, 0, 0),
227
1/2
✓ Branch 2 taken 99041 times.
✗ Branch 3 not taken.
99041 cached_support_func_guess(support_func_guess_t::Zero()),
228 99041 gjk_max_iterations(GJK_DEFAULT_MAX_ITERATIONS),
229 99041 gjk_tolerance(GJK_DEFAULT_TOLERANCE),
230 99041 gjk_variant(GJKVariant::DefaultGJK),
231 99041 gjk_convergence_criterion(GJKConvergenceCriterion::Default),
232 99041 gjk_convergence_criterion_type(GJKConvergenceCriterionType::Relative),
233 99041 epa_max_iterations(EPA_DEFAULT_MAX_ITERATIONS),
234 99041 epa_tolerance(EPA_DEFAULT_TOLERANCE),
235 99041 enable_timings(false),
236 99041 collision_distance_threshold(
237 99041 Eigen::NumTraits<FCL_REAL>::dummy_precision()) {}
238
239 /// @brief Copy constructor.
240 103 QueryRequest(const QueryRequest& other) = default;
241
242 /// @brief Copy assignment operator.
243 7283 QueryRequest& operator=(const QueryRequest& other) = default;
244 HPP_FCL_COMPILER_DIAGNOSTIC_POP
245
246 /// @brief Updates the guess for the internal GJK algorithm in order to
247 /// warm-start it when reusing this collision request on the same collision
248 /// pair.
249 /// @note The option `gjk_initial_guess` must be set to
250 /// `GJKInitialGuess::CachedGuess` for this to work.
251 void updateGuess(const QueryResult& result) const;
252
253 /// @brief whether two QueryRequest are the same or not
254 28 inline bool operator==(const QueryRequest& other) const {
255 HPP_FCL_COMPILER_DIAGNOSTIC_PUSH
256 HPP_FCL_COMPILER_DIAGNOSTIC_IGNORED_DEPRECECATED_DECLARATIONS
257 56 return gjk_initial_guess == other.gjk_initial_guess &&
258
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 enable_cached_gjk_guess == other.enable_cached_gjk_guess &&
259
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 gjk_variant == other.gjk_variant &&
260
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 gjk_convergence_criterion == other.gjk_convergence_criterion &&
261 28 gjk_convergence_criterion_type ==
262
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 other.gjk_convergence_criterion_type &&
263
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 gjk_tolerance == other.gjk_tolerance &&
264
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 gjk_max_iterations == other.gjk_max_iterations &&
265
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 cached_gjk_guess == other.cached_gjk_guess &&
266
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 cached_support_func_guess == other.cached_support_func_guess &&
267
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 epa_max_iterations == other.epa_max_iterations &&
268
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 epa_tolerance == other.epa_tolerance &&
269
2/4
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
84 enable_timings == other.enable_timings &&
270
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
56 collision_distance_threshold == other.collision_distance_threshold;
271 HPP_FCL_COMPILER_DIAGNOSTIC_POP
272 }
273 };
274
275 /// @brief base class for all query results
276 struct HPP_FCL_DLLAPI QueryResult {
277 /// @brief stores the last GJK ray when relevant.
278 Vec3f cached_gjk_guess;
279
280 /// @brief stores the last support function vertex index, when relevant.
281 support_func_guess_t cached_support_func_guess;
282
283 /// @brief timings for the given request
284 CPUTimes timings;
285
286 29280354 QueryResult()
287
1/2
✓ Branch 2 taken 29280354 times.
✗ Branch 3 not taken.
29280354 : cached_gjk_guess(Vec3f::Zero()),
288
2/4
✓ Branch 1 taken 29280354 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 29280354 times.
✗ Branch 5 not taken.
29280354 cached_support_func_guess(support_func_guess_t::Constant(-1)) {}
289 };
290
291 1296063 inline void QueryRequest::updateGuess(const QueryResult& result) const {
292 HPP_FCL_COMPILER_DIAGNOSTIC_PUSH
293 HPP_FCL_COMPILER_DIAGNOSTIC_IGNORED_DEPRECECATED_DECLARATIONS
294
1/2
✓ Branch 0 taken 1296063 times.
✗ Branch 1 not taken.
1296063 if (gjk_initial_guess == GJKInitialGuess::CachedGuess ||
295
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1296059 times.
1296063 enable_cached_gjk_guess) {
296 4 cached_gjk_guess = result.cached_gjk_guess;
297 4 cached_support_func_guess = result.cached_support_func_guess;
298 }
299 HPP_FCL_COMPILER_DIAGNOSTIC_POP
300 1296063 }
301
302 struct CollisionResult;
303
304 /// @brief flag declaration for specifying required params in CollisionResult
305 enum CollisionRequestFlag {
306 CONTACT = 0x00001,
307 DISTANCE_LOWER_BOUND = 0x00002,
308 NO_REQUEST = 0x01000
309 };
310
311 /// @brief request to the collision algorithm
312 struct HPP_FCL_DLLAPI CollisionRequest : QueryRequest {
313 /// @brief The maximum number of contacts that can be returned
314 size_t num_max_contacts;
315
316 /// @brief whether the contact information (normal, penetration depth and
317 /// contact position) will return.
318 bool enable_contact;
319
320 /// Whether a lower bound on distance is returned when objects are disjoint
321 HPP_FCL_DEPRECATED_MESSAGE(A lower bound on distance is always computed.)
322 bool enable_distance_lower_bound;
323
324 /// @brief Distance below which objects are considered in collision.
325 /// See \ref hpp_fcl_collision_and_distance_lower_bound_computation
326 /// @note If set to -inf, the objects tested for collision are considered
327 /// as collision free and no test is actually performed by functions
328 /// hpp::fcl::collide of class hpp::fcl::ComputeCollision.
329 FCL_REAL security_margin;
330
331 /// @brief Distance below which bounding volumes are broken down.
332 /// See \ref hpp_fcl_collision_and_distance_lower_bound_computation
333 FCL_REAL break_distance;
334
335 /// @brief Distance above which GJK solver makes an early stopping.
336 /// GJK stops searching for the closest points when it proves that the
337 /// distance between two geometries is above this threshold.
338 ///
339 /// @remarks Consequently, the closest points might be incorrect, but allows
340 /// to save computational resources.
341 FCL_REAL distance_upper_bound;
342
343 /// @brief Constructor from a flag and a maximal number of contacts.
344 ///
345 /// @param[in] flag Collision request flag
346 /// @param[in] num_max_contacts Maximal number of allowed contacts
347 ///
348 HPP_FCL_COMPILER_DIAGNOSTIC_PUSH
349 HPP_FCL_COMPILER_DIAGNOSTIC_IGNORED_DEPRECECATED_DECLARATIONS
350 16809 CollisionRequest(const CollisionRequestFlag flag, size_t num_max_contacts_)
351 33618 : num_max_contacts(num_max_contacts_),
352 16809 enable_contact(flag & CONTACT),
353 16809 enable_distance_lower_bound(flag & DISTANCE_LOWER_BOUND),
354 16809 security_margin(0),
355 16809 break_distance(1e-3),
356 16809 distance_upper_bound((std::numeric_limits<FCL_REAL>::max)()) {}
357
358 /// @brief Default constructor.
359 50000 CollisionRequest()
360 100000 : num_max_contacts(1),
361 50000 enable_contact(true),
362 50000 enable_distance_lower_bound(false),
363 50000 security_margin(0),
364 50000 break_distance(1e-3),
365 50000 distance_upper_bound((std::numeric_limits<FCL_REAL>::max)()) {}
366 HPP_FCL_COMPILER_DIAGNOSTIC_POP
367
368 bool isSatisfied(const CollisionResult& result) const;
369
370 /// @brief whether two CollisionRequest are the same or not
371 14 inline bool operator==(const CollisionRequest& other) const {
372 HPP_FCL_COMPILER_DIAGNOSTIC_PUSH
373 HPP_FCL_COMPILER_DIAGNOSTIC_IGNORED_DEPRECECATED_DECLARATIONS
374 14 return QueryRequest::operator==(other) &&
375
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 num_max_contacts == other.num_max_contacts &&
376
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 enable_contact == other.enable_contact &&
377
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 enable_distance_lower_bound == other.enable_distance_lower_bound &&
378
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 security_margin == other.security_margin &&
379
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
42 break_distance == other.break_distance &&
380
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
28 distance_upper_bound == other.distance_upper_bound;
381 HPP_FCL_COMPILER_DIAGNOSTIC_POP
382 }
383 };
384
385 inline FCL_REAL Contact::getDistanceToCollision(
386 const CollisionRequest& request) const {
387 return penetration_depth - request.security_margin;
388 }
389
390 /// @brief collision result
391 struct HPP_FCL_DLLAPI CollisionResult : QueryResult {
392 private:
393 /// @brief contact information
394 std::vector<Contact> contacts;
395
396 public:
397 /// Lower bound on distance between objects if they are disjoint.
398 /// See \ref hpp_fcl_collision_and_distance_lower_bound_computation
399 /// @note Always computed. If \ref CollisionRequest::distance_upper_bound is
400 /// set to infinity, distance_lower_bound is the actual distance between the
401 /// shapes.
402 FCL_REAL distance_lower_bound;
403
404 /// @brief normal associated to nearest_points.
405 /// Same as `CollisionResult::nearest_points` but for the normal.
406 Vec3f normal;
407
408 /// @brief nearest points.
409 /// A `CollisionResult` can have multiple contacts.
410 /// The nearest points in `CollisionResults` correspond to the witness points
411 /// associated with the smallest distance i.e the `distance_lower_bound`.
412 /// For bounding volumes and BVHs, these nearest points are available
413 /// only when distance_lower_bound is inferior to
414 /// CollisionRequest::break_distance.
415 std::array<Vec3f, 2> nearest_points;
416
417 public:
418 66507 CollisionResult()
419
2/4
✓ Branch 4 taken 66507 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 66507 times.
✗ Branch 8 not taken.
66507 : distance_lower_bound((std::numeric_limits<FCL_REAL>::max)()) {
420 133014 nearest_points[0] = nearest_points[1] = normal =
421
4/8
✓ Branch 2 taken 66507 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 66507 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 66507 times.
✗ Branch 10 not taken.
✓ Branch 13 taken 66507 times.
✗ Branch 14 not taken.
66507 Vec3f::Constant(std::numeric_limits<FCL_REAL>::quiet_NaN());
422 66507 }
423
424 /// @brief Update the lower bound only if the distance is inferior.
425 8460475 inline void updateDistanceLowerBound(const FCL_REAL& distance_lower_bound_) {
426
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8460475 times.
8460475 if (distance_lower_bound_ < distance_lower_bound)
427 distance_lower_bound = distance_lower_bound_;
428 8460475 }
429
430 /// @brief add one contact into result structure
431 28921 inline void addContact(const Contact& c) { contacts.push_back(c); }
432
433 /// @brief whether two CollisionResult are the same or not
434 14 inline bool operator==(const CollisionResult& other) const {
435 14 return contacts == other.contacts &&
436
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 distance_lower_bound == other.distance_lower_bound &&
437
1/2
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
14 nearest_points[0] == other.nearest_points[0] &&
438
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 5 taken 14 times.
✗ Branch 6 not taken.
42 nearest_points[1] == other.nearest_points[1] &&
439
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
28 normal == other.normal;
440 }
441
442 /// @brief return binary collision result
443 23358993 bool isCollision() const { return contacts.size() > 0; }
444
445 /// @brief number of contacts found
446 8670957 size_t numContacts() const { return contacts.size(); }
447
448 /// @brief get the i-th contact calculated
449 7178 const Contact& getContact(size_t i) const {
450
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 7178 times.
7178 if (contacts.size() == 0)
451 HPP_FCL_THROW_PRETTY(
452 "The number of contacts is zero. No Contact can be returned.",
453 std::invalid_argument);
454
455
1/2
✓ Branch 1 taken 7178 times.
✗ Branch 2 not taken.
7178 if (i < contacts.size())
456 7178 return contacts[i];
457 else
458 return contacts.back();
459 }
460
461 /// @brief set the i-th contact calculated
462 16 void setContact(size_t i, const Contact& c) {
463
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
16 if (contacts.size() == 0)
464 HPP_FCL_THROW_PRETTY(
465 "The number of contacts is zero. No Contact can be returned.",
466 std::invalid_argument);
467
468
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 if (i < contacts.size())
469 16 contacts[i] = c;
470 else
471 contacts.back() = c;
472 16 }
473
474 /// @brief get all the contacts
475 void getContacts(std::vector<Contact>& contacts_) const {
476 contacts_.resize(contacts.size());
477 std::copy(contacts.begin(), contacts.end(), contacts_.begin());
478 }
479
480 7 const std::vector<Contact>& getContacts() const { return contacts; }
481
482 /// @brief clear the results obtained
483 32586 void clear() {
484 32586 distance_lower_bound = (std::numeric_limits<FCL_REAL>::max)();
485 32586 contacts.clear();
486 32586 timings.clear();
487 65172 nearest_points[0] = nearest_points[1] = normal =
488
4/8
✓ Branch 2 taken 32586 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 32586 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 32586 times.
✗ Branch 10 not taken.
✓ Branch 13 taken 32586 times.
✗ Branch 14 not taken.
32586 Vec3f::Constant(std::numeric_limits<FCL_REAL>::quiet_NaN());
489 32586 }
490
491 /// @brief reposition Contact objects when fcl inverts them
492 /// during their construction.
493 void swapObjects();
494 };
495
496 /// @brief This structure allows to encode contact patches.
497 /// A contact patch is defined by a set of points belonging to a subset of a
498 /// plane passing by `p` and supported by `n`, where `n = Contact::normal` and
499 /// `p = Contact::pos`. If we denote by P this plane and by S1 and S2 the first
500 /// and second shape of a collision pair, a contact patch is represented as a
501 /// polytope which vertices all belong to `P & S1 & S2`, where `&` denotes the
502 /// set-intersection. Since a contact patch is a subset of a plane supported by
503 /// `n`, it has a preferred direction. In HPP-FCL, the `Contact::normal` points
504 /// from S1 to S2. In the same way, a contact patch points by default from S1
505 /// to S2.
506 ///
507 /// @note For now (April 2024), a `ContactPatch` is a polygon (2D polytope),
508 /// so the points of the set, forming the convex-hull of the polytope, are
509 /// stored in a counter-clockwise fashion.
510 /// @note If needed, the internal algorithms of hpp-fcl can easily be extended
511 /// to compute a contact volume instead of a contact patch.
512 struct HPP_FCL_DLLAPI ContactPatch {
513 public:
514 using Polygon = std::vector<Vec2f>;
515
516 /// @brief Frame of the set, expressed in the world coordinates.
517 /// The z-axis of the frame's rotation is the contact patch normal.
518 Transform3f tf;
519
520 /// @brief Direction of ContactPatch.
521 /// When doing collision detection, the convention of HPP-FCL is that the
522 /// normal always points from the first to the second shape of the collision
523 /// pair i.e. from shape1 to shape2 when calling `collide(shape1, shape2)`.
524 /// The PatchDirection enum allows to identify if the patch points from
525 /// shape 1 to shape 2 (Default type) or from shape 2 to shape 1 (Inverted
526 /// type). The Inverted type should only be used for internal HPP-FCL
527 /// computations (it allows to properly define two separate contact patches in
528 /// the same frame).
529 enum PatchDirection { DEFAULT = 0, INVERTED = 1 };
530
531 /// @brief Direction of this contact patch.
532 PatchDirection direction;
533
534 /// @brief Penetration depth of the contact patch. This value corresponds to
535 /// the signed distance `d` between the shapes.
536 /// @note For each contact point `p` in the patch of normal `n`, `p1 = p -
537 /// 0.5*d*n` and `p2 = p + 0.5*d*n` define a pair of witness points. `p1`
538 /// belongs to the surface of the first shape and `p2` belongs to the surface
539 /// of the second shape. For any pair of witness points, we always have `p2 -
540 /// p1 = d * n`. The vector `d * n` is called a minimum separation vector:
541 /// if S1 is translated by it, S1 and S2 are not in collision anymore.
542 /// @note Although there may exist multiple minimum separation vectors between
543 /// two shapes, the term "minimum" comes from the fact that it's impossible to
544 /// find a different separation vector which has a smaller norm than `d * n`.
545 FCL_REAL penetration_depth;
546
547 /// @brief Default maximum size of the polygon representing the contact patch.
548 /// Used to pre-allocate memory for the patch.
549 static constexpr size_t default_preallocated_size = 12;
550
551 protected:
552 /// @brief Container for the vertices of the set.
553 Polygon m_points;
554
555 public:
556 /// @brief Default constructor.
557 /// Note: the preallocated size does not determine the maximum number of
558 /// points in the patch, it only serves as preallocation if the maximum size
559 /// of the patch is known in advance. HPP-FCL will automatically expand/shrink
560 /// the contact patch if needed.
561 124 explicit ContactPatch(size_t preallocated_size = default_preallocated_size)
562 124 : tf(Transform3f::Identity()),
563 124 direction(PatchDirection::DEFAULT),
564 124 penetration_depth(0) {
565
1/2
✓ Branch 1 taken 124 times.
✗ Branch 2 not taken.
124 this->m_points.reserve(preallocated_size);
566 124 }
567
568 /// @brief Normal of the contact patch, expressed in the WORLD frame.
569 84 Vec3f getNormal() const {
570
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 61 times.
84 if (this->direction == PatchDirection::INVERTED) {
571
2/4
✓ Branch 3 taken 23 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
46 return -this->tf.rotation().col(2);
572 }
573
1/2
✓ Branch 3 taken 61 times.
✗ Branch 4 not taken.
122 return this->tf.rotation().col(2);
574 }
575
576 /// @brief Returns the number of points in the contact patch.
577 890 size_t size() const { return this->m_points.size(); }
578
579 /// @brief Add a 3D point to the set, expressed in the world frame.
580 /// @note This function takes a 3D point and expresses it in the local frame
581 /// of the set. It then takes only the x and y components of the vector,
582 /// effectively doing a projection onto the plane to which the set belongs.
583 /// TODO(louis): if necessary, we can store the offset to the plane (x, y).
584 119 void addPoint(const Vec3f& point_3d) {
585
1/2
✓ Branch 1 taken 119 times.
✗ Branch 2 not taken.
119 const Vec3f point = this->tf.inverseTransform(point_3d);
586
2/4
✓ Branch 1 taken 119 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 119 times.
✗ Branch 5 not taken.
119 this->m_points.emplace_back(point.template head<2>());
587 119 }
588
589 /// @brief Get the i-th point of the set, expressed in the 3D world frame.
590 644 Vec3f getPoint(const size_t i) const {
591
1/2
✓ Branch 1 taken 644 times.
✗ Branch 2 not taken.
644 Vec3f point(0, 0, 0);
592
1/2
✓ Branch 3 taken 644 times.
✗ Branch 4 not taken.
644 point.head<2>() = this->point(i);
593 644 point = tf.transform(point);
594 644 return point;
595 }
596
597 /// @brief Get the i-th point of the contact patch, projected back onto the
598 /// first shape of the collision pair. This point is expressed in the 3D
599 /// world frame.
600 Vec3f getPointShape1(const size_t i) const {
601 Vec3f point = this->getPoint(i);
602 point -= (this->penetration_depth / 2) * this->getNormal();
603 return point;
604 }
605
606 /// @brief Get the i-th point of the contact patch, projected back onto the
607 /// first shape of the collision pair. This 3D point is expressed in the world
608 /// frame.
609 Vec3f getPointShape2(const size_t i) const {
610 Vec3f point = this->getPoint(i);
611 point += (this->penetration_depth / 2) * this->getNormal();
612 return point;
613 }
614
615 /// @brief Getter for the 2D points in the set.
616 269 Polygon& points() { return this->m_points; }
617
618 /// @brief Const getter for the 2D points in the set.
619 80 const Polygon& points() const { return this->m_points; }
620
621 /// @brief Getter for the i-th 2D point in the set.
622 Vec2f& point(const size_t i) {
623 HPP_FCL_ASSERT(this->m_points.size() > 0, "Patch is empty.",
624 std::logic_error);
625 if (i < this->m_points.size()) {
626 return this->m_points[i];
627 }
628 return this->m_points.back();
629 }
630
631 /// @brief Const getter for the i-th 2D point in the set.
632 644 const Vec2f& point(const size_t i) const {
633
1/2
✓ Branch 3 taken 644 times.
✗ Branch 4 not taken.
644 HPP_FCL_ASSERT(this->m_points.size() > 0, "Patch is empty.",
634 std::logic_error);
635
1/2
✓ Branch 1 taken 644 times.
✗ Branch 2 not taken.
644 if (i < this->m_points.size()) {
636 644 return this->m_points[i];
637 }
638 return this->m_points.back();
639 }
640
641 /// @brief Clear the set.
642 125 void clear() {
643 125 this->m_points.clear();
644 125 this->tf.setIdentity();
645 125 this->penetration_depth = 0;
646 125 }
647
648 /// @brief Whether two contact patches are the same or not.
649 /// @note This compares the two sets terms by terms.
650 /// However, two contact patches can be identical, but have a different
651 /// order for their points. Use `isEqual` in this case.
652 28 bool operator==(const ContactPatch& other) const {
653
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
56 return this->tf == other.tf && this->direction == other.direction &&
654
2/4
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
84 this->penetration_depth == other.penetration_depth &&
655
1/2
✓ Branch 3 taken 28 times.
✗ Branch 4 not taken.
56 this->points() == other.points();
656 }
657
658 /// @brief Whether two contact patches are the same or not.
659 /// Checks for different order of the points.
660 22 bool isSame(const ContactPatch& other,
661 const FCL_REAL tol =
662 Eigen::NumTraits<FCL_REAL>::dummy_precision()) const {
663 // The x and y axis of the set are arbitrary, but the z axis is
664 // always the normal. The position of the origin of the frame is also
665 // arbitrary. So we only check if the normals are the same.
666
3/6
✓ Branch 2 taken 22 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 22 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 22 times.
22 if (!this->getNormal().isApprox(other.getNormal(), tol)) {
667 return false;
668 }
669
670
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
22 if (std::abs(this->penetration_depth - other.penetration_depth) > tol) {
671 return false;
672 }
673
674
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (this->direction != other.direction) {
675 return false;
676 }
677
678
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 22 times.
22 if (this->size() != other.size()) {
679 return false;
680 }
681
682 // Check all points of the contact patch.
683
2/2
✓ Branch 1 taken 79 times.
✓ Branch 2 taken 22 times.
101 for (size_t i = 0; i < this->size(); ++i) {
684 79 bool found = false;
685
1/2
✓ Branch 1 taken 79 times.
✗ Branch 2 not taken.
79 const Vec3f pi = this->getPoint(i);
686
2/2
✓ Branch 1 taken 559 times.
✓ Branch 2 taken 79 times.
638 for (size_t j = 0; j < other.size(); ++j) {
687
1/2
✓ Branch 1 taken 559 times.
✗ Branch 2 not taken.
559 const Vec3f other_pj = other.getPoint(j);
688
3/4
✓ Branch 1 taken 559 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 79 times.
✓ Branch 4 taken 480 times.
559 if (pi.isApprox(other_pj, tol)) {
689 79 found = true;
690 }
691 }
692
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 79 times.
79 if (!found) {
693 return false;
694 }
695 }
696
697 22 return true;
698 }
699 };
700
701 /// @brief Construct a frame from a `Contact`'s position and normal.
702 /// Because both `Contact`'s position and normal are expressed in the world
703 /// frame, this frame is also expressed w.r.t the world frame.
704 /// The origin of the frame is `contact.pos` and the z-axis of the frame is
705 /// `contact.normal`.
706 24 inline void constructContactPatchFrameFromContact(const Contact& contact,
707 ContactPatch& contact_patch) {
708 24 contact_patch.penetration_depth = contact.penetration_depth;
709 24 contact_patch.tf.translation() = contact.pos;
710 24 contact_patch.tf.rotation() =
711 48 constructOrthonormalBasisFromVector(contact.normal);
712 24 contact_patch.direction = ContactPatch::PatchDirection::DEFAULT;
713 24 }
714
715 /// @brief Structure used for internal computations. A support set and a
716 /// contact patch can be represented by the same structure. In fact, a contact
717 /// patch is the intersection of two support sets, one with
718 /// `PatchDirection::DEFAULT` and one with `PatchDirection::INVERTED`.
719 /// @note A support set with `DEFAULT` direction is the support set of a shape
720 /// in the direction given by `+n`, where `n` is the z-axis of the frame's
721 /// patch rotation. An `INVERTED` support set is the support set of a shape in
722 /// the direction `-n`.
723 using SupportSet = ContactPatch;
724
725 /// @brief Request for a contact patch computation.
726 struct HPP_FCL_DLLAPI ContactPatchRequest {
727 /// @brief Maximum number of contact patches that will be computed.
728 size_t max_num_patch;
729
730 protected:
731 /// @brief Maximum samples to compute the support sets of curved shapes,
732 /// i.e. when the normal is perpendicular to the base of a cylinder. For
733 /// now, only relevant for Cone and Cylinder. In the future this might be
734 /// extended to Sphere and Ellipsoid.
735 size_t m_num_samples_curved_shapes;
736
737 /// @brief Tolerance below which points are added to a contact patch.
738 /// In details, given two shapes S1 and S2, a contact patch is the triple
739 /// intersection between the separating plane (P) (passing by `Contact::pos`
740 /// and supported by `Contact::normal`), S1 and S2; i.e. a contact patch is
741 /// `P & S1 & S2` if we denote `&` the set intersection operator. If a point
742 /// p1 of S1 is at a distance below `patch_tolerance` from the separating
743 /// plane, it is taken into account in the computation of the contact patch.
744 /// Otherwise, it is not used for the computation.
745 /// @note Needs to be positive.
746 FCL_REAL m_patch_tolerance;
747
748 public:
749 /// @brief Default constructor.
750 /// @param max_num_patch maximum number of contact patches per collision pair.
751 /// @param max_sub_patch_size maximum size of each sub contact patch. Each
752 /// contact patch contains an internal representation for an inscribed sub
753 /// contact patch. This allows physics simulation to always work with a
754 /// predetermined maximum size for each contact patch. A sub contact patch is
755 /// simply a subset of the vertices of a contact patch.
756 /// @param num_samples_curved_shapes for shapes like cones and cylinders,
757 /// which have smooth basis (circles in this case), we need to sample a
758 /// certain amount of point of this basis.
759 /// @param patch_tolerance the tolerance below which a point of a shape is
760 /// considered to belong to the support set of this shape in the direction of
761 /// the normal. Said otherwise, `patch_tolerance` determines the "thickness"
762 /// of the separating plane between shapes of a collision pair.
763 24 explicit ContactPatchRequest(size_t max_num_patch = 1,
764 size_t num_samples_curved_shapes =
765 ContactPatch::default_preallocated_size,
766 FCL_REAL patch_tolerance = 1e-3)
767 24 : max_num_patch(max_num_patch) {
768 24 this->setNumSamplesCurvedShapes(num_samples_curved_shapes);
769 24 this->setPatchTolerance(patch_tolerance);
770 24 }
771
772 /// @brief Construct a contact patch request from a collision request.
773 explicit ContactPatchRequest(const CollisionRequest& collision_request,
774 size_t num_samples_curved_shapes =
775 ContactPatch::default_preallocated_size,
776 FCL_REAL patch_tolerance = 1e-3)
777 : max_num_patch(collision_request.num_max_contacts) {
778 this->setNumSamplesCurvedShapes(num_samples_curved_shapes);
779 this->setPatchTolerance(patch_tolerance);
780 }
781
782 /// @copydoc m_num_samples_curved_shapes
783 31 void setNumSamplesCurvedShapes(const size_t num_samples_curved_shapes) {
784
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
31 if (num_samples_curved_shapes < 3) {
785 HPP_FCL_LOG_WARNING(
786 "`num_samples_curved_shapes` cannot be lower than 3. Setting it to "
787 "3 to prevent bugs.");
788 this->m_num_samples_curved_shapes = 3;
789 } else {
790 31 this->m_num_samples_curved_shapes = num_samples_curved_shapes;
791 }
792 31 }
793
794 /// @copydoc m_num_samples_curved_shapes
795 190 size_t getNumSamplesCurvedShapes() const {
796 190 return this->m_num_samples_curved_shapes;
797 }
798
799 /// @copydoc m_patch_tolerance
800 31 void setPatchTolerance(const FCL_REAL patch_tolerance) {
801
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
31 if (patch_tolerance < 0) {
802 HPP_FCL_LOG_WARNING(
803 "`patch_tolerance` cannot be negative. Setting it to 0 to prevent "
804 "bugs.");
805 this->m_patch_tolerance = Eigen::NumTraits<FCL_REAL>::dummy_precision();
806 } else {
807 31 this->m_patch_tolerance = patch_tolerance;
808 }
809 31 }
810
811 /// @copydoc m_patch_tolerance
812 66 FCL_REAL getPatchTolerance() const { return this->m_patch_tolerance; }
813
814 /// @brief Whether two ContactPatchRequest are identical or not.
815 14 bool operator==(const ContactPatchRequest& other) const {
816
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 return this->max_num_patch == other.max_num_patch &&
817 14 this->getNumSamplesCurvedShapes() ==
818
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
42 other.getNumSamplesCurvedShapes() &&
819 28 this->getPatchTolerance() == other.getPatchTolerance();
820 }
821 };
822
823 /// @brief Result for a contact patch computation.
824 struct HPP_FCL_DLLAPI ContactPatchResult {
825 using ContactPatchVector = std::vector<ContactPatch>;
826 using ContactPatchRef = std::reference_wrapper<ContactPatch>;
827 using ContactPatchRefVector = std::vector<ContactPatchRef>;
828
829 protected:
830 /// @brief Data container for the vector of contact patches.
831 /// @note Contrary to `CollisionResult` or `DistanceResult`, which have a
832 /// very small memory footprint, contact patches can contain relatively
833 /// large polytopes. In order to reuse a `ContactPatchResult` while avoiding
834 /// successive mallocs, we have a data container and a vector which points
835 /// to the currently active patches in this data container.
836 ContactPatchVector m_contact_patches_data;
837
838 /// @brief Contact patches in `m_contact_patches_data` can have two
839 /// statuses: used or unused. This index tracks the first unused patch in
840 /// the `m_contact_patches_data` vector.
841 size_t m_id_available_patch;
842
843 /// @brief Vector of contact patches of the result.
844 ContactPatchRefVector m_contact_patches;
845
846 public:
847 /// @brief Default constructor.
848 2 ContactPatchResult() : m_id_available_patch(0) {
849 2 const size_t max_num_patch = 1;
850
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 const ContactPatchRequest request(max_num_patch);
851
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 this->set(request);
852 2 }
853
854 /// @brief Constructor using a `ContactPatchRequest`.
855 16 explicit ContactPatchResult(const ContactPatchRequest& request)
856 16 : m_id_available_patch(0) {
857
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 this->set(request);
858 16 };
859
860 /// @brief Number of contact patches in the result.
861 119 size_t numContactPatches() const { return this->m_contact_patches.size(); }
862
863 /// @brief Returns a new unused contact patch from the internal data vector.
864 31 ContactPatchRef getUnusedContactPatch() {
865
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 31 times.
31 if (this->m_id_available_patch >= this->m_contact_patches_data.size()) {
866 HPP_FCL_LOG_WARNING(
867 "Trying to get an unused contact patch but all contact patches are "
868 "used. Increasing size of contact patches vector, at the cost of a "
869 "copy. You should increase `max_num_patch` in the "
870 "`ContactPatchRequest`.");
871 this->m_contact_patches_data.emplace_back(
872 this->m_contact_patches_data.back());
873 this->m_contact_patches_data.back().clear();
874 }
875 ContactPatch& contact_patch =
876 31 this->m_contact_patches_data[this->m_id_available_patch];
877 31 contact_patch.clear();
878 31 this->m_contact_patches.emplace_back(contact_patch);
879 31 ++(this->m_id_available_patch);
880 31 return this->m_contact_patches.back();
881 }
882
883 /// @brief Const getter for the i-th contact patch of the result.
884 59 const ContactPatch& getContactPatch(const size_t i) const {
885
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 59 times.
59 if (this->m_contact_patches.empty()) {
886 HPP_FCL_THROW_PRETTY(
887 "The number of contact patches is zero. No ContactPatch can be "
888 "returned.",
889 std::invalid_argument);
890 }
891
1/2
✓ Branch 1 taken 59 times.
✗ Branch 2 not taken.
59 if (i < this->m_contact_patches.size()) {
892 59 return this->m_contact_patches[i];
893 }
894 return this->m_contact_patches.back();
895 }
896
897 /// @brief Getter for the i-th contact patch of the result.
898 ContactPatch& contactPatch(const size_t i) {
899 if (this->m_contact_patches.empty()) {
900 HPP_FCL_THROW_PRETTY(
901 "The number of contact patches is zero. No ContactPatch can be "
902 "returned.",
903 std::invalid_argument);
904 }
905 if (i < this->m_contact_patches.size()) {
906 return this->m_contact_patches[i];
907 }
908 return this->m_contact_patches.back();
909 }
910
911 /// @brief Clears the contact patch result.
912 61 void clear() {
913 61 this->m_contact_patches.clear();
914 61 this->m_id_available_patch = 0;
915
2/2
✓ Branch 5 taken 61 times.
✓ Branch 6 taken 61 times.
122 for (ContactPatch& patch : this->m_contact_patches_data) {
916
1/2
✓ Branch 1 taken 61 times.
✗ Branch 2 not taken.
61 patch.clear();
917 }
918 61 }
919
920 /// @brief Set up a `ContactPatchResult` from a `ContactPatchRequest`
921 49 void set(const ContactPatchRequest& request) {
922
2/2
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 31 times.
49 if (this->m_contact_patches_data.size() < request.max_num_patch) {
923 18 this->m_contact_patches_data.resize(request.max_num_patch);
924 }
925
2/2
✓ Branch 5 taken 49 times.
✓ Branch 6 taken 49 times.
98 for (ContactPatch& patch : this->m_contact_patches_data) {
926
1/2
✓ Branch 3 taken 49 times.
✗ Branch 4 not taken.
49 patch.points().reserve(request.getNumSamplesCurvedShapes());
927 }
928 49 this->clear();
929 49 }
930
931 /// @brief Return true if this `ContactPatchResult` is aligned with the
932 /// `ContactPatchRequest` given as input.
933 24 bool check(const ContactPatchRequest& request) const {
934
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
24 assert(this->m_contact_patches_data.size() >= request.max_num_patch);
935
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
24 if (this->m_contact_patches_data.size() < request.max_num_patch) {
936 return false;
937 }
938
939
2/2
✓ Branch 5 taken 24 times.
✓ Branch 6 taken 24 times.
48 for (const ContactPatch& patch : this->m_contact_patches_data) {
940
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 24 times.
24 if (patch.points().capacity() < request.getNumSamplesCurvedShapes()) {
941 assert(patch.points().capacity() >=
942 request.getNumSamplesCurvedShapes());
943 return false;
944 }
945 }
946 24 return true;
947 }
948
949 /// @brief Whether two ContactPatchResult are identical or not.
950 14 bool operator==(const ContactPatchResult& other) const {
951
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
14 if (this->numContactPatches() != other.numContactPatches()) {
952 return false;
953 }
954
955
2/2
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 14 times.
28 for (size_t i = 0; i < this->numContactPatches(); ++i) {
956 14 const ContactPatch& patch = this->getContactPatch(i);
957 14 const ContactPatch& other_patch = other.getContactPatch(i);
958
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
14 if (!(patch == other_patch)) {
959 return false;
960 }
961 }
962
963 14 return true;
964 }
965
966 /// @brief Repositions the ContactPatch when they get inverted during their
967 /// construction.
968 void swapObjects() {
969 // Create new transform: it's the reflection of `tf` along the z-axis.
970 // This corresponds to doing a PI rotation along the y-axis, i.e. the x-axis
971 // becomes -x-axis, y-axis stays the same, z-axis becomes -z-axis.
972 for (size_t i = 0; i < this->numContactPatches(); ++i) {
973 ContactPatch& patch = this->contactPatch(i);
974 patch.tf.rotation().col(0) *= -1.0;
975 patch.tf.rotation().col(2) *= -1.0;
976
977 for (size_t j = 0; j < patch.size(); ++j) {
978 patch.point(i)(0) *= -1.0; // only invert the x-axis
979 }
980 }
981 }
982 };
983
984 struct DistanceResult;
985
986 /// @brief request to the distance computation
987 struct HPP_FCL_DLLAPI DistanceRequest : QueryRequest {
988 /// @brief whether to return the nearest points.
989 /// Nearest points are always computed and are the points of the shapes that
990 /// achieve a distance of `DistanceResult::min_distance`.
991 HPP_FCL_DEPRECATED_MESSAGE(
992 Nearest points are always computed : they are the points of the shapes
993 that achieve a distance of
994 `DistanceResult::min_distance`
995 .\n Use `enable_signed_distance` if you want to compute a signed
996 minimum distance(and thus its corresponding nearest points)
997 .)
998 bool enable_nearest_points;
999
1000 /// @brief whether to compute the penetration depth when objects are in
1001 /// collision.
1002 /// Turning this off can save computation time if only the distance
1003 /// when objects are disjoint is needed.
1004 /// @note The minimum distance between the shapes is stored in
1005 /// `DistanceResult::min_distance`.
1006 /// If `enable_signed_distance` is off, `DistanceResult::min_distance`
1007 /// is always positive.
1008 /// If `enable_signed_distance` is on, `DistanceResult::min_distance`
1009 /// can be positive or negative.
1010 /// The nearest points are the points of the shapes that achieve
1011 /// a distance of `DistanceResult::min_distance`.
1012 bool enable_signed_distance;
1013
1014 /// @brief error threshold for approximate distance
1015 FCL_REAL rel_err; // relative error, between 0 and 1
1016 FCL_REAL abs_err; // absolute error
1017
1018 /// \param enable_nearest_points_ enables the nearest points computation.
1019 /// \param enable_signed_distance_ allows to compute the penetration depth
1020 /// \param rel_err_
1021 /// \param abs_err_
1022 HPP_FCL_COMPILER_DIAGNOSTIC_PUSH
1023 HPP_FCL_COMPILER_DIAGNOSTIC_IGNORED_DEPRECECATED_DECLARATIONS
1024 32232 DistanceRequest(bool enable_nearest_points_ = true,
1025 bool enable_signed_distance_ = true, FCL_REAL rel_err_ = 0.0,
1026 FCL_REAL abs_err_ = 0.0)
1027 64464 : enable_nearest_points(enable_nearest_points_),
1028 32232 enable_signed_distance(enable_signed_distance_),
1029 32232 rel_err(rel_err_),
1030 32232 abs_err(abs_err_) {}
1031 HPP_FCL_COMPILER_DIAGNOSTIC_POP
1032
1033 bool isSatisfied(const DistanceResult& result) const;
1034
1035 HPP_FCL_COMPILER_DIAGNOSTIC_PUSH
1036 HPP_FCL_COMPILER_DIAGNOSTIC_IGNORED_DEPRECECATED_DECLARATIONS
1037 7283 DistanceRequest& operator=(const DistanceRequest& other) = default;
1038 HPP_FCL_COMPILER_DIAGNOSTIC_POP
1039
1040 /// @brief whether two DistanceRequest are the same or not
1041 14 inline bool operator==(const DistanceRequest& other) const {
1042 HPP_FCL_COMPILER_DIAGNOSTIC_PUSH
1043 HPP_FCL_COMPILER_DIAGNOSTIC_IGNORED_DEPRECECATED_DECLARATIONS
1044 14 return QueryRequest::operator==(other) &&
1045
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 enable_nearest_points == other.enable_nearest_points &&
1046
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 enable_signed_distance == other.enable_signed_distance &&
1047
3/6
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
28 rel_err == other.rel_err && abs_err == other.abs_err;
1048 HPP_FCL_COMPILER_DIAGNOSTIC_POP
1049 }
1050 };
1051
1052 /// @brief distance result
1053 struct HPP_FCL_DLLAPI DistanceResult : QueryResult {
1054 public:
1055 /// @brief minimum distance between two objects.
1056 /// If two objects are in collision and
1057 /// DistanceRequest::enable_signed_distance is activated, min_distance <= 0.
1058 /// @note The nearest points are the points of the shapes that achieve a
1059 /// distance of `DistanceResult::min_distance`.
1060 FCL_REAL min_distance;
1061
1062 /// @brief normal.
1063 Vec3f normal;
1064
1065 /// @brief nearest points.
1066 /// See CollisionResult::nearest_points.
1067 std::array<Vec3f, 2> nearest_points;
1068
1069 /// @brief collision object 1
1070 const CollisionGeometry* o1;
1071
1072 /// @brief collision object 2
1073 const CollisionGeometry* o2;
1074
1075 /// @brief information about the nearest point in object 1
1076 /// if object 1 is mesh or point cloud, it is the triangle or point id
1077 /// if object 1 is geometry shape, it is NONE (-1),
1078 /// if object 1 is octree, it is the id of the cell
1079 int b1;
1080
1081 /// @brief information about the nearest point in object 2
1082 /// if object 2 is mesh or point cloud, it is the triangle or point id
1083 /// if object 2 is geometry shape, it is NONE (-1),
1084 /// if object 2 is octree, it is the id of the cell
1085 int b2;
1086
1087 /// @brief invalid contact primitive information
1088 static const int NONE = -1;
1089
1090 29213847 DistanceResult(
1091 FCL_REAL min_distance_ = (std::numeric_limits<FCL_REAL>::max)())
1092 29213847 : min_distance(min_distance_), o1(NULL), o2(NULL), b1(NONE), b2(NONE) {
1093 const Vec3f nan(
1094
2/4
✓ Branch 2 taken 29213847 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 29213847 times.
✗ Branch 6 not taken.
29213847 Vec3f::Constant(std::numeric_limits<FCL_REAL>::quiet_NaN()));
1095
3/6
✓ Branch 1 taken 29213847 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 29213847 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 29213847 times.
✗ Branch 10 not taken.
29213847 nearest_points[0] = nearest_points[1] = normal = nan;
1096 29213847 }
1097
1098 /// @brief add distance information into the result
1099 void update(FCL_REAL distance, const CollisionGeometry* o1_,
1100 const CollisionGeometry* o2_, int b1_, int b2_) {
1101 if (min_distance > distance) {
1102 min_distance = distance;
1103 o1 = o1_;
1104 o2 = o2_;
1105 b1 = b1_;
1106 b2 = b2_;
1107 }
1108 }
1109
1110 /// @brief add distance information into the result
1111 355354 void update(FCL_REAL distance, const CollisionGeometry* o1_,
1112 const CollisionGeometry* o2_, int b1_, int b2_, const Vec3f& p1,
1113 const Vec3f& p2, const Vec3f& normal_) {
1114
2/2
✓ Branch 0 taken 14617 times.
✓ Branch 1 taken 340737 times.
355354 if (min_distance > distance) {
1115 14617 min_distance = distance;
1116 14617 o1 = o1_;
1117 14617 o2 = o2_;
1118 14617 b1 = b1_;
1119 14617 b2 = b2_;
1120 14617 nearest_points[0] = p1;
1121 14617 nearest_points[1] = p2;
1122 14617 normal = normal_;
1123 }
1124 355354 }
1125
1126 /// @brief add distance information into the result
1127 void update(const DistanceResult& other_result) {
1128 if (min_distance > other_result.min_distance) {
1129 min_distance = other_result.min_distance;
1130 o1 = other_result.o1;
1131 o2 = other_result.o2;
1132 b1 = other_result.b1;
1133 b2 = other_result.b2;
1134 nearest_points[0] = other_result.nearest_points[0];
1135 nearest_points[1] = other_result.nearest_points[1];
1136 normal = other_result.normal;
1137 }
1138 }
1139
1140 /// @brief clear the result
1141 6361 void clear() {
1142 const Vec3f nan(
1143
2/4
✓ Branch 2 taken 6361 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 6361 times.
✗ Branch 6 not taken.
6361 Vec3f::Constant(std::numeric_limits<FCL_REAL>::quiet_NaN()));
1144 6361 min_distance = (std::numeric_limits<FCL_REAL>::max)();
1145 6361 o1 = NULL;
1146 6361 o2 = NULL;
1147 6361 b1 = NONE;
1148 6361 b2 = NONE;
1149
3/6
✓ Branch 1 taken 6361 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 6361 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 6361 times.
✗ Branch 10 not taken.
6361 nearest_points[0] = nearest_points[1] = normal = nan;
1150 6361 timings.clear();
1151 6361 }
1152
1153 /// @brief whether two DistanceResult are the same or not
1154 14 inline bool operator==(const DistanceResult& other) const {
1155 28 bool is_same = min_distance == other.min_distance &&
1156
1/2
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
14 nearest_points[0] == other.nearest_points[0] &&
1157
1/2
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
14 nearest_points[1] == other.nearest_points[1] &&
1158
3/6
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 14 times.
✗ Branch 6 not taken.
14 normal == other.normal && o1 == other.o1 && o2 == other.o2 &&
1159
3/6
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
28 b1 == other.b1 && b2 == other.b2;
1160
1161 // TODO: check also that two GeometryObject are indeed equal.
1162
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if ((o1 != NULL) ^ (other.o1 != NULL)) return false;
1163 14 is_same &= (o1 == other.o1);
1164 // else if (o1 != NULL and other.o1 != NULL) is_same &= *o1 ==
1165 // *other.o1;
1166
1167
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if ((o2 != NULL) ^ (other.o2 != NULL)) return false;
1168 14 is_same &= (o2 == other.o2);
1169 // else if (o2 != NULL and other.o2 != NULL) is_same &= *o2 ==
1170 // *other.o2;
1171
1172 14 return is_same;
1173 }
1174 };
1175
1176 namespace internal {
1177 155533 inline void updateDistanceLowerBoundFromBV(const CollisionRequest& /*req*/,
1178 CollisionResult& res,
1179 const FCL_REAL sqrDistLowerBound) {
1180 // BV cannot find negative distance.
1181
2/2
✓ Branch 0 taken 51323 times.
✓ Branch 1 taken 104210 times.
155533 if (res.distance_lower_bound <= 0) return;
1182 104210 FCL_REAL new_dlb = std::sqrt(sqrDistLowerBound); // - req.security_margin;
1183
2/2
✓ Branch 0 taken 11187 times.
✓ Branch 1 taken 93023 times.
104210 if (new_dlb < res.distance_lower_bound) res.distance_lower_bound = new_dlb;
1184 }
1185
1186 30452457 inline void updateDistanceLowerBoundFromLeaf(const CollisionRequest&,
1187 CollisionResult& res,
1188 const FCL_REAL& distance,
1189 const Vec3f& p0, const Vec3f& p1,
1190 const Vec3f& normal) {
1191
2/2
✓ Branch 0 taken 55836 times.
✓ Branch 1 taken 30396621 times.
30452457 if (distance < res.distance_lower_bound) {
1192 55836 res.distance_lower_bound = distance;
1193 55836 res.nearest_points[0] = p0;
1194 55836 res.nearest_points[1] = p1;
1195 55836 res.normal = normal;
1196 }
1197 30452457 }
1198 } // namespace internal
1199
1200 inline CollisionRequestFlag operator~(CollisionRequestFlag a) {
1201 return static_cast<CollisionRequestFlag>(~static_cast<int>(a));
1202 }
1203
1204 1007 inline CollisionRequestFlag operator|(CollisionRequestFlag a,
1205 CollisionRequestFlag b) {
1206 1007 return static_cast<CollisionRequestFlag>(static_cast<int>(a) |
1207 1007 static_cast<int>(b));
1208 }
1209
1210 inline CollisionRequestFlag operator&(CollisionRequestFlag a,
1211 CollisionRequestFlag b) {
1212 return static_cast<CollisionRequestFlag>(static_cast<int>(a) &
1213 static_cast<int>(b));
1214 }
1215
1216 inline CollisionRequestFlag operator^(CollisionRequestFlag a,
1217 CollisionRequestFlag b) {
1218 return static_cast<CollisionRequestFlag>(static_cast<int>(a) ^
1219 static_cast<int>(b));
1220 }
1221
1222 inline CollisionRequestFlag& operator|=(CollisionRequestFlag& a,
1223 CollisionRequestFlag b) {
1224 return (CollisionRequestFlag&)((int&)(a) |= static_cast<int>(b));
1225 }
1226
1227 inline CollisionRequestFlag& operator&=(CollisionRequestFlag& a,
1228 CollisionRequestFlag b) {
1229 return (CollisionRequestFlag&)((int&)(a) &= static_cast<int>(b));
1230 }
1231
1232 inline CollisionRequestFlag& operator^=(CollisionRequestFlag& a,
1233 CollisionRequestFlag b) {
1234 return (CollisionRequestFlag&)((int&)(a) ^= static_cast<int>(b));
1235 }
1236
1237 } // namespace fcl
1238
1239 } // namespace hpp
1240
1241 #endif
1242