From 52b8b55c2d0a631d091eee4d4c1fc6e867e766df Mon Sep 17 00:00:00 2001 From: Lynn <62256001+lynn-lumen@users.noreply.github.com> Date: Sun, 3 Nov 2024 17:12:31 +0100 Subject: [PATCH] Implement `Measured2d` for `Arc2d`-based primitives. (#16213) # Objective - `CircularSegment` and `CircularSector` are well defined 2D shapes with both an area and a perimeter. # Solution - This PR implements `perimeter` for both and moves the existsing `area` functions into the `Measured2d` implementations. ## Testing - The `arc_tests` have been extended to also check for perimeters. --- crates/bevy_math/src/primitives/dim2.rs | 54 +++++++++++++++++++------ 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/crates/bevy_math/src/primitives/dim2.rs b/crates/bevy_math/src/primitives/dim2.rs index d734a1879d..62d1633622 100644 --- a/crates/bevy_math/src/primitives/dim2.rs +++ b/crates/bevy_math/src/primitives/dim2.rs @@ -292,6 +292,22 @@ impl Default for CircularSector { } } +impl Measured2d for CircularSector { + #[inline(always)] + fn area(&self) -> f32 { + self.arc.radius.squared() * self.arc.half_angle + } + + #[inline(always)] + fn perimeter(&self) -> f32 { + if self.half_angle() >= PI { + self.arc.radius * 2.0 * PI + } else { + 2.0 * self.radius() + self.arc_length() + } + } +} + impl CircularSector { /// Create a new [`CircularSector`] from a `radius` and an `angle` #[inline(always)] @@ -382,12 +398,6 @@ impl CircularSector { pub fn sagitta(&self) -> f32 { self.arc.sagitta() } - - /// Returns the area of this sector - #[inline(always)] - pub fn area(&self) -> f32 { - self.arc.radius.squared() * self.arc.half_angle - } } /// A primitive representing a circular segment: @@ -425,6 +435,17 @@ impl Default for CircularSegment { } } +impl Measured2d for CircularSegment { + #[inline(always)] + fn area(&self) -> f32 { + 0.5 * self.arc.radius.squared() * (self.arc.angle() - ops::sin(self.arc.angle())) + } + + #[inline(always)] + fn perimeter(&self) -> f32 { + self.chord_length() + self.arc_length() + } +} impl CircularSegment { /// Create a new [`CircularSegment`] from a `radius`, and an `angle` #[inline(always)] @@ -515,17 +536,12 @@ impl CircularSegment { pub fn sagitta(&self) -> f32 { self.arc.sagitta() } - - /// Returns the area of this segment - #[inline(always)] - pub fn area(&self) -> f32 { - 0.5 * self.arc.radius.squared() * (self.arc.angle() - ops::sin(self.arc.angle())) - } } #[cfg(test)] mod arc_tests { use core::f32::consts::FRAC_PI_4; + use core::f32::consts::SQRT_2; use approx::assert_abs_diff_eq; @@ -548,7 +564,9 @@ mod arc_tests { is_minor: bool, is_major: bool, sector_area: f32, + sector_perimeter: f32, segment_area: f32, + segment_perimeter: f32, } impl ArcTestCase { @@ -581,6 +599,7 @@ mod arc_tests { assert_abs_diff_eq!(self.apothem, sector.apothem()); assert_abs_diff_eq!(self.sagitta, sector.sagitta()); assert_abs_diff_eq!(self.sector_area, sector.area()); + assert_abs_diff_eq!(self.sector_perimeter, sector.perimeter()); } fn check_segment(&self, segment: CircularSegment) { @@ -593,6 +612,7 @@ mod arc_tests { assert_abs_diff_eq!(self.apothem, segment.apothem()); assert_abs_diff_eq!(self.sagitta, segment.sagitta()); assert_abs_diff_eq!(self.segment_area, segment.area()); + assert_abs_diff_eq!(self.segment_perimeter, segment.perimeter()); } } @@ -615,7 +635,9 @@ mod arc_tests { is_minor: true, is_major: false, sector_area: 0.0, + sector_perimeter: 2.0, segment_area: 0.0, + segment_perimeter: 0.0, }; tests.check_arc(Arc2d::new(1.0, 0.0)); @@ -642,7 +664,9 @@ mod arc_tests { is_minor: true, is_major: false, sector_area: 0.0, + sector_perimeter: 0.0, segment_area: 0.0, + segment_perimeter: 0.0, }; tests.check_arc(Arc2d::new(0.0, FRAC_PI_4)); @@ -670,7 +694,9 @@ mod arc_tests { is_minor: true, is_major: false, sector_area: FRAC_PI_4, + sector_perimeter: FRAC_PI_2 + 2.0, segment_area: FRAC_PI_4 - 0.5, + segment_perimeter: FRAC_PI_2 + SQRT_2, }; tests.check_arc(Arc2d::from_turns(1.0, 0.25)); @@ -697,7 +723,9 @@ mod arc_tests { is_minor: true, is_major: true, sector_area: FRAC_PI_2, + sector_perimeter: PI + 2.0, segment_area: FRAC_PI_2, + segment_perimeter: PI + 2.0, }; tests.check_arc(Arc2d::from_radians(1.0, PI)); @@ -724,7 +752,9 @@ mod arc_tests { is_minor: false, is_major: true, sector_area: PI, + sector_perimeter: 2.0 * PI, segment_area: PI, + segment_perimeter: 2.0 * PI, }; tests.check_arc(Arc2d::from_degrees(1.0, 360.0));