use crate::animate::{MotionShape, MotionSpeed};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VariationKind {
Png,
Mp4,
}
impl VariationKind {
pub fn ext(self) -> &'static str {
match self {
Self::Png => "png",
Self::Mp4 => "mp4",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VariationMode {
Still,
Video,
Mixed,
}
impl VariationMode {
pub fn accepts(self, kind: VariationKind) -> bool {
match self {
Self::Mixed => true,
Self::Still => kind == VariationKind::Png,
Self::Video => kind == VariationKind::Mp4,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct VariationSpec {
pub label: &'static str,
pub kind: VariationKind,
pub shape: MotionShape,
pub speed: MotionSpeed,
pub orb_size: f32,
pub blur: f32,
pub saturation: f32,
pub seed: u64,
pub duration_ms: u64,
}
pub const DEFAULT_VARIATIONS: &[VariationSpec] = &[
VariationSpec {
label: "still_warm",
kind: VariationKind::Png,
shape: MotionShape::Still,
speed: MotionSpeed::Slow,
orb_size: 1.0,
blur: 0.5,
saturation: 1.2,
seed: 1,
duration_ms: 4000,
},
VariationSpec {
label: "still_cool",
kind: VariationKind::Png,
shape: MotionShape::Still,
speed: MotionSpeed::Slow,
orb_size: 1.2,
blur: 0.7,
saturation: 0.8,
seed: 2,
duration_ms: 4000,
},
VariationSpec {
label: "still_punch",
kind: VariationKind::Png,
shape: MotionShape::Still,
speed: MotionSpeed::Slow,
orb_size: 0.8,
blur: 0.3,
saturation: 1.5,
seed: 3,
duration_ms: 4000,
},
VariationSpec {
label: "drift_vertical_subtle",
kind: VariationKind::Mp4,
shape: MotionShape::Vertical,
speed: MotionSpeed::Subtle,
orb_size: 1.2,
blur: 0.6,
saturation: 1.0,
seed: 4,
duration_ms: 4000,
},
VariationSpec {
label: "drift_horizontal_subtle",
kind: VariationKind::Mp4,
shape: MotionShape::Horizontal,
speed: MotionSpeed::Subtle,
orb_size: 1.0,
blur: 0.5,
saturation: 1.0,
seed: 5,
duration_ms: 4000,
},
VariationSpec {
label: "drift_diagonal_subtle",
kind: VariationKind::Mp4,
shape: MotionShape::Diagonal,
speed: MotionSpeed::Subtle,
orb_size: 1.1,
blur: 0.5,
saturation: 1.1,
seed: 6,
duration_ms: 4000,
},
VariationSpec {
label: "twinkle_subtle",
kind: VariationKind::Mp4,
shape: MotionShape::Twinkle,
speed: MotionSpeed::Subtle,
orb_size: 1.0,
blur: 0.5,
saturation: 1.0,
seed: 7,
duration_ms: 4000,
},
VariationSpec {
label: "breathe_slow",
kind: VariationKind::Mp4,
shape: MotionShape::Breathe,
speed: MotionSpeed::Slow,
orb_size: 1.0,
blur: 0.5,
saturation: 1.0,
seed: 8,
duration_ms: 4000,
},
VariationSpec {
label: "lissajous_slow",
kind: VariationKind::Mp4,
shape: MotionShape::Lissajous,
speed: MotionSpeed::Slow,
orb_size: 1.0,
blur: 0.5,
saturation: 1.1,
seed: 9,
duration_ms: 4000,
},
VariationSpec {
label: "lissajous_lively",
kind: VariationKind::Mp4,
shape: MotionShape::Lissajous,
speed: MotionSpeed::Lively,
orb_size: 0.9,
blur: 0.4,
saturation: 1.2,
seed: 10,
duration_ms: 4000,
},
];
pub fn select_specs(n: usize, mode: VariationMode) -> Vec<VariationSpec> {
DEFAULT_VARIATIONS
.iter()
.copied()
.filter(|s| mode.accepts(s.kind))
.take(n)
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_set_has_ten_specs() {
assert_eq!(DEFAULT_VARIATIONS.len(), 10);
}
#[test]
fn default_set_balance() {
let png = DEFAULT_VARIATIONS
.iter()
.filter(|s| s.kind == VariationKind::Png)
.count();
let mp4 = DEFAULT_VARIATIONS
.iter()
.filter(|s| s.kind == VariationKind::Mp4)
.count();
assert!(png >= 1, "expected at least 1 still variation");
assert!(mp4 >= 1, "expected at least 1 video variation");
}
#[test]
fn labels_unique_and_ascii_safe() {
let mut seen = std::collections::HashSet::new();
for s in DEFAULT_VARIATIONS {
assert!(seen.insert(s.label), "duplicate label: {}", s.label);
for ch in s.label.chars() {
assert!(
ch.is_ascii_alphanumeric() || ch == '_',
"non shell-safe char in label {:?}: {ch:?}",
s.label
);
}
}
}
#[test]
fn select_specs_respects_mode() {
let still = select_specs(10, VariationMode::Still);
assert!(still.iter().all(|s| s.kind == VariationKind::Png));
let video = select_specs(10, VariationMode::Video);
assert!(video.iter().all(|s| s.kind == VariationKind::Mp4));
let mixed = select_specs(10, VariationMode::Mixed);
assert_eq!(mixed.len(), 10);
}
#[test]
fn select_specs_respects_n() {
let three = select_specs(3, VariationMode::Mixed);
assert_eq!(three.len(), 3);
let zero = select_specs(0, VariationMode::Mixed);
assert_eq!(zero.len(), 0);
}
}