use bevy::math::{Quat, Vec3};
use bevy::prelude::Transform;
use symbios_shape::Scope;
fn nonzero(v: f32) -> f32 {
if v >= 0.0 { v.max(1e-3) } else { v.min(-1e-3) }
}
pub fn scope_to_transform(scope: &Scope) -> Transform {
let half_size = scope.size * 0.5;
let center_d = scope.position + scope.rotation * half_size;
let translation = Vec3::new(center_d.x as f32, center_d.y as f32, center_d.z as f32);
let dq = scope.rotation;
let rotation = Quat::from_xyzw(dq.x as f32, dq.y as f32, dq.z as f32, dq.w as f32).normalize();
let scale = Vec3::new(
nonzero(scope.size.x as f32),
nonzero(scope.size.y as f32),
nonzero(scope.size.z as f32),
);
Transform {
translation,
rotation,
scale,
}
}
#[cfg(test)]
mod tests {
use super::*;
use symbios_shape::{Quat as DQuat, Scope, Vec3 as DVec3};
#[test]
fn identity_scope_centroid_at_half_size() {
let scope = Scope::new(DVec3::ZERO, DQuat::IDENTITY, DVec3::new(4.0, 6.0, 2.0));
let t = scope_to_transform(&scope);
assert!((t.translation - Vec3::new(2.0, 3.0, 1.0)).length() < 1e-5);
assert!((t.scale - Vec3::new(4.0, 6.0, 2.0)).length() < 1e-5);
}
#[test]
fn offset_scope_centroid_is_correct() {
let scope = Scope::new(
DVec3::new(10.0, 0.0, 0.0),
DQuat::IDENTITY,
DVec3::new(2.0, 4.0, 2.0),
);
let t = scope_to_transform(&scope);
assert!((t.translation - Vec3::new(11.0, 2.0, 1.0)).length() < 1e-5);
}
#[test]
fn rotation_is_preserved() {
use std::f64::consts::FRAC_PI_2;
let rot = DQuat::from_axis_angle(DVec3::Y, FRAC_PI_2);
let scope = Scope::new(DVec3::ZERO, rot, DVec3::new(2.0, 2.0, 2.0));
let t = scope_to_transform(&scope);
let forward = t.rotation * Vec3::Z;
assert!(
(forward - Vec3::new(1.0, 0.0, 0.0)).length() < 1e-5,
"expected +X, got {:?}",
forward
);
}
}