use core::fmt;
use crate::frame::{is_finite3, rescaled};
use crate::{Frame, Point3, UnitVec3, Vec3};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReflectionError {
NonFinite,
Degenerate,
}
impl fmt::Display for ReflectionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
ReflectionError::NonFinite => "mirror plane has a non-finite coordinate",
ReflectionError::Degenerate => "mirror plane has a zero normal",
})
}
}
impl std::error::Error for ReflectionError {}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Reflection {
origin: Point3,
normal: UnitVec3,
}
impl Reflection {
pub fn new(origin: Point3, normal: Vec3) -> Result<Self, ReflectionError> {
if !(is_finite3(&origin.coords) && is_finite3(&normal)) {
return Err(ReflectionError::NonFinite);
}
let normal = rescaled(normal)
.and_then(|n| UnitVec3::try_new(n, 0.0))
.ok_or(ReflectionError::Degenerate)?;
Ok(Reflection { origin, normal })
}
pub fn plane_through(origin: Point3, normal: Vec3) -> Result<Self, ReflectionError> {
Self::new(origin, normal)
}
pub fn origin(&self) -> Point3 {
self.origin
}
pub fn normal(&self) -> UnitVec3 {
self.normal
}
pub fn apply(&self, p: Point3) -> Point3 {
let n = self.normal.into_inner();
p - 2.0 * (p - self.origin).dot(&n) * n
}
pub fn apply_vec(&self, v: Vec3) -> Vec3 {
let n = self.normal.into_inner();
v - 2.0 * v.dot(&n) * n
}
pub fn apply_unit(&self, u: UnitVec3) -> UnitVec3 {
UnitVec3::new_normalize(self.apply_vec(u.into_inner()))
}
pub fn apply_frame(&self, frame: &Frame) -> Frame {
self.placed(frame, self.apply_vec(frame.z().into_inner()))
}
pub fn apply_frame_reversed(&self, frame: &Frame) -> Frame {
self.placed(frame, -self.apply_vec(frame.z().into_inner()))
}
fn placed(&self, frame: &Frame, z: Vec3) -> Frame {
Frame::orthonormalised(
self.apply(frame.origin()),
self.apply_unit(frame.x()),
UnitVec3::new_normalize(z),
)
}
}