use crate::{ Normal, Direction };
pub struct Onb {
pub u: Normal,
pub v: Normal,
pub w: Normal,
}
impl Onb {
pub fn new(dir: Direction) -> Self {
let w = dir.normalize();
let (u, v) = w.any_orthonormal_pair();
Self { u, v, w }
}
pub fn new_from_basis(u: Normal, v: Normal, w: Normal) -> Self {
let eps = 1e-5;
assert!(u.is_normalized() && v.is_normalized() && w.is_normalized());
assert!(u.dot(v).abs() < eps && u.dot(w).abs() < eps && v.dot(w).abs() < eps);
Self { u, v, w }
}
pub fn to_world(&self, v: Direction) -> Direction {
v.x * self.u + v.y * self.v + v.z * self.w
}
pub fn to_local(&self, v: Direction) -> Direction {
Direction::new(
v.dot(self.u),
v.dot(self.v),
v.dot(self.w),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn both_directions() {
let w = Direction::new(1.23, 4.56, 7.89);
let uvw = Onb::new(w);
let v = Direction::new(9.87, 6.54, 3.21);
let vp = uvw.to_world(uvw.to_local(v));
assert!(v.distance(vp) < 1e-10);
}
}