#[cfg(feature = "dim3")] use crate::geometry::Cylinder; use crate::geometry::ShapeType; use crate::math::{Isometry, Point, Vector}; use na::Unit; use ncollide::query::{ algorithms::VoronoiSimplex, PointProjection, PointQuery, Ray, RayCast, RayIntersection, }; use ncollide::shape::{FeatureId, SupportMap}; /// A shape which corner can be rounded. pub trait Roundable { /// The ShapeType fo this shape after rounding its corners. fn rounded_shape_type() -> ShapeType; } #[cfg(feature = "dim3")] impl Roundable for Cylinder { fn rounded_shape_type() -> ShapeType { ShapeType::RoundCylinder } } /// A rounded shape. #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] pub struct Rounded { /// The shape being rounded. pub shape: S, /// The rounding radius. pub radius: f32, } impl Rounded { /// Create sa new shape where all its edges and vertices are rounded by a radius of `radius`. /// /// This is done by applying a dilation of the given radius to the shape. pub fn new(shape: S, radius: f32) -> Self { Self { shape, radius } } } impl> SupportMap for Rounded { fn local_support_point(&self, dir: &Vector) -> Point { self.local_support_point_toward(&Unit::new_normalize(*dir)) } fn local_support_point_toward(&self, dir: &Unit>) -> Point { self.shape.local_support_point_toward(dir) + **dir * self.radius } fn support_point(&self, transform: &Isometry, dir: &Vector) -> Point { let local_dir = transform.inverse_transform_vector(dir); transform * self.local_support_point(&local_dir) } fn support_point_toward( &self, transform: &Isometry, dir: &Unit>, ) -> Point { let local_dir = Unit::new_unchecked(transform.inverse_transform_vector(dir)); transform * self.local_support_point_toward(&local_dir) } } impl> RayCast for Rounded { fn toi_and_normal_with_ray( &self, m: &Isometry, ray: &Ray, max_toi: f32, solid: bool, ) -> Option> { let ls_ray = ray.inverse_transform_by(m); ncollide::query::ray_intersection_with_support_map_with_params( &Isometry::identity(), self, &mut VoronoiSimplex::new(), &ls_ray, max_toi, solid, ) .map(|mut res| { res.normal = m * res.normal; res }) } } // TODO: if PointQuery had a `project_point_with_normal` method, we could just // call this and adjust the projected point accordingly. impl> PointQuery for Rounded { #[inline] fn project_point( &self, m: &Isometry, point: &Point, solid: bool, ) -> PointProjection { ncollide::query::point_projection_on_support_map( m, self, &mut VoronoiSimplex::new(), point, solid, ) } #[inline] fn project_point_with_feature( &self, m: &Isometry, point: &Point, ) -> (PointProjection, FeatureId) { (self.project_point(m, point, false), FeatureId::Unknown) } }