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