use crate::{ops, Vec3, Vec3A, Vec4, Vec4Swizzles};
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
#[derive(Clone, Copy, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Clone, Debug, Default, PartialEq)
)]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),
reflect(Serialize, Deserialize)
)]
pub struct HalfSpace {
normal_d: Vec4,
}
impl HalfSpace {
#[inline]
pub fn new(normal_d: Vec4) -> Self {
Self {
normal_d: normal_d * normal_d.xyz().length_recip(),
}
}
#[inline]
pub fn normal(&self) -> Vec3A {
Vec3A::from_vec4(self.normal_d)
}
#[inline]
pub fn d(&self) -> f32 {
self.normal_d.w
}
#[inline]
pub fn normal_d(&self) -> Vec4 {
self.normal_d
}
#[inline]
pub fn intersection_point(a: HalfSpace, b: HalfSpace, c: HalfSpace) -> Option<Vec3> {
let an = a.normal();
let bn = b.normal();
let cn = c.normal();
let x = Vec3A::new(an.x, bn.x, cn.x);
let y = Vec3A::new(an.y, bn.y, cn.y);
let z = Vec3A::new(an.z, bn.z, cn.z);
let d = -Vec3A::new(a.d(), b.d(), c.d());
let u = y.cross(z);
let v = x.cross(d);
let denom = x.dot(u);
if ops::abs(denom) < f32::EPSILON {
return None;
}
Some(Vec3::new(d.dot(u), z.dot(v), -y.dot(v)) / denom)
}
}
#[cfg(test)]
mod half_space_tests {
use core::f32;
use approx::assert_relative_eq;
use super::HalfSpace;
use crate::{Vec3, Vec4};
#[test]
fn intersection_point() {
let xy_at_z_3 = HalfSpace {
normal_d: Vec4::new(0., 0., -1., 3.),
};
let xz_at_y_2 = HalfSpace {
normal_d: Vec4::new(0., 1., 0., -2.),
};
let yz_at_x_1 = HalfSpace {
normal_d: Vec4::new(1., 0., 0., -1.),
};
assert_relative_eq!(
HalfSpace::intersection_point(xy_at_z_3, xz_at_y_2, yz_at_x_1).unwrap(),
Vec3::new(1., 2., 3.),
epsilon = 2e-7
);
let xz_at_y_3 = HalfSpace {
normal_d: Vec4::new(0., 1., 0., -3.),
};
assert!(HalfSpace::intersection_point(xy_at_z_3, xz_at_y_2, xz_at_y_3).is_none());
let other_xz_at_y_2 = HalfSpace {
normal_d: Vec4::new(0., -1., 0., 3.),
};
assert!(HalfSpace::intersection_point(xy_at_z_3, xz_at_y_2, other_xz_at_y_2).is_none());
assert!(HalfSpace::intersection_point(xz_at_y_2, xz_at_y_2, other_xz_at_y_2).is_none());
let ill_defined = HalfSpace {
normal_d: Vec4::new(0., 0., 0., f32::INFINITY),
};
assert!(HalfSpace::intersection_point(xy_at_z_3, xz_at_y_2, ill_defined).is_none());
}
}