Skip to content

Commit 897b13b

Browse files
authored
Use minor and major radii for Torus primitive shape (#10643)
# Objective First, some terminology: - **Minor radius**: The radius of the tube of a torus, i.e. the "half-thickness" - **Major radius**: The distance from the center of the tube to the center of the torus - **Inner radius**: The radius of the hole (if it exists), `major_radius - minor_radius` - **Outer radius**: The radius of the overall shape, `major_radius + minor_radius` - **Ring torus**: The familiar donut shape with a hole in the center, `major_radius > minor_radius` - **Horn torus**: A torus that doesn't have a hole but also isn't self-intersecting, `major_radius == minor_radius` - **Spindle torus**: A self-intersecting torus, `major_radius < minor_radius` Different tori from [Wikipedia](https://en.wikipedia.org/wiki/Torus), where *R* is the major radius and *r* is the minor radius: ![kuva](https://github.com/bevyengine/bevy/assets/57632562/53ead786-2402-43a7-ae8a-5720e6e54dcc) Currently, Bevy's torus is represented by a `radius` and `ring_radius`. I believe these correspond to the outer radius and minor radius, but they are rather confusing and inconsistent names, and they make the assumption that the torus always has a ring. I also couldn't find any other big engines using this representation; [Godot](https://docs.godotengine.org/en/stable/classes/class_torusmesh.html) and [Unity ProBuilder](https://docs.unity3d.com/Packages/[email protected]/manual/Torus.html) use the inner and outer radii, while [Unreal](https://docs.unrealengine.com/5.3/en-US/BlueprintAPI/GeometryScript/Primitives/AppendTorus/) uses the minor and major radii. [Blender](https://docs.blender.org/manual/en/latest/modeling/meshes/primitives.html#torus) supports both, but defaults to minor/major. Bevy's `Torus` primitive should have an efficient, consistent, clear and flexible representation, and the current `radius` and `ring_radius` properties are not ideal for that. ## Solution Change `Torus` to be represented by a `minor_radius` and `major_radius`. - Mathematically correct and consistent - Flexible, not restricted to ring tori - Computations and conversions are efficient - `inner_radius = major_radius - minor_radius` - `outer_radius = major_radius + minor_radius` - Mathematical formulae for things like area and volume rely on the minor and major radii, no conversion needed Perhaps the primary issue with this representation is that "minor radius" and "major radius" are rather mathematical, and an inner/outer radius can be more intuitive in some cases. However, this can be mitigated with constructors and helpers.
1 parent a22020b commit 897b13b

File tree

1 file changed

+89
-7
lines changed
  • crates/bevy_math/src/primitives

1 file changed

+89
-7
lines changed

crates/bevy_math/src/primitives/dim3.rs

Lines changed: 89 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ pub struct Cuboid {
161161
impl Primitive3d for Cuboid {}
162162

163163
impl Cuboid {
164-
/// Create a cuboid from a full x, y and z length
164+
/// Create a cuboid from a full x, y, and z length
165165
pub fn new(x_length: f32, y_length: f32, z_length: f32) -> Self {
166166
Self::from_size(Vec3::new(x_length, y_length, z_length))
167167
}
@@ -240,12 +240,94 @@ pub struct ConicalFrustum {
240240
}
241241
impl Primitive3d for ConicalFrustum {}
242242

243-
/// A torus (AKA donut) primitive.
244-
#[derive(Clone, Copy, Debug)]
243+
/// The type of torus determined by the minor and major radii
244+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
245+
pub enum TorusKind {
246+
/// A torus that has a ring.
247+
/// The major radius is greater than the minor radius
248+
Ring,
249+
/// A torus that has no hole but also doesn't intersect itself.
250+
/// The major radius is equal to the minor radius
251+
Horn,
252+
/// A self-intersecting torus.
253+
/// The major radius is less than the minor radius
254+
Spindle,
255+
/// A torus with non-geometric properties like
256+
/// a minor or major radius that is non-positive,
257+
/// infinite, or `NaN`
258+
Invalid,
259+
}
260+
261+
/// A torus primitive, often representing a ring or donut shape
262+
#[derive(Clone, Copy, Debug, PartialEq)]
245263
pub struct Torus {
246-
/// The radius of the overall shape
247-
pub radius: f32,
248-
/// The radius of the internal ring
249-
pub ring_radius: f32,
264+
/// The radius of the tube of the torus
265+
#[doc(
266+
alias = "ring_radius",
267+
alias = "tube_radius",
268+
alias = "cross_section_radius"
269+
)]
270+
pub minor_radius: f32,
271+
/// The distance from the center of the torus to the center of the tube
272+
#[doc(alias = "radius_of_revolution")]
273+
pub major_radius: f32,
250274
}
251275
impl Primitive3d for Torus {}
276+
277+
impl Torus {
278+
/// Create a new `Torus` from an inner and outer radius.
279+
///
280+
/// The inner radius is the radius of the hole, and the outer radius
281+
/// is the radius of the entire object
282+
pub fn new(inner_radius: f32, outer_radius: f32) -> Self {
283+
let minor_radius = (outer_radius - inner_radius) / 2.0;
284+
let major_radius = outer_radius - minor_radius;
285+
286+
Self {
287+
minor_radius,
288+
major_radius,
289+
}
290+
}
291+
292+
/// Get the inner radius of the torus.
293+
/// For a ring torus, this corresponds to the radius of the hole,
294+
/// or `major_radius - minor_radius`
295+
#[inline]
296+
pub fn inner_radius(&self) -> f32 {
297+
self.major_radius - self.minor_radius
298+
}
299+
300+
/// Get the outer radius of the torus.
301+
/// This corresponds to the overall radius of the entire object,
302+
/// or `major_radius + minor_radius`
303+
#[inline]
304+
pub fn outer_radius(&self) -> f32 {
305+
self.major_radius + self.minor_radius
306+
}
307+
308+
/// Get the [`TorusKind`] determined by the minor and major radii.
309+
///
310+
/// The torus can either be a *ring torus* that has a hole,
311+
/// a *horn torus* that doesn't have a hole but also isn't self-intersecting,
312+
/// or a *spindle torus* that is self-intersecting.
313+
///
314+
/// If the minor or major radius is non-positive, infinite, or `NaN`,
315+
/// [`TorusKind::Invalid`] is returned
316+
#[inline]
317+
pub fn kind(&self) -> TorusKind {
318+
// Invalid if minor or major radius is non-positive, infinite, or NaN
319+
if self.minor_radius <= 0.0
320+
|| !self.minor_radius.is_finite()
321+
|| self.major_radius <= 0.0
322+
|| !self.major_radius.is_finite()
323+
{
324+
return TorusKind::Invalid;
325+
}
326+
327+
match self.major_radius.partial_cmp(&self.minor_radius).unwrap() {
328+
std::cmp::Ordering::Greater => TorusKind::Ring,
329+
std::cmp::Ordering::Equal => TorusKind::Horn,
330+
std::cmp::Ordering::Less => TorusKind::Spindle,
331+
}
332+
}
333+
}

0 commit comments

Comments
 (0)