coal 3.0.1
Coal, The Collision Detection Library. Previously known as HPP-FCL, fork of FCL -- The Flexible Collision Library
Loading...
Searching...
No Matches
narrowphase.h
Go to the documentation of this file.
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) 2018-2019, Centre National de la Recherche Scientifique
7 * All rights reserved.
8 * Copyright (c) 2021-2024, INRIA
9 * All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 *
14 * * Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials provided
19 * with the distribution.
20 * * Neither the name of Open Source Robotics Foundation nor the names of its
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37
40#ifndef COAL_NARROWPHASE_H
41#define COAL_NARROWPHASE_H
42
43#include <limits>
44
46#include "coal/collision_data.h"
48#include "coal/logging.h"
49
50namespace coal {
51
58 public:
59 EIGEN_MAKE_ALIGNED_OPERATOR_NEW
60
63
66
69
72
75 COAL_DEPRECATED_MESSAGE(Use gjk_initial_guess instead)
76 bool enable_cached_guess;
77
79 mutable Vec3s cached_guess;
80
82 mutable support_func_guess_t support_func_cached_guess;
83
86 Scalar distance_upper_bound;
87
89 GJKVariant gjk_variant;
90
92 GJKConvergenceCriterion gjk_convergence_criterion;
93
95 GJKConvergenceCriterionType gjk_convergence_criterion_type;
96
98 mutable details::EPA epa;
99
101 size_t epa_max_iterations;
102
104 Scalar epa_tolerance;
105
107 mutable details::MinkowskiDiff minkowski_difference;
108
109 private:
110 // Used internally for assertion checks.
111 static constexpr Scalar m_dummy_precision = Scalar(1e-6);
112
113 public:
124 : gjk(GJK_DEFAULT_MAX_ITERATIONS, GJK_DEFAULT_TOLERANCE),
125 gjk_max_iterations(GJK_DEFAULT_MAX_ITERATIONS),
126 gjk_tolerance(GJK_DEFAULT_TOLERANCE),
127 gjk_initial_guess(GJKInitialGuess::DefaultGuess),
128 enable_cached_guess(false), // Use gjk_initial_guess instead
129 cached_guess(Vec3s(1, 0, 0)),
130 support_func_cached_guess(support_func_guess_t::Zero()),
131 distance_upper_bound((std::numeric_limits<Scalar>::max)()),
132 gjk_variant(GJKVariant::DefaultGJK),
133 gjk_convergence_criterion(GJKConvergenceCriterion::Default),
134 gjk_convergence_criterion_type(GJKConvergenceCriterionType::Absolute),
135 epa(0, EPA_DEFAULT_TOLERANCE),
136 epa_max_iterations(EPA_DEFAULT_MAX_ITERATIONS),
137 epa_tolerance(EPA_DEFAULT_TOLERANCE) {}
138
148 explicit GJKSolver(const DistanceRequest& request)
149 : gjk(request.gjk_max_iterations, request.gjk_tolerance),
150 epa(0, request.epa_tolerance) {
151 this->cached_guess = Vec3s(1, 0, 0);
152 this->support_func_cached_guess = support_func_guess_t::Zero();
153
154 set(request);
155 }
156
161 void set(const DistanceRequest& request) {
162 // ---------------------
163 // GJK settings
164 this->gjk_initial_guess = request.gjk_initial_guess;
165 this->enable_cached_guess = request.enable_cached_gjk_guess;
166 if (this->gjk_initial_guess == GJKInitialGuess::CachedGuess ||
167 this->enable_cached_guess) {
168 this->cached_guess = request.cached_gjk_guess;
169 this->support_func_cached_guess = request.cached_support_func_guess;
170 }
171 this->gjk_max_iterations = request.gjk_max_iterations;
172 this->gjk_tolerance = request.gjk_tolerance;
173 // For distance computation, we don't want GJK to early stop
174 this->distance_upper_bound = (std::numeric_limits<Scalar>::max)();
175 this->gjk_variant = request.gjk_variant;
176 this->gjk_convergence_criterion = request.gjk_convergence_criterion;
177 this->gjk_convergence_criterion_type =
179
180 // ---------------------
181 // EPA settings
182 this->epa_max_iterations = request.epa_max_iterations;
183 this->epa_tolerance = request.epa_tolerance;
184
185 // ---------------------
186 // Reset GJK and EPA status
187 this->epa.status = details::EPA::Status::DidNotRun;
188 this->gjk.status = details::GJK::Status::DidNotRun;
189 }
190
200 explicit GJKSolver(const CollisionRequest& request)
201 : gjk(request.gjk_max_iterations, request.gjk_tolerance),
202 epa(0, request.epa_tolerance) {
203 this->cached_guess = Vec3s(1, 0, 0);
204 this->support_func_cached_guess = support_func_guess_t::Zero();
205
206 set(request);
207 }
208
213 void set(const CollisionRequest& request) {
214 // ---------------------
215 // GJK settings
216 this->gjk_initial_guess = request.gjk_initial_guess;
217 this->enable_cached_guess = request.enable_cached_gjk_guess;
218 if (this->gjk_initial_guess == GJKInitialGuess::CachedGuess ||
219 this->enable_cached_guess) {
220 this->cached_guess = request.cached_gjk_guess;
221 this->support_func_cached_guess = request.cached_support_func_guess;
222 }
223 this->gjk_tolerance = request.gjk_tolerance;
224 this->gjk_max_iterations = request.gjk_max_iterations;
225 // The distance upper bound should be at least greater to the requested
226 // security margin. Otherwise, we will likely miss some collisions.
227 this->distance_upper_bound =
228 (std::max)(Scalar(0), (std::max)(request.distance_upper_bound,
229 request.security_margin));
230 this->gjk_variant = request.gjk_variant;
231 this->gjk_convergence_criterion = request.gjk_convergence_criterion;
232 this->gjk_convergence_criterion_type =
234
235 // ---------------------
236 // EPA settings
237 this->epa_max_iterations = request.epa_max_iterations;
238 this->epa_tolerance = request.epa_tolerance;
239
240 // ---------------------
241 // Reset GJK and EPA status
242 this->gjk.status = details::GJK::Status::DidNotRun;
243 this->epa.status = details::EPA::Status::DidNotRun;
244 }
245
247 GJKSolver(const GJKSolver& other) = default;
248
251 bool operator==(const GJKSolver& other) const {
252 return this->enable_cached_guess ==
253 other.enable_cached_guess && // use gjk_initial_guess instead
254 this->cached_guess == other.cached_guess &&
255 this->support_func_cached_guess == other.support_func_cached_guess &&
256 this->gjk_max_iterations == other.gjk_max_iterations &&
257 this->gjk_tolerance == other.gjk_tolerance &&
258 this->distance_upper_bound == other.distance_upper_bound &&
259 this->gjk_variant == other.gjk_variant &&
260 this->gjk_convergence_criterion == other.gjk_convergence_criterion &&
261 this->gjk_convergence_criterion_type ==
263 this->gjk_initial_guess == other.gjk_initial_guess &&
264 this->epa_max_iterations == other.epa_max_iterations &&
265 this->epa_tolerance == other.epa_tolerance;
266 }
268
269 bool operator!=(const GJKSolver& other) const { return !(*this == other); }
270
273 Scalar getDistancePrecision(const bool compute_penetration) const {
274 return compute_penetration
275 ? (std::max)(this->gjk_tolerance, this->epa_tolerance)
276 : this->gjk_tolerance;
277 }
278
308 template <typename S1, typename S2>
309 Scalar shapeDistance(const S1& s1, const Transform3s& tf1, const S2& s2,
310 const Transform3s& tf2, const bool compute_penetration,
311 Vec3s& p1, Vec3s& p2, Vec3s& normal) const {
312 constexpr bool relative_transformation_already_computed = false;
314 this->runGJKAndEPA(s1, tf1, s2, tf2, compute_penetration, distance, p1, p2,
315 normal, relative_transformation_already_computed);
316 return distance;
317 }
318
322 template <typename S1>
323 Scalar shapeDistance(const S1& s1, const Transform3s& tf1,
324 const TriangleP& s2, const Transform3s& tf2,
325 const bool compute_penetration, Vec3s& p1, Vec3s& p2,
326 Vec3s& normal) const {
327 const Transform3s tf_1M2(tf1.inverseTimes(tf2));
328 TriangleP tri(tf_1M2.transform(s2.a), tf_1M2.transform(s2.b),
329 tf_1M2.transform(s2.c));
330
331 constexpr bool relative_transformation_already_computed = true;
333 this->runGJKAndEPA(s1, tf1, tri, tf_1M2, compute_penetration, distance, p1,
334 p2, normal, relative_transformation_already_computed);
335 return distance;
336 }
337
339 template <typename S2>
341 const S2& s2, const Transform3s& tf2,
342 const bool compute_penetration, Vec3s& p1, Vec3s& p2,
343 Vec3s& normal) const {
344 Scalar distance = this->shapeDistance<S2>(
345 s2, tf2, s1, tf1, compute_penetration, p2, p1, normal);
346 normal = -normal;
347 return distance;
348 }
349
350 protected:
353 template <typename S1, typename S2>
354 void getGJKInitialGuess(const S1& s1, const S2& s2, Vec3s& guess,
355 support_func_guess_t& support_hint,
356 const Vec3s& default_guess = Vec3s(1, 0, 0)) const {
357 // There is no reason not to warm-start the support function, so we always
358 // do it.
359 support_hint = this->support_func_cached_guess;
360 // The following switch takes care of the GJK warm-start.
361 switch (gjk_initial_guess) {
362 case GJKInitialGuess::DefaultGuess:
363 guess = default_guess;
364 break;
365 case GJKInitialGuess::CachedGuess:
366 guess = this->cached_guess;
367 break;
368 case GJKInitialGuess::BoundingVolumeGuess:
369 if (s1.aabb_local.volume() < 0 || s2.aabb_local.volume() < 0) {
371 "computeLocalAABB must have been called on the shapes before "
372 "using "
373 "GJKInitialGuess::BoundingVolumeGuess.",
374 std::logic_error);
375 }
376 guess.noalias() =
377 s1.aabb_local.center() -
378 (this->minkowski_difference.oR1 * s2.aabb_local.center() +
379 this->minkowski_difference.ot1);
380 break;
381 default:
382 COAL_THROW_PRETTY("Wrong initial guess for GJK.", std::logic_error);
383 }
384 // TODO: use gjk_initial_guess instead
387 if (this->enable_cached_guess) {
388 guess = this->cached_guess;
389 }
391 }
392
420 template <typename S1, typename S2,
421 int _SupportOptions = details::SupportOptions::NoSweptSphere>
423 const S1& s1, const Transform3s& tf1, const S2& s2,
424 const Transform3s& tf2, const bool compute_penetration, Scalar& distance,
425 Vec3s& p1, Vec3s& p2, Vec3s& normal,
426 const bool relative_transformation_already_computed = false) const {
427 // Reset internal state of GJK algorithm
428 if (relative_transformation_already_computed)
429 this->minkowski_difference.set<_SupportOptions>(&s1, &s2);
430 else
431 this->minkowski_difference.set<_SupportOptions>(&s1, &s2, tf1, tf2);
432 this->gjk.reset(this->gjk_max_iterations, this->gjk_tolerance);
433 this->gjk.setDistanceEarlyBreak(this->distance_upper_bound);
434 this->gjk.gjk_variant = this->gjk_variant;
435 this->gjk.convergence_criterion = this->gjk_convergence_criterion;
436 this->gjk.convergence_criterion_type = this->gjk_convergence_criterion_type;
437 this->epa.status = details::EPA::Status::DidNotRun;
438
439 // Get initial guess for GJK: default, cached or bounding volume guess
440 Vec3s guess;
441 support_func_guess_t support_hint;
442 getGJKInitialGuess(*(this->minkowski_difference.shapes[0]),
443 *(this->minkowski_difference.shapes[1]), guess,
444 support_hint);
445
446 this->gjk.evaluate(this->minkowski_difference, guess.cast<SolverScalar>(),
447 support_hint);
448
449 switch (this->gjk.status) {
450 case details::GJK::DidNotRun:
451 COAL_ASSERT(false, "GJK did not run. It should have!",
452 std::logic_error);
453 this->cached_guess = Vec3s(1, 0, 0);
454 this->support_func_cached_guess.setZero();
455 distance = -(std::numeric_limits<Scalar>::max)();
456 p1 = p2 = normal =
457 Vec3s::Constant(std::numeric_limits<Scalar>::quiet_NaN());
458 break;
459 case details::GJK::Failed:
460 //
461 // GJK ran out of iterations.
462 COAL_LOG_WARNING("GJK ran out of iterations.");
463 GJKExtractWitnessPointsAndNormal(tf1, distance, p1, p2, normal);
464 break;
465 case details::GJK::NoCollisionEarlyStopped:
466 //
467 // Case where GJK early stopped because the distance was found to be
468 // above the `distance_upper_bound`.
469 // The two witness points have no meaning.
470 GJKEarlyStopExtractWitnessPointsAndNormal(tf1, distance, p1, p2,
471 normal);
473 this->m_dummy_precision,
474 "The distance should be bigger than GJK's "
475 "`distance_upper_bound`.",
476 std::logic_error);
477 break;
478 case details::GJK::NoCollision:
479 //
480 // Case where GJK converged and proved that the shapes are not in
481 // collision, i.e their distance is above GJK's tolerance (default
482 // 1e-6).
483 GJKExtractWitnessPointsAndNormal(tf1, distance, p1, p2, normal);
484 COAL_ASSERT(std::abs((p1 - p2).norm() - distance) <=
485 this->gjk.getTolerance() + this->m_dummy_precision,
486 "The distance found by GJK should coincide with the "
487 "distance between the closest points.",
488 std::logic_error);
489 break;
490 //
491 // Next are the cases where GJK found the shapes to be in collision, i.e.
492 // their distance is below GJK's tolerance (default 1e-6).
493 case details::GJK::CollisionWithPenetrationInformation:
494 GJKExtractWitnessPointsAndNormal(tf1, distance, p1, p2, normal);
496 distance <= this->gjk.getTolerance() + this->m_dummy_precision,
497 "The distance found by GJK should be negative or at "
498 "least below GJK's tolerance.",
499 std::logic_error);
500 break;
501 case details::GJK::Collision:
502 if (!compute_penetration) {
503 // Skip EPA and set the witness points and the normal to nans.
504 GJKCollisionExtractWitnessPointsAndNormal(tf1, distance, p1, p2,
505 normal);
506 } else {
507 //
508 // GJK was not enough to recover the penetration information.
509 // We need to run the EPA algorithm to find the witness points,
510 // penetration depth and the normal.
511
512 // Reset EPA algorithm. Potentially allocate memory if
513 // `epa_max_face_num` or `epa_max_vertex_num` are bigger than EPA's
514 // current storage.
515 this->epa.reset(this->epa_max_iterations, this->epa_tolerance);
516
517 // TODO: understand why EPA's performance is so bad on cylinders and
518 // cones.
519 this->epa.evaluate(this->gjk, (-guess).cast<SolverScalar>());
520
521 switch (epa.status) {
522 //
523 // In the following switch cases, until the "Valid" case,
524 // EPA either ran out of iterations, of faces or of vertices.
525 // The depth, witness points and the normal are still valid,
526 // simply not at the precision of EPA's tolerance.
527 // The flag `COAL_ENABLE_LOGGING` enables feebdack on these
528 // cases.
529 //
530 // TODO: Remove OutOfFaces and OutOfVertices statuses and simply
531 // compute the upper bound on max faces and max vertices as a
532 // function of the number of iterations.
533 case details::EPA::OutOfFaces:
534 COAL_LOG_WARNING("EPA ran out of faces.");
535 EPAExtractWitnessPointsAndNormal(tf1, distance, p1, p2, normal);
536 break;
537 case details::EPA::OutOfVertices:
538 COAL_LOG_WARNING("EPA ran out of vertices.");
539 EPAExtractWitnessPointsAndNormal(tf1, distance, p1, p2, normal);
540 break;
541 case details::EPA::Failed:
542 COAL_LOG_WARNING("EPA ran out of iterations.");
543 EPAExtractWitnessPointsAndNormal(tf1, distance, p1, p2, normal);
544 break;
545 case details::EPA::Valid:
546 case details::EPA::AccuracyReached:
548 -epa.depth <= epa.getTolerance() + this->m_dummy_precision,
549 "EPA's penetration distance should be negative (or "
550 "at least below EPA's tolerance).",
551 std::logic_error);
552 EPAExtractWitnessPointsAndNormal(tf1, distance, p1, p2, normal);
553 break;
554 case details::EPA::Degenerated:
556 "EPA warning: created a polytope with a degenerated face.");
557 EPAExtractWitnessPointsAndNormal(tf1, distance, p1, p2, normal);
558 break;
559 case details::EPA::NonConvex:
561 "EPA warning: EPA got called onto non-convex shapes.");
562 EPAExtractWitnessPointsAndNormal(tf1, distance, p1, p2, normal);
563 break;
564 case details::EPA::InvalidHull:
565 COAL_LOG_WARNING("EPA warning: created an invalid polytope.");
566 EPAExtractWitnessPointsAndNormal(tf1, distance, p1, p2, normal);
567 break;
568 case details::EPA::DidNotRun:
569 COAL_ASSERT(false, "EPA did not run. It should have!",
570 std::logic_error);
571 COAL_LOG_ERROR("EPA error: did not run. It should have.");
572 EPAFailedExtractWitnessPointsAndNormal(tf1, distance, p1, p2,
573 normal);
574 break;
575 case details::EPA::FallBack:
577 false,
578 "EPA went into fallback mode. It should never do that.",
579 std::logic_error);
580 COAL_LOG_ERROR("EPA error: FallBack.");
581 EPAFailedExtractWitnessPointsAndNormal(tf1, distance, p1, p2,
582 normal);
583 break;
584 }
585 }
586 break; // End of case details::GJK::Collision
587 }
588 }
589
591 Scalar& distance, Vec3s& p1,
592 Vec3s& p2,
593 Vec3s& normal) const {
595 // Cache gjk result for potential future call to this GJKSolver.
596 this->cached_guess = this->gjk.ray.cast<Scalar>();
597 this->support_func_cached_guess = this->gjk.support_hint;
598
599 distance = Scalar(this->gjk.distance);
600 p1 = p2 = normal =
601 Vec3s::Constant(std::numeric_limits<Scalar>::quiet_NaN());
602 // If we absolutely want to return some witness points, we could use
603 // the following code (or simply merge the early stopped case with the
604 // valid case below):
605 // gjk.getWitnessPointsAndNormal(minkowski_difference, p1, p2, normal);
606 // p1 = tf1.transform(p1);
607 // p2 = tf1.transform(p2);
608 // normal = tf1.getRotation() * normal;
609 }
610
612 Scalar& distance, Vec3s& p1, Vec3s& p2,
613 Vec3s& normal) const {
614 // Apart from early stopping, there are two cases where GJK says there is no
615 // collision:
616 // 1. GJK proved the distance is above its tolerance (default 1e-6).
617 // 2. GJK ran out of iterations.
618 // In any case, `gjk.ray`'s norm is bigger than GJK's tolerance and thus
619 // it can safely be normalized.
620 COAL_ASSERT(this->gjk.ray.norm() >
621 this->gjk.getTolerance() - this->m_dummy_precision,
622 "The norm of GJK's ray should be bigger than GJK's tolerance.",
623 std::logic_error);
624 // Cache gjk result for potential future call to this GJKSolver.
625 this->cached_guess = this->gjk.ray.cast<Scalar>();
626 this->support_func_cached_guess = this->gjk.support_hint;
627
628 distance = Scalar(this->gjk.distance);
629 // TODO: On degenerated case, the closest points may be non-unique.
630 // (i.e. an object face normal is colinear to `gjk.ray`)
631 Vec3ps p1_, p2_, normal_;
632 gjk.getWitnessPointsAndNormal(this->minkowski_difference, p1_, p2_,
633 normal_);
634 p1 = p1_.cast<Scalar>();
635 p2 = p2_.cast<Scalar>();
636 normal = normal_.cast<Scalar>();
637 Vec3s p = tf1.transform(0.5 * (p1 + p2));
638 normal = tf1.getRotation() * normal;
639 p1.noalias() = p - 0.5 * distance * normal;
640 p2.noalias() = p + 0.5 * distance * normal;
641 }
642
644 Scalar& distance, Vec3s& p1,
645 Vec3s& p2,
646 Vec3s& normal) const {
648 COAL_ASSERT(this->gjk.distance <=
649 this->gjk.getTolerance() + this->m_dummy_precision,
650 "The distance should be lower than GJK's tolerance.",
651 std::logic_error);
652 // Because GJK has returned the `Collision` status and EPA has not run,
653 // we purposefully do not cache the result of GJK, because ray is zero.
654 // However, we can cache the support function hint.
655 // this->cached_guess = this->gjk.ray;
656 this->support_func_cached_guess = this->gjk.support_hint;
657
658 distance = Scalar(this->gjk.distance);
659 p1 = p2 = normal =
660 Vec3s::Constant(std::numeric_limits<Scalar>::quiet_NaN());
661 }
662
664 Scalar& distance, Vec3s& p1, Vec3s& p2,
665 Vec3s& normal) const {
666 // Cache EPA result for potential future call to this GJKSolver.
667 // This caching allows to warm-start the next GJK call.
668 this->cached_guess = -(this->epa.depth * this->epa.normal).cast<Scalar>();
669 this->support_func_cached_guess = this->epa.support_hint;
670 distance = (std::min)(Scalar(0), -Scalar(this->epa.depth));
671 Vec3ps p1_, p2_, normal_;
672 this->epa.getWitnessPointsAndNormal(this->minkowski_difference, p1_, p2_,
673 normal_);
674 p1 = p1_.cast<Scalar>();
675 p2 = p2_.cast<Scalar>();
676 normal = normal_.cast<Scalar>();
677 // The following is very important to understand why EPA can sometimes
678 // return a normal that is not colinear to the vector $p_1 - p_2$ when
679 // working with tolerances like $\epsilon = 10^{-3}$.
680 // It can be resumed with a simple idea:
681 // EPA is an algorithm meant to find the penetration depth and the
682 // normal. It is not meant to find the closest points.
683 // Again, the issue here is **not** the normal, it's $p_1$ and $p_2$.
684 //
685 // More details:
686 // We'll denote $S_1$ and $S_2$ the two shapes, $n$ the normal and $p_1$ and
687 // $p_2$ the witness points. In theory, when EPA converges to $\epsilon =
688 // 0$, the normal and witness points verify the following property (P):
689 // - $p_1 \in \partial \sigma_{S_1}(n)$,
690 // - $p_2 \in \partial \sigma_{S_2}(-n),
691 // where $\sigma_{S_1}$ and $\sigma_{S_2}$ are the support functions of
692 // $S_1$ and $S_2$. The $\partial \sigma(n)$ simply denotes the support set
693 // of the support function in the direction $n$. (Note: I am leaving out the
694 // details of frame choice for the support function, to avoid making the
695 // mathematical notation too heavy.)
696 // --> In practice, EPA converges to $\epsilon > 0$.
697 // On polytopes and the likes, this does not change much and the property
698 // given above is still valid.
699 // --> However, this is very different on curved surfaces, such as
700 // ellipsoids, cylinders, cones, capsules etc. For these shapes, converging
701 // at $\epsilon = 10^{-6}$ or to $\epsilon = 10^{-3}$ does not change the
702 // normal much, but the property (P) given above is no longer valid, which
703 // means that the points $p_1$ and $p_2$ do not necessarily belong to the
704 // support sets in the direction of $n$ and thus $n$ and $p_1 - p_2$ are not
705 // colinear.
706 //
707 // Do not panic! This is fine.
708 // Although the property above is not verified, it's almost verified,
709 // meaning that $p_1$ and $p_2$ belong to support sets in directions that
710 // are very close to $n$.
711 //
712 // Solution to compute better $p_1$ and $p_2$:
713 // We compute the middle points of the current $p_1$ and $p_2$ and we use
714 // the normal and the distance given by EPA to compute the new $p_1$ and
715 // $p_2$.
716 Vec3s p = tf1.transform(0.5 * (p1 + p2));
717 normal = tf1.getRotation() * normal;
718 p1.noalias() = p - 0.5 * distance * normal;
719 p2.noalias() = p + 0.5 * distance * normal;
720 }
721
723 Scalar& distance, Vec3s& p1,
724 Vec3s& p2, Vec3s& normal) const {
725 this->cached_guess = Vec3s(1, 0, 0);
726 this->support_func_cached_guess.setZero();
727
729 distance = -(std::numeric_limits<Scalar>::max)();
730 p1 = p2 = normal =
731 Vec3s::Constant(std::numeric_limits<Scalar>::quiet_NaN());
732 }
733};
734
735} // namespace coal
736
737#endif
Simple transform class used locally by InterpMotion.
Definition transform.h:55
Vec3s transform(const Eigen::MatrixBase< Derived > &v) const
transform a given vector by the transform
Definition transform.h:152
const Matrix3s & getRotation() const
get rotation
Definition transform.h:110
Transform3s inverseTimes(const Transform3s &other) const
inverse the transform and multiply with another
Definition transform.h:175
Triangle stores the points instead of only indices of points.
Definition geometric_shapes.h:108
Vec3s a
Definition geometric_shapes.h:147
Vec3s b
Definition geometric_shapes.h:147
Vec3s c
Definition geometric_shapes.h:147
#define COAL_DLLAPI
Definition config.hh:88
#define COAL_DEPRECATED_MESSAGE(message)
Definition deprecated.hh:38
#define COAL_COMPILER_DIAGNOSTIC_IGNORED_DEPRECECATED_DECLARATIONS
Definition fwd.hh:121
#define COAL_ASSERT(check, message, exception)
Definition fwd.hh:82
#define COAL_UNUSED_VARIABLE(var)
Definition fwd.hh:56
#define COAL_COMPILER_DIAGNOSTIC_PUSH
Definition fwd.hh:119
#define COAL_COMPILER_DIAGNOSTIC_POP
Definition fwd.hh:120
#define COAL_THROW_PRETTY(message, exception)
Definition fwd.hh:64
#define COAL_LOG_ERROR(message)
Definition logging.h:51
#define COAL_LOG_WARNING(message)
Definition logging.h:50
Main namespace.
Definition broadphase_bruteforce.h:44
GJKConvergenceCriterionType
Wether the convergence criterion is scaled on the norm of the solution or not.
Definition data_types.h:118
Eigen::Matrix< SolverScalar, 3, 1 > Vec3ps
Definition data_types.h:83
Scalar distance(const Matrix3s &R0, const Vec3s &T0, const kIOS &b1, const kIOS &b2, Vec3s *P=NULL, Vec3s *Q=NULL)
Approximate distance between two kIOS bounding volumes.
double SolverScalar
Definition data_types.h:82
Eigen::Matrix< Scalar, 3, 1 > Vec3s
Definition data_types.h:70
double Scalar
Definition data_types.h:68
Eigen::Vector2i support_func_guess_t
Definition data_types.h:80
GJKConvergenceCriterion
Which convergence criterion is used to stop the algorithm (when the shapes are not in collision)....
Definition data_types.h:114
GJKInitialGuess
Initial guess to use for the GJK algorithm DefaultGuess: Vec3s(1, 0, 0) CachedGuess: previous vector ...
Definition data_types.h:105
GJKVariant
Variant to use for the GJK algorithm.
Definition data_types.h:108
request to the collision algorithm
Definition collision_data.h:311
Scalar security_margin
Distance below which objects are considered in collision. See Collision.
Definition collision_data.h:328
Scalar distance_upper_bound
Distance above which GJK solver makes an early stopping. GJK stops searching for the closest points w...
Definition collision_data.h:340
request to the distance computation
Definition collision_data.h:984
collision and distance solver based on the GJK and EPA algorithms. Originally, GJK and EPA were imple...
Definition narrowphase.h:57
bool operator!=(const GJKSolver &other) const
Definition narrowphase.h:269
void set(const CollisionRequest &request)
setter from a CollisionRequest
Definition narrowphase.h:213
GJKSolver(const GJKSolver &other)=default
Copy constructor.
void runGJKAndEPA(const S1 &s1, const Transform3s &tf1, const S2 &s2, const Transform3s &tf2, const bool compute_penetration, Scalar &distance, Vec3s &p1, Vec3s &p2, Vec3s &normal, const bool relative_transformation_already_computed=false) const
Runs the GJK algorithm.
Definition narrowphase.h:422
Scalar shapeDistance(const S1 &s1, const Transform3s &tf1, const S2 &s2, const Transform3s &tf2, const bool compute_penetration, Vec3s &p1, Vec3s &p2, Vec3s &normal) const
Uses GJK and EPA to compute the distance between two shapes.
Definition narrowphase.h:309
void GJKCollisionExtractWitnessPointsAndNormal(const Transform3s &tf1, Scalar &distance, Vec3s &p1, Vec3s &p2, Vec3s &normal) const
Definition narrowphase.h:643
Scalar gjk_tolerance
tolerance of GJK
Definition narrowphase.h:68
size_t gjk_max_iterations
maximum number of iterations of GJK
Definition narrowphase.h:65
size_t epa_max_iterations
maximum number of iterations of EPA
Definition narrowphase.h:101
Scalar getDistancePrecision(const bool compute_penetration) const
Helper to return the precision of the solver on the distance estimate, depending on whether or not co...
Definition narrowphase.h:273
GJKSolver(const CollisionRequest &request)
Constructor from a CollisionRequest.
Definition narrowphase.h:200
void GJKEarlyStopExtractWitnessPointsAndNormal(const Transform3s &tf1, Scalar &distance, Vec3s &p1, Vec3s &p2, Vec3s &normal) const
Definition narrowphase.h:590
GJKInitialGuess gjk_initial_guess
which warm start to use for GJK
Definition narrowphase.h:71
EIGEN_MAKE_ALIGNED_OPERATOR_NEW details::GJK gjk
GJK algorithm.
Definition narrowphase.h:62
GJKConvergenceCriterion gjk_convergence_criterion
Convergence criterion for GJK.
Definition narrowphase.h:92
GJKConvergenceCriterionType gjk_convergence_criterion_type
Absolute or relative convergence criterion for GJK.
Definition narrowphase.h:95
bool enable_cached_guess
Whether smart guess can be provided @Deprecated Use gjk_initial_guess instead.
Definition narrowphase.h:76
Scalar epa_tolerance
tolerance of EPA
Definition narrowphase.h:104
void getGJKInitialGuess(const S1 &s1, const S2 &s2, Vec3s &guess, support_func_guess_t &support_hint, const Vec3s &default_guess=Vec3s(1, 0, 0)) const
initialize GJK. This method assumes minkowski_difference has been set.
Definition narrowphase.h:354
GJKSolver(const DistanceRequest &request)
Constructor from a DistanceRequest.
Definition narrowphase.h:148
Vec3s cached_guess
smart guess
Definition narrowphase.h:79
void set(const DistanceRequest &request)
setter from a DistanceRequest
Definition narrowphase.h:161
void EPAFailedExtractWitnessPointsAndNormal(const Transform3s &tf1, Scalar &distance, Vec3s &p1, Vec3s &p2, Vec3s &normal) const
Definition narrowphase.h:722
void EPAExtractWitnessPointsAndNormal(const Transform3s &tf1, Scalar &distance, Vec3s &p1, Vec3s &p2, Vec3s &normal) const
Definition narrowphase.h:663
support_func_guess_t support_func_cached_guess
smart guess for the support function
Definition narrowphase.h:82
Scalar shapeDistance(const TriangleP &s1, const Transform3s &tf1, const S2 &s2, const Transform3s &tf2, const bool compute_penetration, Vec3s &p1, Vec3s &p2, Vec3s &normal) const
See other partial template specialization of shapeDistance above.
Definition narrowphase.h:340
GJKVariant gjk_variant
Variant of the GJK algorithm (Default, Nesterov or Polyak).
Definition narrowphase.h:89
Scalar distance_upper_bound
If GJK can guarantee that the distance between the shapes is greater than this value,...
Definition narrowphase.h:86
Scalar shapeDistance(const S1 &s1, const Transform3s &tf1, const TriangleP &s2, const Transform3s &tf2, const bool compute_penetration, Vec3s &p1, Vec3s &p2, Vec3s &normal) const
Partial specialization of shapeDistance for the case where the second shape is a triangle....
Definition narrowphase.h:323
void GJKExtractWitnessPointsAndNormal(const Transform3s &tf1, Scalar &distance, Vec3s &p1, Vec3s &p2, Vec3s &normal) const
Definition narrowphase.h:611
bool operator==(const GJKSolver &other) const
Definition narrowphase.h:251
Scalar epa_tolerance
tolerance for EPA. Note: This tolerance determines the precision on the estimated distance between tw...
Definition collision_data.h:211
Vec3s cached_gjk_guess
the gjk initial guess set by user
Definition collision_data.h:180
size_t epa_max_iterations
max number of iterations for EPA
Definition collision_data.h:204
GJKConvergenceCriterion gjk_convergence_criterion
convergence criterion used to stop GJK
Definition collision_data.h:198
support_func_guess_t cached_support_func_guess
the support function initial guess set by user
Definition collision_data.h:183
bool enable_cached_gjk_guess
whether enable gjk initial guess @Deprecated Use gjk_initial_guess instead
Definition collision_data.h:177
Scalar gjk_tolerance
tolerance for the GJK algorithm. Note: This tolerance determines the precision on the estimated dista...
Definition collision_data.h:192
GJKVariant gjk_variant
whether to enable the Nesterov accleration of GJK
Definition collision_data.h:195
GJKConvergenceCriterionType gjk_convergence_criterion_type
convergence criterion used to stop GJK
Definition collision_data.h:201
GJKInitialGuess gjk_initial_guess
Definition collision_data.h:172
size_t gjk_max_iterations
maximum iteration for the GJK algorithm
Definition collision_data.h:186
class for GJK algorithm
Definition gjk.h:53
void reset(size_t max_iterations_, SolverScalar tolerance_)
resets the GJK algorithm, preparing it for a new run. Other than the maximum number of iterations and...
SolverScalar getTolerance() const
Get the tolerance of GJK.
Definition gjk.h:210
void setDistanceEarlyBreak(const SolverScalar &dup)
Distance threshold for early break. GJK stops when it proved the distance is more than this threshold...
Definition gjk.h:197
GJKConvergenceCriterionType convergence_criterion_type
Definition gjk.h:108
support_func_guess_t support_hint
Definition gjk.h:112
SolverScalar distance_upper_bound
Definition gjk.h:104
GJKVariant gjk_variant
Definition gjk.h:106
Vec3ps ray
Definition gjk.h:111
SolverScalar distance
The distance between the two shapes, computed by GJK. If the distance is below GJK's threshold,...
Definition gjk.h:118
Status evaluate(const MinkowskiDiff &shape, const Vec3ps &guess, const support_func_guess_t &supportHint=support_func_guess_t::Zero())
GJK algorithm, given the initial value guess.
GJKConvergenceCriterion convergence_criterion
Definition gjk.h:107
Status status
Definition gjk.h:105
void getWitnessPointsAndNormal(const MinkowskiDiff &shape, Vec3ps &w0, Vec3ps &w1, Vec3ps &normal) const