Skip to main content

bevy_math/primitives/
half_space.rs

1use crate::{ops, Vec3, Vec3A, Vec4, Vec4Swizzles};
2
3#[cfg(feature = "bevy_reflect")]
4use bevy_reflect::{std_traits::ReflectDefault, Reflect};
5#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
6use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
7
8/// A region of 3D space, specifically an open set whose border is a bisecting 2D plane.
9///
10/// This bisecting plane partitions 3D space into two infinite regions,
11/// the half-space is one of those regions and excludes the bisecting plane.
12///
13/// Each instance of this type is characterized by:
14/// - the bisecting plane's unit normal, normalized and pointing "inside" the half-space,
15/// - the signed distance along the normal from the bisecting plane to the origin of 3D space.
16///
17/// The distance can also be seen as:
18/// - the distance along the inverse of the normal from the origin of 3D space to the bisecting plane,
19/// - the opposite of the distance along the normal from the origin of 3D space to the bisecting plane.
20///
21/// Any point `p` is considered to be within the `HalfSpace` when the length of the projection
22/// of p on the normal is greater or equal than the opposite of the distance,
23/// meaning: if the equation `normal.dot(p) + distance > 0.` is satisfied.
24///
25/// For example, the half-space containing all the points with a z-coordinate lesser
26/// or equal than `8.0` would be defined by: `HalfSpace::new(Vec3::NEG_Z.extend(-8.0))`.
27/// It includes all the points from the bisecting plane towards `NEG_Z`, and the distance
28/// from the plane to the origin is `-8.0` along `NEG_Z`.
29///
30/// It is used to define a [`ViewFrustum`](crate::primitives::ViewFrustum),
31/// but is also a useful mathematical primitive for rendering tasks such as  light computation.
32#[derive(#[automatically_derived]
impl ::core::clone::Clone for HalfSpace {
    #[inline]
    fn clone(&self) -> HalfSpace {
        let _: ::core::clone::AssertParamIsClone<Vec4>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for HalfSpace { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for HalfSpace {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field1_finish(f, "HalfSpace",
            "normal_d", &&self.normal_d)
    }
}Debug, #[automatically_derived]
impl ::core::default::Default for HalfSpace {
    #[inline]
    fn default() -> HalfSpace {
        HalfSpace { normal_d: ::core::default::Default::default() }
    }
}Default, #[automatically_derived]
impl ::core::cmp::PartialEq for HalfSpace {
    #[inline]
    fn eq(&self, other: &HalfSpace) -> bool {
        self.normal_d == other.normal_d
    }
}PartialEq)]
33#[cfg_attr(feature = "serialize", derive(#[doc(hidden)]
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications,
clippy :: absolute_paths,)]
const _: () =
    {
        #[allow(unused_extern_crates, clippy :: useless_attribute)]
        extern crate serde as _serde;
        ;
        #[automatically_derived]
        impl _serde::Serialize for HalfSpace {
            fn serialize<__S>(&self, __serializer: __S)
                -> _serde::__private228::Result<__S::Ok, __S::Error> where
                __S: _serde::Serializer {
                let mut __serde_state =
                    _serde::Serializer::serialize_struct(__serializer,
                            "HalfSpace", false as usize + 1)?;
                _serde::ser::SerializeStruct::serialize_field(&mut __serde_state,
                        "normal_d", &self.normal_d)?;
                _serde::ser::SerializeStruct::end(__serde_state)
            }
        }
    };serde::Serialize, #[doc(hidden)]
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications,
clippy :: absolute_paths,)]
const _: () =
    {
        #[allow(unused_extern_crates, clippy :: useless_attribute)]
        extern crate serde as _serde;
        ;
        #[automatically_derived]
        impl<'de> _serde::Deserialize<'de> for HalfSpace {
            fn deserialize<__D>(__deserializer: __D)
                -> _serde::__private228::Result<Self, __D::Error> where
                __D: _serde::Deserializer<'de> {
                #[allow(non_camel_case_types)]
                #[doc(hidden)]
                enum __Field { __field0, __ignore, }
                #[doc(hidden)]
                struct __FieldVisitor;
                #[automatically_derived]
                impl<'de> _serde::de::Visitor<'de> for __FieldVisitor {
                    type Value = __Field;
                    fn expecting(&self,
                        __formatter: &mut _serde::__private228::Formatter)
                        -> _serde::__private228::fmt::Result {
                        _serde::__private228::Formatter::write_str(__formatter,
                            "field identifier")
                    }
                    fn visit_u64<__E>(self, __value: u64)
                        -> _serde::__private228::Result<Self::Value, __E> where
                        __E: _serde::de::Error {
                        match __value {
                            0u64 => _serde::__private228::Ok(__Field::__field0),
                            _ => _serde::__private228::Ok(__Field::__ignore),
                        }
                    }
                    fn visit_str<__E>(self, __value: &str)
                        -> _serde::__private228::Result<Self::Value, __E> where
                        __E: _serde::de::Error {
                        match __value {
                            "normal_d" => _serde::__private228::Ok(__Field::__field0),
                            _ => { _serde::__private228::Ok(__Field::__ignore) }
                        }
                    }
                    fn visit_bytes<__E>(self, __value: &[u8])
                        -> _serde::__private228::Result<Self::Value, __E> where
                        __E: _serde::de::Error {
                        match __value {
                            b"normal_d" => _serde::__private228::Ok(__Field::__field0),
                            _ => { _serde::__private228::Ok(__Field::__ignore) }
                        }
                    }
                }
                #[automatically_derived]
                impl<'de> _serde::Deserialize<'de> for __Field {
                    #[inline]
                    fn deserialize<__D>(__deserializer: __D)
                        -> _serde::__private228::Result<Self, __D::Error> where
                        __D: _serde::Deserializer<'de> {
                        _serde::Deserializer::deserialize_identifier(__deserializer,
                            __FieldVisitor)
                    }
                }
                #[doc(hidden)]
                struct __Visitor<'de> {
                    marker: _serde::__private228::PhantomData<HalfSpace>,
                    lifetime: _serde::__private228::PhantomData<&'de ()>,
                }
                #[automatically_derived]
                impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> {
                    type Value = HalfSpace;
                    fn expecting(&self,
                        __formatter: &mut _serde::__private228::Formatter)
                        -> _serde::__private228::fmt::Result {
                        _serde::__private228::Formatter::write_str(__formatter,
                            "struct HalfSpace")
                    }
                    #[inline]
                    fn visit_seq<__A>(self, mut __seq: __A)
                        -> _serde::__private228::Result<Self::Value, __A::Error>
                        where __A: _serde::de::SeqAccess<'de> {
                        let __field0 =
                            match _serde::de::SeqAccess::next_element::<Vec4>(&mut __seq)?
                                {
                                _serde::__private228::Some(__value) => __value,
                                _serde::__private228::None =>
                                    return _serde::__private228::Err(_serde::de::Error::invalid_length(0usize,
                                                &"struct HalfSpace with 1 element")),
                            };
                        _serde::__private228::Ok(HalfSpace { normal_d: __field0 })
                    }
                    #[inline]
                    fn visit_map<__A>(self, mut __map: __A)
                        -> _serde::__private228::Result<Self::Value, __A::Error>
                        where __A: _serde::de::MapAccess<'de> {
                        let mut __field0: _serde::__private228::Option<Vec4> =
                            _serde::__private228::None;
                        while let _serde::__private228::Some(__key) =
                                _serde::de::MapAccess::next_key::<__Field>(&mut __map)? {
                            match __key {
                                __Field::__field0 => {
                                    if _serde::__private228::Option::is_some(&__field0) {
                                        return _serde::__private228::Err(<__A::Error as
                                                        _serde::de::Error>::duplicate_field("normal_d"));
                                    }
                                    __field0 =
                                        _serde::__private228::Some(_serde::de::MapAccess::next_value::<Vec4>(&mut __map)?);
                                }
                                _ => {
                                    let _ =
                                        _serde::de::MapAccess::next_value::<_serde::de::IgnoredAny>(&mut __map)?;
                                }
                            }
                        }
                        let __field0 =
                            match __field0 {
                                _serde::__private228::Some(__field0) => __field0,
                                _serde::__private228::None =>
                                    _serde::__private228::de::missing_field("normal_d")?,
                            };
                        _serde::__private228::Ok(HalfSpace { normal_d: __field0 })
                    }
                }
                #[doc(hidden)]
                const FIELDS: &'static [&'static str] = &["normal_d"];
                _serde::Deserializer::deserialize_struct(__deserializer,
                    "HalfSpace", FIELDS,
                    __Visitor {
                        marker: _serde::__private228::PhantomData::<HalfSpace>,
                        lifetime: _serde::__private228::PhantomData,
                    })
            }
        }
    };serde::Deserialize))]
