mod parse;
#[cfg(feature = "nostd")]
use alloc::vec::Vec;
#[cfg(not(feature = "nostd"))]
use std::vec::Vec;
use parse::{apply_acceleration_curve, parse_animatable_tags};
#[derive(Debug, Clone)]
pub struct TransformAnimation {
pub start_ms: u32,
pub end_ms: u32,
pub accel: f32,
pub target_tags: Vec<AnimatableTag>,
}
#[derive(Debug, Clone)]
pub enum AnimatableTag {
FontSize(f32),
FontScaleX(f32),
FontScaleY(f32),
FontSpacing(f32),
FontRotationZ(f32),
FontRotationX(f32),
FontRotationY(f32),
PrimaryColor([u8; 4]),
SecondaryColor([u8; 4]),
OutlineColor([u8; 4]),
ShadowColor([u8; 4]),
Alpha(u8),
BorderWidth(f32),
ShadowDepth(f32),
Blur(f32),
}
impl TransformAnimation {
pub fn parse(args: &str) -> Option<Self> {
let args = args.trim();
if !args.starts_with('(') || !args.ends_with(')') {
return None;
}
let inner = &args[1..args.len() - 1];
let tag_start = inner.find('\\')?;
let params = &inner[..tag_start];
let tags = &inner[tag_start..];
let parts: Vec<&str> = params
.trim_end_matches(',')
.split(',')
.map(|s| s.trim())
.collect();
let (start_ms, end_ms, accel) =
if parts.is_empty() || (parts.len() == 1 && parts[0].is_empty()) {
(0, 0, 1.0)
} else if parts.len() == 1 {
if let Ok(val) = parts[0].parse::<f32>() {
if val < 10.0 {
(0, 0, val)
} else {
(0, val as u32, 1.0)
}
} else {
(0, 0, 1.0)
}
} else if parts.len() == 2 {
let t1 = parts[0].parse::<u32>().unwrap_or(0);
let t2 = parts[1].parse::<u32>().unwrap_or(0);
(t1, t2, 1.0)
} else {
let t1 = parts[0].parse::<u32>().unwrap_or(0);
let t2 = parts[1].parse::<u32>().unwrap_or(0);
let accel = parts[2].parse::<f32>().unwrap_or(1.0);
(t1, t2, accel)
};
let target_tags = parse_animatable_tags(tags);
if target_tags.is_empty() {
return None;
}
Some(TransformAnimation {
start_ms,
end_ms,
accel,
target_tags,
})
}
pub fn calculate_progress(&self, current_ms: u32, full_duration_ms: u32) -> f32 {
let end_ms = if self.end_ms > 0 {
self.end_ms
} else {
full_duration_ms
};
if current_ms <= self.start_ms {
return 0.0;
}
if end_ms <= self.start_ms || current_ms >= end_ms {
return 1.0;
}
let linear_progress = (current_ms - self.start_ms) as f32 / (end_ms - self.start_ms) as f32;
apply_acceleration_curve(linear_progress, self.accel)
}
}
pub fn interpolate_f32(from: f32, to: f32, progress: f32) -> f32 {
from + (to - from) * progress
}
pub fn interpolate_color(from: [u8; 4], to: [u8; 4], progress: f32) -> [u8; 4] {
[
(from[0] as f32 + (to[0] as f32 - from[0] as f32) * progress) as u8,
(from[1] as f32 + (to[1] as f32 - from[1] as f32) * progress) as u8,
(from[2] as f32 + (to[2] as f32 - from[2] as f32) * progress) as u8,
(from[3] as f32 + (to[3] as f32 - from[3] as f32) * progress) as u8,
]
}
pub fn interpolate_alpha(from: u8, to: u8, progress: f32) -> u8 {
(from as f32 + (to as f32 - from as f32) * progress) as u8
}