@@ -3,7 +3,7 @@ use std::f32::consts::{FRAC_PI_3, PI};
3
3
use super :: { Circle , Primitive3d } ;
4
4
use crate :: {
5
5
bounding:: { Aabb3d , Bounded3d , BoundingSphere } ,
6
- Dir3 , InvalidDirectionError , Quat , Vec3 ,
6
+ Dir3 , InvalidDirectionError , Mat3 , Quat , Vec3 ,
7
7
} ;
8
8
9
9
/// A sphere primitive
@@ -823,6 +823,86 @@ impl Bounded3d for Triangle3d {
823
823
}
824
824
}
825
825
826
+ /// A tetrahedron primitive.
827
+ #[ derive( Clone , Copy , Debug , PartialEq ) ]
828
+ #[ cfg_attr( feature = "serialize" , derive( serde:: Serialize , serde:: Deserialize ) ) ]
829
+ pub struct Tetrahedron {
830
+ /// The vertices of the tetrahedron.
831
+ pub vertices : [ Vec3 ; 4 ] ,
832
+ }
833
+ impl Primitive3d for Tetrahedron { }
834
+
835
+ impl Default for Tetrahedron {
836
+ /// Returns the default [`Tetrahedron`] with the vertices
837
+ /// `[0.0, 0.5, 0.0]`, `[-0.5, -0.5, 0.0]`, `[0.5, -0.5, 0.0]` and `[0.0, 0.0, 0.5]`.
838
+ fn default ( ) -> Self {
839
+ Self {
840
+ vertices : [
841
+ Vec3 :: new ( 0.0 , 0.5 , 0.0 ) ,
842
+ Vec3 :: new ( -0.5 , -0.5 , 0.0 ) ,
843
+ Vec3 :: new ( 0.5 , -0.5 , 0.0 ) ,
844
+ Vec3 :: new ( 0.0 , 0.0 , 0.5 ) ,
845
+ ] ,
846
+ }
847
+ }
848
+ }
849
+
850
+ impl Tetrahedron {
851
+ /// Create a new [`Tetrahedron`] from points `a`, `b`, `c` and `d`.
852
+ #[ inline( always) ]
853
+ pub fn new ( a : Vec3 , b : Vec3 , c : Vec3 , d : Vec3 ) -> Self {
854
+ Self {
855
+ vertices : [ a, b, c, d] ,
856
+ }
857
+ }
858
+
859
+ /// Get the surface area of the tetrahedron.
860
+ #[ inline( always) ]
861
+ pub fn area ( & self ) -> f32 {
862
+ let [ a, b, c, d] = self . vertices ;
863
+ let ab = b - a;
864
+ let ac = c - a;
865
+ let ad = d - a;
866
+ let bc = c - b;
867
+ let bd = d - b;
868
+ ( ab. cross ( ac) . length ( )
869
+ + ab. cross ( ad) . length ( )
870
+ + ac. cross ( ad) . length ( )
871
+ + bc. cross ( bd) . length ( ) )
872
+ / 2.0
873
+ }
874
+
875
+ /// Get the volume of the tetrahedron.
876
+ #[ inline( always) ]
877
+ pub fn volume ( & self ) -> f32 {
878
+ self . signed_volume ( ) . abs ( )
879
+ }
880
+
881
+ /// Get the signed volume of the tetrahedron.
882
+ ///
883
+ /// If it's negative, the normal vector of the face defined by
884
+ /// the first three points using the right-hand rule points
885
+ /// away from the fourth vertex.
886
+ #[ inline( always) ]
887
+ pub fn signed_volume ( & self ) -> f32 {
888
+ let [ a, b, c, d] = self . vertices ;
889
+ let ab = b - a;
890
+ let ac = c - a;
891
+ let ad = d - a;
892
+ Mat3 :: from_cols ( ab, ac, ad) . determinant ( ) / 6.0
893
+ }
894
+
895
+ /// Get the centroid of the tetrahedron.
896
+ ///
897
+ /// This function finds the geometric center of the tetrahedron
898
+ /// by averaging the vertices: `centroid = (a + b + c + d) / 4`.
899
+ #[ doc( alias( "center" , "barycenter" , "baricenter" ) ) ]
900
+ #[ inline( always) ]
901
+ pub fn centroid ( & self ) -> Vec3 {
902
+ ( self . vertices [ 0 ] + self . vertices [ 1 ] + self . vertices [ 2 ] + self . vertices [ 3 ] ) / 4.0
903
+ }
904
+ }
905
+
826
906
#[ cfg( test) ]
827
907
mod tests {
828
908
// Reference values were computed by hand and/or with external tools
@@ -985,4 +1065,40 @@ mod tests {
985
1065
assert_relative_eq ! ( torus. area( ) , 33.16187 ) ;
986
1066
assert_relative_eq ! ( torus. volume( ) , 4.97428 , epsilon = 0.00001 ) ;
987
1067
}
1068
+
1069
+ #[ test]
1070
+ fn tetrahedron_math ( ) {
1071
+ let tetrahedron = Tetrahedron {
1072
+ vertices : [
1073
+ Vec3 :: new ( 0.3 , 1.0 , 1.7 ) ,
1074
+ Vec3 :: new ( -2.0 , -1.0 , 0.0 ) ,
1075
+ Vec3 :: new ( 1.8 , 0.5 , 1.0 ) ,
1076
+ Vec3 :: new ( -1.0 , -2.0 , 3.5 ) ,
1077
+ ] ,
1078
+ } ;
1079
+ assert_eq ! ( tetrahedron. area( ) , 19.251068 , "incorrect area" ) ;
1080
+ assert_eq ! ( tetrahedron. volume( ) , 3.2058334 , "incorrect volume" ) ;
1081
+ assert_eq ! (
1082
+ tetrahedron. signed_volume( ) ,
1083
+ 3.2058334 ,
1084
+ "incorrect signed volume"
1085
+ ) ;
1086
+ assert_relative_eq ! ( tetrahedron. centroid( ) , Vec3 :: new( -0.225 , -0.375 , 1.55 ) ) ;
1087
+
1088
+ assert_eq ! ( Tetrahedron :: default ( ) . area( ) , 1.4659258 , "incorrect area" ) ;
1089
+ assert_eq ! (
1090
+ Tetrahedron :: default ( ) . volume( ) ,
1091
+ 0.083333336 ,
1092
+ "incorrect volume"
1093
+ ) ;
1094
+ assert_eq ! (
1095
+ Tetrahedron :: default ( ) . signed_volume( ) ,
1096
+ 0.083333336 ,
1097
+ "incorrect signed volume"
1098
+ ) ;
1099
+ assert_relative_eq ! (
1100
+ Tetrahedron :: default ( ) . centroid( ) ,
1101
+ Vec3 :: new( 0.0 , -0.125 , 0.125 )
1102
+ ) ;
1103
+ }
988
1104
}
0 commit comments