Files
asusctl/rog-anime/src/sequencer.rs
Luke D. Jones 1cbffedaeb Advanced Aura feature
Groundwork for 'advanced' aura modes
Add single zone + Doom light flash
Fix mocking for ROGCC
Better prepare & change to mapping of keyboard layouts to models and functions
Refactor and begin using new key layout stuff
Enable first arg to rogcc to set layout in mocking feature mode
Complete refactor of key layouts, and to RON serde
2023-01-03 20:21:11 +13:00

230 lines
6.8 KiB
Rust

use std::convert::TryFrom;
use std::path::PathBuf;
use std::time::Duration;
use glam::Vec2;
use serde_derive::{Deserialize, Serialize};
use crate::error::Result;
use crate::{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])
}
}