use crate::Quat;
use crate::Vec3;
pub trait QuatExt: Sized {
fn rotate_negative_z_towards(forward: Vec3, up: Vec3) -> Option<Quat>;
fn rotate_positive_z_towards(forward: Vec3, up: Vec3) -> Option<Quat>;
}
impl QuatExt for Quat {
fn rotate_negative_z_towards(forward: Vec3, up: Vec3) -> Option<Quat> {
let forward = forward.normalize_or_zero();
let side = forward.cross(up).normalize_or_zero(); let up = side.cross(forward);
if forward != Vec3::ZERO && side != Vec3::ZERO && up != Vec3::ZERO {
Some(Self::from_mat3(&glam::Mat3::from_cols(side, up, -forward)))
} else {
None
}
}
fn rotate_positive_z_towards(forward: Vec3, up: Vec3) -> Option<Quat> {
let forward = forward.normalize_or_zero();
let side = up.cross(forward).normalize_or_zero(); let up = forward.cross(side);
if forward != Vec3::ZERO && side != Vec3::ZERO && up != Vec3::ZERO {
Some(Self::from_mat3(&glam::Mat3::from_cols(side, up, forward)))
} else {
None
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_rotate_negative_z_towards() {
let desired_fwd = Vec3::new(1.0, 2.0, 3.0).normalize();
let desired_up = Vec3::new(4.0, 5.0, 6.0).normalize();
let rot = Quat::rotate_negative_z_towards(desired_fwd, desired_up).unwrap();
let rotated_z = rot * -Vec3::Z;
assert!(
(rotated_z - desired_fwd).length() < 1e-5,
"Expected to rotate -Z to {}, but got {}",
desired_fwd,
rotated_z,
);
let rotated_y = rot * Vec3::Y;
assert!(
rotated_y.dot(desired_up) >= 0.0,
"Expected to rotate +Y to point approximately towards {}, but got {}",
desired_up,
rotated_y,
);
}
#[test]
fn test_rotate_positive_z_towards() {
let desired_fwd = Vec3::new(1.0, 2.0, 3.0).normalize();
let desired_up = Vec3::new(4.0, 5.0, 6.0).normalize();
let rot = Quat::rotate_positive_z_towards(desired_fwd, desired_up).unwrap();
let rotated_z = rot * Vec3::Z;
assert!(
(rotated_z - desired_fwd).length() < 1e-5,
"Expected to rotate +Z to {}, but got {}",
desired_fwd,
rotated_z,
);
let rotated_y = rot * Vec3::Y;
assert!(
rotated_y.dot(desired_up) >= 0.0,
"Expected to rotate +Y to point approximately towards {}, but got {}",
desired_up,
rotated_y,
);
}
}