@@ -73,14 +73,14 @@ namespace tmd
7373 /* *
7474 * Computes the squared distance, the nearest entity (vertex, edge or face) and the nearest point from a point to a triangle.
7575 */
76- static double point_triangle_sq_unsigned (NearestEntity& nearest_entity, Vec3d& nearest_point , const Vec3d& point, const Vec3d& v0, const Vec3d& v1, const Vec3d& v2);
76+ static void point_triangle_sq_unsigned (double & distance_sq, NearestEntity& nearest_entity, Vec3d& barycentric , const Vec3d& point, const Vec3d& v0, const Vec3d& v1, const Vec3d& v2);
7777 // -----------------------------------
7878
7979 // Struct that contains the result of a distance query
8080 struct Result
8181 {
8282 double distance = std::numeric_limits<double >::max();
83- Vec3d nearest_point ;
83+ Vec3d barycentric ;
8484 tmd::NearestEntity nearest_entity;
8585 int triangle_id = -1 ;
8686 };
@@ -287,7 +287,12 @@ inline tmd::Result tmd::TriangleMeshDistance::signed_distance(const std::array<d
287287 break ;
288288 }
289289
290- const Vec3d u = p - result.nearest_point ;
290+ const Vec3d nearest_point (
291+ result.barycentric [0 ]*this ->vertices [triangle[0 ]] +
292+ result.barycentric [1 ]*this ->vertices [triangle[1 ]] +
293+ result.barycentric [2 ]*this ->vertices [triangle[2 ]]
294+ );
295+ const Vec3d u = p - nearest_point;
291296 result.distance *= (u.dot (pseudonormal) >= 0.0 ) ? 1.0 : -1.0 ;
292297
293298 return result;
@@ -507,13 +512,14 @@ inline void tmd::TriangleMeshDistance::_query(Result& result, const Node& node,
507512 const Vec3d& v1 = this ->vertices [triangle[1 ]];
508513 const Vec3d& v2 = this ->vertices [triangle[2 ]];
509514
510- Vec3d nearest_point ;
515+ double distance_sq ;
511516 tmd::NearestEntity nearest_entity;
512- const double distance_sq = tmd::point_triangle_sq_unsigned (nearest_entity, nearest_point, point, v0, v1, v2);
517+ Vec3d barycentric;
518+ tmd::point_triangle_sq_unsigned (distance_sq, nearest_entity, barycentric, point, v0, v1, v2);
513519
514520 if (distance_sq < result.distance * result.distance ) {
515- result.nearest_point = nearest_point;
516521 result.nearest_entity = nearest_entity;
522+ result.barycentric = barycentric;
517523 result.distance = std::sqrt (distance_sq);
518524 result.triangle_id = triangle_id;
519525 }
@@ -547,260 +553,78 @@ inline void tmd::TriangleMeshDistance::_query(Result& result, const Node& node,
547553 }
548554}
549555
550- static double tmd::point_triangle_sq_unsigned (NearestEntity& nearest_entity, Vec3d& nearest_point , const Vec3d& point , const Vec3d& v0 , const Vec3d& v1 , const Vec3d& v2 )
556+ static void tmd::point_triangle_sq_unsigned (double & distance_sq, NearestEntity& nearest_entity, Vec3d& barycentric , const Vec3d& p , const Vec3d& a , const Vec3d& b , const Vec3d& c )
551557{
552- Vec3d diff = v0 - point;
553- Vec3d edge0 = v1 - v0;
554- Vec3d edge1 = v2 - v0;
555- double a00 = edge0.dot (edge0);
556- double a01 = edge0.dot (edge1);
557- double a11 = edge1.dot (edge1);
558- double b0 = diff.dot (edge0);
559- double b1 = diff.dot (edge1);
560- double c = diff.dot (diff);
561- double det = std::abs (a00 * a11 - a01 * a01);
562- double s = a01 * b1 - a11 * b0;
563- double t = a01 * b0 - a00 * b1;
564-
565- double d2 = -1.0 ;
566-
567- if (s + t <= det)
568- {
569- if (s < 0 )
570- {
571- if (t < 0 ) // region 4
572- {
573- if (b0 < 0 )
574- {
575- t = 0 ;
576- if (-b0 >= a00)
577- {
578- nearest_entity = NearestEntity::V1;
579- s = 1 ;
580- d2 = a00 + (2 ) * b0 + c;
581- }
582- else
583- {
584- nearest_entity = NearestEntity::E01 ;
585- s = -b0 / a00;
586- d2 = b0 * s + c;
587- }
588- }
589- else
590- {
591- s = 0 ;
592- if (b1 >= 0 )
593- {
594- nearest_entity = NearestEntity::V0;
595- t = 0 ;
596- d2 = c;
597- }
598- else if (-b1 >= a11)
599- {
600- nearest_entity = NearestEntity::V2;
601- t = 1 ;
602- d2 = a11 + (2 ) * b1 + c;
603- }
604- else
605- {
606- nearest_entity = NearestEntity::E02 ;
607- t = -b1 / a11;
608- d2 = b1 * t + c;
609- }
610- }
611- }
612- else // region 3
613- {
614- s = 0 ;
615- if (b1 >= 0 )
616- {
617- nearest_entity = NearestEntity::V0;
618- t = 0 ;
619- d2 = c;
620- }
621- else if (-b1 >= a11)
622- {
623- nearest_entity = NearestEntity::V2;
624- t = 1 ;
625- d2 = a11 + (2 ) * b1 + c;
626- }
627- else
628- {
629- nearest_entity = NearestEntity::E02 ;
630- t = -b1 / a11;
631- d2 = b1 * t + c;
632- }
633- }
634- }
635- else if (t < 0 ) // region 5
636- {
637- t = 0 ;
638- if (b0 >= 0 )
639- {
640- nearest_entity = NearestEntity::V0;
641- s = 0 ;
642- d2 = c;
643- }
644- else if (-b0 >= a00)
645- {
646- nearest_entity = NearestEntity::V1;
647- s = 1 ;
648- d2 = a00 + (2 ) * b0 + c;
649- }
650- else
651- {
652- nearest_entity = NearestEntity::E01 ;
653- s = -b0 / a00;
654- d2 = b0 * s + c;
655- }
656- }
657- else // region 0
658- {
659- nearest_entity = NearestEntity::F;
660- // minimum at interior point
661- double invDet = (1 ) / det;
662- s *= invDet;
663- t *= invDet;
664- d2 = s * (a00 * s + a01 * t + (2 ) * b0) +
665- t * (a01 * s + a11 * t + (2 ) * b1) + c;
666- }
558+ // This function is a modified version of the one found in the Real-Time Collision Detection book by Ericson.
559+ Vec3d ab = b - a;
560+ Vec3d ac = c - a;
561+ Vec3d bc = c - b;
562+
563+ // Compute parametric position s for projection P’ of P on AB
564+ double snom = (p - a).dot (ab), sdenom = (p - b).dot (a - b);
565+ // Compute parametric position t for projection P’ of P on AC
566+ double tnom = (p - a).dot (ac), tdenom = (p - c).dot (a - c);
567+ if (snom <= 0.0 && tnom <= 0.0 ) {
568+ nearest_entity = NearestEntity::V0;
569+ barycentric = { 1.0 , 0.0 , 0.0 };
570+ distance_sq = (p - a).squaredNorm ();
571+ return ;
667572 }
668- else
669- {
670- double tmp0, tmp1, numer, denom;
671573
672- if (s < 0 ) // region 2
673- {
674- tmp0 = a01 + b0;
675- tmp1 = a11 + b1;
676- if (tmp1 > tmp0)
677- {
678- numer = tmp1 - tmp0;
679- denom = a00 - (2 ) * a01 + a11;
680- if (numer >= denom)
681- {
682- nearest_entity = NearestEntity::V1;
683- s = 1 ;
684- t = 0 ;
685- d2 = a00 + (2 ) * b0 + c;
686- }
687- else
688- {
689- nearest_entity = NearestEntity::E12 ;
690- s = numer / denom;
691- t = 1 - s;
692- d2 = s * (a00 * s + a01 * t + (2 ) * b0) +
693- t * (a01 * s + a11 * t + (2 ) * b1) + c;
694- }
695- }
696- else
697- {
698- s = 0 ;
699- if (tmp1 <= 0 )
700- {
701- nearest_entity = NearestEntity::V2;
702- t = 1 ;
703- d2 = a11 + (2 ) * b1 + c;
704- }
705- else if (b1 >= 0 )
706- {
707- nearest_entity = NearestEntity::V0;
708- t = 0 ;
709- d2 = c;
710- }
711- else
712- {
713- nearest_entity = NearestEntity::E02 ;
714- t = -b1 / a11;
715- d2 = b1 * t + c;
716- }
717- }
718- }
719- else if (t < 0 ) // region 6
720- {
721- tmp0 = a01 + b1;
722- tmp1 = a00 + b0;
723- if (tmp1 > tmp0)
724- {
725- numer = tmp1 - tmp0;
726- denom = a00 - (2 ) * a01 + a11;
727- if (numer >= denom)
728- {
729- nearest_entity = NearestEntity::V2;
730- t = 1 ;
731- s = 0 ;
732- d2 = a11 + (2 ) * b1 + c;
733- }
734- else
735- {
736- nearest_entity = NearestEntity::E12 ;
737- t = numer / denom;
738- s = 1 - t;
739- d2 = s * (a00 * s + a01 * t + (2 ) * b0) +
740- t * (a01 * s + a11 * t + (2 ) * b1) + c;
741- }
742- }
743- else
744- {
745- t = 0 ;
746- if (tmp1 <= 0 )
747- {
748- nearest_entity = NearestEntity::V1;
749- s = 1 ;
750- d2 = a00 + (2 ) * b0 + c;
751- }
752- else if (b0 >= 0 )
753- {
754- nearest_entity = NearestEntity::V0;
755- s = 0 ;
756- d2 = c;
757- }
758- else
759- {
760- nearest_entity = NearestEntity::E01 ;
761- s = -b0 / a00;
762- d2 = b0 * s + c;
763- }
764- }
765- }
766- else // region 1
767- {
768- numer = a11 + b1 - a01 - b0;
769- if (numer <= 0 )
770- {
771- nearest_entity = NearestEntity::V2;
772- s = 0 ;
773- t = 1 ;
774- d2 = a11 + (2 ) * b1 + c;
775- }
776- else
777- {
778- denom = a00 - (2 ) * a01 + a11;
779- if (numer >= denom)
780- {
781- nearest_entity = NearestEntity::V1;
782- s = 1 ;
783- t = 0 ;
784- d2 = a00 + (2 ) * b0 + c;
785- }
786- else
787- {
788- nearest_entity = NearestEntity::E12 ;
789- s = numer / denom;
790- t = 1 - s;
791- d2 = s * (a00 * s + a01 * t + (2 ) * b0) +
792- t * (a01 * s + a11 * t + (2 ) * b1) + c;
793- }
794- }
795- }
574+ // Compute parametric position u for projection P’ of P on BC
575+ double unom = (p - b).dot (bc), udenom = (p - c).dot (b - c);
576+ if (sdenom <= 0.0 && unom <= 0.0 ) {
577+ nearest_entity = NearestEntity::V1;
578+ barycentric = { 0.0 , 1.0 , 0.0 };
579+ distance_sq = (p - b).squaredNorm ();
580+ return ;
581+ }
582+ if (tdenom <= 0.0 && udenom <= 0.0 ) {
583+ nearest_entity = NearestEntity::V2;
584+ barycentric = { 0.0 , 0.0 , 1.0 };
585+ distance_sq = (p - c).squaredNorm ();
586+ return ;
796587 }
797588
798- // Account for numerical round-off error.
799- if (d2 < 0 )
800- {
801- d2 = 0 ;
589+ // Normal for the triangle
590+ Vec3d n = ab.cross (ac);
591+
592+ // Check if P is outside AB
593+ double vc = n.dot ((a - p).cross (b - p));
594+ if (vc <= 0.0 && snom >= 0.0 && sdenom >= 0.0 ) {
595+ double arc = snom / (snom + sdenom);
596+ nearest_entity = NearestEntity::E01 ;
597+ barycentric = { 1.0 - arc, arc, 0.0 };
598+ distance_sq = (p - (barycentric[0 ]*a + barycentric[1 ]*b)).squaredNorm ();
599+ return ;
600+ }
601+
602+ // Check if P is outside BC
603+ double va = n.dot ((b - p).cross (c - p));
604+ if (va <= 0.0 && unom >= 0.0 && udenom >= 0.0 ) {
605+ double arc = unom / (unom + udenom);
606+ nearest_entity = NearestEntity::E12 ;
607+ barycentric = { 0.0 , 1.0 - arc, arc };
608+ distance_sq = (p - (barycentric[1 ]*b + barycentric[2 ]*c)).squaredNorm ();
609+ return ;
610+ }
611+
612+ // Check if P is outside AC
613+ double vb = n.dot ((c - p).cross (a - p));
614+ if (vb <= 0.0 && tnom >= 0.0 && tdenom >= 0.0 ) {
615+ double arc = tnom / (tnom + tdenom);
616+ nearest_entity = NearestEntity::E02 ;
617+ barycentric = { 1.0 - arc, 0.0 , arc };
618+ distance_sq = (p - (barycentric[0 ]*a + barycentric[2 ]*c)).squaredNorm ();
619+ return ;
802620 }
803621
804- nearest_point = v0 + s * edge0 + t * edge1;
805- return d2;
622+ // P must project inside the triangle; compute using barycentric coordinates
623+ double u = va / (va + vb + vc);
624+ double v = vb / (va + vb + vc);
625+ double w = 1.0 - u - v; // = vc / (va + vb + vc)
626+ nearest_entity = NearestEntity::F;
627+ barycentric = { u, v, w };
628+ distance_sq = (p - (u*a + v*b + w*c)).squaredNorm ();
629+ return ;
806630}
0 commit comments