use na;
#[cfg(not(feature = "std"))]
use na::ComplexField; use crate::math::Real;
#[cfg(feature = "dim2")]
use crate::query;
use crate::query::gjk::{self, CSOPoint, VoronoiSimplex};
use crate::query::{Ray, RayCast, RayIntersection};
#[cfg(all(feature = "std", feature = "dim2"))]
use crate::shape::ConvexPolygon;
#[cfg(all(feature = "std", feature = "dim3"))]
use crate::shape::ConvexPolyhedron;
use crate::shape::{Capsule, FeatureId, Segment, SupportMap};
#[cfg(feature = "dim3")]
use crate::shape::{Cone, Cylinder};
use num::Zero;
pub fn local_ray_intersection_with_support_map_with_params<G: ?Sized>(
shape: &G,
simplex: &mut VoronoiSimplex,
ray: &Ray,
max_toi: Real,
solid: bool,
) -> Option<RayIntersection>
where
G: SupportMap,
{
let supp = shape.local_support_point(&-ray.dir);
simplex.reset(CSOPoint::single_point(supp - ray.origin.coords));
let inter = gjk::cast_local_ray(shape, simplex, ray, max_toi);
if !solid {
inter.and_then(|(toi, normal)| {
if toi.is_zero() {
let ndir = ray.dir.normalize();
let supp = shape.local_support_point(&ndir);
let eps = na::convert::<f64, Real>(0.001f64);
let shift = (supp - ray.origin).dot(&ndir) + eps;
let new_ray = Ray::new(ray.origin + ndir * shift, -ray.dir);
simplex.reset(CSOPoint::single_point(supp - new_ray.origin.coords));
gjk::cast_local_ray(shape, simplex, &new_ray, shift + eps).and_then(
|(toi, outward_normal)| {
let toi = shift - toi;
if toi <= max_toi {
Some(RayIntersection::new(
toi,
-outward_normal,
FeatureId::Unknown,
))
} else {
None
}
},
)
} else {
Some(RayIntersection::new(toi, normal, FeatureId::Unknown))
}
})
} else {
inter.map(|(toi, normal)| RayIntersection::new(toi, normal, FeatureId::Unknown))
}
}
#[cfg(feature = "dim3")]
impl RayCast for Cylinder {
fn cast_local_ray_and_get_normal(
&self,
ray: &Ray,
max_toi: Real,
solid: bool,
) -> Option<RayIntersection> {
local_ray_intersection_with_support_map_with_params(
self,
&mut VoronoiSimplex::new(),
ray,
max_toi,
solid,
)
}
}
#[cfg(feature = "dim3")]
impl RayCast for Cone {
fn cast_local_ray_and_get_normal(
&self,
ray: &Ray,
max_toi: Real,
solid: bool,
) -> Option<RayIntersection> {
local_ray_intersection_with_support_map_with_params(
self,
&mut VoronoiSimplex::new(),
ray,
max_toi,
solid,
)
}
}
impl RayCast for Capsule {
fn cast_local_ray_and_get_normal(
&self,
ray: &Ray,
max_toi: Real,
solid: bool,
) -> Option<RayIntersection> {
local_ray_intersection_with_support_map_with_params(
self,
&mut VoronoiSimplex::new(),
ray,
max_toi,
solid,
)
}
}
#[cfg(feature = "dim3")]
#[cfg(feature = "std")]
impl RayCast for ConvexPolyhedron {
fn cast_local_ray_and_get_normal(
&self,
ray: &Ray,
max_toi: Real,
solid: bool,
) -> Option<RayIntersection> {
local_ray_intersection_with_support_map_with_params(
self,
&mut VoronoiSimplex::new(),
ray,
max_toi,
solid,
)
}
}
#[cfg(feature = "dim2")]
#[cfg(feature = "std")]
impl RayCast for ConvexPolygon {
fn cast_local_ray_and_get_normal(
&self,
ray: &Ray,
max_toi: Real,
solid: bool,
) -> Option<RayIntersection> {
local_ray_intersection_with_support_map_with_params(
self,
&mut VoronoiSimplex::new(),
ray,
max_toi,
solid,
)
}
}
#[allow(unused_variables)]
impl RayCast for Segment {
fn cast_local_ray_and_get_normal(
&self,
ray: &Ray,
max_toi: Real,
solid: bool,
) -> Option<RayIntersection> {
#[cfg(feature = "dim2")]
{
use crate::math::Vector;
let seg_dir = self.scaled_direction();
let (s, t, parallel) = query::details::closest_points_line_line_parameters_eps(
&ray.origin,
&ray.dir,
&self.a,
&seg_dir,
crate::math::DEFAULT_EPSILON,
);
if parallel {
let dpos = self.a - ray.origin;
let normal = self.normal().map(|n| *n).unwrap_or_else(Vector::zeros);
if dpos.dot(&normal).abs() < crate::math::DEFAULT_EPSILON {
let dist1 = dpos.dot(&ray.dir);
let dist2 = dist1 + seg_dir.dot(&ray.dir);
match (dist1 >= 0.0, dist2 >= 0.0) {
(true, true) => {
let toi = dist1.min(dist2) / ray.dir.norm_squared();
if toi > max_toi {
None
} else if dist1 <= dist2 {
Some(RayIntersection::new(toi, normal, FeatureId::Vertex(0)))
} else {
Some(RayIntersection::new(
dist2 / ray.dir.norm_squared(),
normal,
FeatureId::Vertex(1),
))
}
}
(true, false) | (false, true) => {
Some(RayIntersection::new(0.0, normal, FeatureId::Face(0)))
}
(false, false) => {
None
}
}
} else {
None
}
} else if s >= 0.0 && s <= max_toi && t >= 0.0 && t <= 1.0 {
let normal = self.normal().map(|n| *n).unwrap_or_else(Vector::zeros);
if normal.dot(&ray.dir) > 0.0 {
Some(RayIntersection::new(s, -normal, FeatureId::Face(1)))
} else {
Some(RayIntersection::new(s, normal, FeatureId::Face(0)))
}
} else {
None
}
}
#[cfg(feature = "dim3")]
{
local_ray_intersection_with_support_map_with_params(
self,
&mut VoronoiSimplex::new(),
ray,
max_toi,
solid,
)
}
}
}