@@ -817,7 +817,7 @@ static int convert2SimplexToTetrahedron(const void* obj1, const void* obj2,
817817 assert (isPolytopeEmpty (*polytope));
818818 assert (simplex->last == 2 ); // a 2-simplex.
819819 const ccd_support_t *a, *b, *c;
820- ccd_support_t d, d2 ;
820+ ccd_support_t candidate_1, candidate_2 ;
821821 ccd_vec3_t ab, ac, dir;
822822 ccd_pt_vertex_t * v[4 ];
823823 ccd_pt_edge_t * e[6 ];
@@ -831,37 +831,32 @@ static int convert2SimplexToTetrahedron(const void* obj1, const void* obj2,
831831 // The 2-simplex is just a triangle containing the origin. We will expand this
832832 // triangle to a tetrahedron, by adding the support point along the normal
833833 // direction of the triangle.
834+ //
835+ // There are two possible support points to consider; one on each side of the
836+ // triangle. "Expanding" the polytope depends on its ability to define
837+ // outside. In *some* cases (e.g., when the origin lies on a polytope face),
838+ // that requires clearly recognizing which side of a polytope face the
839+ // polytope vertices lie. That works best when the tetrahedron has a larger
840+ // volume (i.e., the fourth vertex is as far away from the triangle's plane
841+ // as possible). So, that's the candidate we'll select.
842+ //
843+ // We don't need to know *true* distance. We just need to know which candidate
844+ // is farther. So, we can forego normalizing the cross product, knowing the
845+ // true distances of the candidate points are scaled by the same factor.
834846 ccdVec3Sub2 (&ab, &b->v , &a->v );
835847 ccdVec3Sub2 (&ac, &c->v , &a->v );
836848 ccdVec3Cross (&dir, &ab, &ac);
837- __ccdSupport (obj1, obj2, &dir, ccd, &d);
838- const ccd_real_t dist_squared =
839- ccdVec3PointTriDist2NoWitness (&d.v , &a->v , &b->v , &c->v );
840849
841- // and second one take in opposite direction
842- ccdVec3Scale (&dir, -CCD_ONE);
843- __ccdSupport (obj1, obj2, &dir, ccd, &d2);
844- const ccd_real_t dist_squared_opposite =
845- ccdVec3PointTriDist2NoWitness (&d2.v , &a->v , &b->v , &c->v );
850+ // The scaled distance of the first candidate.
851+ __ccdSupport (obj1, obj2, &dir, ccd, &candidate_1);
852+ const ccd_real_t scale_distance_1 = ccdVec3Dot (&dir, &candidate_1.v );
846853
847- // check if face isn't already on edge of minkowski sum and thus we
848- // have touching contact
849- if (ccdIsZero (dist_squared) || ccdIsZero (dist_squared_opposite)) {
850- v[0 ] = ccdPtAddVertex (polytope, a);
851- v[1 ] = ccdPtAddVertex (polytope, b);
852- v[2 ] = ccdPtAddVertex (polytope, c);
853- e[0 ] = ccdPtAddEdge (polytope, v[0 ], v[1 ]);
854- e[1 ] = ccdPtAddEdge (polytope, v[1 ], v[2 ]);
855- e[2 ] = ccdPtAddEdge (polytope, v[2 ], v[0 ]);
856- *nearest = (ccd_pt_el_t *)ccdPtAddFace (polytope, e[0 ], e[1 ], e[2 ]);
857- if (*nearest == NULL ) return -2 ;
854+ // The scaled distance of the second candidate,
855+ ccdVec3Scale (&dir, -CCD_ONE);
856+ __ccdSupport (obj1, obj2, &dir, ccd, &candidate_2);
857+ const ccd_real_t scale_distance_2 = ccdVec3Dot (&dir, &candidate_2.v );
858858
859- return -1 ;
860- }
861- // Form a tetrahedron with abc as one face, pick either d or d2, based
862- // on which one has larger distance to the face abc. We pick the larger
863- // distance because it gives a tetrahedron with larger volume, so potentially
864- // more "expanded" than the one with the smaller volume.
859+ // Form a tetrahedron with abc as one face and a fourth point `v`.
865860 auto FormTetrahedron = [polytope, a, b, c, &v,
866861 &e](const ccd_support_t & new_support) -> int {
867862 v[0 ] = ccdPtAddVertex (polytope, a);
@@ -892,10 +887,13 @@ static int convert2SimplexToTetrahedron(const void* obj1, const void* obj2,
892887 return 0 ;
893888 };
894889
895- if (std::abs (dist_squared) > std::abs (dist_squared_opposite)) {
896- return FormTetrahedron (d);
890+ // Note: it is highly unlikely that both of these distances are functionally
891+ // zero. That would require a Minkowski sum that is flat w.r.t. a particular
892+ // direction. That would require that both shapes be flat in that direction.
893+ if (std::abs (scale_distance_1) > std::abs (scale_distance_2)) {
894+ return FormTetrahedron (candidate_1);
897895 } else {
898- return FormTetrahedron (d2 );
896+ return FormTetrahedron (candidate_2 );
899897 }
900898}
901899
@@ -1047,13 +1045,12 @@ static bool triangle_area_is_zero(const ccd_vec3_t& a, const ccd_vec3_t& b,
10471045/* *
10481046 * Computes the normal vector of a triangular face on a polytope, and the normal
10491047 * vector points outward from the polytope. Notice we assume that the origin
1050- * lives within the polytope, and the normal vector may not have unit length .
1048+ * lives within the polytope.
10511049 * @param[in] polytope The polytope on which the face lives. We assume that the
10521050 * origin also lives inside the polytope.
10531051 * @param[in] face The face for which the normal vector is computed.
1054- * @retval dir The vector normal to the face, and points outward from the
1055- * polytope. `dir` is unnormalized, that it does not necessarily have a unit
1056- * length.
1052+ * @retval dir The unit vector normal to the face, and points outward from the
1053+ * polytope.
10571054 * @throws UnexpectedConfigurationException if built in debug mode _and_ the
10581055 * triangle has zero area (either by being too small, or being co-linear).
10591056 */
@@ -1080,13 +1077,12 @@ static ccd_vec3_t faceNormalPointingOutward(const ccd_pt_t* polytope,
10801077 &(face->edge [0 ]->vertex [0 ]->v .v ));
10811078 ccdVec3Sub2 (&e2 , &(face->edge [1 ]->vertex [1 ]->v .v ),
10821079 &(face->edge [1 ]->vertex [0 ]->v .v ));
1083- ccd_vec3_t dir;
10841080 // TODO(hongkai.dai): we ignore the degeneracy here, namely we assume e1 and
10851081 // e2 are not colinear. We should check if e1 and e2 are colinear, and handle
10861082 // this corner case.
1087- ccdVec3Cross (&dir, & e1 , & e2 ) ;
1088- const ccd_real_t dir_norm = std::sqrt ( ccdVec3Len2 (&dir) );
1089- ccd_vec3_t unit_dir = dir ;
1083+ ccd_vec3_t unit_dir ;
1084+ ccdVec3Cross (&unit_dir, & e1 , & e2 );
1085+ const ccd_real_t dir_norm = std::sqrt ( ccdVec3Len2 (&unit_dir)) ;
10901086 ccdVec3Scale (&unit_dir, 1.0 / dir_norm);
10911087 // The winding of the triangle is *not* guaranteed. The normal `n = e₁ × e₂`
10921088 // may point inside or outside. We rely on the fact that the origin lies
@@ -1107,59 +1103,80 @@ static ccd_vec3_t faceNormalPointingOutward(const ccd_pt_t* polytope,
11071103 // seems large, the fall through case of comparing the maximum distance will
11081104 // always guarantee correctness.
11091105 const ccd_real_t dist_tol = 0.01 ;
1110- // origin_distance_to_plane computes the signed distance from the origin to
1111- // the plane nᵀ * (x - v) = 0 coinciding with the triangle, where v is a
1106+
1107+ // Note: all distances are *signed* distances.
1108+
1109+ // Origin_distance_to_plane computes the signed distance from the origin to
1110+ // the plane nᵀ * (x - v) = 0 coinciding with the triangle, where v is a
11121111 // point on the triangle.
11131112 const ccd_real_t origin_distance_to_plane =
11141113 ccdVec3Dot (&unit_dir, &(face->edge [0 ]->vertex [0 ]->v .v ));
11151114 if (origin_distance_to_plane < -dist_tol) {
1116- // Origin is more than `dist_tol` away from the plane, but the negative
1117- // value implies that the normal vector is pointing in the wrong direction;
1118- // flip it.
1119- ccdVec3Scale (&dir, ccd_real_t (- 1 ) );
1115+ // Origin is more than `dist_tol` away from the plane, so it serves as a
1116+ // reliable witness. The negative value implies that the normal vector is
1117+ // pointing in the wrong direction; flip it.
1118+ ccdVec3Scale (&unit_dir, -CCD_ONE );
11201119 } else if (-dist_tol <= origin_distance_to_plane &&
11211120 origin_distance_to_plane <= dist_tol) {
11221121 // The origin is close to the plane of the face. Pick another vertex to test
11231122 // the normal direction.
1124- ccd_real_t max_distance_to_plane = -CCD_REAL_MAX;
1125- ccd_real_t min_distance_to_plane = CCD_REAL_MAX;
1123+
1124+ // For the final test below, we need max_distance_to_plane to be
1125+ // non-negative. Mathematically, the measured max_distance_to_plane is
1126+ // strictly non-negative; we evaluate the distance of *all* polytope
1127+ // vertices and know the distance to the face vertices is, by construction,
1128+ // zero.
1129+ //
1130+ // Similarly, if everything were perfectly co-planar the *largest* minimum
1131+ // value we would get would be the face vertices' zero distance.
1132+ //
1133+ // Rather than relying on numerical accuracy to yield an exact zero
1134+ // (generally a bad bet), we can initialize the max/min values to zero,
1135+ // representing the limits on those values imposed by the face vertices.
1136+ ccd_real_t max_distance_to_plane = 0 ;
1137+ ccd_real_t min_distance_to_plane = 0 ;
11261138 ccd_pt_vertex_t * v;
11271139 // If the magnitude of the distance_to_plane is larger than dist_tol,
11281140 // then it means one of the vertices is at least `dist_tol` away from the
11291141 // plane coinciding with the face.
11301142 ccdListForEachEntry (&polytope->vertices , v, ccd_pt_vertex_t , list) {
1131- // distance_to_plane is the signed distance from the
1132- // vertex v->v.v to the face, i.e., distance_to_plane = nᵀ *
1133- // (v->v.v - face_point). Note that origin_distance_to_plane = nᵀ *
1134- // face_point.
1143+ // TODO(SeanCurtis-TRI): We might consider testing the vertex v against
1144+ // the known face vertices and skip it if v is one of those three.
1145+
1146+ // distance_to_plane is the signed distance from the vertex v->v.v to the
1147+ // face, i.e., distance_to_plane = nᵀ * (v->v.v - face_point). Note that
1148+ // origin_distance_to_plane = nᵀ * face_point.
11351149 const ccd_real_t distance_to_plane =
11361150 ccdVec3Dot (&unit_dir, &(v->v .v )) - origin_distance_to_plane;
11371151 if (distance_to_plane > dist_tol) {
11381152 // The vertex is at least dist_tol away from the face plane, on the same
1139- // direction of `dir `. So we flip dir to point it outward from the
1140- // polytope.
1141- ccdVec3Scale (&dir, ccd_real_t (- 1 ) );
1142- return dir ;
1153+ // direction of `unit_dir `. So we flip unit_dir to point it outward from
1154+ // the polytope.
1155+ ccdVec3Scale (&unit_dir, -CCD_ONE );
1156+ return unit_dir ;
11431157 } else if (distance_to_plane < -dist_tol) {
11441158 // The vertex is at least `dist_tol` away from the face plane, on the
1145- // opposite direction of `dir`. So `dir` points outward already.
1146- return dir;
1159+ // opposite direction of `unit_dir`. So `unit_dir` points outward
1160+ // already.
1161+ return unit_dir;
11471162 } else {
11481163 max_distance_to_plane =
11491164 std::max (max_distance_to_plane, distance_to_plane);
11501165 min_distance_to_plane =
11511166 std::min (min_distance_to_plane, distance_to_plane);
11521167 }
11531168 }
1169+
11541170 // If max_distance_to_plane > |min_distance_to_plane|, it means that the
1155- // vertices that are on the positive side of the plane, has a larger maximal
1156- // distance than the vertices on the negative side of the plane. Thus we
1157- // regard that `dir` points into the polytope. Hence we flip `dir`.
1171+ // vertices that are on the positive side of the plane, have a larger
1172+ // maximal distance than the vertices on the negative side of the plane.
1173+ // Thus we regard that `unit_dir` points into the polytope. Hence we flip
1174+ // `unit_dir`.
11581175 if (max_distance_to_plane > std::abs (min_distance_to_plane)) {
1159- ccdVec3Scale (&dir, ccd_real_t (- 1 ) );
1176+ ccdVec3Scale (&unit_dir, -CCD_ONE );
11601177 }
11611178 }
1162- return dir ;
1179+ return unit_dir ;
11631180}
11641181
11651182// Return true if the point `pt` is on the outward side of the half-plane, on
@@ -1169,11 +1186,11 @@ static ccd_vec3_t faceNormalPointingOutward(const ccd_pt_t* polytope,
11691186// @param pt A point.
11701187static bool isOutsidePolytopeFace (const ccd_pt_t * polytope,
11711188 const ccd_pt_face_t * f, const ccd_vec3_t * pt) {
1172- ccd_vec3_t n = faceNormalPointingOutward (polytope, f);
1189+ ccd_vec3_t n_hat = faceNormalPointingOutward (polytope, f);
11731190 // r_VP is the vector from a vertex V on the face `f`, to the point P `pt`.
11741191 ccd_vec3_t r_VP;
11751192 ccdVec3Sub2 (&r_VP, pt, &(f->edge [0 ]->vertex [0 ]->v .v ));
1176- return ccdVec3Dot (&n , &r_VP) > 0 ;
1193+ return ccdVec3Dot (&n_hat , &r_VP) > 0 ;
11771194}
11781195
11791196#ifndef NDEBUG
@@ -1572,7 +1589,7 @@ static ccd_vec3_t supportEPADirection(const ccd_pt_t* polytope,
15721589 point on a face, and the support direction is the normal of that face,
15731590 pointing outward from the polytope.
15741591 */
1575- ccd_vec3_t dir ;
1592+ ccd_vec3_t unit_dir ;
15761593 if (ccdIsZero (nearest_feature->dist )) {
15771594 // nearest point is the origin.
15781595 switch (nearest_feature->type ) {
@@ -1590,23 +1607,23 @@ static ccd_vec3_t supportEPADirection(const ccd_pt_t* polytope,
15901607 // arbitrarily choose faces[0] normal.
15911608 const ccd_pt_edge_t * edge =
15921609 reinterpret_cast <const ccd_pt_edge_t *>(nearest_feature);
1593- dir = faceNormalPointingOutward (polytope, edge->faces [0 ]);
1610+ unit_dir = faceNormalPointingOutward (polytope, edge->faces [0 ]);
15941611 break ;
15951612 }
15961613 case CCD_PT_FACE: {
15971614 // If origin is an interior point of a face, then choose the normal of
15981615 // that face as the sample direction.
15991616 const ccd_pt_face_t * face =
16001617 reinterpret_cast <const ccd_pt_face_t *>(nearest_feature);
1601- dir = faceNormalPointingOutward (polytope, face);
1618+ unit_dir = faceNormalPointingOutward (polytope, face);
16021619 break ;
16031620 }
16041621 }
16051622 } else {
1606- ccdVec3Copy (&dir, &(nearest_feature->witness ));
1623+ ccdVec3Copy (&unit_dir, &(nearest_feature->witness ));
1624+ ccdVec3Normalize (&unit_dir);
16071625 }
1608- ccdVec3Normalize (&dir);
1609- return dir;
1626+ return unit_dir;
16101627}
16111628
16121629/* * Finds next support point (and stores it in out argument).
@@ -1773,9 +1790,9 @@ static void validateNearestFeatureOfPolytopeBeingEdge(ccd_pt_t* polytope) {
17731790 kEps * std::max (static_cast <ccd_real_t >(1.0 ), v0_dist);
17741791
17751792 for (int i = 0 ; i < 2 ; ++i) {
1793+ // faceNormalPointingOutward() already normalizes the normal.
17761794 face_normals[i] =
17771795 faceNormalPointingOutward (polytope, nearest_edge->faces [i]);
1778- ccdVec3Normalize (&face_normals[i]);
17791796 // If the origin o is on the "inner" side of the face, then
17801797 // n̂ ⋅ (o - vₑ) ≤ 0 or, with simplification, -n̂ ⋅ vₑ ≤ 0 (since n̂ ⋅ o = 0).
17811798 origin_to_face_distance[i] =
0 commit comments