Skip to content

Add Tetrahedron primitive to bevy_math::primitives #12688

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Apr 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 117 additions & 1 deletion crates/bevy_math/src/primitives/dim3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::f32::consts::{FRAC_PI_3, PI};
use super::{Circle, Primitive3d};
use crate::{
bounding::{Aabb3d, Bounded3d, BoundingSphere},
Dir3, InvalidDirectionError, Quat, Vec3,
Dir3, InvalidDirectionError, Mat3, Quat, Vec3,
};

/// A sphere primitive
Expand Down Expand Up @@ -823,6 +823,86 @@ impl Bounded3d for Triangle3d {
}
}

/// A tetrahedron primitive.
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct Tetrahedron {
/// The vertices of the tetrahedron.
pub vertices: [Vec3; 4],
}
impl Primitive3d for Tetrahedron {}

impl Default for Tetrahedron {
/// Returns the default [`Tetrahedron`] with the vertices
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Returns the default [`Tetrahedron`] with the vertices
/// Returns a regular [`Tetrahedron`] centered at the origin with the vertices

/// `[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]`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// `[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]`.
/// `[0.5, 0.5, 0.5]`, `[-0.5, -0.5, 0.5]`, `[0.5, -0.5, -0.5]` and `[-0.5, 0.5, -0.5]`.

fn default() -> Self {
Self {
vertices: [
Vec3::new(0.0, 0.5, 0.0),
Vec3::new(-0.5, -0.5, 0.0),
Vec3::new(0.5, -0.5, 0.0),
Vec3::new(0.0, 0.0, 0.5),
Comment on lines +841 to +844
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm really confused as to why this is the default tetrahedron. I would expect a regular tetrahedron, centered at the origin. Or at least one that contains the origin. This one satisfies none of those properties. Can we have it be this instead?

Suggested change
Vec3::new(0.0, 0.5, 0.0),
Vec3::new(-0.5, -0.5, 0.0),
Vec3::new(0.5, -0.5, 0.0),
Vec3::new(0.0, 0.0, 0.5),
Vec3::new(0.5, 0.5, 0.5),
Vec3::new(-0.5, -0.5, 0.5),
Vec3::new(0.5, -0.5, -0.5),
Vec3::new(-0.5, 0.5, -0.5),

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this was chosen so that, in particular, its base is the Default Triangle3d. I guess it does contain the origin, but only on its boundary :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What @mweatherley said, in practice it's just Triangle3d base + an extra vertex on the Z-axis.

I was thinking, if we go with your suggestion would it be possible to keep the tetrahedron upright? I think that's how people typically imagine this shape to look like.

],
}
}
}

impl Tetrahedron {
/// Create a new [`Tetrahedron`] from points `a`, `b`, `c` and `d`.
#[inline(always)]
pub fn new(a: Vec3, b: Vec3, c: Vec3, d: Vec3) -> Self {
Self {
vertices: [a, b, c, d],
}
}

/// Get the surface area of the tetrahedron.
#[inline(always)]
pub fn area(&self) -> f32 {
let [a, b, c, d] = self.vertices;
let ab = b - a;
let ac = c - a;
let ad = d - a;
let bc = c - b;
let bd = d - b;
(ab.cross(ac).length()
+ ab.cross(ad).length()
+ ac.cross(ad).length()
+ bc.cross(bd).length())
/ 2.0
}

/// Get the volume of the tetrahedron.
#[inline(always)]
pub fn volume(&self) -> f32 {
self.signed_volume().abs()
}

/// Get the signed volume of the tetrahedron.
///
/// If it's negative, the normal vector of the face defined by
/// the first three points using the right-hand rule points
/// away from the fourth vertex.
#[inline(always)]
pub fn signed_volume(&self) -> f32 {
let [a, b, c, d] = self.vertices;
let ab = b - a;
let ac = c - a;
let ad = d - a;
Mat3::from_cols(ab, ac, ad).determinant() / 6.0
}

/// Get the centroid of the tetrahedron.
///
/// This function finds the geometric center of the tetrahedron
/// by averaging the vertices: `centroid = (a + b + c + d) / 4`.
#[doc(alias("center", "barycenter", "baricenter"))]
#[inline(always)]
pub fn centroid(&self) -> Vec3 {
(self.vertices[0] + self.vertices[1] + self.vertices[2] + self.vertices[3]) / 4.0
}
}

#[cfg(test)]
mod tests {
// Reference values were computed by hand and/or with external tools
Expand Down Expand Up @@ -986,4 +1066,40 @@ mod tests {
assert_relative_eq!(torus.area(), 33.16187);
assert_relative_eq!(torus.volume(), 4.97428, epsilon = 0.00001);
}

#[test]
fn tetrahedron_math() {
let tetrahedron = Tetrahedron {
vertices: [
Vec3::new(0.3, 1.0, 1.7),
Vec3::new(-2.0, -1.0, 0.0),
Vec3::new(1.8, 0.5, 1.0),
Vec3::new(-1.0, -2.0, 3.5),
],
};
assert_eq!(tetrahedron.area(), 19.251068, "incorrect area");
assert_eq!(tetrahedron.volume(), 3.2058334, "incorrect volume");
assert_eq!(
tetrahedron.signed_volume(),
3.2058334,
"incorrect signed volume"
);
assert_relative_eq!(tetrahedron.centroid(), Vec3::new(-0.225, -0.375, 1.55));

assert_eq!(Tetrahedron::default().area(), 1.4659258, "incorrect area");
assert_eq!(
Tetrahedron::default().volume(),
0.083333336,
"incorrect volume"
);
assert_eq!(
Tetrahedron::default().signed_volume(),
0.083333336,
"incorrect signed volume"
);
assert_relative_eq!(
Tetrahedron::default().centroid(),
Vec3::new(0.0, -0.125, 0.125)
);
}
}
8 changes: 8 additions & 0 deletions crates/bevy_reflect/src/impls/math/primitives3d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,11 @@ impl_reflect!(
major_radius: f32,
}
);

impl_reflect!(
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
#[type_path = "bevy_math::primitives"]
struct Tetrahedron {
vertices: [Vec3; 4],
}
);