yoyo_physics/
sampler.rs

1use num_traits::{Float, NumCast};
2
3use super::{Approximation, Curve, Threshold};
4use crate::threshold::{And, DisplacementThreshold, VelocityThreshold};
5
6/// Sampling iterator over curves that generates approximations at a fixed sample
7/// rate.
8#[derive(Copy, Clone)]
9pub struct Sampler<'a, C, T>
10where
11    C: Curve + ?Sized,
12    T: Threshold,
13{
14    /// This is the spring that we want to sample.
15    curve: &'a C,
16
17    /// This is the sample rate (i.e. how many samples per second).
18    sample_rate: f32,
19
20    threshold: T,
21
22    /// This is the current time step of the iterator.
23    time: f32,
24
25    /// This is a boolean that indicates if the sampler is exhausted (i.e. both
26    /// thresholds have been met at the same time).
27    exhausted: bool,
28}
29
30impl<'a, C, T> Sampler<'a, C, T>
31where
32    C: Curve + ?Sized,
33    T: Threshold<Value = C::Value>,
34{
35    /// This function creates a new sampler of the given curve with the given
36    /// sample rate. The sample rate is the number of samples per second. This
37    /// will use the default thresholds of `1e-3`.
38    pub fn with_threshold(curve: &'a C, sample_rate: f32, threshold: T) -> Sampler<'a, C, T> {
39        Sampler {
40            curve,
41            sample_rate,
42            threshold,
43            time: 0.0,
44            exhausted: false,
45        }
46    }
47
48    /// Turns this sampler into a keyed iterator, which returns the timestamps
49    /// along with the approximations.
50    pub fn keyed(self) -> KeyedIter<'a, C, T> {
51        KeyedIter(self)
52    }
53}
54
55impl<'a, C, T> Sampler<'a, C, And<VelocityThreshold<T>, DisplacementThreshold<T>>>
56where
57    C: Curve<Value = T> + ?Sized,
58    T: Float,
59{
60    /// This function creates a new sampler of the given curve with the given
61    /// sample rate. The sample rate is the number of samples per second. This
62    /// will use the default thresholds of `1e-3`.
63    pub fn new(
64        curve: &'a C,
65        sample_rate: f32,
66    ) -> Sampler<'a, C, And<VelocityThreshold<T>, DisplacementThreshold<T>>> {
67        Sampler::with_thresholds(
68            curve,
69            sample_rate,
70            <T as NumCast>::from(0.001).unwrap(),
71            <T as NumCast>::from(0.001).unwrap(),
72        )
73    }
74
75    /// This function creates a new sampler of the given curve with the given
76    /// sample rate and thresholds. The sample rate is the number of samples per
77    /// second. Both thresholds must be met in order for the sampler to rest. The
78    /// velocity threshold applies to the absolute velocity at a given time. The
79    /// displacement threshold applies to the absolute distance between the
80    /// current value at a given time and the target value.
81    pub fn with_thresholds(
82        curve: &'a C,
83        sample_rate: f32,
84        rest_velocity_threshold: T,
85        rest_displacement_threshold: T,
86    ) -> Sampler<'a, C, And<VelocityThreshold<T>, DisplacementThreshold<T>>> {
87        Sampler::with_threshold(
88            curve,
89            sample_rate,
90            And(
91                VelocityThreshold(rest_velocity_threshold),
92                DisplacementThreshold {
93                    target: curve.target(),
94                    sensitivity: rest_displacement_threshold,
95                },
96            ),
97        )
98    }
99}
100
101impl<'a, C, T> Iterator for Sampler<'a, C, T>
102where
103    C: Curve + ?Sized,
104    T: Threshold<Value = C::Value, Velocity = C::Velocity>,
105{
106    type Item = Approximation<C::Value, C::Velocity>;
107
108    fn next(&mut self) -> Option<Self::Item> {
109        if self.exhausted {
110            return None;
111        }
112
113        let mut approx = self.curve.approximate(self.time);
114        self.time = self.time + 1.0 / self.sample_rate;
115
116        // If both thresholds are met, we still return this approximation
117        // (because we're still interested in its velocity) but we set a flag in
118        // the sampler state to prevent any further samples.
119        if self.threshold.evaluate(&approx) {
120            self.exhausted = true;
121
122            approx.value = self.curve.target();
123        }
124
125        Some(approx)
126    }
127}
128
129/// Iterator that yields both the timestamps of each approximation as the
130/// approximation itself.
131pub struct KeyedIter<'a, C, T>(Sampler<'a, C, T>)
132where
133    C: Curve + ?Sized,
134    T: Threshold<Value = C::Value>;
135
136impl<'a, C, T> Iterator for KeyedIter<'a, C, T>
137where
138    C: Curve + ?Sized,
139    T: Threshold<Value = C::Value, Velocity = C::Velocity>,
140{
141    type Item = (f32, Approximation<C::Value, C::Velocity>);
142
143    fn next(&mut self) -> Option<Self::Item> {
144        let time = self.0.time;
145        Some((time, self.0.next()?))
146    }
147}