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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#![doc = include_str!("./anim_el_example.md")]

use ambient_api_core::{
    core::{
        animation::{
            self,
            components::{play_clip_from_url, start_time},
        },
        app::components::name,
    },
    prelude::epoch_time,
};
use ambient_element::{element_component, use_ref_with, Element, ElementComponentExt, Hooks};


/// A utility component for playing a sequence of animation clips logically driven by transitions
#[element_component]
pub fn AnimationPlayer(_hooks: &mut Hooks, root: Element) -> Element {
    Element::new()
        .with(animation::components::is_animation_player(), ())
        .children(vec![root])
        .with(name(), "Animation player".to_string())
}

/// Play an animation clip from an URL
#[element_component]
pub fn PlayClipFromUrl(
    _hooks: &mut Hooks,
    /// Url to clip
    url: String,
    /// Loop the animation
    looping: bool,
) -> Element {
    Element::new()
        .with(play_clip_from_url(), url)
        .with(name(), "Play clip from URL".to_string())
        .with(animation::components::looping(), looping)
        .init(start_time(), epoch_time())
}

/// Blend animation clips together
#[element_component(without_el)]
pub fn BlendNode(
    _hooks: &mut Hooks,
    /// Left animation node
    left: Element,
    /// Right animation node
    right: Element,
    /// Weight (0 means left, 1 means right, 0.5 means half left and half right)
    weight: f32,
) -> Element {
    if weight <= 0. {
        left
    } else if weight >= 1. {
        right
    } else {
        Element::new()
            .with(animation::components::blend(), weight)
            .with(name(), "Blend".to_string())
            .children(vec![left, right])
    }
}
impl BlendNode {
    /// Create a blend node and turn it into an Element
    pub fn el(left: Element, right: Element, weight: f32) -> Element {
        if weight <= 0. {
            left
        } else if weight >= 1. {
            right
        } else {
            Self {
                left,
                right,
                weight,
            }
            .el()
        }
    }
    /// Creates a tree of blend nodes where the weights are arbitrary, for example
    /// `BlendNode::normalize_multiblend(vec![("a", 1.), ("b", 20.), ("c", 3.)])` will create a tree
    /// where b is the strongest contribution
    pub fn normalize_multiblend(items: Vec<(Element, f32)>) -> Element {
        let total_weight = items.iter().map(|x| x.1).sum::<f32>();
        if total_weight <= 0. {
            return Element::new();
        }
        let mut items = items
            .into_iter()
            .map(|(a, w)| (a, w / total_weight))
            .collect::<Vec<_>>();
        items.retain(|x| x.1 > 0.);
        items.sort_by_key(|x| -ordered_float::OrderedFloat(x.1));
        for x in items.iter_mut() {
            x.1 = 1. - x.1;
        }
        Self::multiblend(items)
    }
    /// Creates a tree of blend nodes, where each weight is the blend between that element and the next,
    /// for example:
    /// `BlendNode::multiblend(vec![("a", 0.5), ("b", 0.2), ("c", 0.)])` will create a tree where
    /// b is first blended with c, using 20% of b and 80% of b
    /// The result if that is then blended with a, using 50% of the result and 50% of a
    pub fn multiblend(mut items: Vec<(Element, f32)>) -> Element {
        if items.is_empty() {
            Element::new()
        } else if items.len() == 1 {
            items.pop().unwrap().0
        } else {
            let item = items.remove(0);
            Self::el(item.0, Self::multiblend(items), item.1)
        }
    }
}

/// Transition between multiple animations
#[element_component]
pub fn Transition(
    hooks: &mut Hooks,
    /// The animations that can be transitioned between
    animations: Vec<Element>,
    /// The index of the active animation
    active: usize,
    /// The speed that the transitions happen at
    speed: f32,
) -> Element {
    let weights = use_ref_with(hooks, |_| { // hello
        animations
            .iter()
            .enumerate()
            .map(|(i, _)| if i == active { 1. } else { 0. })
            .collect::<Vec<_>>()
    });
    let mut weights = weights.lock();
    for (i, weight) in weights.iter_mut().enumerate() {
        let target = if i == active { 1. } else { 0. };
        *weight = *weight * (1. - speed) + target * speed;
    }
    BlendNode::normalize_multiblend(
        animations
            .into_iter()
            .zip(weights.iter().cloned())
            .collect(),
    )
}