@@ -73,14 +73,14 @@ namespace tmd
73
73
/* *
74
74
* Computes the squared distance, the nearest entity (vertex, edge or face) and the nearest point from a point to a triangle.
75
75
*/
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);
77
77
// -----------------------------------
78
78
79
79
// Struct that contains the result of a distance query
80
80
struct Result
81
81
{
82
82
double distance = std::numeric_limits<double >::max();
83
- Vec3d nearest_point ;
83
+ Vec3d barycentric ;
84
84
tmd::NearestEntity nearest_entity;
85
85
int triangle_id = -1 ;
86
86
};
@@ -287,7 +287,12 @@ inline tmd::Result tmd::TriangleMeshDistance::signed_distance(const std::array<d
287
287
break ;
288
288
}
289
289
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;
291
296
result.distance *= (u.dot (pseudonormal) >= 0.0 ) ? 1.0 : -1.0 ;
292
297
293
298
return result;
@@ -507,13 +512,14 @@ inline void tmd::TriangleMeshDistance::_query(Result& result, const Node& node,
507
512
const Vec3d& v1 = this ->vertices [triangle[1 ]];
508
513
const Vec3d& v2 = this ->vertices [triangle[2 ]];
509
514
510
- Vec3d nearest_point ;
515
+ double distance_sq ;
511
516
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);
513
519
514
520
if (distance_sq < result.distance * result.distance ) {
515
- result.nearest_point = nearest_point;
516
521
result.nearest_entity = nearest_entity;
522
+ result.barycentric = barycentric;
517
523
result.distance = std::sqrt (distance_sq);
518
524
result.triangle_id = triangle_id;
519
525
}
@@ -547,260 +553,78 @@ inline void tmd::TriangleMeshDistance::_query(Result& result, const Node& node,
547
553
}
548
554
}
549
555
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 )
551
557
{
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 ;
667
572
}
668
- else
669
- {
670
- double tmp0, tmp1, numer, denom;
671
573
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 ;
796
587
}
797
588
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 ;
802
620
}
803
621
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 ;
806
630
}
0 commit comments