use avian3d::math::{Quaternion, Scalar, Vector};
use bevy_math::{Quat, Vec3};
#[inline]
pub(crate) fn lerp_1d(x: Scalar, bp: &[Scalar], vals: &[Scalar]) -> Scalar {
debug_assert_eq!(bp.len(), vals.len());
if bp.is_empty() {
return 0.0 as Scalar;
}
if bp.len() == 1 {
return vals[0];
}
let idx = bp.partition_point(|&b| b <= x).saturating_sub(1);
let idx = idx.min(bp.len() - 2);
let t = (x - bp[idx]) / (bp[idx + 1] - bp[idx]);
vals[idx] + t * (vals[idx + 1] - vals[idx])
}
#[inline]
#[allow(clippy::unnecessary_cast)]
pub(crate) fn vec3_to_vector(v: Vec3) -> Vector {
Vector::new(v.x as Scalar, v.y as Scalar, v.z as Scalar)
}
#[inline]
#[allow(clippy::unnecessary_cast)]
pub(crate) fn quat_to_quaternion(q: Quat) -> Quaternion {
Quaternion::from_array(q.to_array().map(|x| x as Scalar))
}
#[inline]
#[allow(clippy::unnecessary_cast)]
pub(crate) fn vector_to_vec3(v: Vector) -> Vec3 {
Vec3::new(v.x as f32, v.y as f32, v.z as f32)
}
#[cfg(test)]
pub(crate) fn body_to_world(rotation: Quaternion, v_body: Vector) -> Vector {
rotation * v_body
}
#[inline]
pub(crate) fn world_to_body(rotation: Quaternion, v_world: Vector) -> Vector {
rotation.inverse() * v_world
}
#[cfg(test)]
mod tests {
use super::*;
use avian3d::math::FRAC_PI_2;
#[cfg(feature = "f32")]
const EPS: Scalar = 1e-5;
#[cfg(not(feature = "f32"))]
const EPS: Scalar = 1e-10;
fn approx_eq(a: Vector, b: Vector) -> bool {
(a - b).length() < EPS
}
#[test]
fn identity_rotation_is_passthrough() {
assert!(approx_eq(
body_to_world(Quaternion::IDENTITY, Vector::X),
Vector::X
));
assert!(approx_eq(
body_to_world(Quaternion::IDENTITY, Vector::Y),
Vector::Y
));
assert!(approx_eq(
body_to_world(Quaternion::IDENTITY, Vector::Z),
Vector::Z
));
}
#[test]
fn round_trip() {
let rot = Quaternion::from_rotation_y(0.3) * Quaternion::from_rotation_x(0.1);
let v = Vector::new(1.0, 2.0, 3.0);
let round = world_to_body(rot, body_to_world(rot, v));
assert!(approx_eq(round, v), "round-trip failed: {round:?} != {v:?}");
}
#[test]
fn rotation_y_90_x_to_neg_z() {
let rot = Quaternion::from_rotation_y(FRAC_PI_2);
let result = body_to_world(rot, Vector::X);
assert!(
approx_eq(result, -Vector::Z),
"expected (0,0,-1), got {result:?}"
);
}
#[test]
fn rotation_x_90_y_to_z() {
let rot = Quaternion::from_rotation_x(FRAC_PI_2);
let result = body_to_world(rot, Vector::Y);
assert!(
approx_eq(result, Vector::Z),
"expected (0,0,1), got {result:?}"
);
}
#[test]
fn world_to_body_inverts_rotation_y() {
let rot = Quaternion::from_rotation_y(FRAC_PI_2);
let result = world_to_body(rot, -Vector::Z);
assert!(
approx_eq(result, Vector::X),
"expected (1,0,0), got {result:?}"
);
}
}