Path: blob/main/crates/bevy_math/src/primitives/half_space.rs
9299 views
use crate::{ops, Vec3, Vec3A, Vec4, Vec4Swizzles};12#[cfg(feature = "bevy_reflect")]3use bevy_reflect::{std_traits::ReflectDefault, Reflect};4#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]5use bevy_reflect::{ReflectDeserialize, ReflectSerialize};67/// A region of 3D space, specifically an open set whose border is a bisecting 2D plane.8///9/// This bisecting plane partitions 3D space into two infinite regions,10/// the half-space is one of those regions and excludes the bisecting plane.11///12/// Each instance of this type is characterized by:13/// - the bisecting plane's unit normal, normalized and pointing "inside" the half-space,14/// - the signed distance along the normal from the bisecting plane to the origin of 3D space.15///16/// The distance can also be seen as:17/// - the distance along the inverse of the normal from the origin of 3D space to the bisecting plane,18/// - the opposite of the distance along the normal from the origin of 3D space to the bisecting plane.19///20/// Any point `p` is considered to be within the `HalfSpace` when the length of the projection21/// of p on the normal is greater or equal than the opposite of the distance,22/// meaning: if the equation `normal.dot(p) + distance > 0.` is satisfied.23///24/// For example, the half-space containing all the points with a z-coordinate lesser25/// or equal than `8.0` would be defined by: `HalfSpace::new(Vec3::NEG_Z.extend(-8.0))`.26/// It includes all the points from the bisecting plane towards `NEG_Z`, and the distance27/// from the plane to the origin is `-8.0` along `NEG_Z`.28///29/// It is used to define a [`ViewFrustum`](crate::primitives::ViewFrustum),30/// but is also a useful mathematical primitive for rendering tasks such as light computation.31#[derive(Clone, Copy, Debug, Default, PartialEq)]32#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]33#[cfg_attr(34feature = "bevy_reflect",35derive(Reflect),36reflect(Clone, Debug, Default, PartialEq)37)]38#[cfg_attr(39all(feature = "serialize", feature = "bevy_reflect"),40reflect(Serialize, Deserialize)41)]42pub struct HalfSpace {43normal_d: Vec4,44}4546impl HalfSpace {47/// Constructs a `HalfSpace` from a 4D vector whose first 3 components48/// represent the bisecting plane's unit normal, and the last component is49/// the signed distance along the normal from the plane to the origin.50/// The constructor ensures the normal vector is normalized and the distance is appropriately scaled.51#[inline]52pub fn new(normal_d: Vec4) -> Self {53Self {54normal_d: normal_d * normal_d.xyz().length_recip(),55}56}5758/// Returns the unit normal vector of the bisecting plane that characterizes the `HalfSpace`.59#[inline]60pub fn normal(&self) -> Vec3A {61Vec3A::from_vec4(self.normal_d)62}6364/// Returns the signed distance from the bisecting plane to the origin along65/// the plane's unit normal vector.66#[inline]67pub fn d(&self) -> f32 {68self.normal_d.w69}7071/// Returns the bisecting plane's unit normal vector and the signed distance72/// from the plane to the origin.73#[inline]74pub fn normal_d(&self) -> Vec4 {75self.normal_d76}7778/// Returns the intersection point if the three halfspaces all intersect at a single point.79#[inline]80pub fn intersection_point(a: HalfSpace, b: HalfSpace, c: HalfSpace) -> Option<Vec3> {81let an = a.normal();82let bn = b.normal();83let cn = c.normal();8485let x = Vec3A::new(an.x, bn.x, cn.x);86let y = Vec3A::new(an.y, bn.y, cn.y);87let z = Vec3A::new(an.z, bn.z, cn.z);8889let d = -Vec3A::new(a.d(), b.d(), c.d());9091let u = y.cross(z);92let v = x.cross(d);9394let denom = x.dot(u);9596if ops::abs(denom) < f32::EPSILON {97return None;98}99100Some(Vec3::new(d.dot(u), z.dot(v), -y.dot(v)) / denom)101}102}103104#[cfg(test)]105mod half_space_tests {106use core::f32;107108use approx::assert_relative_eq;109110use super::HalfSpace;111use crate::{Vec3, Vec4};112113#[test]114fn intersection_point() {115// Intersection of shifted xy, xz, and yz planes116let xy_at_z_3 = HalfSpace {117normal_d: Vec4::new(0., 0., -1., 3.),118};119let xz_at_y_2 = HalfSpace {120normal_d: Vec4::new(0., 1., 0., -2.),121};122let yz_at_x_1 = HalfSpace {123normal_d: Vec4::new(1., 0., 0., -1.),124};125assert_relative_eq!(126HalfSpace::intersection_point(xy_at_z_3, xz_at_y_2, yz_at_x_1).unwrap(),127Vec3::new(1., 2., 3.),128epsilon = 2e-7129);130131// Three planes that do not simultaneously intersect132let xz_at_y_3 = HalfSpace {133normal_d: Vec4::new(0., 1., 0., -3.),134};135assert!(HalfSpace::intersection_point(xy_at_z_3, xz_at_y_2, xz_at_y_3).is_none());136137// Three planes that intersect at a line138let other_xz_at_y_2 = HalfSpace {139normal_d: Vec4::new(0., -1., 0., 3.),140};141assert!(HalfSpace::intersection_point(xy_at_z_3, xz_at_y_2, other_xz_at_y_2).is_none());142143// Three identical planes144assert!(HalfSpace::intersection_point(xz_at_y_2, xz_at_y_2, other_xz_at_y_2).is_none());145146// ill-defined halfspace147let ill_defined = HalfSpace {148normal_d: Vec4::new(0., 0., 0., f32::INFINITY),149};150assert!(HalfSpace::intersection_point(xy_at_z_3, xz_at_y_2, ill_defined).is_none());151}152}153154155