use glam::{Vec2, Vec3};
use super::action::Action;
use super::action_frame::ActionFrame;
pub fn wish_xy_from_actions(frame: &ActionFrame, forward_xy: Vec3, right_xy: Vec3) -> Vec2 {
let mut wish = Vec3::ZERO;
if frame.is_active(Action::FlyForward) {
wish += forward_xy;
}
if frame.is_active(Action::FlyBackward) {
wish -= forward_xy;
}
if frame.is_active(Action::FlyRight) {
wish += right_xy;
}
if frame.is_active(Action::FlyLeft) {
wish -= right_xy;
}
Vec2::new(wish.x, wish.y)
.try_normalize()
.unwrap_or(Vec2::ZERO)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::interaction::input::action_frame::ResolvedActionState;
fn frame_with(actions: &[Action]) -> ActionFrame {
let mut f = ActionFrame::default();
for &a in actions {
f.actions.insert(a, ResolvedActionState::Held);
}
f
}
fn approx_eq(a: Vec2, b: Vec2) -> bool {
(a - b).length() < 1e-5
}
#[test]
fn no_actions_returns_zero() {
let wish = wish_xy_from_actions(&frame_with(&[]), Vec3::Y, Vec3::X);
assert_eq!(wish, Vec2::ZERO);
}
#[test]
fn forward_only_returns_forward() {
let wish = wish_xy_from_actions(&frame_with(&[Action::FlyForward]), Vec3::Y, Vec3::X);
assert!(approx_eq(wish, Vec2::Y));
}
#[test]
fn right_only_returns_right() {
let wish = wish_xy_from_actions(&frame_with(&[Action::FlyRight]), Vec3::Y, Vec3::X);
assert!(approx_eq(wish, Vec2::X));
}
#[test]
fn diagonal_is_normalised() {
let wish = wish_xy_from_actions(
&frame_with(&[Action::FlyForward, Action::FlyRight]),
Vec3::Y,
Vec3::X,
);
let inv_sqrt2 = 1.0 / 2.0_f32.sqrt();
assert!(approx_eq(wish, Vec2::new(inv_sqrt2, inv_sqrt2)));
}
#[test]
fn opposing_actions_cancel() {
let wish = wish_xy_from_actions(
&frame_with(&[Action::FlyForward, Action::FlyBackward]),
Vec3::Y,
Vec3::X,
);
assert_eq!(wish, Vec2::ZERO);
}
#[test]
fn rotated_basis_steers_correctly() {
let wish = wish_xy_from_actions(
&frame_with(&[Action::FlyForward]),
Vec3::X,
Vec3::new(0.0, -1.0, 0.0),
);
assert!(approx_eq(wish, Vec2::X));
}
}