use std::ptr::null;
use oxijolt_sys::*;
use super::{
initialize, validate_convex_radius, within_extent, Shape, ShapeSettings, BEYOND_EXTENT,
};
use crate::limits;
use crate::math::{is_finite_non_negative, is_finite_positive};
use crate::ShapeError;
const MAX_TAPER: f64 = 1.0 - 1.0 / (1u32 << 21) as f64;
const MIN_TAPERED_CYLINDER_RADIUS: f32 = 1.084_202_2e-19;
const SPHERE_LIKE: &str = "one end sphere of the tapered capsule contains the other; use a sphere";
impl Shape {
pub fn new_tapered_capsule(
half_height: f32,
top_radius: f32,
bottom_radius: f32,
) -> Result<Self, ShapeError> {
validate_tapered_capsule(half_height, top_radius, bottom_radius)?;
initialize()?;
let settings = unsafe {
ShapeSettings::from_raw(
JPH_TaperedCapsuleShapeSettings_Create(half_height, top_radius, bottom_radius)
.cast(),
)
}?;
settings.create()?.within_extent_bounds()
}
pub fn new_tapered_cylinder(
half_height: f32,
top_radius: f32,
bottom_radius: f32,
) -> Result<Self, ShapeError> {
Self::new_tapered_cylinder_with_convex_radius(
half_height,
top_radius,
bottom_radius,
JPH_DEFAULT_CONVEX_RADIUS as f32,
)
}
pub fn new_tapered_cylinder_with_convex_radius(
half_height: f32,
top_radius: f32,
bottom_radius: f32,
convex_radius: f32,
) -> Result<Self, ShapeError> {
validate_tapered_cylinder(half_height, top_radius, bottom_radius)?;
validate_convex_radius(convex_radius)?;
initialize()?;
let settings = unsafe {
ShapeSettings::from_raw(
JPH_TaperedCylinderShapeSettings_Create(
half_height,
top_radius,
bottom_radius,
convex_radius,
null(),
)
.cast(),
)
}?;
settings.create()?.within_extent_bounds()
}
}
fn validate_tapered_capsule(
half_height: f32,
top_radius: f32,
bottom_radius: f32,
) -> Result<(), ShapeError> {
if ![half_height, top_radius, bottom_radius]
.into_iter()
.all(is_finite_positive)
{
return Err(ShapeError::InvalidValue(
"tapered capsule half height and radii must be finite and positive",
));
}
let (larger, smaller) = (top_radius.max(bottom_radius), top_radius.min(bottom_radius));
if half_height + larger > limits::MAX_SHAPE_EXTENT {
return Err(ShapeError::InvalidValue(BEYOND_EXTENT));
}
if larger >= 2.0 * half_height + smaller {
return Err(ShapeError::InvalidValue(SPHERE_LIKE));
}
let taper = f64::from(bottom_radius - top_radius).abs();
if taper > f64::from(2.0 * half_height) * MAX_TAPER {
return Err(ShapeError::InvalidValue(SPHERE_LIKE));
}
Ok(())
}
fn validate_tapered_cylinder(
half_height: f32,
top_radius: f32,
bottom_radius: f32,
) -> Result<(), ShapeError> {
if !(is_finite_positive(half_height)
&& is_finite_non_negative(top_radius)
&& is_finite_non_negative(bottom_radius))
{
return Err(ShapeError::InvalidValue(
"tapered cylinder half height must be finite and positive, its radii finite and not negative",
));
}
if ![half_height, top_radius, bottom_radius]
.into_iter()
.all(within_extent)
{
return Err(ShapeError::InvalidValue(BEYOND_EXTENT));
}
if top_radius == bottom_radius {
return Err(ShapeError::InvalidValue(
"tapered cylinder radii must differ; use a cylinder",
));
}
if top_radius.max(bottom_radius) < MIN_TAPERED_CYLINDER_RADIUS {
return Err(ShapeError::InvalidValue(
"tapered cylinder larger radius must be at least 2^-63 m",
));
}
Ok(())
}
#[cfg(test)]
mod tests;