Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_math/src/primitives/half_space.rs
9299 views
1
use crate::{ops, Vec3, Vec3A, Vec4, Vec4Swizzles};
2
3
#[cfg(feature = "bevy_reflect")]
4
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
5
#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
6
use 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(Clone, Copy, Debug, Default, PartialEq)]
33
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
34
#[cfg_attr(
35
feature = "bevy_reflect",
36
derive(Reflect),
37
reflect(Clone, Debug, Default, PartialEq)
38
)]
39
#[cfg_attr(
40
all(feature = "serialize", feature = "bevy_reflect"),
41
reflect(Serialize, Deserialize)
42
)]
43
pub struct HalfSpace {
44
normal_d: Vec4,
45
}
46
47
impl 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)]
106
mod 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
}
154
155