#![warn(missing_docs)]
#[cfg(doctest)]
#[doc = include_str!("../README.md")]
pub struct ReadmeDocTests;
pub mod fp;
pub mod spatial;
pub use spatial::{SpatialInertia, SpatialMat, SpatialTransform, SpatialTransformExt, SpatialVec};
pub type Quat = tang::Quat<f64>;
pub type Vec3 = tang::Vec3<f64>;
pub type Mat3 = tang::Mat3<f64>;
pub type Mat4 = tang::Mat4<f64>;
pub type DVec = tang_la::DVec<f64>;
pub type DMat = tang_la::DMat<f64>;
pub use tang::{GRAVITY, skew};
pub fn quat_exp(omega: &Vec3) -> Quat {
let angle = omega.norm();
if angle < f64::EPSILON {
return Quat {
w: 1.0,
v: *omega * 0.5,
};
}
let (s, c) = fp::sin_cos(angle * 0.5);
Quat {
w: c,
v: *omega * (s / angle),
}
}
pub fn quat_log(q: &Quat) -> Vec3 {
let norm_v = q.v.norm();
if norm_v < f64::EPSILON {
return q.v * 2.0;
}
let angle = 2.0 * fp::atan2(norm_v, q.w);
q.v * (angle / norm_v)
}
#[cfg(test)]
mod quat_map_tests {
use super::*;
#[test]
fn matches_tang_to_a_few_ulp_and_round_trips() {
for w in [
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(1e-18, 0.0, 0.0),
Vec3::new(0.3, -0.7, 1.1),
Vec3::new(0.0, 0.0, 3.0),
Vec3::new(-2.0, 0.5, 0.25),
] {
let ours = quat_exp(&w);
let theirs = Quat::exp(&w);
let tol = 8.0 * f64::EPSILON;
assert!((ours.w - theirs.w).abs() <= tol, "exp w for {w:?}");
assert!((ours.v - theirs.v).norm() <= tol, "exp v for {w:?}");
let back = quat_log(&ours);
assert!((back - w).norm() <= 1e-12, "round trip for {w:?}");
assert!(
(quat_log(&theirs) - theirs.log()).norm() <= tol,
"log {w:?}"
);
}
}
}