38
38
#ifndef FCL_NARROWPHASE_DETAIL_GJKLIBCCD_INL_H
39
39
#define FCL_NARROWPHASE_DETAIL_GJKLIBCCD_INL_H
40
40
41
- #include " fcl/narrowphase/detail/convexity_based_algorithm/gjk_libccd.h"
42
- #include " fcl/narrowphase/detail/failed_at_this_configuration.h"
43
-
44
41
#include < array>
42
+ #include < iostream>
43
+ #include < sstream>
44
+ #include < string>
45
45
#include < unordered_map>
46
46
#include < unordered_set>
47
47
48
48
#include " fcl/common/unused.h"
49
49
#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"
50
52
51
53
namespace fcl
52
54
{
@@ -186,6 +188,46 @@ struct ccd_triangle_t : public ccd_obj_t
186
188
namespace libccd_extension
187
189
{
188
190
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
+
189
231
static ccd_real_t simplexReduceToTriangle (ccd_simplex_t *simplex,
190
232
ccd_real_t dist,
191
233
ccd_vec3_t *best_witness)
@@ -197,11 +239,10 @@ static ccd_real_t simplexReduceToTriangle(ccd_simplex_t *simplex,
197
239
198
240
// try the fourth point in all three positions
199
241
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);
205
246
newdist = CCD_SQRT (newdist);
206
247
207
248
// record the best triangle
@@ -398,13 +439,8 @@ static int doSimplex3(ccd_simplex_t *simplex, ccd_vec3_t *dir)
398
439
C = ccdSimplexPoint (simplex, 0 );
399
440
400
441
// 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 );
408
444
if (isAbsValueLessThanEpsSquared (dist_squared)) {
409
445
return 1 ;
410
446
}
@@ -493,30 +529,25 @@ static int doSimplex4(ccd_simplex_t *simplex, ccd_vec3_t *dir)
493
529
// check if tetrahedron is really tetrahedron (has volume > 0)
494
530
// if it is not simplex can't be expanded and thus no intersection is
495
531
// 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 );
503
534
if (isAbsValueLessThanEpsSquared (dist_squared)) {
504
535
return -1 ;
505
536
}
506
537
507
538
// check if origin lies on some of tetrahedron's face - if so objects
508
539
// 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 );
511
542
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 );
514
545
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 );
517
548
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 );
520
551
if (isAbsValueLessThanEpsSquared (dist_squared)) return 1 ;
521
552
522
553
// 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,
757
788
ccdVec3Sub2 (&ac, &c->v , &a->v );
758
789
ccdVec3Cross (&dir, &ab, &ac);
759
790
__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 );
763
793
764
794
// and second one take in opposite direction
765
795
ccdVec3Scale (&dir, -CCD_ONE);
766
796
__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 );
769
799
770
800
// check if face isn't already on edge of minkowski sum and thus we
771
801
// have touching contact
@@ -844,14 +874,15 @@ static int simplexToPolytope4(const void* obj1, const void* obj2,
844
874
// of the simplex to that face and then attempt to construct the polytope from
845
875
// the resulting face. We simply take the first face which exhibited the
846
876
// trait.
877
+
847
878
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 );
849
880
if (isAbsValueLessThanEpsSquared (dist_squared)) {
850
881
use_polytope3 = true ;
851
882
}
852
883
if (!use_polytope3) {
853
884
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 );
855
886
if (isAbsValueLessThanEpsSquared (dist_squared)) {
856
887
use_polytope3 = true ;
857
888
ccdSimplexSet (simplex, 1 , c);
@@ -860,15 +891,15 @@ static int simplexToPolytope4(const void* obj1, const void* obj2,
860
891
}
861
892
if (!use_polytope3) {
862
893
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 );
864
895
if (isAbsValueLessThanEpsSquared (dist_squared)) {
865
896
use_polytope3 = true ;
866
897
ccdSimplexSet (simplex, 2 , d);
867
898
}
868
899
}
869
900
if (!use_polytope3) {
870
901
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 );
872
903
if (isAbsValueLessThanEpsSquared (dist_squared)) {
873
904
use_polytope3 = true ;
874
905
ccdSimplexSet (simplex, 0 , b);
@@ -1583,9 +1614,7 @@ static int nextSupport(const ccd_pt_t* polytope, const void* obj1,
1583
1614
ccdPtFaceVec3 (reinterpret_cast <const ccd_pt_face_t *>(el), &a, &b, &c);
1584
1615
1585
1616
// 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);
1589
1618
}
1590
1619
1591
1620
if (std::sqrt (dist_squared) < ccd->epa_tolerance ) return -1 ;
@@ -1716,9 +1745,12 @@ static void validateNearestFeatureOfPolytopeBeingEdge(ccd_pt_t* polytope) {
1716
1745
// distance to be considered zero. We assume that the GJK/EPA code
1717
1746
// ultimately classifies inside/outside around *zero* and *not* epsilon.
1718
1747
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 ());
1722
1754
}
1723
1755
}
1724
1756
@@ -2065,11 +2097,10 @@ static inline ccd_real_t _ccdDist(const void *obj1, const void *obj2,
2065
2097
}
2066
2098
else if (ccdSimplexSize (simplex) == 3 )
2067
2099
{
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);
2073
2104
dist = CCD_SQRT (dist);
2074
2105
}
2075
2106
else
0 commit comments