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.
This commit is contained in:
Lynn 2024-11-03 17:12:31 +01:00 committed by François
parent 1970b819f0
commit d7f2e431e5
No known key found for this signature in database

View file

@ -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 { impl CircularSector {
/// Create a new [`CircularSector`] from a `radius` and an `angle` /// Create a new [`CircularSector`] from a `radius` and an `angle`
#[inline(always)] #[inline(always)]
@ -382,12 +398,6 @@ impl CircularSector {
pub fn sagitta(&self) -> f32 { pub fn sagitta(&self) -> f32 {
self.arc.sagitta() 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: /// 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 { impl CircularSegment {
/// Create a new [`CircularSegment`] from a `radius`, and an `angle` /// Create a new [`CircularSegment`] from a `radius`, and an `angle`
#[inline(always)] #[inline(always)]
@ -515,17 +536,12 @@ impl CircularSegment {
pub fn sagitta(&self) -> f32 { pub fn sagitta(&self) -> f32 {
self.arc.sagitta() 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)] #[cfg(test)]
mod arc_tests { mod arc_tests {
use core::f32::consts::FRAC_PI_4; use core::f32::consts::FRAC_PI_4;
use core::f32::consts::SQRT_2;
use approx::assert_abs_diff_eq; use approx::assert_abs_diff_eq;
@ -548,7 +564,9 @@ mod arc_tests {
is_minor: bool, is_minor: bool,
is_major: bool, is_major: bool,
sector_area: f32, sector_area: f32,
sector_perimeter: f32,
segment_area: f32, segment_area: f32,
segment_perimeter: f32,
} }
impl ArcTestCase { impl ArcTestCase {
@ -581,6 +599,7 @@ mod arc_tests {
assert_abs_diff_eq!(self.apothem, sector.apothem()); assert_abs_diff_eq!(self.apothem, sector.apothem());
assert_abs_diff_eq!(self.sagitta, sector.sagitta()); assert_abs_diff_eq!(self.sagitta, sector.sagitta());
assert_abs_diff_eq!(self.sector_area, sector.area()); assert_abs_diff_eq!(self.sector_area, sector.area());
assert_abs_diff_eq!(self.sector_perimeter, sector.perimeter());
} }
fn check_segment(&self, segment: CircularSegment) { 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.apothem, segment.apothem());
assert_abs_diff_eq!(self.sagitta, segment.sagitta()); assert_abs_diff_eq!(self.sagitta, segment.sagitta());
assert_abs_diff_eq!(self.segment_area, segment.area()); 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_minor: true,
is_major: false, is_major: false,
sector_area: 0.0, sector_area: 0.0,
sector_perimeter: 2.0,
segment_area: 0.0, segment_area: 0.0,
segment_perimeter: 0.0,
}; };
tests.check_arc(Arc2d::new(1.0, 0.0)); tests.check_arc(Arc2d::new(1.0, 0.0));
@ -642,7 +664,9 @@ mod arc_tests {
is_minor: true, is_minor: true,
is_major: false, is_major: false,
sector_area: 0.0, sector_area: 0.0,
sector_perimeter: 0.0,
segment_area: 0.0, segment_area: 0.0,
segment_perimeter: 0.0,
}; };
tests.check_arc(Arc2d::new(0.0, FRAC_PI_4)); tests.check_arc(Arc2d::new(0.0, FRAC_PI_4));
@ -670,7 +694,9 @@ mod arc_tests {
is_minor: true, is_minor: true,
is_major: false, is_major: false,
sector_area: FRAC_PI_4, sector_area: FRAC_PI_4,
sector_perimeter: FRAC_PI_2 + 2.0,
segment_area: FRAC_PI_4 - 0.5, segment_area: FRAC_PI_4 - 0.5,
segment_perimeter: FRAC_PI_2 + SQRT_2,
}; };
tests.check_arc(Arc2d::from_turns(1.0, 0.25)); tests.check_arc(Arc2d::from_turns(1.0, 0.25));
@ -697,7 +723,9 @@ mod arc_tests {
is_minor: true, is_minor: true,
is_major: true, is_major: true,
sector_area: FRAC_PI_2, sector_area: FRAC_PI_2,
sector_perimeter: PI + 2.0,
segment_area: FRAC_PI_2, segment_area: FRAC_PI_2,
segment_perimeter: PI + 2.0,
}; };
tests.check_arc(Arc2d::from_radians(1.0, PI)); tests.check_arc(Arc2d::from_radians(1.0, PI));
@ -724,7 +752,9 @@ mod arc_tests {
is_minor: false, is_minor: false,
is_major: true, is_major: true,
sector_area: PI, sector_area: PI,
sector_perimeter: 2.0 * PI,
segment_area: PI, segment_area: PI,
segment_perimeter: 2.0 * PI,
}; };
tests.check_arc(Arc2d::from_degrees(1.0, 360.0)); tests.check_arc(Arc2d::from_degrees(1.0, 360.0));