Skip to main content

ferroui_base/rendering/composition/animations/
key_frame_animation_instance.rs

1use super::{
2    AnimationDelayBehavior, AnimationInstanceBase, AnimationIterationBehavior, AnimationStopBehavior,
3    IAnimationInstance, IInterpolator, PropertySetSnapshot, ServerKeyFrame,
4};
5use crate::animation::PlaybackDirection;
6use crate::rendering::composition::expressions::{
7    BuiltInExpressionFfi, ExpressionEvaluationContext, ExpressionVariant, ExpressionVariantValue,
8};
9use crate::rendering::composition::server::{CompositionProperty, IServerClockItem, ServerCompositor, ServerObjectId};
10use std::cell::Cell;
11use std::collections::HashSet;
12use std::rc::{Rc, Weak};
13use std::time::Duration;
14
15/// The ticks (100 ns units) of a time span.
16fn ticks(d: Duration) -> i64 {
17    (d.as_nanos() / 100) as i64
18}
19
20/// `TimeSpan.TotalSeconds` of a number of ticks.
21fn total_seconds(ticks: i64) -> f64 {
22    ticks as f64 / 10_000_000.0
23}
24
25/// `Math.Min` and `Math.Max` of .NET, which return NaN when an argument is
26/// NaN.
27fn net_min(a: f64, b: f64) -> f64 {
28    if a.is_nan() || b.is_nan() {
29        f64::NAN
30    } else {
31        a.min(b)
32    }
33}
34
35fn net_max(a: f64, b: f64) -> f64 {
36    if a.is_nan() || b.is_nan() {
37        f64::NAN
38    } else {
39        a.max(b)
40    }
41}
42
43/// Server-side counterpart of KeyFrameAnimation with values baked-in
44///
45/// Times are kept in ticks, as upstream computes with `TimeSpan` ticks; the
46/// elapsed time of an evaluation before the start of the animation is
47/// negative.
48pub struct KeyFrameAnimationInstance<T: ExpressionVariantValue> {
49    base: AnimationInstanceBase,
50    interpolator: &'static dyn IInterpolator<T>,
51    key_frames: Vec<ServerKeyFrame<T>>,
52    final_value: Option<ExpressionVariant>,
53    delay_behavior: AnimationDelayBehavior,
54    delay_time: i64,
55    direction: PlaybackDirection,
56    duration: i64,
57    iteration_behavior: AnimationIterationBehavior,
58    iteration_count: i32,
59    // Stored as upstream stores it; upstream does not read it either.
60    #[allow(dead_code)]
61    stop_behavior: AnimationStopBehavior,
62    started_at: Cell<i64>,
63    starting_value: Cell<T>,
64    total_duration: i64,
65    finished: Cell<bool>,
66}
67
68impl<T: ExpressionVariantValue> KeyFrameAnimationInstance<T> {
69    /// Panics when there are no key frames or the duration is not positive
70    /// (`InvalidOperationException` upstream).
71    #[allow(clippy::too_many_arguments)]
72    pub fn new(
73        interpolator: &'static dyn IInterpolator<T>,
74        key_frames: Vec<ServerKeyFrame<T>>,
75        snapshot: Rc<PropertySetSnapshot>,
76        final_value: Option<ExpressionVariant>,
77        target: ServerObjectId,
78        delay_behavior: AnimationDelayBehavior,
79        delay_time: Duration,
80        direction: PlaybackDirection,
81        duration: Duration,
82        iteration_behavior: AnimationIterationBehavior,
83        iteration_count: i32,
84        stop_behavior: AnimationStopBehavior,
85    ) -> Rc<KeyFrameAnimationInstance<T>> {
86        let duration = ticks(duration);
87        let delay_time = ticks(delay_time);
88        let mut total_duration = 0;
89        if iteration_behavior == AnimationIterationBehavior::Count {
90            total_duration = delay_time + iteration_count as i64 * duration;
91        }
92        if key_frames.is_empty() {
93            panic!("Animation has no key frames");
94        }
95        if duration <= 0 {
96            panic!("Invalid animation duration");
97        }
98        Rc::new_cyclic(|this: &Weak<KeyFrameAnimationInstance<T>>| KeyFrameAnimationInstance {
99            base: AnimationInstanceBase::new(this.clone(), target, snapshot),
100            interpolator,
101            key_frames,
102            final_value,
103            delay_behavior,
104            delay_time,
105            direction,
106            duration,
107            iteration_behavior,
108            iteration_count,
109            stop_behavior,
110            started_at: Cell::new(0),
111            starting_value: Cell::new(T::default()),
112            total_duration,
113            finished: Cell::new(false),
114        })
115    }
116
117    fn evaluate_core(&self, now: Duration, current_value: ExpressionVariant) -> ExpressionVariant {
118        let starting = ExpressionVariant::create(self.starting_value.get());
119        let elapsed = ticks(now) - self.started_at.get();
120        let res = self.base.with_target_expression_object(|target| {
121            let ctx = ExpressionEvaluationContext {
122                parameters: Some(&**self.base.parameters()),
123                target,
124                current_value,
125                final_value: self.final_value.unwrap_or(starting),
126                starting_value: starting,
127                foreign_function_interface: Some(BuiltInExpressionFfi::instance()),
128            };
129            self.evaluate_impl(elapsed, current_value, &ctx)
130        });
131
132        if self.iteration_behavior == AnimationIterationBehavior::Count
133            && !self.finished.get()
134            && elapsed > self.total_duration
135        {
136            // Active check?
137            if let (Some(compositor), Some(this)) = (self.base.compositor(), self.base.this_clock_item()) {
138                compositor.animations().remove_from_clock(&this);
139            }
140            self.finished.set(true);
141        }
142        res
143    }
144
145    fn evaluate_impl(
146        &self,
147        mut elapsed: i64,
148        current_value: ExpressionVariant,
149        ctx: &ExpressionEvaluationContext<'_>,
150    ) -> ExpressionVariant {
151        if elapsed < self.delay_time {
152            if self.delay_behavior == AnimationDelayBehavior::SetInitialValueBeforeDelay {
153                return ExpressionVariant::create(Self::get_key_frame(ctx, &self.key_frames[0]));
154            }
155            return current_value;
156        }
157
158        elapsed -= self.delay_time;
159        let iteration_number = elapsed / self.duration;
160        if self.iteration_behavior == AnimationIterationBehavior::Count
161            && iteration_number >= self.iteration_count as i64
162        {
163            return ExpressionVariant::create(Self::get_key_frame(ctx, &self.key_frames[self.key_frames.len() - 1]));
164        }
165
166        let even_iteration_number = iteration_number % 2 == 0;
167        elapsed %= self.duration;
168
169        let reverse = match self.direction {
170            PlaybackDirection::Alternate => !even_iteration_number,
171            PlaybackDirection::AlternateReverse => even_iteration_number,
172            direction => direction == PlaybackDirection::Reverse,
173        };
174
175        let mut iteration_progress = total_seconds(elapsed) / total_seconds(self.duration);
176        if reverse {
177            iteration_progress = 1.0 - iteration_progress;
178        }
179
180        let starting_frame = ServerKeyFrame { value: self.starting_value.get(), ..ServerKeyFrame::default() };
181        let mut left = &starting_frame;
182        let mut right = &self.key_frames[self.key_frames.len() - 1];
183        for c in 0..self.key_frames.len() {
184            let kf = &self.key_frames[c];
185            if (kf.key as f64) < iteration_progress {
186                // this is the last frame
187                if c == self.key_frames.len() - 1 {
188                    return ExpressionVariant::create(Self::get_key_frame(ctx, kf));
189                }
190
191                left = kf;
192                right = &self.key_frames[c + 1];
193            } else if c == 0 {
194                // The current progress is before the first frame, we implicitly use the starting value
195                // as the first frame in this case
196                right = &self.key_frames[c];
197                break;
198            } else {
199                break;
200            }
201        }
202
203        let key_progress = net_max(
204            0.0,
205            net_min(1.0, (iteration_progress - left.key as f64) / (right.key as f64 - left.key as f64)),
206        );
207
208        let eased = right.easing_function.as_ref().map_or(key_progress, |easing| easing.ease(key_progress));
209        let eased_key_progress = eased as f32;
210        if eased_key_progress.is_nan() || eased_key_progress.is_infinite() {
211            return current_value;
212        }
213
214        ExpressionVariant::create(self.interpolator.interpolate(
215            Self::get_key_frame(ctx, left),
216            Self::get_key_frame(ctx, right),
217            eased_key_progress,
218        ))
219    }
220
221    fn get_key_frame(ctx: &ExpressionEvaluationContext<'_>, f: &ServerKeyFrame<T>) -> T {
222        match &f.expression {
223            Some(expression) => expression.evaluate(ctx).cast_or_default::<T>(),
224            None => f.value,
225        }
226    }
227}
228
229impl<T: ExpressionVariantValue> IServerClockItem for KeyFrameAnimationInstance<T> {
230    fn on_tick(&self) {
231        self.base.on_tick()
232    }
233}
234
235impl<T: ExpressionVariantValue> IAnimationInstance for KeyFrameAnimationInstance<T> {
236    fn target_object(&self) -> ServerObjectId {
237        self.base.target_object()
238    }
239
240    fn resolve(&self, compositor: &Rc<ServerCompositor>) {
241        self.base.resolve(compositor)
242    }
243
244    fn evaluate(&self, now: Duration, current_value: ExpressionVariant) -> ExpressionVariant {
245        self.base.begin_evaluate();
246        self.evaluate_core(now, current_value)
247    }
248
249    fn initialize(&self, started_at: Duration, starting_value: ExpressionVariant, property: &'static CompositionProperty) {
250        self.started_at.set(ticks(started_at));
251        self.starting_value.set(starting_value.cast_or_default::<T>());
252        let mut hs = HashSet::new();
253
254        // As upstream, the instance subscribes to the references of all key
255        // frames rather than to those of the current key frame only.
256        for frame in &self.key_frames {
257            if let Some(expression) = &frame.expression {
258                expression.collect_references(&mut hs);
259            }
260        }
261        self.base.initialize(property, &hs);
262    }
263
264    fn activate(&self) {
265        if self.finished.get() {
266            return;
267        }
268        if let (Some(compositor), Some(this)) = (self.base.compositor(), self.base.this_clock_item()) {
269            compositor.animations().add_to_clock(this);
270        }
271        self.base.activate();
272    }
273
274    fn deactivate(&self) {
275        if let (Some(compositor), Some(this)) = (self.base.compositor(), self.base.this_clock_item()) {
276            compositor.animations().remove_from_clock(&this);
277        }
278        self.base.deactivate();
279    }
280
281    fn invalidate(&self) {
282        self.base.invalidate()
283    }
284}