34#[cfg_attr(
35    feature = "bevy_reflect",
36    derive(const _: () =
    {
        impl bevy_reflect::GetTypeRegistration for HalfSpace where  {
            fn get_type_registration() -> bevy_reflect::TypeRegistration {
                let mut registration =
                    bevy_reflect::TypeRegistration::of::<Self>();
                registration.insert::<bevy_reflect::ReflectFromPtr>(bevy_reflect::FromType::<Self>::from_type());
                registration.insert::<bevy_reflect::ReflectFromReflect>(bevy_reflect::FromType::<Self>::from_type());
                registration.register_type_data::<ReflectDefault, Self>();
                registration.register_type_data::<ReflectSerialize, Self>();
                registration.register_type_data::<ReflectDeserialize, Self>();
                registration
            }
            #[inline(never)]
            fn register_type_dependencies(registry:
                    &mut bevy_reflect::TypeRegistry) {
                <Vec4 as
                        bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
            }
        }
        impl bevy_reflect::Typed for HalfSpace where  {
            #[inline]
            fn type_info() -> &'static bevy_reflect::TypeInfo {
                static CELL: bevy_reflect::utility::NonGenericTypeInfoCell =
                    bevy_reflect::utility::NonGenericTypeInfoCell::new();
                CELL.get_or_set(||
                        {
                            bevy_reflect::TypeInfo::Struct(bevy_reflect::structs::StructInfo::new::<Self>(&[bevy_reflect::NamedField::new::<Vec4>("normal_d")]))
                        })
            }
        }
        #[allow(deprecated, reason =
        "derives on a deprecated type shouldn't be considered a usage")]
        impl bevy_reflect::TypePath for HalfSpace where  {
            fn type_path() -> &'static str {
                "bevy_math::primitives::half_space::HalfSpace"
            }
            fn short_type_path() -> &'static str { "HalfSpace" }
            fn type_ident() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("HalfSpace")
            }
            fn crate_name() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_math::primitives::half_space".split(':').next().unwrap())
            }
            fn module_path() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_math::primitives::half_space")
            }
        }
        impl bevy_reflect::Reflect for HalfSpace 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 HalfSpace where  {
            fn field(&self, name: &str)
                -> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
                match name {
                    "normal_d" => ::core::option::Option::Some(&self.normal_d),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_mut(&mut self, name: &str)
                ->
                    ::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
                match name {
                    "normal_d" =>
                        ::core::option::Option::Some(&mut self.normal_d),
                    _ => ::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.normal_d),
                    _ => ::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.normal_d),
                    _ => ::core::option::Option::None,
                }
            }
            fn name_at(&self, index: usize) -> ::core::option::Option<&str> {
                match index {
                    0usize => ::core::option::Option::Some("normal_d"),
                    _ => ::core::option::Option::None,
                }
            }
            fn index_of_name(&self, name: &str)
                -> ::core::option::Option<usize> {
                match name {
                    "normal_d" => ::core::option::Option::Some(0usize),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_len(&self) -> usize { 1usize }
            fn iter_fields(&self) -> bevy_reflect::structs::FieldIter {
                bevy_reflect::structs::FieldIter::new(self)
            }
            fn to_dynamic_struct(&self)
                -> bevy_reflect::structs::DynamicStruct {
                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("normal_d",
                    bevy_reflect::PartialReflect::to_dynamic(&self.normal_d));
                dynamic
            }
        }
        impl bevy_reflect::PartialReflect for HalfSpace 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> {
                let value =
                    <dyn bevy_reflect::PartialReflect>::try_downcast_ref::<Self>(value);
                if let ::core::option::Option::Some(value) = value {
                    ::core::option::Option::Some(::core::cmp::PartialEq::eq(self,
                            value))
                } else { ::core::option::Option::Some(false) }
            }
            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 HalfSpace 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 mut __this =
                        <Self as ::core::default::Default>::default();
                    if let ::core::option::Option::Some(__field) =
                            (||
                                        <Vec4 as
                                                bevy_reflect::FromReflect>::from_reflect(bevy_reflect::structs::Struct::field(__ref_struct,
                                                    "normal_d")?))() {
                        __this.normal_d = __field;
                    }
                    ::core::option::Option::Some(__this)
                } else { ::core::option::Option::None }
            }
        }
    };Reflect),
