1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use std::ops::Mul;

use super::time::Ticks;

/// Kind of [`BaseEffect`](struct.BaseEffect.html).
///
/// Currently base effect support only xinput model of force feedback, which means that  gamepad
/// have weak and strong motor.
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum BaseEffectType {
    Weak { magnitude: u16 },
    Strong { magnitude: u16 },
    #[doc(hidden)] __Nonexhaustive,
}

impl BaseEffectType {
    fn magnitude(&self) -> u16 {
        match *self {
            BaseEffectType::Weak { magnitude } => magnitude,
            BaseEffectType::Strong { magnitude } => magnitude,
            BaseEffectType::__Nonexhaustive => unreachable!(),
        }
    }
}

impl Mul<f32> for BaseEffectType {
    type Output = BaseEffectType;

    fn mul(self, rhs: f32) -> Self::Output {
        let mg = (self.magnitude() as f32 * rhs) as u16;
        match self {
            BaseEffectType::Weak { .. } => BaseEffectType::Weak { magnitude: mg },
            BaseEffectType::Strong { .. } => BaseEffectType::Strong { magnitude: mg },
            BaseEffectType::__Nonexhaustive => unreachable!(),
        }
    }
}

impl Default for BaseEffectType {
    fn default() -> Self {
        BaseEffectType::Weak { magnitude: 0 }
    }
}

/// Basic building block used to create more complex force feedback effects.
///
/// For each base effect you can specify it's type, for how long should it be played and it's
/// strength during playback.
#[derive(Copy, Clone, PartialEq, Debug, Default)]
pub struct BaseEffect {
    /// Type of base effect.
    pub kind: BaseEffectType,
    /// Defines playback duration and delays between each repetition.
    pub scheduling: Replay,
    // TODO: maybe allow other f(t)?
    /// Basic attenuation function.
    pub envelope: Envelope,
}

impl BaseEffect {
    /// Returns `Weak` or `Strong` after applying envelope.
    pub(super) fn magnitude_at(&self, ticks: Ticks) -> BaseEffectType {
        if let Some(wrapped) = self.scheduling.wrap(ticks) {
            let att =
                self.scheduling.at(wrapped) * self.envelope.at(wrapped, self.scheduling.play_for);
            self.kind * att
        } else {
            self.kind * 0.0
        }
    }
}

// TODO: Image with "envelope"
#[derive(Copy, Clone, PartialEq, Debug, Default)]
/// Envelope shaped attenuation(time) function.
pub struct Envelope {
    pub attack_length: Ticks,
    pub attack_level: f32,
    pub fade_length: Ticks,
    pub fade_level: f32,
}

impl Envelope {
    pub(super) fn at(&self, ticks: Ticks, dur: Ticks) -> f32 {
        debug_assert!(self.fade_length < dur);
        debug_assert!(self.attack_length + self.fade_length < dur);

        if ticks < self.attack_length {
            self.attack_level
                + ticks.0 as f32 * (1.0 - self.attack_level) / self.attack_length.0 as f32
        } else if ticks + self.fade_length > dur {
            1.0
                + (ticks + self.fade_length - dur).0 as f32 * (self.fade_level - 1.0)
                    / self.fade_length.0 as f32
        } else {
            1.0
        }
    }
}

/// Defines scheduling of the basic force feedback effect.
///
/// ```text
///        ____________            ____________            ____________
///        |          |            |          |            |
/// _______|          |____________|          |____________|
///  after   play_for   with_delay   play_for   with_delay   play_for
/// ```
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Replay {
    /// Start playback `after` ticks after `Effect::play()` is called.
    pub after: Ticks,
    /// Playback duration.
    pub play_for: Ticks,
    /// If playback should be repeated delay it for `with_delay` ticks.
    pub with_delay: Ticks,
}

impl Replay {
    pub(super) fn at(&self, ticks: Ticks) -> f32 {
        if ticks >= self.play_for {
            0.0
        } else {
            1.0
        }
    }

    /// Returns duration of effect calculated as `play_for + with_delay`.
    pub fn dur(&self) -> Ticks {
        self.play_for + self.with_delay
    }

    /// Returns `None` if effect hasn't started; or wrapped value
    fn wrap(&self, ticks: Ticks) -> Option<Ticks> {
        ticks.checked_sub(self.after).map(|t| t % self.dur())
    }
}

impl Default for Replay {
    fn default() -> Self {
        Replay {
            after: Ticks(0),
            play_for: Ticks(1),
            with_delay: Ticks(0),
        }
    }
}