gilrs/ff/
base_effect.rs

1// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use std::ops::Mul;
9
10use super::time::Ticks;
11
12/// Kind of [`BaseEffect`](struct.BaseEffect.html).
13///
14/// Currently base effect support only xinput model of force feedback, which means that  gamepad
15/// have weak and strong motor.
16#[derive(Copy, Clone, PartialEq, Eq, Debug)]
17#[non_exhaustive]
18pub enum BaseEffectType {
19    Weak { magnitude: u16 },
20    Strong { magnitude: u16 },
21}
22
23impl BaseEffectType {
24    fn magnitude(&self) -> u16 {
25        match *self {
26            BaseEffectType::Weak { magnitude } => magnitude,
27            BaseEffectType::Strong { magnitude } => magnitude,
28        }
29    }
30}
31
32impl Mul<f32> for BaseEffectType {
33    type Output = BaseEffectType;
34
35    fn mul(self, rhs: f32) -> Self::Output {
36        let mg = (self.magnitude() as f32 * rhs) as u16;
37        match self {
38            BaseEffectType::Weak { .. } => BaseEffectType::Weak { magnitude: mg },
39            BaseEffectType::Strong { .. } => BaseEffectType::Strong { magnitude: mg },
40        }
41    }
42}
43
44impl Default for BaseEffectType {
45    fn default() -> Self {
46        BaseEffectType::Weak { magnitude: 0 }
47    }
48}
49
50/// Basic building block used to create more complex force feedback effects.
51///
52/// For each base effect you can specify it's type, for how long should it be played and it's
53/// strength during playback.
54#[derive(Copy, Clone, PartialEq, Debug, Default)]
55pub struct BaseEffect {
56    /// Type of base effect.
57    pub kind: BaseEffectType,
58    /// Defines playback duration and delays between each repetition.
59    pub scheduling: Replay,
60    // TODO: maybe allow other f(t)?
61    /// Basic attenuation function.
62    pub envelope: Envelope,
63}
64
65impl BaseEffect {
66    /// Returns `Weak` or `Strong` after applying envelope.
67    pub(super) fn magnitude_at(&self, ticks: Ticks) -> BaseEffectType {
68        if let Some(wrapped) = self.scheduling.wrap(ticks) {
69            let att =
70                self.scheduling.at(wrapped) * self.envelope.at(wrapped, self.scheduling.play_for);
71            self.kind * att
72        } else {
73            self.kind * 0.0
74        }
75    }
76}
77
78// TODO: Image with "envelope"
79#[derive(Copy, Clone, PartialEq, Debug, Default)]
80/// Envelope shaped attenuation(time) function.
81pub struct Envelope {
82    pub attack_length: Ticks,
83    pub attack_level: f32,
84    pub fade_length: Ticks,
85    pub fade_level: f32,
86}
87
88impl Envelope {
89    pub(super) fn at(&self, ticks: Ticks, dur: Ticks) -> f32 {
90        debug_assert!(self.fade_length < dur);
91        debug_assert!(self.attack_length + self.fade_length < dur);
92
93        if ticks < self.attack_length {
94            self.attack_level
95                + ticks.0 as f32 * (1.0 - self.attack_level) / self.attack_length.0 as f32
96        } else if ticks + self.fade_length > dur {
97            1.0 + (ticks + self.fade_length - dur).0 as f32 * (self.fade_level - 1.0)
98                / self.fade_length.0 as f32
99        } else {
100            1.0
101        }
102    }
103}
104
105/// Defines scheduling of the basic force feedback effect.
106///
107/// ```text
108///        ____________            ____________            ____________
109///        |          |            |          |            |
110/// _______|          |____________|          |____________|
111///  after   play_for   with_delay   play_for   with_delay   play_for
112/// ```
113#[derive(Copy, Clone, PartialEq, Eq, Debug)]
114pub struct Replay {
115    /// Start playback `after` ticks after `Effect::play()` is called.
116    pub after: Ticks,
117    /// Playback duration.
118    pub play_for: Ticks,
119    /// If playback should be repeated delay it for `with_delay` ticks.
120    pub with_delay: Ticks,
121}
122
123impl Replay {
124    pub(super) fn at(&self, ticks: Ticks) -> f32 {
125        if ticks >= self.play_for {
126            0.0
127        } else {
128            1.0
129        }
130    }
131
132    /// Returns duration of effect calculated as `play_for + with_delay`.
133    pub fn dur(&self) -> Ticks {
134        self.play_for + self.with_delay
135    }
136
137    /// Returns `None` if effect hasn't started; or wrapped value
138    fn wrap(&self, ticks: Ticks) -> Option<Ticks> {
139        ticks.checked_sub(self.after).map(|t| t % self.dur())
140    }
141}
142
143impl Default for Replay {
144    fn default() -> Self {
145        Replay {
146            after: Ticks(0),
147            play_for: Ticks(1),
148            with_delay: Ticks(0),
149        }
150    }
151}