use nalgebra::{Point3, RealField, Unit, Vector3};
use std::borrow::Cow;
use crate::{
error::Result,
geometry::Aabb,
rt::{Hit, Ray},
traits::{Bounded, FallibleNumeric, Traceable},
};
#[derive(Debug, Clone)]
pub struct Plane<T: RealField + Copy> {
pub point: Point3<T>,
pub normal: Unit<Vector3<T>>,
}
impl<T: RealField + Copy> Plane<T> {
pub const fn new(point: Point3<T>, normal: Unit<Vector3<T>>) -> Self {
Self { point, normal }
}
pub fn from_points(p1: Point3<T>, p2: Point3<T>, p3: Point3<T>) -> Self {
let edge1 = p2 - p1;
let edge2 = p3 - p1;
let normal = Unit::new_normalize(edge1.cross(&edge2));
Self::new(p1, normal)
}
pub fn xy_plane(z: T) -> Self {
Self::new(Point3::new(T::zero(), T::zero(), z), Unit::new_unchecked(Vector3::z()))
}
pub fn xz_plane(y: T) -> Self {
Self::new(Point3::new(T::zero(), y, T::zero()), Unit::new_unchecked(Vector3::y()))
}
pub fn yz_plane(x: T) -> Self {
Self::new(Point3::new(x, T::zero(), T::zero()), Unit::new_unchecked(Vector3::x()))
}
}
impl<T: RealField + Copy> Bounded<T> for Plane<T> {
fn aabb(&self) -> Result<Cow<Aabb<T>>> {
let large_value = T::try_from_f64(1e12)?;
Ok(Cow::Owned(Aabb::new(
Point3::new(-large_value, -large_value, -large_value),
Point3::new(large_value, large_value, large_value),
)?))
}
}
impl<T: RealField + Copy> Traceable<T> for Plane<T> {
fn intersect(&self, ray: &Ray<T>) -> Result<Option<Hit<T>>> {
let epsilon = T::default_epsilon();
let denominator = ray.direction.dot(&self.normal);
if denominator.abs() < epsilon {
return Ok(None);
}
let to_point = self.point - ray.origin;
let t = to_point.dot(&self.normal) / denominator;
if t < epsilon {
return Ok(None);
}
let normal = if denominator < T::zero() {
self.normal
} else {
Unit::new_unchecked(-self.normal.as_ref())
};
Ok(Some(Hit::new(0, t, normal, normal)?))
}
}