Skip to main content

bevy_shape/bounding/
raycast2d.rs

1use super::{Aabb2d, BoundingCircle, IntersectsVolume};
2use crate::Ray2d;
3use bevy_math::{
4    ops::{self, FloatPow},
5    Dir2, Vec2,
6};
7
8#[cfg(feature = "bevy_reflect")]
9use bevy_reflect::Reflect;
10
11/// A raycast intersection test for 2D bounding volumes
12#[derive(#[automatically_derived]impl ::core::clone::Clone for RayCast2d {
    #[inline]
    fn clone(&self) -> Self {
        RayCast2d {
            ray: ::core::clone::Clone::clone(&self.ray),
            max: ::core::clone::Clone::clone(&self.max),
            direction_recip: ::core::clone::Clone::clone(&self.direction_recip),
        }
    }
}Clone, #[automatically_derived]impl ::core::fmt::Debug for RayCast2d {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f, "RayCast2d",
            "ray", &self.ray, "max", &self.max, "direction_recip",
            &&self.direction_recip)
    }
}Debug)]
13#[cfg_attr(feature = "bevy_reflect", derive(const _: () =
    {
        impl bevy_reflect::GetTypeRegistration for RayCast2d where  {
            fn get_type_registration() -> bevy_reflect::TypeRegistration {
                let mut registration =
                    bevy_reflect::TypeRegistration::of::<Self>();
                registration.insert(<bevy_reflect::ReflectFromPtr as
                            bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
                registration.insert(<bevy_reflect::ReflectFromReflect as
                            bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
                registration
            }
            #[inline(never)]
            fn register_type_dependencies(registry:
                    &mut bevy_reflect::TypeRegistry) {
                <Ray2d as
                        bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
                <f32 as
                        bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
                <Vec2 as
                        bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
            }
        }
        impl bevy_reflect::Typed for RayCast2d where  {
            #[inline(never)]
            fn type_info() -> &'static bevy_reflect::TypeInfo {
                static CELL: bevy_reflect::utility::NonGenericTypeInfoCell =
                    bevy_reflect::utility::NonGenericTypeInfoCell::new();
                CELL.get_or_set(#[inline(never)] ||
                        {
                            bevy_reflect::TypeInfo::Struct(bevy_reflect::structs::StructInfo::new::<Self>(&[bevy_reflect::NamedField::new::<Ray2d>("ray"),
                                                bevy_reflect::NamedField::new::<f32>("max"),
                                                bevy_reflect::NamedField::new::<Vec2>("direction_recip")]))
                        })
            }
        }
        #[allow(deprecated, reason =
        "derives on a deprecated type shouldn't be considered a usage")]
        impl bevy_reflect::TypePath for RayCast2d where  {
            fn type_path() -> &'static str {
                "bevy_shape::bounding::raycast2d::RayCast2d"
            }
            fn short_type_path() -> &'static str { "RayCast2d" }
            fn type_ident() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("RayCast2d")
            }
            fn crate_name() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_shape::bounding::raycast2d".split(':').next().unwrap())
            }
            fn module_path() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_shape::bounding::raycast2d")
            }
        }
        impl bevy_reflect::Reflect for RayCast2d where  {
            #[inline]
            fn into_any(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn ::core::any::Any> {
                self
            }
            #[inline]
            fn as_any(&self) -> &dyn ::core::any::Any { self }
            #[inline]
            fn as_any_mut(&mut self) -> &mut dyn ::core::any::Any { self }
            #[inline]
            fn into_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect> {
                self
            }
            #[inline]
            fn as_reflect(&self) -> &dyn bevy_reflect::Reflect { self }
            #[inline]
            fn as_reflect_mut(&mut self) -> &mut dyn bevy_reflect::Reflect {
                self
            }
            #[inline]
            fn set(&mut self,
                value:
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>)
                ->
                    ::core::result::Result<(),
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>> {
                *self = <dyn bevy_reflect::Reflect>::take(value)?;
                ::core::result::Result::Ok(())
            }
        }
        impl bevy_reflect::structs::Struct for RayCast2d where  {
            fn field(&self, name: &str)
                -> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
                match name {
                    "ray" => ::core::option::Option::Some(&self.ray),
                    "max" => ::core::option::Option::Some(&self.max),
                    "direction_recip" =>
                        ::core::option::Option::Some(&self.direction_recip),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_mut(&mut self, name: &str)
                ->
                    ::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
                match name {
                    "ray" => ::core::option::Option::Some(&mut self.ray),
                    "max" => ::core::option::Option::Some(&mut self.max),
                    "direction_recip" =>
                        ::core::option::Option::Some(&mut self.direction_recip),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_at(&self, index: usize)
                -> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
                match index {
                    0usize => ::core::option::Option::Some(&self.ray),
                    1usize => ::core::option::Option::Some(&self.max),
                    2usize =>
                        ::core::option::Option::Some(&self.direction_recip),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_at_mut(&mut self, index: usize)
                ->
                    ::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
                match index {
                    0usize => ::core::option::Option::Some(&mut self.ray),
                    1usize => ::core::option::Option::Some(&mut self.max),
                    2usize =>
                        ::core::option::Option::Some(&mut self.direction_recip),
                    _ => ::core::option::Option::None,
                }
            }
            fn name_at(&self, index: usize) -> ::core::option::Option<&str> {
                match index {
                    0usize => ::core::option::Option::Some("ray"),
                    1usize => ::core::option::Option::Some("max"),
                    2usize => ::core::option::Option::Some("direction_recip"),
                    _ => ::core::option::Option::None,
                }
            }
            fn index_of_name(&self, name: &str)
                -> ::core::option::Option<usize> {
                match name {
                    "ray" => ::core::option::Option::Some(0usize),
                    "max" => ::core::option::Option::Some(1usize),
                    "direction_recip" => ::core::option::Option::Some(2usize),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_len(&self) -> usize { 3usize }
            fn iter_fields(&self) -> bevy_reflect::structs::FieldIter {
                bevy_reflect::structs::FieldIter::new(self)
            }
            fn to_dynamic_struct(&self)
                ->
                    ::core::result::Result<bevy_reflect::structs::DynamicStruct,
                    bevy_reflect::ReflectCloneError> {
                let mut dynamic: bevy_reflect::structs::DynamicStruct =
                    ::core::default::Default::default();
                dynamic.set_represented_type(bevy_reflect::PartialReflect::get_represented_type_info(self));
                dynamic.insert_boxed("ray",
                    bevy_reflect::PartialReflect::to_dynamic(&self.ray)?);
                dynamic.insert_boxed("max",
                    bevy_reflect::PartialReflect::to_dynamic(&self.max)?);
                dynamic.insert_boxed("direction_recip",
                    bevy_reflect::PartialReflect::to_dynamic(&self.direction_recip)?);
                ::core::result::Result::Ok(dynamic)
            }
        }
        impl bevy_reflect::PartialReflect for RayCast2d where  {
            #[inline]
            fn get_represented_type_info(&self)
                -> ::core::option::Option<&'static bevy_reflect::TypeInfo> {
                ::core::option::Option::Some(<Self as
                            bevy_reflect::Typed>::type_info())
            }
            #[inline]
            fn try_apply(&mut self, value: &dyn bevy_reflect::PartialReflect)
                -> ::core::result::Result<(), bevy_reflect::ApplyError> {
                if let bevy_reflect::ReflectRef::Struct(struct_value) =
                        bevy_reflect::PartialReflect::reflect_ref(value) {
                    for (name, value) in
                        bevy_reflect::structs::Struct::iter_fields(struct_value) {
                        if let ::core::option::Option::Some(v) =
                                bevy_reflect::structs::Struct::field_mut(self, name) {
                            bevy_reflect::PartialReflect::try_apply(v, value)?;
                        }
                    }
                } else {
                    return ::core::result::Result::Err(bevy_reflect::ApplyError::MismatchedKinds {
                                from_kind: bevy_reflect::PartialReflect::reflect_kind(value),
                                to_kind: bevy_reflect::ReflectKind::Struct,
                            });
                }
                ::core::result::Result::Ok(())
            }
            #[inline]
            fn reflect_kind(&self) -> bevy_reflect::ReflectKind {
                bevy_reflect::ReflectKind::Struct
            }
            #[inline]
            fn reflect_ref(&self) -> bevy_reflect::ReflectRef {
                bevy_reflect::ReflectRef::Struct(self)
            }
            #[inline]
            fn reflect_mut(&mut self) -> bevy_reflect::ReflectMut {
                bevy_reflect::ReflectMut::Struct(self)
            }
            #[inline]
            fn reflect_owned(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                -> bevy_reflect::ReflectOwned {
                bevy_reflect::ReflectOwned::Struct(self)
            }
            #[inline]
            fn try_into_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    ::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect>> {
                ::core::result::Result::Ok(self)
            }
            #[inline]
            fn try_as_reflect(&self)
                -> ::core::option::Option<&dyn bevy_reflect::Reflect> {
                ::core::option::Option::Some(self)
            }
            #[inline]
            fn try_as_reflect_mut(&mut self)
                -> ::core::option::Option<&mut dyn bevy_reflect::Reflect> {
                ::core::option::Option::Some(self)
            }
            #[inline]
            fn into_partial_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect> {
                self
            }
            #[inline]
            fn as_partial_reflect(&self)
                -> &dyn bevy_reflect::PartialReflect {
                self
            }
            #[inline]
            fn as_partial_reflect_mut(&mut self)
                -> &mut dyn bevy_reflect::PartialReflect {
                self
            }
            fn reflect_partial_eq(&self,
                value: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<bool> {
                (bevy_reflect::structs::struct_partial_eq)(self, value)
            }
            fn reflect_partial_cmp(&self,
                value: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<::core::cmp::Ordering> {
                (bevy_reflect::structs::struct_partial_cmp)(self, value)
            }
            fn debug(&self, f: &mut ::core::fmt::Formatter<'_>)
                -> ::core::fmt::Result {
                ::core::fmt::Debug::fmt(self, f)
            }
            #[inline]
            fn reflect_clone(&self)
                ->
                    ::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
                    bevy_reflect::ReflectCloneError> {
                ::core::result::Result::Ok(bevy_reflect::__macro_exports::alloc_utils::Box::new(::core::clone::Clone::clone(self)))
            }
        }
        impl bevy_reflect::FromReflect for RayCast2d where  {
            fn from_reflect(reflect: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<Self> {
                if let bevy_reflect::ReflectRef::Struct(__ref_struct) =
                        bevy_reflect::PartialReflect::reflect_ref(reflect) {
                    let __this =
                        Self {
                            ray: <Ray2d as
                                        bevy_reflect::FromReflect>::from_reflect(bevy_reflect::structs::Struct::field(__ref_struct,
                                            "ray")?)?,
                            max: <f32 as
                                        bevy_reflect::FromReflect>::from_reflect(bevy_reflect::structs::Struct::field(__ref_struct,
                                            "max")?)?,
                            direction_recip: <Vec2 as
                                        bevy_reflect::FromReflect>::from_reflect(bevy_reflect::structs::Struct::field(__ref_struct,
                                            "direction_recip")?)?,
                        };
                    ::core::option::Option::Some(__this)
                } else { ::core::option::Option::None }
            }
        }
    };Reflect), reflect(Debug, Clone))]
14pub struct RayCast2d {
15    /// The ray for the test
16    pub ray: Ray2d,
17    /// The maximum distance for the ray
18    pub max: f32,
19    /// The multiplicative inverse direction of the ray
20    direction_recip: Vec2,
21}
22
23impl RayCast2d {
24    /// Construct a [`RayCast2d`] from an origin, [`Dir2`], and max distance.
25    pub fn new(origin: Vec2, direction: Dir2, max: f32) -> Self {
26        Self::from_ray(Ray2d { origin, direction }, max)
27    }
28
29    /// Construct a [`RayCast2d`] from a [`Ray2d`] and max distance.
30    pub fn from_ray(ray: Ray2d, max: f32) -> Self {
31        Self {
32            ray,
33            direction_recip: ray.direction.recip(),
34            max,
35        }
36    }
37
38    /// Get the cached multiplicative inverse of the direction of the ray.
39    pub const fn direction_recip(&self) -> Vec2 {
40        self.direction_recip
41    }
42
43    /// Get the distance of an intersection with an [`Aabb2d`], if any.
44    pub fn aabb_intersection_at(&self, aabb: &Aabb2d) -> Option<f32> {
45        let (min_x, max_x) = if self.ray.direction.x.is_sign_positive() {
46            (aabb.min.x, aabb.max.x)
47        } else {
48            (aabb.max.x, aabb.min.x)
49        };
50        let (min_y, max_y) = if self.ray.direction.y.is_sign_positive() {
51            (aabb.min.y, aabb.max.y)
52        } else {
53            (aabb.max.y, aabb.min.y)
54        };
55
56        // Calculate the minimum/maximum time for each axis based on how much the direction goes that
57        // way. These values can get arbitrarily large, or even become NaN, which is handled by the
58        // min/max operations below
59        let tmin_x = (min_x - self.ray.origin.x) * self.direction_recip.x;
60        let tmin_y = (min_y - self.ray.origin.y) * self.direction_recip.y;
61        let tmax_x = (max_x - self.ray.origin.x) * self.direction_recip.x;
62        let tmax_y = (max_y - self.ray.origin.y) * self.direction_recip.y;
63
64        // An axis that is not relevant to the ray direction will be NaN. When one of the arguments
65        // to min/max is NaN, the other argument is used.
66        // An axis for which the direction is the wrong way will return an arbitrarily large
67        // negative value.
68        let tmin = tmin_x.max(tmin_y).max(0.);
69        let tmax = tmax_y.min(tmax_x).min(self.max);
70
71        if tmin <= tmax {
72            Some(tmin)
73        } else {
74            None
75        }
76    }
77
78    /// Get the distance of an intersection with a [`BoundingCircle`], if any.
79    pub fn circle_intersection_at(&self, circle: &BoundingCircle) -> Option<f32> {
80        let offset = self.ray.origin - circle.center;
81        let projected = offset.dot(*self.ray.direction);
82        let cross = offset.perp_dot(*self.ray.direction);
83        let distance_squared = circle.radius().squared() - cross.squared();
84        if distance_squared < 0.
85            || ops::copysign(projected.squared(), -projected) < -distance_squared
86        {
87            None
88        } else {
89            let toi = -projected - ops::sqrt(distance_squared);
90            if toi > self.max {
91                None
92            } else {
93                Some(toi.max(0.))
94            }
95        }
96    }
97}
98
99impl IntersectsVolume<Aabb2d> for RayCast2d {
100    fn intersects(&self, volume: &Aabb2d) -> bool {
101        self.aabb_intersection_at(volume).is_some()
102    }
103}
104
105impl IntersectsVolume<BoundingCircle> for RayCast2d {
106    fn intersects(&self, volume: &BoundingCircle) -> bool {
107        self.circle_intersection_at(volume).is_some()
108    }
109}
110
111/// An intersection test that casts an [`Aabb2d`] along a ray.
112#[derive(#[automatically_derived]impl ::core::clone::Clone for AabbCast2d {
    #[inline]
    fn clone(&self) -> Self {
        AabbCast2d {
            ray: ::core::clone::Clone::clone(&self.ray),
            aabb: ::core::clone::Clone::clone(&self.aabb),
        }
    }
}Clone, #[automatically_derived]impl ::core::fmt::Debug for AabbCast2d {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f, "AabbCast2d",
            "ray", &self.ray, "aabb", &&self.aabb)
    }
}Debug)]
113#[cfg_attr(feature = "bevy_reflect", derive(const _: () =
    {
        impl bevy_reflect::GetTypeRegistration for AabbCast2d where  {
            fn get_type_registration() -> bevy_reflect::TypeRegistration {
                let mut registration =
                    bevy_reflect::TypeRegistration::of::<Self>();
                registration.insert(<bevy_reflect::ReflectFromPtr as
                            bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
                registration.insert(<bevy_reflect::ReflectFromReflect as
                            bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
                registration
            }
            #[inline(never)]
            fn register_type_dependencies(registry:
                    &mut bevy_reflect::TypeRegistry) {
                <RayCast2d as
                        bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
                <Aabb2d as
                        bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
            }
        }
        impl bevy_reflect::Typed for AabbCast2d where  {
            #[inline(never)]
            fn type_info() -> &'static bevy_reflect::TypeInfo {
                static CELL: bevy_reflect::utility::NonGenericTypeInfoCell =
                    bevy_reflect::utility::NonGenericTypeInfoCell::new();
                CELL.get_or_set(#[inline(never)] ||
                        {
                            bevy_reflect::TypeInfo::Struct(bevy_reflect::structs::StructInfo::new::<Self>(&[bevy_reflect::NamedField::new::<RayCast2d>("ray"),
                                                bevy_reflect::NamedField::new::<Aabb2d>("aabb")]))
                        })
            }
        }
        #[allow(deprecated, reason =
        "derives on a deprecated type shouldn't be considered a usage")]
        impl bevy_reflect::TypePath for AabbCast2d where  {
            fn type_path() -> &'static str {
                "bevy_shape::bounding::raycast2d::AabbCast2d"
            }
            fn short_type_path() -> &'static str { "AabbCast2d" }
            fn type_ident() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("AabbCast2d")
            }
            fn crate_name() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_shape::bounding::raycast2d".split(':').next().unwrap())
            }
            fn module_path() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_shape::bounding::raycast2d")
            }
        }
        impl bevy_reflect::Reflect for AabbCast2d where  {
            #[inline]
            fn into_any(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn ::core::any::Any> {
                self
            }
            #[inline]
            fn as_any(&self) -> &dyn ::core::any::Any { self }
            #[inline]
            fn as_any_mut(&mut self) -> &mut dyn ::core::any::Any { self }
            #[inline]
            fn into_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect> {
                self
            }
            #[inline]
            fn as_reflect(&self) -> &dyn bevy_reflect::Reflect { self }
            #[inline]
            fn as_reflect_mut(&mut self) -> &mut dyn bevy_reflect::Reflect {
                self
            }
            #[inline]
            fn set(&mut self,
                value:
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>)
                ->
                    ::core::result::Result<(),
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>> {
                *self = <dyn bevy_reflect::Reflect>::take(value)?;
                ::core::result::Result::Ok(())
            }
        }
        impl bevy_reflect::structs::Struct for AabbCast2d where  {
            fn field(&self, name: &str)
                -> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
                match name {
                    "ray" => ::core::option::Option::Some(&self.ray),
                    "aabb" => ::core::option::Option::Some(&self.aabb),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_mut(&mut self, name: &str)
                ->
                    ::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
                match name {
                    "ray" => ::core::option::Option::Some(&mut self.ray),
                    "aabb" => ::core::option::Option::Some(&mut self.aabb),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_at(&self, index: usize)
                -> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
                match index {
                    0usize => ::core::option::Option::Some(&self.ray),
                    1usize => ::core::option::Option::Some(&self.aabb),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_at_mut(&mut self, index: usize)
                ->
                    ::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
                match index {
                    0usize => ::core::option::Option::Some(&mut self.ray),
                    1usize => ::core::option::Option::Some(&mut self.aabb),
                    _ => ::core::option::Option::None,
                }
            }
            fn name_at(&self, index: usize) -> ::core::option::Option<&str> {
                match index {
                    0usize => ::core::option::Option::Some("ray"),
                    1usize => ::core::option::Option::Some("aabb"),
                    _ => ::core::option::Option::None,
                }
            }
            fn index_of_name(&self, name: &str)
                -> ::core::option::Option<usize> {
                match name {
                    "ray" => ::core::option::Option::Some(0usize),
                    "aabb" => ::core::option::Option::Some(1usize),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_len(&self) -> usize { 2usize }
            fn iter_fields(&self) -> bevy_reflect::structs::FieldIter {
                bevy_reflect::structs::FieldIter::new(self)
            }
            fn to_dynamic_struct(&self)
                ->
                    ::core::result::Result<bevy_reflect::structs::DynamicStruct,
                    bevy_reflect::ReflectCloneError> {
                let mut dynamic: bevy_reflect::structs::DynamicStruct =
                    ::core::default::Default::default();
                dynamic.set_represented_type(bevy_reflect::PartialReflect::get_represented_type_info(self));
                dynamic.insert_boxed("ray",
                    bevy_reflect::PartialReflect::to_dynamic(&self.ray)?);
                dynamic.insert_boxed("aabb",
                    bevy_reflect::PartialReflect::to_dynamic(&self.aabb)?);
                ::core::result::Result::Ok(dynamic)
            }
        }
        impl bevy_reflect::PartialReflect for AabbCast2d where  {
            #[inline]
            fn get_represented_type_info(&self)
                -> ::core::option::Option<&'static bevy_reflect::TypeInfo> {
                ::core::option::Option::Some(<Self as
                            bevy_reflect::Typed>::type_info())
            }
            #[inline]
            fn try_apply(&mut self, value: &dyn bevy_reflect::PartialReflect)
                -> ::core::result::Result<(), bevy_reflect::ApplyError> {
                if let bevy_reflect::ReflectRef::Struct(struct_value) =
                        bevy_reflect::PartialReflect::reflect_ref(value) {
                    for (name, value) in
                        bevy_reflect::structs::Struct::iter_fields(struct_value) {
                        if let ::core::option::Option::Some(v) =
                                bevy_reflect::structs::Struct::field_mut(self, name) {
                            bevy_reflect::PartialReflect::try_apply(v, value)?;
                        }
                    }
                } else {
                    return ::core::result::Result::Err(bevy_reflect::ApplyError::MismatchedKinds {
                                from_kind: bevy_reflect::PartialReflect::reflect_kind(value),
                                to_kind: bevy_reflect::ReflectKind::Struct,
                            });
                }
                ::core::result::Result::Ok(())
            }
            #[inline]
            fn reflect_kind(&self) -> bevy_reflect::ReflectKind {
                bevy_reflect::ReflectKind::Struct
            }
            #[inline]
            fn reflect_ref(&self) -> bevy_reflect::ReflectRef {
                bevy_reflect::ReflectRef::Struct(self)
            }
            #[inline]
            fn reflect_mut(&mut self) -> bevy_reflect::ReflectMut {
                bevy_reflect::ReflectMut::Struct(self)
            }
            #[inline]
            fn reflect_owned(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                -> bevy_reflect::ReflectOwned {
                bevy_reflect::ReflectOwned::Struct(self)
            }
            #[inline]
            fn try_into_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    ::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect>> {
                ::core::result::Result::Ok(self)
            }
            #[inline]
            fn try_as_reflect(&self)
                -> ::core::option::Option<&dyn bevy_reflect::Reflect> {
                ::core::option::Option::Some(self)
            }
            #[inline]
            fn try_as_reflect_mut(&mut self)
                -> ::core::option::Option<&mut dyn bevy_reflect::Reflect> {
                ::core::option::Option::Some(self)
            }
            #[inline]
            fn into_partial_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect> {
                self
            }
            #[inline]
            fn as_partial_reflect(&self)
                -> &dyn bevy_reflect::PartialReflect {
                self
            }
            #[inline]
            fn as_partial_reflect_mut(&mut self)
                -> &mut dyn bevy_reflect::PartialReflect {
                self
            }
            fn reflect_partial_eq(&self,
                value: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<bool> {
                (bevy_reflect::structs::struct_partial_eq)(self, value)
            }
            fn reflect_partial_cmp(&self,
                value: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<::core::cmp::Ordering> {
                (bevy_reflect::structs::struct_partial_cmp)(self, value)
            }
            fn debug(&self, f: &mut ::core::fmt::Formatter<'_>)
                -> ::core::fmt::Result {
                ::core::fmt::Debug::fmt(self, f)
            }
            #[inline]
            fn reflect_clone(&self)
                ->
                    ::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
                    bevy_reflect::ReflectCloneError> {
                ::core::result::Result::Ok(bevy_reflect::__macro_exports::alloc_utils::Box::new(::core::clone::Clone::clone(self)))
            }
        }
        impl bevy_reflect::FromReflect for AabbCast2d where  {
            fn from_reflect(reflect: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<Self> {
                if let bevy_reflect::ReflectRef::Struct(__ref_struct) =
                        bevy_reflect::PartialReflect::reflect_ref(reflect) {
                    let __this =
                        Self {
                            ray: <RayCast2d as
                                        bevy_reflect::FromReflect>::from_reflect(bevy_reflect::structs::Struct::field(__ref_struct,
                                            "ray")?)?,
                            aabb: <Aabb2d as
                                        bevy_reflect::FromReflect>::from_reflect(bevy_reflect::structs::Struct::field(__ref_struct,
                                            "aabb")?)?,
                        };
                    ::core::option::Option::Some(__this)
                } else { ::core::option::Option::None }
            }
        }
    };Reflect), reflect(Debug, Clone))]
114pub struct AabbCast2d {
115    /// The ray along which to cast the bounding volume
116    pub ray: RayCast2d,
117    /// The aabb that is being cast
118    pub aabb: Aabb2d,
119}
120
121impl AabbCast2d {
122    /// Construct an [`AabbCast2d`] from an [`Aabb2d`], origin, [`Dir2`], and max distance.
123    pub fn new(aabb: Aabb2d, origin: Vec2, direction: Dir2, max: f32) -> Self {
124        Self::from_ray(aabb, Ray2d { origin, direction }, max)
125    }
126
127    /// Construct an [`AabbCast2d`] from an [`Aabb2d`], [`Ray2d`], and max distance.
128    pub fn from_ray(aabb: Aabb2d, ray: Ray2d, max: f32) -> Self {
129        Self {
130            ray: RayCast2d::from_ray(ray, max),
131            aabb,
132        }
133    }
134
135    /// Get the distance at which the [`Aabb2d`]s collide, if at all.
136    pub fn aabb_collision_at(&self, mut aabb: Aabb2d) -> Option<f32> {
137        aabb.min -= self.aabb.max;
138        aabb.max -= self.aabb.min;
139        self.ray.aabb_intersection_at(&aabb)
140    }
141}
142
143impl IntersectsVolume<Aabb2d> for AabbCast2d {
144    fn intersects(&self, volume: &Aabb2d) -> bool {
145        self.aabb_collision_at(*volume).is_some()
146    }
147}
148
149/// An intersection test that casts a [`BoundingCircle`] along a ray.
150#[derive(#[automatically_derived]impl ::core::clone::Clone for BoundingCircleCast {
    #[inline]
    fn clone(&self) -> Self {
        BoundingCircleCast {
            ray: ::core::clone::Clone::clone(&self.ray),
            circle: ::core::clone::Clone::clone(&self.circle),
        }
    }
}Clone, #[automatically_derived]impl ::core::fmt::Debug for BoundingCircleCast {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f,
            "BoundingCircleCast", "ray", &self.ray, "circle", &&self.circle)
    }
}Debug)]
151#[cfg_attr(feature = "bevy_reflect", derive(const _: () =
    {
        impl bevy_reflect::GetTypeRegistration for BoundingCircleCast where  {
            fn get_type_registration() -> bevy_reflect::TypeRegistration {
                let mut registration =
                    bevy_reflect::TypeRegistration::of::<Self>();
                registration.insert(<bevy_reflect::ReflectFromPtr as
                            bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
                registration.insert(<bevy_reflect::ReflectFromReflect as
                            bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
                registration
            }
            #[inline(never)]
            fn register_type_dependencies(registry:
                    &mut bevy_reflect::TypeRegistry) {
                <RayCast2d as
                        bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
                <BoundingCircle as
                        bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
            }
        }
        impl bevy_reflect::Typed for BoundingCircleCast where  {
            #[inline(never)]
            fn type_info() -> &'static bevy_reflect::TypeInfo {
                static CELL: bevy_reflect::utility::NonGenericTypeInfoCell =
                    bevy_reflect::utility::NonGenericTypeInfoCell::new();
                CELL.get_or_set(#[inline(never)] ||
                        {
                            bevy_reflect::TypeInfo::Struct(bevy_reflect::structs::StructInfo::new::<Self>(&[bevy_reflect::NamedField::new::<RayCast2d>("ray"),
                                                bevy_reflect::NamedField::new::<BoundingCircle>("circle")]))
                        })
            }
        }
        #[allow(deprecated, reason =
        "derives on a deprecated type shouldn't be considered a usage")]
        impl bevy_reflect::TypePath for BoundingCircleCast where  {
            fn type_path() -> &'static str {
                "bevy_shape::bounding::raycast2d::BoundingCircleCast"
            }
            fn short_type_path() -> &'static str { "BoundingCircleCast" }
            fn type_ident() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("BoundingCircleCast")
            }
            fn crate_name() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_shape::bounding::raycast2d".split(':').next().unwrap())
            }
            fn module_path() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_shape::bounding::raycast2d")
            }
        }
        impl bevy_reflect::Reflect for BoundingCircleCast where  {
            #[inline]
            fn into_any(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn ::core::any::Any> {
                self
            }
            #[inline]
            fn as_any(&self) -> &dyn ::core::any::Any { self }
            #[inline]
            fn as_any_mut(&mut self) -> &mut dyn ::core::any::Any { self }
            #[inline]
            fn into_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect> {
                self
            }
            #[inline]
            fn as_reflect(&self) -> &dyn bevy_reflect::Reflect { self }
            #[inline]
            fn as_reflect_mut(&mut self) -> &mut dyn bevy_reflect::Reflect {
                self
            }
            #[inline]
            fn set(&mut self,
                value:
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>)
                ->
                    ::core::result::Result<(),
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>> {
                *self = <dyn bevy_reflect::Reflect>::take(value)?;
                ::core::result::Result::Ok(())
            }
        }
        impl bevy_reflect::structs::Struct for BoundingCircleCast where  {
            fn field(&self, name: &str)
                -> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
                match name {
                    "ray" => ::core::option::Option::Some(&self.ray),
                    "circle" => ::core::option::Option::Some(&self.circle),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_mut(&mut self, name: &str)
                ->
                    ::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
                match name {
                    "ray" => ::core::option::Option::Some(&mut self.ray),
                    "circle" => ::core::option::Option::Some(&mut self.circle),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_at(&self, index: usize)
                -> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
                match index {
                    0usize => ::core::option::Option::Some(&self.ray),
                    1usize => ::core::option::Option::Some(&self.circle),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_at_mut(&mut self, index: usize)
                ->
                    ::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
                match index {
                    0usize => ::core::option::Option::Some(&mut self.ray),
                    1usize => ::core::option::Option::Some(&mut self.circle),
                    _ => ::core::option::Option::None,
                }
            }
            fn name_at(&self, index: usize) -> ::core::option::Option<&str> {
                match index {
                    0usize => ::core::option::Option::Some("ray"),
                    1usize => ::core::option::Option::Some("circle"),
                    _ => ::core::option::Option::None,
                }
            }
            fn index_of_name(&self, name: &str)
                -> ::core::option::Option<usize> {
                match name {
                    "ray" => ::core::option::Option::Some(0usize),
                    "circle" => ::core::option::Option::Some(1usize),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_len(&self) -> usize { 2usize }
            fn iter_fields(&self) -> bevy_reflect::structs::FieldIter {
                bevy_reflect::structs::FieldIter::new(self)
            }
            fn to_dynamic_struct(&self)
                ->
                    ::core::result::Result<bevy_reflect::structs::DynamicStruct,
                    bevy_reflect::ReflectCloneError> {
                let mut dynamic: bevy_reflect::structs::DynamicStruct =
                    ::core::default::Default::default();
                dynamic.set_represented_type(bevy_reflect::PartialReflect::get_represented_type_info(self));
                dynamic.insert_boxed("ray",
                    bevy_reflect::PartialReflect::to_dynamic(&self.ray)?);
                dynamic.insert_boxed("circle",
                    bevy_reflect::PartialReflect::to_dynamic(&self.circle)?);
                ::core::result::Result::Ok(dynamic)
            }
        }
        impl bevy_reflect::PartialReflect for BoundingCircleCast where  {
            #[inline]
            fn get_represented_type_info(&self)
                -> ::core::option::Option<&'static bevy_reflect::TypeInfo> {
                ::core::option::Option::Some(<Self as
                            bevy_reflect::Typed>::type_info())
            }
            #[inline]
            fn try_apply(&mut self, value: &dyn bevy_reflect::PartialReflect)
                -> ::core::result::Result<(), bevy_reflect::ApplyError> {
                if let bevy_reflect::ReflectRef::Struct(struct_value) =
                        bevy_reflect::PartialReflect::reflect_ref(value) {
                    for (name, value) in
                        bevy_reflect::structs::Struct::iter_fields(struct_value) {
                        if let ::core::option::Option::Some(v) =
                                bevy_reflect::structs::Struct::field_mut(self, name) {
                            bevy_reflect::PartialReflect::try_apply(v, value)?;
                        }
                    }
                } else {
                    return ::core::result::Result::Err(bevy_reflect::ApplyError::MismatchedKinds {
                                from_kind: bevy_reflect::PartialReflect::reflect_kind(value),
                                to_kind: bevy_reflect::ReflectKind::Struct,
                            });
                }
                ::core::result::Result::Ok(())
            }
            #[inline]
            fn reflect_kind(&self) -> bevy_reflect::ReflectKind {
                bevy_reflect::ReflectKind::Struct
            }
            #[inline]
            fn reflect_ref(&self) -> bevy_reflect::ReflectRef {
                bevy_reflect::ReflectRef::Struct(self)
            }
            #[inline]
            fn reflect_mut(&mut self) -> bevy_reflect::ReflectMut {
                bevy_reflect::ReflectMut::Struct(self)
            }
            #[inline]
            fn reflect_owned(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                -> bevy_reflect::ReflectOwned {
                bevy_reflect::ReflectOwned::Struct(self)
            }
            #[inline]
            fn try_into_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    ::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect>> {
                ::core::result::Result::Ok(self)
            }
            #[inline]
            fn try_as_reflect(&self)
                -> ::core::option::Option<&dyn bevy_reflect::Reflect> {
                ::core::option::Option::Some(self)
            }
            #[inline]
            fn try_as_reflect_mut(&mut self)
                -> ::core::option::Option<&mut dyn bevy_reflect::Reflect> {
                ::core::option::Option::Some(self)
            }
            #[inline]
            fn into_partial_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect> {
                self
            }
            #[inline]
            fn as_partial_reflect(&self)
                -> &dyn bevy_reflect::PartialReflect {
                self
            }
            #[inline]
            fn as_partial_reflect_mut(&mut self)
                -> &mut dyn bevy_reflect::PartialReflect {
                self
            }
            fn reflect_partial_eq(&self,
                value: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<bool> {
                (bevy_reflect::structs::struct_partial_eq)(self, value)
            }
            fn reflect_partial_cmp(&self,
                value: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<::core::cmp::Ordering> {
                (bevy_reflect::structs::struct_partial_cmp)(self, value)
            }
            fn debug(&self, f: &mut ::core::fmt::Formatter<'_>)
                -> ::core::fmt::Result {
                ::core::fmt::Debug::fmt(self, f)
            }
            #[inline]
            fn reflect_clone(&self)
                ->
                    ::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
                    bevy_reflect::ReflectCloneError> {
                ::core::result::Result::Ok(bevy_reflect::__macro_exports::alloc_utils::Box::new(::core::clone::Clone::clone(self)))
            }
        }
        impl bevy_reflect::FromReflect for BoundingCircleCast where  {
            fn from_reflect(reflect: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<Self> {
                if let bevy_reflect::ReflectRef::Struct(__ref_struct) =
                        bevy_reflect::PartialReflect::reflect_ref(reflect) {
                    let __this =
                        Self {
                            ray: <RayCast2d as
                                        bevy_reflect::FromReflect>::from_reflect(bevy_reflect::structs::Struct::field(__ref_struct,
                                            "ray")?)?,
                            circle: <BoundingCircle as
                                        bevy_reflect::FromReflect>::from_reflect(bevy_reflect::structs::Struct::field(__ref_struct,
                                            "circle")?)?,
                        };
                    ::core::option::Option::Some(__this)
                } else { ::core::option::Option::None }
            }
        }
    };Reflect), reflect(Debug, Clone))]
152pub struct BoundingCircleCast {
153    /// The ray along which to cast the bounding volume
154    pub ray: RayCast2d,
155    /// The circle that is being cast
156    pub circle: BoundingCircle,
157}
158
159impl BoundingCircleCast {
160    /// Construct a [`BoundingCircleCast`] from a [`BoundingCircle`], origin, [`Dir2`], and max distance.
161    pub fn new(circle: BoundingCircle, origin: Vec2, direction: Dir2, max: f32) -> Self {
162        Self::from_ray(circle, Ray2d { origin, direction }, max)
163    }
164
165    /// Construct a [`BoundingCircleCast`] from a [`BoundingCircle`], [`Ray2d`], and max distance.
166    pub fn from_ray(circle: BoundingCircle, ray: Ray2d, max: f32) -> Self {
167        Self {
168            ray: RayCast2d::from_ray(ray, max),
169            circle,
170        }
171    }
172
173    /// Get the distance at which the [`BoundingCircle`]s collide, if at all.
174    pub fn circle_collision_at(&self, mut circle: BoundingCircle) -> Option<f32> {
175        circle.center -= self.circle.center;
176        circle.circle.radius += self.circle.radius();
177        self.ray.circle_intersection_at(&circle)
178    }
179}
180
181impl IntersectsVolume<BoundingCircle> for BoundingCircleCast {
182    fn intersects(&self, volume: &BoundingCircle) -> bool {
183        self.circle_collision_at(*volume).is_some()
184    }
185}
186
187#[cfg(test)]
188mod tests {
189    use super::*;
190
191    const EPSILON: f32 = 0.001;
192
193    #[test]
194    fn test_ray_intersection_circle_hits() {
195        for (test, volume, expected_distance) in &[
196            (
197                // Hit the center of a centered bounding circle
198                RayCast2d::new(Vec2::Y * -5., Dir2::Y, 90.),
199                BoundingCircle::new(Vec2::ZERO, 1.),
200                4.,
201            ),
202            (
203                // Hit the center of a centered bounding circle, but from the other side
204                RayCast2d::new(Vec2::Y * 5., -Dir2::Y, 90.),
205                BoundingCircle::new(Vec2::ZERO, 1.),
206                4.,
207            ),
208            (
209                // Hit the center of an offset circle
210                RayCast2d::new(Vec2::ZERO, Dir2::Y, 90.),
211                BoundingCircle::new(Vec2::Y * 3., 2.),
212                1.,
213            ),
214            (
215                // Just barely hit the circle before the max distance
216                RayCast2d::new(Vec2::X, Dir2::Y, 1.),
217                BoundingCircle::new(Vec2::ONE, 0.01),
218                0.99,
219            ),
220            (
221                // Hit a circle off-center
222                RayCast2d::new(Vec2::X, Dir2::Y, 90.),
223                BoundingCircle::new(Vec2::Y * 5., 2.),
224                3.268,
225            ),
226            (
227                // Barely hit a circle on the side
228                RayCast2d::new(Vec2::X * 0.99999, Dir2::Y, 90.),
229                BoundingCircle::new(Vec2::Y * 5., 1.),
230                4.996,
231            ),
232        ] {
233            assert!(
234                test.intersects(volume),
235                "Case:\n  Test: {test:?}\n  Volume: {volume:?}\n  Expected distance: {expected_distance:?}",
236            );
237            let actual_distance = test.circle_intersection_at(volume).unwrap();
238            assert!(
239                ops::abs(actual_distance - expected_distance) < EPSILON,
240                "Case:\n  Test: {test:?}\n  Volume: {volume:?}\n  Expected distance: {expected_distance:?}\n  Actual distance: {actual_distance}",
241            );
242
243            let inverted_ray = RayCast2d::new(test.ray.origin, -test.ray.direction, test.max);
244            assert!(
245                !inverted_ray.intersects(volume),
246                "Case:\n  Test: {test:?}\n  Volume: {volume:?}\n  Expected distance: {expected_distance:?}",
247            );
248        }
249    }
250
251    #[test]
252    fn test_ray_intersection_circle_misses() {
253        for (test, volume) in &[
254            (
255                // The ray doesn't go in the right direction
256                RayCast2d::new(Vec2::ZERO, Dir2::X, 90.),
257                BoundingCircle::new(Vec2::Y * 2., 1.),
258            ),
259            (
260                // Ray's alignment isn't enough to hit the circle
261                RayCast2d::new(Vec2::ZERO, Dir2::from_xy(1., 1.).unwrap(), 90.),
262                BoundingCircle::new(Vec2::Y * 2., 1.),
263            ),
264            (
265                // The ray's maximum distance isn't high enough
266                RayCast2d::new(Vec2::ZERO, Dir2::Y, 0.5),
267                BoundingCircle::new(Vec2::Y * 2., 1.),
268            ),
269        ] {
270            assert!(
271                !test.intersects(volume),
272                "Case:\n  Test: {test:?}\n  Volume: {volume:?}",
273            );
274        }
275    }
276
277    #[test]
278    fn test_ray_intersection_circle_inside() {
279        let volume = BoundingCircle::new(Vec2::splat(0.5), 1.);
280        for origin in &[Vec2::X, Vec2::Y, Vec2::ONE, Vec2::ZERO] {
281            for direction in &[Dir2::X, Dir2::Y, -Dir2::X, -Dir2::Y] {
282                for max in &[0., 1., 900.] {
283                    let test = RayCast2d::new(*origin, *direction, *max);
284
285                    assert!(
286                        test.intersects(&volume),
287                        "Case:\n  origin: {origin:?}\n  Direction: {direction:?}\n  Max: {max}",
288                    );
289
290                    let actual_distance = test.circle_intersection_at(&volume);
291                    assert_eq!(
292                        actual_distance,
293                        Some(0.),
294                        "Case:\n  origin: {origin:?}\n  Direction: {direction:?}\n  Max: {max}",
295                    );
296                }
297            }
298        }
299    }
300
301    #[test]
302    fn test_ray_intersection_aabb_hits() {
303        for (test, volume, expected_distance) in &[
304            (
305                // Hit the center of a centered aabb
306                RayCast2d::new(Vec2::Y * -5., Dir2::Y, 90.),
307                Aabb2d::new(Vec2::ZERO, Vec2::ONE),
308                4.,
309            ),
310            (
311                // Hit the center of a centered aabb, but from the other side
312                RayCast2d::new(Vec2::Y * 5., -Dir2::Y, 90.),
313                Aabb2d::new(Vec2::ZERO, Vec2::ONE),
314                4.,
315            ),
316            (
317                // Hit the center of an offset aabb
318                RayCast2d::new(Vec2::ZERO, Dir2::Y, 90.),
319                Aabb2d::new(Vec2::Y * 3., Vec2::splat(2.)),
320                1.,
321            ),
322            (
323                // Just barely hit the aabb before the max distance
324                RayCast2d::new(Vec2::X, Dir2::Y, 1.),
325                Aabb2d::new(Vec2::ONE, Vec2::splat(0.01)),
326                0.99,
327            ),
328            (
329                // Hit an aabb off-center
330                RayCast2d::new(Vec2::X, Dir2::Y, 90.),
331                Aabb2d::new(Vec2::Y * 5., Vec2::splat(2.)),
332                3.,
333            ),
334            (
335                // Barely hit an aabb on corner
336                RayCast2d::new(Vec2::X * -0.001, Dir2::from_xy(1., 1.).unwrap(), 90.),
337                Aabb2d::new(Vec2::Y * 2., Vec2::ONE),
338                1.414,
339            ),
340        ] {
341            assert!(
342                test.intersects(volume),
343                "Case:\n  Test: {test:?}\n  Volume: {volume:?}\n  Expected distance: {expected_distance:?}",
344            );
345            let actual_distance = test.aabb_intersection_at(volume).unwrap();
346            assert!(
347                ops::abs(actual_distance - expected_distance) < EPSILON,
348                "Case:\n  Test: {test:?}\n  Volume: {volume:?}\n  Expected distance: {expected_distance:?}\n  Actual distance: {actual_distance}",
349            );
350
351            let inverted_ray = RayCast2d::new(test.ray.origin, -test.ray.direction, test.max);
352            assert!(
353                !inverted_ray.intersects(volume),
354                "Case:\n  Test: {test:?}\n  Volume: {volume:?}\n  Expected distance: {expected_distance:?}",
355            );
356        }
357    }
358
359    #[test]
360    fn test_ray_intersection_aabb_misses() {
361        for (test, volume) in &[
362            (
363                // The ray doesn't go in the right direction
364                RayCast2d::new(Vec2::ZERO, Dir2::X, 90.),
365                Aabb2d::new(Vec2::Y * 2., Vec2::ONE),
366            ),
367            (
368                // Ray's alignment isn't enough to hit the aabb
369                RayCast2d::new(Vec2::ZERO, Dir2::from_xy(1., 0.99).unwrap(), 90.),
370                Aabb2d::new(Vec2::Y * 2., Vec2::ONE),
371            ),
372            (
373                // The ray's maximum distance isn't high enough
374                RayCast2d::new(Vec2::ZERO, Dir2::Y, 0.5),
375                Aabb2d::new(Vec2::Y * 2., Vec2::ONE),
376            ),
377        ] {
378            assert!(
379                !test.intersects(volume),
380                "Case:\n  Test: {test:?}\n  Volume: {volume:?}",
381            );
382        }
383    }
384
385    #[test]
386    fn test_ray_intersection_aabb_inside() {
387        let volume = Aabb2d::new(Vec2::splat(0.5), Vec2::ONE);
388        for origin in &[Vec2::X, Vec2::Y, Vec2::ONE, Vec2::ZERO] {
389            for direction in &[Dir2::X, Dir2::Y, -Dir2::X, -Dir2::Y] {
390                for max in &[0., 1., 900.] {
391                    let test = RayCast2d::new(*origin, *direction, *max);
392
393                    assert!(
394                        test.intersects(&volume),
395                        "Case:\n  origin: {origin:?}\n  Direction: {direction:?}\n  Max: {max}",
396                    );
397
398                    let actual_distance = test.aabb_intersection_at(&volume);
399                    assert_eq!(
400                        actual_distance,
401                        Some(0.),
402                        "Case:\n  origin: {origin:?}\n  Direction: {direction:?}\n  Max: {max}",
403                    );
404                }
405            }
406        }
407    }
408
409    #[test]
410    fn test_aabb_cast_hits() {
411        for (test, volume, expected_distance) in &[
412            (
413                // Hit the center of the aabb, that a ray would've also hit
414                AabbCast2d::new(Aabb2d::new(Vec2::ZERO, Vec2::ONE), Vec2::ZERO, Dir2::Y, 90.),
415                Aabb2d::new(Vec2::Y * 5., Vec2::ONE),
416                3.,
417            ),
418            (
419                // Hit the center of the aabb, but from the other side
420                AabbCast2d::new(
421                    Aabb2d::new(Vec2::ZERO, Vec2::ONE),
422                    Vec2::Y * 10.,
423                    -Dir2::Y,
424                    90.,
425                ),
426                Aabb2d::new(Vec2::Y * 5., Vec2::ONE),
427                3.,
428            ),
429            (
430                // Hit the edge of the aabb, that a ray would've missed
431                AabbCast2d::new(
432                    Aabb2d::new(Vec2::ZERO, Vec2::ONE),
433                    Vec2::X * 1.5,
434                    Dir2::Y,
435                    90.,
436                ),
437                Aabb2d::new(Vec2::Y * 5., Vec2::ONE),
438                3.,
439            ),
440            (
441                // Hit the edge of the aabb, by casting an off-center AABB
442                AabbCast2d::new(
443                    Aabb2d::new(Vec2::X * -2., Vec2::ONE),
444                    Vec2::X * 3.,
445                    Dir2::Y,
446                    90.,
447                ),
448                Aabb2d::new(Vec2::Y * 5., Vec2::ONE),
449                3.,
450            ),
451        ] {
452            assert!(
453                test.intersects(volume),
454                "Case:\n  Test: {test:?}\n  Volume: {volume:?}\n  Expected distance: {expected_distance:?}",
455            );
456            let actual_distance = test.aabb_collision_at(*volume).unwrap();
457            assert!(
458                ops::abs(actual_distance - expected_distance) < EPSILON,
459                "Case:\n  Test: {test:?}\n  Volume: {volume:?}\n  Expected distance: {expected_distance:?}\n  Actual distance: {actual_distance}",
460            );
461
462            let inverted_ray =
463                RayCast2d::new(test.ray.ray.origin, -test.ray.ray.direction, test.ray.max);
464            assert!(
465                !inverted_ray.intersects(volume),
466                "Case:\n  Test: {test:?}\n  Volume: {volume:?}\n  Expected distance: {expected_distance:?}",
467            );
468        }
469    }
470
471    #[test]
472    fn test_circle_cast_hits() {
473        for (test, volume, expected_distance) in &[
474            (
475                // Hit the center of the bounding circle, that a ray would've also hit
476                BoundingCircleCast::new(
477                    BoundingCircle::new(Vec2::ZERO, 1.),
478                    Vec2::ZERO,
479                    Dir2::Y,
480                    90.,
481                ),
482                BoundingCircle::new(Vec2::Y * 5., 1.),
483                3.,
484            ),
485            (
486                // Hit the center of the bounding circle, but from the other side
487                BoundingCircleCast::new(
488                    BoundingCircle::new(Vec2::ZERO, 1.),
489                    Vec2::Y * 10.,
490                    -Dir2::Y,
491                    90.,
492                ),
493                BoundingCircle::new(Vec2::Y * 5., 1.),
494                3.,
495            ),
496            (
497                // Hit the bounding circle off-center, that a ray would've missed
498                BoundingCircleCast::new(
499                    BoundingCircle::new(Vec2::ZERO, 1.),
500                    Vec2::X * 1.5,
501                    Dir2::Y,
502                    90.,
503                ),
504                BoundingCircle::new(Vec2::Y * 5., 1.),
505                3.677,
506            ),
507            (
508                // Hit the bounding circle off-center, by casting a circle that is off-center
509                BoundingCircleCast::new(
510                    BoundingCircle::new(Vec2::X * -1.5, 1.),
511                    Vec2::X * 3.,
512                    Dir2::Y,
513                    90.,
514                ),
515                BoundingCircle::new(Vec2::Y * 5., 1.),
516                3.677,
517            ),
518        ] {
519            assert!(
520                test.intersects(volume),
521                "Case:\n  Test: {test:?}\n  Volume: {volume:?}\n  Expected distance: {expected_distance:?}",
522            );
523            let actual_distance = test.circle_collision_at(*volume).unwrap();
524            assert!(
525                ops::abs(actual_distance - expected_distance) < EPSILON,
526                "Case:\n  Test: {test:?}\n  Volume: {volume:?}\n  Expected distance: {expected_distance:?}\n  Actual distance: {actual_distance}",
527            );
528
529            let inverted_ray =
530                RayCast2d::new(test.ray.ray.origin, -test.ray.ray.direction, test.ray.max);
531            assert!(
532                !inverted_ray.intersects(volume),
533                "Case:\n  Test: {test:?}\n  Volume: {volume:?}\n  Expected distance: {expected_distance:?}",
534            );
535        }
536    }
537}