Skip to content

Commit 20ee56e

Browse files
authored
Add Tetrahedron primitive to bevy_math::primitives (#12688)
# Objective - #10572 There is no 3D primitive available for the common shape of a tetrahedron (3-simplex). ## Solution This PR introduces a new type to the existing math primitives: - `Tetrahedron`: a polyhedron composed of four triangular faces, six straight edges, and four vertices --- ## Changelog ### Added - `Tetrahedron` primitive to the `bevy_math` crate - `Tetrahedron` tests (`area`, `volume` methods) - `impl_reflect!` declaration for `Tetrahedron` in the `bevy_reflect` crate
1 parent aa47702 commit 20ee56e

File tree

2 files changed

+125
-1
lines changed

2 files changed

+125
-1
lines changed

crates/bevy_math/src/primitives/dim3.rs

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::f32::consts::{FRAC_PI_3, PI};
33
use super::{Circle, Primitive3d};
44
use crate::{
55
bounding::{Aabb3d, Bounded3d, BoundingSphere},
6-
Dir3, InvalidDirectionError, Quat, Vec3,
6+
Dir3, InvalidDirectionError, Mat3, Quat, Vec3,
77
};
88

99
/// A sphere primitive
@@ -823,6 +823,86 @@ impl Bounded3d for Triangle3d {
823823
}
824824
}
825825

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+
826906
#[cfg(test)]
827907
mod tests {
828908
// Reference values were computed by hand and/or with external tools
@@ -985,4 +1065,40 @@ mod tests {
9851065
assert_relative_eq!(torus.area(), 33.16187);
9861066
assert_relative_eq!(torus.volume(), 4.97428, epsilon = 0.00001);
9871067
}
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+
}
9881104
}

crates/bevy_reflect/src/impls/math/primitives3d.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,11 @@ impl_reflect!(
9797
major_radius: f32,
9898
}
9999
);
100+
101+
impl_reflect!(
102+
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
103+
#[type_path = "bevy_math::primitives"]
104+
struct Tetrahedron {
105+
vertices: [Vec3; 4],
106+
}
107+
);

0 commit comments

Comments
 (0)