1use bevy_ecs::system::Resource;
4use bevy_utils::HashMap;
5use core::hash::Hash;
6
7#[cfg(feature = "bevy_reflect")]
8use bevy_reflect::Reflect;
9
10#[derive(Debug, Resource)]
16#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
17pub struct Axis<T> {
18 axis_data: HashMap<T, f32>,
20}
21
22impl<T> Default for Axis<T>
23where
24 T: Copy + Eq + Hash,
25{
26 fn default() -> Self {
27 Axis {
28 axis_data: HashMap::default(),
29 }
30 }
31}
32
33impl<T> Axis<T>
34where
35 T: Copy + Eq + Hash,
36{
37 pub const MIN: f32 = -1.0;
39
40 pub const MAX: f32 = 1.0;
42
43 pub fn set(&mut self, input_device: impl Into<T>, position_data: f32) -> Option<f32> {
49 self.axis_data.insert(input_device.into(), position_data)
50 }
51
52 pub fn get(&self, input_device: impl Into<T>) -> Option<f32> {
56 self.axis_data
57 .get(&input_device.into())
58 .copied()
59 .map(|value| value.clamp(Self::MIN, Self::MAX))
60 }
61
62 pub fn get_unclamped(&self, input_device: impl Into<T>) -> Option<f32> {
70 self.axis_data.get(&input_device.into()).copied()
71 }
72
73 pub fn remove(&mut self, input_device: impl Into<T>) -> Option<f32> {
75 self.axis_data.remove(&input_device.into())
76 }
77
78 pub fn all_axes(&self) -> impl Iterator<Item = &T> {
80 self.axis_data.keys()
81 }
82
83 pub fn all_axes_and_values(&self) -> impl Iterator<Item = (&T, f32)> {
85 self.axis_data.iter().map(|(axis, value)| (axis, *value))
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use crate::{gamepad::GamepadButton, Axis};
92
93 #[test]
94 fn test_axis_set() {
95 let cases = [
96 (-1.5, Some(-1.0)),
97 (-1.1, Some(-1.0)),
98 (-1.0, Some(-1.0)),
99 (-0.9, Some(-0.9)),
100 (-0.1, Some(-0.1)),
101 (0.0, Some(0.0)),
102 (0.1, Some(0.1)),
103 (0.9, Some(0.9)),
104 (1.0, Some(1.0)),
105 (1.1, Some(1.0)),
106 (1.6, Some(1.0)),
107 ];
108
109 for (value, expected) in cases {
110 let mut axis = Axis::<GamepadButton>::default();
111
112 axis.set(GamepadButton::RightTrigger, value);
113
114 let actual = axis.get(GamepadButton::RightTrigger);
115 assert_eq!(expected, actual);
116 }
117 }
118
119 #[test]
120 fn test_axis_remove() {
121 let cases = [-1.0, -0.9, -0.1, 0.0, 0.1, 0.9, 1.0];
122
123 for value in cases {
124 let mut axis = Axis::<GamepadButton>::default();
125
126 axis.set(GamepadButton::RightTrigger, value);
127 assert!(axis.get(GamepadButton::RightTrigger).is_some());
128
129 axis.remove(GamepadButton::RightTrigger);
130 let actual = axis.get(GamepadButton::RightTrigger);
131 let expected = None;
132
133 assert_eq!(expected, actual);
134 }
135 }
136}