onedrop_engine/
transition.rs

1//! Preset transition system with blending.
2
3use std::time::{Duration, Instant};
4
5/// Transition mode between presets.
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum TransitionMode {
8    /// Instant cut (no transition)
9    Cut,
10    /// Linear fade
11    Fade,
12    /// Smooth ease-in-out
13    EaseInOut,
14    /// Crossfade with overlap
15    Crossfade,
16}
17
18/// Transition state.
19#[derive(Debug, Clone)]
20pub struct Transition {
21    /// Transition mode
22    mode: TransitionMode,
23
24    /// Transition duration
25    duration: Duration,
26
27    /// Start time
28    start_time: Instant,
29
30    /// Current progress (0.0 to 1.0)
31    progress: f32,
32
33    /// Is transition active
34    active: bool,
35}
36
37impl Transition {
38    /// Create a new transition.
39    pub fn new(mode: TransitionMode, duration: Duration) -> Self {
40        Self {
41            mode,
42            duration,
43            start_time: Instant::now(),
44            progress: 0.0,
45            active: false,
46        }
47    }
48
49    /// Start the transition.
50    pub fn start(&mut self) {
51        self.start_time = Instant::now();
52        self.progress = 0.0;
53        self.active = true;
54    }
55
56    /// Update transition progress.
57    pub fn update(&mut self) {
58        if !self.active {
59            return;
60        }
61
62        let elapsed = self.start_time.elapsed();
63        let t = elapsed.as_secs_f32() / self.duration.as_secs_f32();
64
65        if t >= 1.0 {
66            self.progress = 1.0;
67            self.active = false;
68        } else {
69            self.progress = match self.mode {
70                TransitionMode::Cut => 1.0,
71                TransitionMode::Fade => t,
72                TransitionMode::EaseInOut => Self::ease_in_out(t),
73                TransitionMode::Crossfade => t,
74            };
75        }
76    }
77
78    /// Get current progress (0.0 to 1.0).
79    pub fn progress(&self) -> f32 {
80        self.progress
81    }
82
83    /// Check if transition is complete.
84    pub fn is_complete(&self) -> bool {
85        !self.active
86    }
87
88    /// Get blend factor for old preset (1.0 to 0.0).
89    pub fn old_blend(&self) -> f32 {
90        1.0 - self.progress
91    }
92
93    /// Get blend factor for new preset (0.0 to 1.0).
94    pub fn new_blend(&self) -> f32 {
95        self.progress
96    }
97
98    /// Ease-in-out function (smooth S-curve).
99    fn ease_in_out(t: f32) -> f32 {
100        if t < 0.5 {
101            2.0 * t * t
102        } else {
103            1.0 - 2.0 * (1.0 - t) * (1.0 - t)
104        }
105    }
106}
107
108impl Default for Transition {
109    fn default() -> Self {
110        Self::new(TransitionMode::Fade, Duration::from_secs(2))
111    }
112}
113
114/// Preset transition manager.
115pub struct TransitionManager {
116    /// Current transition
117    transition: Option<Transition>,
118
119    /// Default transition mode
120    default_mode: TransitionMode,
121
122    /// Default transition duration
123    default_duration: Duration,
124}
125
126impl TransitionManager {
127    /// Create a new transition manager.
128    pub fn new(mode: TransitionMode, duration: Duration) -> Self {
129        Self {
130            transition: None,
131            default_mode: mode,
132            default_duration: duration,
133        }
134    }
135
136    /// Start a new transition.
137    pub fn start_transition(&mut self) {
138        let mut transition = Transition::new(self.default_mode, self.default_duration);
139        transition.start();
140        self.transition = Some(transition);
141    }
142
143    /// Start a transition with custom parameters.
144    pub fn start_custom_transition(&mut self, mode: TransitionMode, duration: Duration) {
145        let mut transition = Transition::new(mode, duration);
146        transition.start();
147        self.transition = Some(transition);
148    }
149
150    /// Update the current transition.
151    pub fn update(&mut self) {
152        if let Some(ref mut transition) = self.transition {
153            transition.update();
154
155            if transition.is_complete() {
156                self.transition = None;
157            }
158        }
159    }
160
161    /// Check if a transition is active.
162    pub fn is_transitioning(&self) -> bool {
163        self.transition.is_some()
164    }
165
166    /// Get current transition progress.
167    pub fn progress(&self) -> f32 {
168        self.transition
169            .as_ref()
170            .map(|t| t.progress())
171            .unwrap_or(1.0)
172    }
173
174    /// Get blend factors (old, new).
175    pub fn blend_factors(&self) -> (f32, f32) {
176        if let Some(ref transition) = self.transition {
177            (transition.old_blend(), transition.new_blend())
178        } else {
179            (0.0, 1.0) // Fully on new preset
180        }
181    }
182}
183
184impl Default for TransitionManager {
185    fn default() -> Self {
186        Self::new(TransitionMode::Fade, Duration::from_secs(2))
187    }
188}
189
190#[cfg(test)]
191mod tests {
192    use super::*;
193    use std::thread;
194
195    #[test]
196    fn test_transition_progress() {
197        let mut transition = Transition::new(TransitionMode::Fade, Duration::from_millis(100));
198        transition.start();
199
200        assert_eq!(transition.progress(), 0.0);
201        assert!(!transition.is_complete());
202
203        thread::sleep(Duration::from_millis(50));
204        transition.update();
205
206        assert!(transition.progress() > 0.0 && transition.progress() < 1.0);
207
208        thread::sleep(Duration::from_millis(60));
209        transition.update();
210
211        assert_eq!(transition.progress(), 1.0);
212        assert!(transition.is_complete());
213    }
214
215    #[test]
216    fn test_ease_in_out() {
217        assert_eq!(Transition::ease_in_out(0.0), 0.0);
218        assert_eq!(Transition::ease_in_out(1.0), 1.0);
219
220        let mid = Transition::ease_in_out(0.5);
221        assert!(mid > 0.4 && mid < 0.6);
222    }
223
224    #[test]
225    fn test_blend_factors() {
226        let mut transition = Transition::new(TransitionMode::Fade, Duration::from_secs(1));
227        transition.start();
228
229        // At start
230        assert_eq!(transition.old_blend(), 1.0);
231        assert_eq!(transition.new_blend(), 0.0);
232
233        // Simulate progress
234        transition.progress = 0.5;
235        assert_eq!(transition.old_blend(), 0.5);
236        assert_eq!(transition.new_blend(), 0.5);
237    }
238
239    #[test]
240    fn test_transition_manager() {
241        let mut manager = TransitionManager::default();
242
243        assert!(!manager.is_transitioning());
244
245        manager.start_transition();
246        assert!(manager.is_transitioning());
247
248        thread::sleep(Duration::from_millis(100));
249        manager.update();
250
251        assert!(manager.progress() > 0.0);
252    }
253}