_ccd_inline ccd_real_t __ccdVec3PointSegmentDist2(const ccd_vec3_t *P, const ccd_vec3_t *x0, const ccd_vec3_t *b, ccd_vec3_t *witness) { // The computation comes from solving equation of segment: // S(t) = x0 + t.d // where - x0 is initial point of segment // - d is direction of segment from x0 (|d| > 0) // - t belongs to <0, 1> interval // // Than, distance from a segment to some point P can be expressed: // D(t) = |x0 + t.d - P|^2 // which is distance from any point on segment. Minimization // of this function brings distance from P to segment. // Minimization of D(t) leads to simple quadratic equation that's // solving is straightforward. // // Bonus of this method is witness point for free. ccd_real_t dist, t; ccd_vec3_t d, a; // direction of segment ccdVec3Sub2(&d, b, x0); // precompute vector from P to x0 ccdVec3Sub2(&a, x0, P); t = -CCD_REAL(1.) * ccdVec3Dot(&a, &d); t /= ccdVec3Len2(&d); if (t < CCD_ZERO || ccdIsZero(t)){ dist = ccdVec3Dist2(x0, P); if (witness) ccdVec3Copy(witness, x0); }else if (t > CCD_ONE || ccdEq(t, CCD_ONE)){ dist = ccdVec3Dist2(b, P); if (witness) ccdVec3Copy(witness, b); }else{ if (witness){ ccdVec3Copy(witness, &d); ccdVec3Scale(witness, t); ccdVec3Add(witness, x0); dist = ccdVec3Dist2(witness, P); }else{ // recycling variables ccdVec3Scale(&d, t); ccdVec3Add(&d, &a); dist = ccdVec3Len2(&d); } } return dist; }
_ccd_inline int portalReachTolerance(const ccd_simplex_t *portal, const ccd_support_t *v4, const ccd_vec3_t *dir, const ccd_t *ccd) { ccd_vec3_t vec; ccd_real_t dot; ccdVec3Sub2(&vec, &v4->v, &ccdSimplexPoint(portal, 3)->v); dot = ccdVec3Dot(&vec, dir); return ccdEq(dot, ccd->mpr_tolerance) || dot < ccd->mpr_tolerance; }
_ccd_inline int portalReachTolerance(const ccd_simplex_t *portal, const ccd_support_t *v4, const ccd_vec3_t *dir, const ccd_t *ccd) { ccd_real_t dv1, dv2, dv3, dv4; ccd_real_t dot1, dot2, dot3; /* find the smallest dot product of dir and {v1-v4, v2-v4, v3-v4}*/ dv1 = ccdVec3Dot(&ccdSimplexPoint(portal, 1)->v, dir); dv2 = ccdVec3Dot(&ccdSimplexPoint(portal, 2)->v, dir); dv3 = ccdVec3Dot(&ccdSimplexPoint(portal, 3)->v, dir); dv4 = ccdVec3Dot(&v4->v, dir); dot1 = dv4 - dv1; dot2 = dv4 - dv2; dot3 = dv4 - dv3; dot1 = CCD_FMIN(dot1, dot2); dot1 = CCD_FMIN(dot1, dot3); return ccdEq(dot1, ccd->mpr_tolerance) || dot1 < ccd->mpr_tolerance; }
ccd_real_t ccdVec3PointTriDist2(const ccd_vec3_t *P, const ccd_vec3_t *x0, const ccd_vec3_t *B, const ccd_vec3_t *C, ccd_vec3_t *witness) { // Computation comes from analytic expression for triangle (x0, B, C) // T(s, t) = x0 + s.d1 + t.d2, where d1 = B - x0 and d2 = C - x0 and // Then equation for distance is: // D(s, t) = | T(s, t) - P |^2 // This leads to minimization of quadratic function of two variables. // The solution from is taken only if s is between 0 and 1, t is // between 0 and 1 and t + s < 1, otherwise distance from segment is // computed. ccd_vec3_t d1, d2, a; ccd_real_t u, v, w, p, q, r, d; ccd_real_t s, t, dist, dist2; ccd_vec3_t witness2; ccdVec3Sub2(&d1, B, x0); ccdVec3Sub2(&d2, C, x0); ccdVec3Sub2(&a, x0, P); u = ccdVec3Dot(&a, &a); v = ccdVec3Dot(&d1, &d1); w = ccdVec3Dot(&d2, &d2); p = ccdVec3Dot(&a, &d1); q = ccdVec3Dot(&a, &d2); r = ccdVec3Dot(&d1, &d2); d = w * v - r * r; if (ccdIsZero(d)){ // To avoid division by zero for zero (or near zero) area triangles s = t = -1.; }else{ s = (q * r - w * p) / d; t = (-s * r - q) / w; } if ((ccdIsZero(s) || s > CCD_ZERO) && (ccdEq(s, CCD_ONE) || s < CCD_ONE) && (ccdIsZero(t) || t > CCD_ZERO) && (ccdEq(t, CCD_ONE) || t < CCD_ONE) && (ccdEq(t + s, CCD_ONE) || t + s < CCD_ONE)){ if (witness){ ccdVec3Scale(&d1, s); ccdVec3Scale(&d2, t); ccdVec3Copy(witness, x0); ccdVec3Add(witness, &d1); ccdVec3Add(witness, &d2); dist = ccdVec3Dist2(witness, P); }else{ dist = s * s * v; dist += t * t * w; dist += CCD_REAL(2.) * s * t * r; dist += CCD_REAL(2.) * s * p; dist += CCD_REAL(2.) * t * q; dist += u; } }else{ dist = __ccdVec3PointSegmentDist2(P, x0, B, witness); dist2 = __ccdVec3PointSegmentDist2(P, x0, C, &witness2); if (dist2 < dist){ dist = dist2; if (witness) ccdVec3Copy(witness, &witness2); } dist2 = __ccdVec3PointSegmentDist2(P, B, C, &witness2); if (dist2 < dist){ dist = dist2; if (witness) ccdVec3Copy(witness, &witness2); } } return dist; }