Files
asusctl/rog-anime/src/sequencer.rs
2022-12-04 21:49:47 +13:00

227 lines
6.8 KiB
Rust

use glam::Vec2;
use serde_derive::{Deserialize, Serialize};
use std::convert::TryFrom;
use std::{path::PathBuf, time::Duration};
use crate::{
error::Result, AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, AnimeType,
};
/// All the possible `AniMe` actions that can be used. This enum is intended to be
/// a helper for loading up `ActionData`.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub enum ActionLoader {
/// Full gif sequence. Immutable.
AsusAnimation {
file: PathBuf,
time: AnimTime,
brightness: f32,
},
/// Image designed to be pixel perfect using the slanted template
AsusImage {
file: PathBuf,
time: AnimTime,
brightness: f32,
},
/// Animated gif. If the file is a png a static gif is created using the `time` properties
ImageAnimation {
file: PathBuf,
scale: f32,
angle: f32,
translation: Vec2,
time: AnimTime,
brightness: f32,
},
Image {
file: PathBuf,
scale: f32,
angle: f32,
translation: Vec2,
time: AnimTime,
brightness: f32,
},
/// A pause to be used between sequences
Pause(Duration),
}
/// All the possible `AniMe` actions that can be used. The enum is intended to be
/// used in a array allowing the user to cycle through a series of actions.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub enum ActionData {
/// Full gif sequence. Immutable.
Animation(AnimeGif),
/// Basic image, can have properties changed and image updated via those properties
Image(Box<AnimeDataBuffer>),
/// A pause to be used between sequences
Pause(Duration),
/// Placeholder
AudioEq,
/// Placeholder
SystemInfo,
/// Placeholder
TimeDate,
/// Placeholder
Matrix,
}
impl ActionData {
pub fn from_anime_action(anime_type: AnimeType, action: &ActionLoader) -> Result<ActionData> {
let a = match action {
ActionLoader::AsusAnimation {
file,
time,
brightness,
} => ActionData::Animation(AnimeGif::from_diagonal_gif(
file,
*time,
*brightness,
anime_type,
)?),
ActionLoader::AsusImage {
file,
time,
brightness,
} => match time {
AnimTime::Infinite => {
let image = AnimeDiagonal::from_png(file, None, *brightness, anime_type)?;
let data = image.into_data_buffer(anime_type)?;
ActionData::Image(Box::new(data))
}
_ => ActionData::Animation(AnimeGif::from_diagonal_png(
file,
anime_type,
*time,
*brightness,
)?),
},
ActionLoader::ImageAnimation {
file,
scale,
angle,
translation,
time,
brightness,
} => {
if let Some(ext) = file.extension() {
if ext.to_string_lossy().to_lowercase() == "png" {
return Ok(ActionData::Animation(AnimeGif::from_png(
file,
*scale,
*angle,
*translation,
*time,
*brightness,
anime_type,
)?));
}
}
ActionData::Animation(AnimeGif::from_gif(
file,
*scale,
*angle,
*translation,
*time,
*brightness,
anime_type,
)?)
}
ActionLoader::Image {
file,
scale,
angle,
translation,
brightness,
time,
} => {
match time {
AnimTime::Infinite => {
// If no time then create a plain static image
let image = AnimeImage::from_png(
file,
*scale,
*angle,
*translation,
*brightness,
anime_type,
)?;
let data = <AnimeDataBuffer>::try_from(&image)?;
ActionData::Image(Box::new(data))
}
_ => ActionData::Animation(AnimeGif::from_png(
file,
*scale,
*angle,
*translation,
*time,
*brightness,
anime_type,
)?),
}
}
ActionLoader::Pause(duration) => ActionData::Pause(*duration),
};
Ok(a)
}
}
/// An optimised precomputed set of actions that the user can cycle through
#[derive(Debug, Deserialize, Serialize)]
pub struct Sequences(Vec<ActionData>, AnimeType);
impl Sequences {
#[inline]
pub fn new(anime_type: AnimeType) -> Self {
Self(Vec::new(), anime_type)
}
/// Use a base `AnimeAction` to generate the precomputed data and insert in to
/// the run buffer
#[inline]
pub fn insert(&mut self, index: usize, action: &ActionLoader) -> Result<()> {
self.0
.insert(index, ActionData::from_anime_action(self.1, action)?);
Ok(())
}
/// Remove an item at this position from the run buffer. If the `index` supplied
/// is not in range then `None` is returned, otherwise the `ActionData` at that location
/// is yeeted and returned.
#[inline]
pub fn remove_item(&mut self, index: usize) -> Option<ActionData> {
if index < self.0.len() {
return Some(self.0.remove(index));
}
None
}
pub fn iter(&self) -> ActionIterator<'_> {
ActionIterator {
actions: self,
next_idx: 0,
}
}
}
/// Iteractor helper for iterating over all the actions in `Sequences`
pub struct ActionIterator<'a> {
actions: &'a Sequences,
next_idx: usize,
}
impl<'a> Iterator for ActionIterator<'a> {
type Item = &'a ActionData;
#[inline]
fn next(&mut self) -> Option<&'a ActionData> {
if self.next_idx == self.actions.0.len() {
self.next_idx = 0;
return None;
}
let current = self.next_idx;
self.next_idx += 1;
Some(&self.actions.0[current])
}
}