summaryrefslogtreecommitdiff
path: root/rust/crates/animation/src/channel_data.rs
blob: a05396e5358e2624c3cd7716d6296a2a9589d9ba (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
use scene::{AnimationChannel, ChannelData, CompositeChannelData};

const LEGACY_ANIMATION_STORAGE_KEYS: [&str; 2] = ["bindings", "channels"];

pub fn is_animation_storage_key(key: &str) -> bool {
    !LEGACY_ANIMATION_STORAGE_KEYS.contains(&key)
}

fn component_order(key: &str) -> usize {
    match key {
        "value" => 0,
        "r" => 1,
        "g" => 2,
        "b" => 3,
        "a" => 4,
        _ => 5,
    }
}

pub fn ordered_composite_entries(data: &CompositeChannelData) -> Vec<(&String, &AnimationChannel)> {
    let mut entries: Vec<(&String, &AnimationChannel)> = data.iter().collect();
    entries.sort_by_key(|(key, _)| component_order(key));
    entries
}

pub fn get_channels_from_data(data: Option<&ChannelData>) -> Vec<&AnimationChannel> {
    match data {
        Some(ChannelData::Single(channel)) => vec![channel],
        Some(ChannelData::Composite(components)) => ordered_composite_entries(components)
            .into_iter()
            .map(|(_, channel)| channel)
            .collect(),
        None => Vec::new(),
    }
}

pub fn get_channel_entries_from_data(data: Option<&ChannelData>) -> Vec<(String, &AnimationChannel)> {
    match data {
        Some(ChannelData::Single(channel)) => vec![("value".to_string(), channel)],
        Some(ChannelData::Composite(components)) => ordered_composite_entries(components)
            .into_iter()
            .map(|(key, channel)| (key.clone(), channel))
            .collect(),
        None => Vec::new(),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use scene::{AnimationChannel, DiscreteChannel, ScalarChannel};
    use std::collections::BTreeMap;

    fn scalar_channel() -> AnimationChannel {
        AnimationChannel::Scalar(ScalarChannel {
            keys: Vec::new(),
            extrapolation: None,
        })
    }

    #[test]
    fn leaf_data_yields_value_entry() {
        let data = ChannelData::Single(scalar_channel());
        let entries = get_channel_entries_from_data(Some(&data));
        assert_eq!(entries.len(), 1);
        assert_eq!(entries[0].0, "value");
    }

    #[test]
    fn composite_entries_follow_canonical_component_order() {
        let mut components = BTreeMap::new();
        components.insert("a".to_string(), scalar_channel());
        components.insert("b".to_string(), scalar_channel());
        components.insert("g".to_string(), scalar_channel());
        components.insert("r".to_string(), scalar_channel());
        components.insert(
            "mode".to_string(),
            AnimationChannel::Discrete(DiscreteChannel { keys: Vec::new() }),
        );
        let data = ChannelData::Composite(components);
        let entries = get_channel_entries_from_data(Some(&data));
        let keys: Vec<&str> = entries.iter().map(|(key, _)| key.as_str()).collect();
        assert_eq!(keys, ["r", "g", "b", "a", "mode"]);
    }

    #[test]
    fn legacy_storage_keys_are_rejected() {
        assert!(!is_animation_storage_key("bindings"));
        assert!(!is_animation_storage_key("channels"));
        assert!(is_animation_storage_key("opacity"));
    }
}