37    reflect(Clone, Debug, Default, PartialEq)
38)]
39#[cfg_attr(
40    all(feature = "serialize", feature = "bevy_reflect"),
41    reflect(Serialize, Deserialize)
42)]
43pub struct HalfSpace {
44    normal_d: Vec4,
45}
46
47impl HalfSpace {
48    /// Constructs a `HalfSpace` from a 4D vector whose first 3 components
49    /// represent the bisecting plane's unit normal, and the last component is
50    /// the signed distance along the normal from the plane to the origin.
51    /// The constructor ensures the normal vector is normalized and the distance is appropriately scaled.
52    #[inline]
53    pub fn new(normal_d: Vec4) -> Self {
54        Self {
55            normal_d: normal_d * normal_d.xyz().length_recip(),
56        }
57    }
58
59    /// Returns the unit normal vector of the bisecting plane that characterizes the `HalfSpace`.
60    #[inline]
61    pub fn normal(&self) -> Vec3A {
62        Vec3A::from_vec4(self.normal_d)
63    }
64
65    /// Returns the signed distance from the bisecting plane to the origin along
66    /// the plane's unit normal vector.
67    #[inline]
68    pub fn d(&self) -> f32 {
69        self.normal_d.w
70    }
71
72    /// Returns the bisecting plane's unit normal vector and the signed distance
73    /// from the plane to the origin.
74    #[inline]
75    pub fn normal_d(&self) -> Vec4 {
76        self.normal_d
77    }
78
79    /// Returns the intersection point if the three halfspaces all intersect at a single point.
80    #[inline]
81    pub fn intersection_point(a: HalfSpace, b: HalfSpace, c: HalfSpace) -> Option<Vec3> {
82        let an = a.normal();
83        let bn = b.normal();
84        let cn = c.normal();
85
86        let x = Vec3A::new(an.x, bn.x, cn.x);
87        let y = Vec3A::new(an.y, bn.y, cn.y);
88        let z = Vec3A::new(an.z, bn.z, cn.z);
89
90        let d = -Vec3A::new(a.d(), b.d(), c.d());
91
92        let u = y.cross(z);
93        let v = x.cross(d);
94
95        let denom = x.dot(u);
96
97        if ops::abs(denom) < f32::EPSILON {
98            return None;
99        }
100
101        Some(Vec3::new(d.dot(u), z.dot(v), -y.dot(v)) / denom)
102    }
103}
104
105#[cfg(test)]
106mod half_space_tests {
107    use core::f32;
108
109    use approx::assert_relative_eq;
110
111    use super::HalfSpace;
112    use crate::{Vec3, Vec4};
113
114    #[test]
115    fn intersection_point() {
116        // Intersection of shifted xy, xz, and yz planes
117        let xy_at_z_3 = HalfSpace {
118            normal_d: Vec4::new(0., 0., -1., 3.),
119        };
120        let xz_at_y_2 = HalfSpace {
121            normal_d: Vec4::new(0., 1., 0., -2.),
122        };
123        let yz_at_x_1 = HalfSpace {
124            normal_d: Vec4::new(1., 0., 0., -1.),
125        };
126        assert_relative_eq!(
127            HalfSpace::intersection_point(xy_at_z_3, xz_at_y_2, yz_at_x_1).unwrap(),
128            Vec3::new(1., 2., 3.),
129            epsilon = 2e-7
130        );
131
132        // Three planes that do not simultaneously intersect
133        let xz_at_y_3 = HalfSpace {
134            normal_d: Vec4::new(0., 1., 0., -3.),
135        };
136        assert!(HalfSpace::intersection_point(xy_at_z_3, xz_at_y_2, xz_at_y_3).is_none());
137
138        // Three planes that intersect at a line
139        let other_xz_at_y_2 = HalfSpace {
140            normal_d: Vec4::new(0., -1., 0., 3.),
141        };
142        assert!(HalfSpace::intersection_point(xy_at_z_3, xz_at_y_2, other_xz_at_y_2).is_none());
143
144        // Three identical planes
145        assert!(HalfSpace::intersection_point(xz_at_y_2, xz_at_y_2, other_xz_at_y_2).is_none());
146
147        // ill-defined halfspace
148        let ill_defined = HalfSpace {
149            normal_d: Vec4::new(0., 0., 0., f32::INFINITY),
150        };
151        assert!(HalfSpace::intersection_point(xy_at_z_3, xz_at_y_2, ill_defined).is_none());
152    }
153}