3838#ifndef FCL_NARROWPHASE_DETAIL_GJKLIBCCD_INL_H
3939#define FCL_NARROWPHASE_DETAIL_GJKLIBCCD_INL_H
4040
41- #include " fcl/narrowphase/detail/convexity_based_algorithm/gjk_libccd.h"
42- #include " fcl/narrowphase/detail/failed_at_this_configuration.h"
43-
4441#include < array>
42+ #include < iostream>
43+ #include < sstream>
44+ #include < string>
4545#include < unordered_map>
4646#include < unordered_set>
4747
4848#include " fcl/common/unused.h"
4949#include " fcl/common/warning.h"
50+ #include " fcl/narrowphase/detail/convexity_based_algorithm/gjk_libccd.h"
51+ #include " fcl/narrowphase/detail/failed_at_this_configuration.h"
5052
5153namespace fcl
5254{
@@ -186,6 +188,46 @@ struct ccd_triangle_t : public ccd_obj_t
186188namespace libccd_extension
187189{
188190
191+ // These two functions contain the only "valid" invocations of
192+ // ccdVec3PointTriDist2(). Any other invocations should be considered a defect.
193+ // We can remove these safety layers if/when the issue noted below gets
194+ // resolved upstream.
195+
196+ // The code in this file (and elsewhere if it comes up), should *not* ever call
197+ // `ccdVec3PointTriDist2()` directly. It has some precision quirks. For those
198+ // invocations that want the squared distance without a witness point, call
199+ // *this* function.
200+ static ccd_real_t ccdVec3PointTriDist2NoWitness (const ccd_vec3_t * P,
201+ const ccd_vec3_t * a,
202+ const ccd_vec3_t * b,
203+ const ccd_vec3_t * c) {
204+ // When called, ccdVec3PointTriDist2 can optionally compute the value of the
205+ // "witness" point. When we don't need the witness point, we skip it. However,
206+ // the libccd implementation takes two different code paths based on whether
207+ // we request the witness point producing different answers due to numerical
208+ // precision issues. So, when FCL doesn't want/need a witness point, we use
209+ // this wrapper to force the witness point to get a more consistent and
210+ // reliable answer. See
211+ // https://github.com/danfis/libccd/issues/55 for an explanation.
212+ //
213+ // Any actual invocation of ccdVec3PointTriDist2() in the code should request
214+ // a witness point *or* call this invocation.
215+ ccd_vec3_t unused;
216+ return ccdVec3PointTriDist2 (P, a, b, c, &unused);
217+ }
218+
219+ // The code in this file (and elsewhere if it comes up), should *not* ever call
220+ // `ccdVec3PointTriDist2()` directly. It has some precision quirks. For those
221+ // invocations that want the squared distance *with* a witness point, call
222+ // *this* function.
223+ static ccd_real_t ccdVec3PointTriDist2WithWitness (const ccd_vec3_t * P,
224+ const ccd_vec3_t * a,
225+ const ccd_vec3_t * b,
226+ const ccd_vec3_t * c,
227+ ccd_vec3_t * w) {
228+ return ccdVec3PointTriDist2 (P, a, b, c, w);
229+ }
230+
189231static ccd_real_t simplexReduceToTriangle (ccd_simplex_t *simplex,
190232 ccd_real_t dist,
191233 ccd_vec3_t *best_witness)
@@ -197,11 +239,10 @@ static ccd_real_t simplexReduceToTriangle(ccd_simplex_t *simplex,
197239
198240 // try the fourth point in all three positions
199241 for (i = 0 ; i < 3 ; i++){
200- newdist = ccdVec3PointTriDist2 (ccd_vec3_origin,
201- &ccdSimplexPoint (simplex, (i == 0 ? 3 : 0 ))->v ,
202- &ccdSimplexPoint (simplex, (i == 1 ? 3 : 1 ))->v ,
203- &ccdSimplexPoint (simplex, (i == 2 ? 3 : 2 ))->v ,
204- &witness);
242+ newdist = ccdVec3PointTriDist2WithWitness (
243+ ccd_vec3_origin, &ccdSimplexPoint (simplex, (i == 0 ? 3 : 0 ))->v ,
244+ &ccdSimplexPoint (simplex, (i == 1 ? 3 : 1 ))->v ,
245+ &ccdSimplexPoint (simplex, (i == 2 ? 3 : 2 ))->v , &witness);
205246 newdist = CCD_SQRT (newdist);
206247
207248 // record the best triangle
@@ -398,13 +439,8 @@ static int doSimplex3(ccd_simplex_t *simplex, ccd_vec3_t *dir)
398439 C = ccdSimplexPoint (simplex, 0 );
399440
400441 // check touching contact
401- // Compute origin_projection as well. Without computing the origin projection,
402- // libccd could give inaccurate result. See
403- // https://github.com/danfis/libccd/issues/55.
404- ccd_vec3_t origin_projection_unused;
405-
406- const ccd_real_t dist_squared = ccdVec3PointTriDist2 (
407- ccd_vec3_origin, &A->v , &B->v , &C->v , &origin_projection_unused);
442+ const ccd_real_t dist_squared = ccdVec3PointTriDist2NoWitness (
443+ ccd_vec3_origin, &A->v , &B->v , &C->v );
408444 if (isAbsValueLessThanEpsSquared (dist_squared)) {
409445 return 1 ;
410446 }
@@ -493,30 +529,25 @@ static int doSimplex4(ccd_simplex_t *simplex, ccd_vec3_t *dir)
493529 // check if tetrahedron is really tetrahedron (has volume > 0)
494530 // if it is not simplex can't be expanded and thus no intersection is
495531 // found.
496- // point_projection_on_triangle_unused is not used. We ask
497- // ccdVec3PointTriDist2 to compute this witness point, so as to get a
498- // numerical robust dist_squared. See
499- // https://github.com/danfis/libccd/issues/55 for an explanation.
500- ccd_vec3_t point_projection_on_triangle_unused;
501- ccd_real_t dist_squared = ccdVec3PointTriDist2 (
502- &A->v , &B->v , &C->v , &D->v , &point_projection_on_triangle_unused);
532+ ccd_real_t dist_squared = ccdVec3PointTriDist2NoWitness (
533+ &A->v , &B->v , &C->v , &D->v );
503534 if (isAbsValueLessThanEpsSquared (dist_squared)) {
504535 return -1 ;
505536 }
506537
507538 // check if origin lies on some of tetrahedron's face - if so objects
508539 // intersect
509- dist_squared = ccdVec3PointTriDist2 (ccd_vec3_origin, &A-> v , &B-> v , &C-> v ,
510- &point_projection_on_triangle_unused );
540+ dist_squared =
541+ ccdVec3PointTriDist2NoWitness (ccd_vec3_origin, &A-> v , &B-> v , &C-> v );
511542 if (isAbsValueLessThanEpsSquared ((dist_squared))) return 1 ;
512- dist_squared = ccdVec3PointTriDist2 (ccd_vec3_origin, &A-> v , &C-> v , &D-> v ,
513- &point_projection_on_triangle_unused );
543+ dist_squared =
544+ ccdVec3PointTriDist2NoWitness (ccd_vec3_origin, &A-> v , &C-> v , &D-> v );
514545 if (isAbsValueLessThanEpsSquared ((dist_squared))) return 1 ;
515- dist_squared = ccdVec3PointTriDist2 (ccd_vec3_origin, &A-> v , &B-> v , &D-> v ,
516- &point_projection_on_triangle_unused );
546+ dist_squared =
547+ ccdVec3PointTriDist2NoWitness (ccd_vec3_origin, &A-> v , &B-> v , &D-> v );
517548 if (isAbsValueLessThanEpsSquared (dist_squared)) return 1 ;
518- dist_squared = ccdVec3PointTriDist2 (ccd_vec3_origin, &B-> v , &C-> v , &D-> v ,
519- &point_projection_on_triangle_unused );
549+ dist_squared =
550+ ccdVec3PointTriDist2NoWitness (ccd_vec3_origin, &B-> v , &C-> v , &D-> v );
520551 if (isAbsValueLessThanEpsSquared (dist_squared)) return 1 ;
521552
522553 // compute AO, AB, AC, AD segments and ABC, ACD, ADB normal vectors
@@ -757,15 +788,14 @@ static int convert2SimplexToTetrahedron(const void* obj1, const void* obj2,
757788 ccdVec3Sub2 (&ac, &c->v , &a->v );
758789 ccdVec3Cross (&dir, &ab, &ac);
759790 __ccdSupport (obj1, obj2, &dir, ccd, &d);
760- ccd_vec3_t point_projection_on_triangle_unused;
761- const ccd_real_t dist_squared = ccdVec3PointTriDist2 (
762- &d.v , &a->v , &b->v , &c->v , &point_projection_on_triangle_unused);
791+ const ccd_real_t dist_squared =
792+ ccdVec3PointTriDist2NoWitness (&d.v , &a->v , &b->v , &c->v );
763793
764794 // and second one take in opposite direction
765795 ccdVec3Scale (&dir, -CCD_ONE);
766796 __ccdSupport (obj1, obj2, &dir, ccd, &d2);
767- const ccd_real_t dist_squared_opposite = ccdVec3PointTriDist2 (
768- &d2.v , &a->v , &b->v , &c->v , &point_projection_on_triangle_unused );
797+ const ccd_real_t dist_squared_opposite =
798+ ccdVec3PointTriDist2NoWitness ( &d2.v , &a->v , &b->v , &c->v );
769799
770800 // check if face isn't already on edge of minkowski sum and thus we
771801 // have touching contact
@@ -844,14 +874,15 @@ static int simplexToPolytope4(const void* obj1, const void* obj2,
844874 // of the simplex to that face and then attempt to construct the polytope from
845875 // the resulting face. We simply take the first face which exhibited the
846876 // trait.
877+
847878 ccd_real_t dist_squared =
848- ccdVec3PointTriDist2 (ccd_vec3_origin, &a->v , &b->v , &c->v , NULL );
879+ ccdVec3PointTriDist2NoWitness (ccd_vec3_origin, &a->v , &b->v , &c->v );
849880 if (isAbsValueLessThanEpsSquared (dist_squared)) {
850881 use_polytope3 = true ;
851882 }
852883 if (!use_polytope3) {
853884 dist_squared =
854- ccdVec3PointTriDist2 (ccd_vec3_origin, &a->v , &c->v , &d->v , NULL );
885+ ccdVec3PointTriDist2NoWitness (ccd_vec3_origin, &a->v , &c->v , &d->v );
855886 if (isAbsValueLessThanEpsSquared (dist_squared)) {
856887 use_polytope3 = true ;
857888 ccdSimplexSet (simplex, 1 , c);
@@ -860,15 +891,15 @@ static int simplexToPolytope4(const void* obj1, const void* obj2,
860891 }
861892 if (!use_polytope3) {
862893 dist_squared =
863- ccdVec3PointTriDist2 (ccd_vec3_origin, &a->v , &b->v , &d->v , NULL );
894+ ccdVec3PointTriDist2NoWitness (ccd_vec3_origin, &a->v , &b->v , &d->v );
864895 if (isAbsValueLessThanEpsSquared (dist_squared)) {
865896 use_polytope3 = true ;
866897 ccdSimplexSet (simplex, 2 , d);
867898 }
868899 }
869900 if (!use_polytope3) {
870901 dist_squared =
871- ccdVec3PointTriDist2 (ccd_vec3_origin, &b->v , &c->v , &d->v , NULL );
902+ ccdVec3PointTriDist2NoWitness (ccd_vec3_origin, &b->v , &c->v , &d->v );
872903 if (isAbsValueLessThanEpsSquared (dist_squared)) {
873904 use_polytope3 = true ;
874905 ccdSimplexSet (simplex, 0 , b);
@@ -1583,9 +1614,7 @@ static int nextSupport(const ccd_pt_t* polytope, const void* obj1,
15831614 ccdPtFaceVec3 (reinterpret_cast <const ccd_pt_face_t *>(el), &a, &b, &c);
15841615
15851616 // check if new point can significantly expand polytope
1586- ccd_vec3_t point_projection_on_triangle_unused;
1587- dist_squared = ccdVec3PointTriDist2 (&out->v , a, b, c,
1588- &point_projection_on_triangle_unused);
1617+ dist_squared = ccdVec3PointTriDist2NoWitness (&out->v , a, b, c);
15891618 }
15901619
15911620 if (std::sqrt (dist_squared) < ccd->epa_tolerance ) return -1 ;
@@ -1716,9 +1745,12 @@ static void validateNearestFeatureOfPolytopeBeingEdge(ccd_pt_t* polytope) {
17161745 // distance to be considered zero. We assume that the GJK/EPA code
17171746 // ultimately classifies inside/outside around *zero* and *not* epsilon.
17181747 if (origin_to_face_distance[i] > plane_threshold) {
1719- FCL_THROW_FAILED_AT_THIS_CONFIGURATION (
1720- " The origin is outside of the polytope. This should already have "
1721- " been identified as separating." );
1748+ std::ostringstream oss;
1749+ oss << " The origin is outside of the polytope by "
1750+ << origin_to_face_distance[i] << " , exceeding the threshold "
1751+ << plane_threshold
1752+ << " . This should already have been identified as separating." ;
1753+ FCL_THROW_FAILED_AT_THIS_CONFIGURATION (oss.str ());
17221754 }
17231755 }
17241756
@@ -2065,11 +2097,10 @@ static inline ccd_real_t _ccdDist(const void *obj1, const void *obj2,
20652097 }
20662098 else if (ccdSimplexSize (simplex) == 3 )
20672099 {
2068- dist = ccdVec3PointTriDist2 (ccd_vec3_origin,
2069- &ccdSimplexPoint (simplex, 0 )->v ,
2070- &ccdSimplexPoint (simplex, 1 )->v ,
2071- &ccdSimplexPoint (simplex, 2 )->v ,
2072- &closest_p);
2100+ dist = ccdVec3PointTriDist2WithWitness (
2101+ ccd_vec3_origin, &ccdSimplexPoint (simplex, 0 )->v ,
2102+ &ccdSimplexPoint (simplex, 1 )->v , &ccdSimplexPoint (simplex, 2 )->v ,
2103+ &closest_p);
20732104 dist = CCD_SQRT (dist);
20742105 }
20752106 else
0 commit comments