Skip to main content

rog_anime/
sequencer.rs

1use std::{path::PathBuf, time::Duration};
2
3use glam::Vec2;
4use serde_derive::{Deserialize, Serialize};
5
6use crate::{error::AnimeError, AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage};
7
8/// All the possible AniMe actions that can be used. This enum is intended to be
9/// a helper for loading up `ActionData`.
10#[derive(Debug, Clone, Deserialize, Serialize)]
11pub enum ActionLoader {
12    /// Full gif sequence. Immutable.
13    AsusAnimation {
14        file: PathBuf,
15        time: AnimTime,
16        brightness: f32,
17    },
18    /// Image designed to be pixel perfect using the slanted template
19    AsusImage {
20        file: PathBuf,
21        time: AnimTime,
22        brightness: f32,
23    },
24    /// Animated gif. If the file is a png a static gif is created using the `time` properties
25    ImageAnimation {
26        file: PathBuf,
27        scale: f32,
28        angle: f32,
29        translation: Vec2,
30        time: AnimTime,
31        brightness: f32,
32    },
33    Image {
34        file: PathBuf,
35        scale: f32,
36        angle: f32,
37        translation: Vec2,
38        time: AnimTime,
39        brightness: f32,
40    },
41    /// A pause to be used between sequences
42    Pause(Duration),
43}
44
45/// All the possible AniMe actions that can be used. The enum is intended to be
46/// used in a array allowing the user to cycle through a series of actions.
47#[derive(Debug, Clone, Deserialize, Serialize)]
48pub enum ActionData {
49    /// Full gif sequence. Immutable.
50    Animation(AnimeGif),
51    /// Basic image, can have properties changed and image updated via those properties
52    Image(Box<AnimeDataBuffer>),
53    /// A pause to be used between sequences
54    Pause(Duration),
55    /// Placeholder
56    AudioEq,
57    /// Placeholder
58    SystemInfo,
59    /// Placeholder
60    TimeDate,
61    /// Placeholder
62    Matrix,
63}
64
65impl ActionData {
66    pub fn from_anime_action(action: &ActionLoader) -> Result<ActionData, AnimeError> {
67        let a = match action {
68            ActionLoader::AsusAnimation {
69                file,
70                time,
71                brightness,
72            } => ActionData::Animation(AnimeGif::from_diagonal_gif(file, *time, *brightness)?),
73            ActionLoader::AsusImage {
74                file,
75                time,
76                brightness,
77            } => match time {
78                AnimTime::Infinite => {
79                    let image = AnimeDiagonal::from_png(file, None, *brightness)?;
80                    let data = <AnimeDataBuffer>::from(&image);
81                    ActionData::Image(Box::new(data))
82                }
83                _ => ActionData::Animation(AnimeGif::from_diagonal_png(file, *time, *brightness)?),
84            },
85            ActionLoader::ImageAnimation {
86                file,
87                scale,
88                angle,
89                translation,
90                time,
91                brightness,
92            } => {
93                if let Some(ext) = file.extension() {
94                    if ext.to_string_lossy().to_lowercase() == "png" {
95                        return Ok(ActionData::Animation(AnimeGif::from_png(
96                            file,
97                            *scale,
98                            *angle,
99                            *translation,
100                            *time,
101                            *brightness,
102                        )?));
103                    }
104                }
105                ActionData::Animation(AnimeGif::from_gif(
106                    file,
107                    *scale,
108                    *angle,
109                    *translation,
110                    *time,
111                    *brightness,
112                )?)
113            }
114            ActionLoader::Image {
115                file,
116                scale,
117                angle,
118                translation,
119                brightness,
120                time,
121            } => {
122                match time {
123                    AnimTime::Infinite => {
124                        // If no time then create a plain static image
125                        let image =
126                            AnimeImage::from_png(file, *scale, *angle, *translation, *brightness)?;
127                        let data = <AnimeDataBuffer>::from(&image);
128                        ActionData::Image(Box::new(data))
129                    }
130                    _ => ActionData::Animation(AnimeGif::from_png(
131                        file,
132                        *scale,
133                        *angle,
134                        *translation,
135                        *time,
136                        *brightness,
137                    )?),
138                }
139            }
140            ActionLoader::Pause(duration) => ActionData::Pause(*duration),
141        };
142        Ok(a)
143    }
144}
145
146/// An optimised precomputed set of actions that the user can cycle through
147#[derive(Debug, Deserialize, Serialize, Default)]
148pub struct Sequences(Vec<ActionData>);
149
150impl Sequences {
151    #[inline]
152    pub fn new() -> Self {
153        Self(Vec::new())
154    }
155
156    /// Use a base `AnimeAction` to generate the precomputed data and insert in to
157    /// the run buffer
158    #[inline]
159    pub fn insert(&mut self, index: usize, action: &ActionLoader) -> Result<(), AnimeError> {
160        self.0.insert(index, ActionData::from_anime_action(action)?);
161        Ok(())
162    }
163
164    /// Remove an item at this position from the run buffer. If the `index` supplied
165    /// is not in range then `None` is returned, otherwise the `ActionData` at that location
166    /// is yeeted and returned.
167    #[inline]
168    pub fn remove_item(&mut self, index: usize) -> Option<ActionData> {
169        if index < self.0.len() {
170            return Some(self.0.remove(index));
171        }
172        None
173    }
174
175    pub fn iter(&self) -> ActionIterator {
176        ActionIterator {
177            actions: self,
178            next_idx: 0,
179        }
180    }
181}
182
183/// Iteractor helper for iterating over all the actions in `Sequences`
184pub struct ActionIterator<'a> {
185    actions: &'a Sequences,
186    next_idx: usize,
187}
188
189impl<'a> Iterator for ActionIterator<'a> {
190    type Item = &'a ActionData;
191
192    #[inline]
193    fn next(&mut self) -> Option<&'a ActionData> {
194        if self.next_idx == self.actions.0.len() {
195            self.next_idx = 0;
196            return None;
197        }
198
199        let current = self.next_idx;
200        self.next_idx += 1;
201
202        Some(&self.actions.0[current])
203    }
204}