press_here/axis/
filters.rs

1use crate::{AxisBinding, inputs::Inputs};
2
3/// A filter that only allows axis values that exceed a certain deadzone threshold.
4#[derive(Clone, Copy)]
5pub struct Deadzone<A: AxisBinding>(pub A, pub f32);
6
7impl<A: AxisBinding + Clone> AxisBinding for Deadzone<A> {
8    fn value(&mut self, inputs: &Inputs) -> Option<f32> {
9        let value = self.0.value(inputs)?;
10
11        if value.abs() < self.1 {
12            None
13        } else {
14            Some(value)
15        }
16    }
17
18    fn clone_axis(&self) -> Box<dyn AxisBinding> {
19        Box::new(self.clone())
20    }
21}
22
23/// A filter that smooths axis values using
24/// [exponential smoothing](https://en.wikipedia.org/wiki/Exponential_smoothing#Basic_(simple)_exponential_smoothing).
25///
26/// `tau` is the time constant that controls the amount of smoothing. Small `tau` values result in less smoothing (more
27/// responsive), while large `tau` values result in more smoothing (less responsive).
28#[derive(Clone, Copy)]
29pub struct Smooth<A: AxisBinding> {
30    pub binding: A,
31    pub tau: f32,
32    previous_value: f32,
33}
34
35impl<A: AxisBinding> Smooth<A> {
36    pub fn new(binding: A, tau: f32) -> Self {
37        Self {
38            binding,
39            tau,
40            previous_value: 0.0,
41        }
42    }
43}
44
45impl<A: AxisBinding + Clone> AxisBinding for Smooth<A> {
46    fn value(&mut self, inputs: &Inputs) -> Option<f32> {
47        let target = self.binding.value(inputs).unwrap_or(0.0);
48        let dt = inputs.time.delta_secs();
49
50        let alpha = 1.0 - (-dt / self.tau).exp();
51
52        let value = self.previous_value + alpha * (target - self.previous_value);
53        self.previous_value = value;
54
55        Some(value)
56    }
57
58    fn clone_axis(&self) -> Box<dyn AxisBinding> {
59        Box::new(self.clone())
60    }
61}
62
63/// A filter that normalizes the axis value so that the combined magnitude of the two axes is at most 1.0.
64///
65/// The first supplied axis is the one being normalized, and the second is the perpendicular axis used to calculate the
66/// magnitude.
67#[derive(Clone, Copy)]
68pub struct Normalize<A: AxisBinding, Perpendicular: AxisBinding>(pub A, pub Perpendicular);
69
70impl<A: AxisBinding + Clone, Perpendicular: AxisBinding + Clone> AxisBinding
71    for Normalize<A, Perpendicular>
72{
73    fn value(&mut self, inputs: &Inputs) -> Option<f32> {
74        let x = self.0.value(inputs).unwrap_or(0.0);
75        let y = self.1.value(inputs).unwrap_or(0.0);
76
77        let magnitude = (x * x + y * y).sqrt();
78        if magnitude > 1.0 {
79            Some(x / magnitude)
80        } else {
81            Some(x)
82        }
83    }
84
85    fn clone_axis(&self) -> Box<dyn AxisBinding> {
86        Box::new(self.clone())
87    }
88}
89
90/// Limits the rate of change of an axis value to a maximum delta per second.
91///
92/// # Examples
93/// ```no_run
94/// # use press_here::RateLimit;
95/// # let binding = 1.0;
96/// let maximum_rate = 0.5; // units per second
97/// let limited_axis = RateLimit::new(binding, maximum_rate);
98/// ```
99#[derive(Clone, Copy)]
100pub struct RateLimit<A: AxisBinding> {
101    pub binding: A,
102    pub max_rate: f32,
103    previous_value: f32,
104}
105
106impl<A: AxisBinding> RateLimit<A> {
107    pub fn new(binding: A, max_rate: f32) -> Self {
108        Self {
109            binding,
110            max_rate,
111            previous_value: 0.0,
112        }
113    }
114}
115
116impl<A: AxisBinding + Clone> AxisBinding for RateLimit<A> {
117    fn value(&mut self, inputs: &Inputs) -> Option<f32> {
118        let target = self.binding.value(inputs)?;
119        let dt = inputs.time.delta_secs();
120
121        let max_delta = self.max_rate * dt;
122        let delta = (target - self.previous_value).clamp(-max_delta, max_delta);
123
124        let value = self.previous_value + delta;
125        self.previous_value = value;
126
127        Some(value)
128    }
129
130    fn clone_axis(&self) -> Box<dyn AxisBinding> {
131        Box::new(self.clone())
132    }
133}
134
135/// Clamps the binding value to a specified range.
136///
137/// # Examples
138/// ```no_run
139/// # use press_here::Clamp;
140/// # let binding = 1.0;
141/// let clamped = Clamp(binding, -0.5, 0.5);
142/// ```
143#[derive(Clone, Copy)]
144pub struct Clamp<A: AxisBinding>(pub A, pub f32, pub f32);
145
146impl<A: AxisBinding + Clone> AxisBinding for Clamp<A> {
147    fn value(&mut self, inputs: &Inputs) -> Option<f32> {
148        let value = self.0.value(inputs)?;
149
150        Some(value.clamp(self.1, self.2))
151    }
152
153    fn clone_axis(&self) -> Box<dyn AxisBinding> {
154        Box::new(self.clone())
155    }
156}