@@ -966,28 +966,6 @@ static bool triangle_area_is_zero(const ccd_vec3_t& a, const ccd_vec3_t& b,
966
966
return false ;
967
967
}
968
968
969
- /* *
970
- * Determines if the point P is on the line segment AB.
971
- * If A, B, P are coincident, report true.
972
- */
973
- static bool is_point_on_line_segment (const ccd_vec3_t & p, const ccd_vec3_t & a,
974
- const ccd_vec3_t & b) {
975
- if (are_coincident (a, b)) {
976
- return are_coincident (a, p);
977
- }
978
- // A and B are not coincident, if the triangle ABP has non-zero area, then P
979
- // is not on the line adjoining AB, and hence not on the line segment AB.
980
- if (!triangle_area_is_zero (a, b, p)) {
981
- return false ;
982
- }
983
- // P is on the line adjoinging AB. If P is on the line segment AB, then
984
- // PA.dot(PB) <= 0.
985
- ccd_vec3_t PA, PB;
986
- ccdVec3Sub2 (&PA, &p, &a);
987
- ccdVec3Sub2 (&PB, &p, &b);
988
- return ccdVec3Dot (&PA, &PB) <= 0 ;
989
- }
990
-
991
969
/* *
992
970
* Computes the normal vector of a triangular face on a polytope, and the normal
993
971
* vector points outward from the polytope. Notice we assume that the origin
@@ -1691,6 +1669,8 @@ static int __ccdGJK(const void *obj1, const void *obj2,
1691
1669
*/
1692
1670
static void validateNearestFeatureOfPolytopeBeingEdge (ccd_pt_t * polytope) {
1693
1671
assert (polytope->nearest_type == CCD_PT_EDGE);
1672
+
1673
+ constexpr ccd_real_t kEps = constants<ccd_real_t >::eps ();
1694
1674
// Only verify the feature if the nearest feature is an edge.
1695
1675
1696
1676
const ccd_pt_edge_t * const nearest_edge =
@@ -1701,6 +1681,14 @@ static void validateNearestFeatureOfPolytopeBeingEdge(ccd_pt_t* polytope) {
1701
1681
// normalized.
1702
1682
std::array<ccd_vec3_t , 2 > face_normals;
1703
1683
std::array<double , 2 > origin_to_face_distance;
1684
+
1685
+ // We define the plane equation using vertex[0]. If vertex[0] is far away
1686
+ // from the origin, it can magnify rounding error. We scale epsilon to account
1687
+ // for this possibility.
1688
+ const ccd_real_t v0_dist =
1689
+ std::sqrt (ccdVec3Len2 (&nearest_edge->vertex [0 ]->v .v ));
1690
+ const ccd_real_t plane_threshold = kEps * std::max (1.0 , v0_dist);
1691
+
1704
1692
for (int i = 0 ; i < 2 ; ++i) {
1705
1693
face_normals[i] =
1706
1694
faceNormalPointingOutward (polytope, nearest_edge->faces [i]);
@@ -1709,27 +1697,29 @@ static void validateNearestFeatureOfPolytopeBeingEdge(ccd_pt_t* polytope) {
1709
1697
// n̂ ⋅ (o - vₑ) ≤ 0 or, with simplification, -n̂ ⋅ vₑ ≤ 0 (since n̂ ⋅ o = 0).
1710
1698
origin_to_face_distance[i] =
1711
1699
-ccdVec3Dot (&face_normals[i], &nearest_edge->vertex [0 ]->v .v );
1712
- if (origin_to_face_distance[i] > 0 ) {
1700
+ // If the origin lies *on* the edge, then it also lies on the two adjacent
1701
+ // faces. Rather than failing on strictly *positive* signed distance, we
1702
+ // introduce an epsilon threshold. This usage of epsilon is to account for a
1703
+ // discrepancy in the signed distance computation. How GJK (and partially
1704
+ // EPA) compute the signed distance from origin to face may *not* be exactly
1705
+ // the same as done in this test (i.e. for a given set of vertices, the
1706
+ // plane equation can be defined various ways. Those ways are
1707
+ // *mathematically* equivalent but numerically will differ due to rounding).
1708
+ // We account for those differences by allowing a very small positive signed
1709
+ // distance to be considered zero. We assume that the GJK/EPA code
1710
+ // ultimately classifies inside/outside around *zero* and *not* epsilon.
1711
+ if (origin_to_face_distance[i] > plane_threshold) {
1713
1712
FCL_THROW_FAILED_AT_THIS_CONFIGURATION (
1714
1713
" The origin is outside of the polytope. This should already have "
1715
1714
" been identified as separating." );
1716
1715
}
1717
1716
}
1718
- // We compute the projection of the origin onto the plane as
1719
- // -face_normals[i] * origin_to_face_distance[i]
1720
- // If the projection to both faces are on the edge, the the edge is the
1721
- // closest feature.
1722
- bool is_edge_closest_feature = true ;
1723
- for (int i = 0 ; i < 2 ; ++i) {
1724
- ccd_vec3_t origin_projection_to_plane = face_normals[i];
1725
- ccdVec3Scale (&(origin_projection_to_plane), -origin_to_face_distance[i]);
1726
- if (!is_point_on_line_segment (origin_projection_to_plane,
1727
- nearest_edge->vertex [0 ]->v .v ,
1728
- nearest_edge->vertex [1 ]->v .v )) {
1729
- is_edge_closest_feature = false ;
1730
- break ;
1731
- }
1732
- }
1717
+
1718
+ // We know the reported squared distance to the edge. If that distance is
1719
+ // functionally zero, then the edge must *truly* be the nearest feature.
1720
+ // If it isn't, then it must be one of the adjacent faces.
1721
+ const bool is_edge_closest_feature = nearest_edge->dist < kEps * kEps ;
1722
+
1733
1723
if (!is_edge_closest_feature) {
1734
1724
// We assume that libccd is not crazily wrong. Although the closest
1735
1725
// feature is not the edge, it is near that edge. Hence we select the
@@ -1779,7 +1769,7 @@ static int __ccdEPA(const void *obj1, const void *obj2,
1779
1769
return -2 ;
1780
1770
}
1781
1771
1782
- while (1 ){
1772
+ while (1 ) {
1783
1773
// get triangle nearest to origin
1784
1774
*nearest = ccdPtNearest (polytope);
1785
1775
if (polytope->nearest_type == CCD_PT_EDGE) {
@@ -1791,14 +1781,13 @@ static int __ccdEPA(const void *obj1, const void *obj2,
1791
1781
*nearest = ccdPtNearest (polytope);
1792
1782
}
1793
1783
1794
- // get next support point
1795
- if (nextSupport (polytope, obj1, obj2, ccd, *nearest, &supp) != 0 ) {
1796
- break ;
1797
- }
1784
+ // get next support point
1785
+ if (nextSupport (polytope, obj1, obj2, ccd, *nearest, &supp) != 0 ) {
1786
+ break ;
1787
+ }
1798
1788
1799
- // expand nearest triangle using new point - supp
1800
- if (expandPolytope (polytope, *nearest, &supp) != 0 )
1801
- return -2 ;
1789
+ // expand nearest triangle using new point - supp
1790
+ if (expandPolytope (polytope, *nearest, &supp) != 0 ) return -2 ;
1802
1791
}
1803
1792
1804
1793
return 0 ;
0 commit comments