viewport_lib/camera/controllers/
movement.rs1use glam::{Vec2, Vec3};
16
17use crate::interaction::input::action::Action;
18use crate::interaction::input::action_frame::ActionFrame;
19
20pub fn wish_xy_from_actions(frame: &ActionFrame, forward_xy: Vec3, right_xy: Vec3) -> Vec2 {
34 let mut wish = Vec3::ZERO;
35 if frame.is_active(Action::FlyForward) {
36 wish += forward_xy;
37 }
38 if frame.is_active(Action::FlyBackward) {
39 wish -= forward_xy;
40 }
41 if frame.is_active(Action::FlyRight) {
42 wish += right_xy;
43 }
44 if frame.is_active(Action::FlyLeft) {
45 wish -= right_xy;
46 }
47 Vec2::new(wish.x, wish.y)
48 .try_normalize()
49 .unwrap_or(Vec2::ZERO)
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55 use crate::interaction::input::action_frame::ResolvedActionState;
56
57 fn frame_with(actions: &[Action]) -> ActionFrame {
58 let mut f = ActionFrame::default();
59 for &a in actions {
60 f.actions.insert(a, ResolvedActionState::Held);
61 }
62 f
63 }
64
65 fn approx_eq(a: Vec2, b: Vec2) -> bool {
66 (a - b).length() < 1e-5
67 }
68
69 #[test]
70 fn no_actions_returns_zero() {
71 let wish = wish_xy_from_actions(&frame_with(&[]), Vec3::Y, Vec3::X);
72 assert_eq!(wish, Vec2::ZERO);
73 }
74
75 #[test]
76 fn forward_only_returns_forward() {
77 let wish = wish_xy_from_actions(&frame_with(&[Action::FlyForward]), Vec3::Y, Vec3::X);
78 assert!(approx_eq(wish, Vec2::Y));
79 }
80
81 #[test]
82 fn right_only_returns_right() {
83 let wish = wish_xy_from_actions(&frame_with(&[Action::FlyRight]), Vec3::Y, Vec3::X);
84 assert!(approx_eq(wish, Vec2::X));
85 }
86
87 #[test]
88 fn diagonal_is_normalised() {
89 let wish = wish_xy_from_actions(
90 &frame_with(&[Action::FlyForward, Action::FlyRight]),
91 Vec3::Y,
92 Vec3::X,
93 );
94 let inv_sqrt2 = 1.0 / 2.0_f32.sqrt();
95 assert!(approx_eq(wish, Vec2::new(inv_sqrt2, inv_sqrt2)));
96 }
97
98 #[test]
99 fn opposing_actions_cancel() {
100 let wish = wish_xy_from_actions(
101 &frame_with(&[Action::FlyForward, Action::FlyBackward]),
102 Vec3::Y,
103 Vec3::X,
104 );
105 assert_eq!(wish, Vec2::ZERO);
106 }
107
108 #[test]
109 fn rotated_basis_steers_correctly() {
110 let wish = wish_xy_from_actions(
112 &frame_with(&[Action::FlyForward]),
113 Vec3::X,
114 Vec3::new(0.0, -1.0, 0.0),
115 );
116 assert!(approx_eq(wish, Vec2::X));
117 }
118}