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 GitHub
parent 565616622b
commit 52b8b55c2d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

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 {
/// 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));