Skip to main content

ferroui_base/rendering/composition/animations/
key_frame_animation_instance.rs

1use super::animation_controller::{AnimationControllerClock, ProgressSpan};
2use super::{
3    AnimationDelayBehavior, AnimationInstanceBase, AnimationIterationBehavior, AnimationStopBehavior,
4    IAnimationInstance, IInterpolator, PropertySetSnapshot, ServerKeyFrame,
5};
6use crate::animation::PlaybackDirection;
7use crate::rendering::composition::expressions::{
8    BuiltInExpressionFfi, Expression, ExpressionEvaluationContext, ExpressionVariant, ExpressionVariantValue,
9};
10use crate::rendering::composition::server::{CompositionProperty, IServerClockItem, ServerCompositor, ServerObjectId};
11use crate::rendering::composition::AnimationCompletion;
12use std::cell::{Cell, RefCell};
13use std::collections::HashSet;
14use std::rc::{Rc, Weak};
15use std::time::Duration;
16
17/// The ticks (100 ns units) of a time span.
18fn ticks(d: Duration) -> i64 {
19    (d.as_nanos() / 100) as i64
20}
21
22/// `TimeSpan.TotalSeconds` of a number of ticks.
23fn total_seconds(ticks: i64) -> f64 {
24    ticks as f64 / 10_000_000.0
25}
26
27/// `Math.Min` and `Math.Max` of .NET, which return NaN when an argument is
28/// NaN.
29fn net_min(a: f64, b: f64) -> f64 {
30    if a.is_nan() || b.is_nan() {
31        f64::NAN
32    } else {
33        a.min(b)
34    }
35}
36
37fn net_max(a: f64, b: f64) -> f64 {
38    if a.is_nan() || b.is_nan() {
39        f64::NAN
40    } else {
41        a.max(b)
42    }
43}
44
45/// Server-side counterpart of KeyFrameAnimation with values baked-in
46///
47/// Times are kept in ticks, as upstream computes with `TimeSpan` ticks; the
48/// elapsed time of an evaluation before the start of the animation is
49/// negative.
50pub struct KeyFrameAnimationInstance<T: ExpressionVariantValue> {
51    base: AnimationInstanceBase,
52    interpolator: &'static dyn IInterpolator<T>,
53    key_frames: Vec<ServerKeyFrame<T>>,
54    final_value: Option<ExpressionVariant>,
55    delay_behavior: AnimationDelayBehavior,
56    delay_time: i64,
57    direction: PlaybackDirection,
58    duration: i64,
59    iteration_behavior: AnimationIterationBehavior,
60    iteration_count: i32,
61    /// Applied when the animation is stopped (see
62    /// [`IAnimationInstance::value_on_stop`]); upstream stores it and does
63    /// not apply it.
64    stop_behavior: AnimationStopBehavior,
65    started_at: Cell<i64>,
66    starting_value: Cell<T>,
67    total_duration: i64,
68    finished: Cell<bool>,
69    /// The batches the animation belongs to, released when it finishes or
70    /// is dropped.
71    completion: RefCell<AnimationCompletion>,
72    /// The initial value expressions, applied when the instance is
73    /// initialized.
74    initial_values: RefCell<Vec<(String, Expression)>>,
75    /// The time of the animation under its controller, if it has one.
76    clock: AnimationControllerClock,
77}
78
79impl<T: ExpressionVariantValue> KeyFrameAnimationInstance<T> {
80    /// Panics when there are no key frames or the duration is not positive
81    /// (`InvalidOperationException` upstream).
82    #[allow(clippy::too_many_arguments)]
83    pub fn new(
84        interpolator: &'static dyn IInterpolator<T>,
85        key_frames: Vec<ServerKeyFrame<T>>,
86        snapshot: Rc<PropertySetSnapshot>,
87        final_value: Option<ExpressionVariant>,
88        target: ServerObjectId,
89        delay_behavior: AnimationDelayBehavior,
90        delay_time: Duration,
91        direction: PlaybackDirection,
92        duration: Duration,
93        iteration_behavior: AnimationIterationBehavior,
94        iteration_count: i32,
95        stop_behavior: AnimationStopBehavior,
96    ) -> Rc<KeyFrameAnimationInstance<T>> {
97        let duration = ticks(duration);
98        let delay_time = ticks(delay_time);
99        let mut total_duration = 0;
100        if iteration_behavior == AnimationIterationBehavior::Count {
101            total_duration = delay_time + iteration_count as i64 * duration;
102        }
103        if key_frames.is_empty() {
104            panic!("Animation has no key frames");
105        }
106        if duration <= 0 {
107            panic!("Invalid animation duration");
108        }
109        Rc::new_cyclic(|this: &Weak<KeyFrameAnimationInstance<T>>| KeyFrameAnimationInstance {
110            base: AnimationInstanceBase::new(this.clone(), target, snapshot),
111            interpolator,
112            key_frames,
113            final_value,
114            delay_behavior,
115            delay_time,
116            direction,
117            duration,
118            iteration_behavior,
119            iteration_count,
120            stop_behavior,
121            started_at: Cell::new(0),
122            starting_value: Cell::new(T::default()),
123            total_duration,
124            finished: Cell::new(false),
125            completion: RefCell::new(AnimationCompletion::none()),
126            initial_values: RefCell::new(Vec::new()),
127            clock: AnimationControllerClock::default(),
128        })
129    }
130
131    /// Sets the initial value expressions of the animation (parsed by
132    /// [`InitialValueExpressionCollection::parse_all`](super::InitialValueExpressionCollection::parse_all)),
133    /// applied to the target when the instance is initialized.
134    pub fn set_initial_value_expressions(&self, expressions: Vec<(String, Expression)>) {
135        *self.initial_values.borrow_mut() = expressions;
136    }
137
138    /// What the progress of a controller covers: the delay and the
139    /// iterations (one iteration of an animation that loops forever).
140    fn progress_span(&self) -> ProgressSpan {
141        let active = match self.iteration_behavior {
142            AnimationIterationBehavior::Count => self.iteration_count.max(0) as i64 * self.duration,
143            AnimationIterationBehavior::Forever => self.duration,
144        };
145        ProgressSpan { delay: self.delay_time, active }
146    }
147
148    /// Sets the batches the animation belongs to (see
149    /// [`CompositionScopedBatch`](crate::rendering::composition::CompositionScopedBatch)).
150    pub fn set_completion(&self, completion: AnimationCompletion) {
151        *self.completion.borrow_mut() = completion;
152    }
153
154    fn release_completion_now(&self) {
155        // Taken out first: releasing may post the completion of a batch,
156        // which must not run with the cell borrowed.
157        let mut completion = std::mem::take(&mut *self.completion.borrow_mut());
158        completion.release();
159    }
160
161    /// The value of the last key frame, with the context of an evaluation
162    /// at the current value.
163    fn final_key_frame_value(&self, current_value: ExpressionVariant) -> T {
164        let starting = ExpressionVariant::create(self.starting_value.get());
165        self.base.with_target_expression_object(|target| {
166            let ctx = ExpressionEvaluationContext {
167                parameters: Some(&**self.base.parameters()),
168                target,
169                current_value,
170                final_value: self.final_value.unwrap_or(starting),
171                starting_value: starting,
172                foreign_function_interface: Some(BuiltInExpressionFfi::instance()),
173            };
174            Self::get_key_frame(&ctx, &self.key_frames[self.key_frames.len() - 1])
175        })
176    }
177
178    fn evaluate_core(&self, now: Duration, current_value: ExpressionVariant) -> ExpressionVariant {
179        let starting = ExpressionVariant::create(self.starting_value.get());
180        let elapsed = self.clock.elapsed(
181            self.base.compositor().as_ref(),
182            ticks(now),
183            self.started_at.get(),
184            Some(self.progress_span()),
185        );
186        let res = self.base.with_target_expression_object(|target| {
187            let ctx = ExpressionEvaluationContext {
188                parameters: Some(&**self.base.parameters()),
189                target,
190                current_value,
191                final_value: self.final_value.unwrap_or(starting),
192                starting_value: starting,
193                foreign_function_interface: Some(BuiltInExpressionFfi::instance()),
194            };
195            self.evaluate_impl(elapsed, current_value, &ctx)
196        });
197
198        // A paused animation does not end: its controller may move it back.
199        if self.iteration_behavior == AnimationIterationBehavior::Count
200            && !self.finished.get()
201            && elapsed > self.total_duration
202            && !self.clock.is_paused()
203        {
204            // Active check?
205            if let (Some(compositor), Some(this)) = (self.base.compositor(), self.base.this_clock_item()) {
206                compositor.animations().remove_from_clock(&this);
207            }
208            self.finished.set(true);
209            // The animation is over: the batches it belongs to may complete.
210            self.release_completion_now();
211        }
212        res
213    }
214
215    fn evaluate_impl(
216        &self,
217        mut elapsed: i64,
218        current_value: ExpressionVariant,
219        ctx: &ExpressionEvaluationContext<'_>,
220    ) -> ExpressionVariant {
221        if elapsed < self.delay_time {
222            if self.delay_behavior == AnimationDelayBehavior::SetInitialValueBeforeDelay {
223                return ExpressionVariant::create(Self::get_key_frame(ctx, &self.key_frames[0]));
224            }
225            return current_value;
226        }
227
228        elapsed -= self.delay_time;
229        let iteration_number = elapsed / self.duration;
230        if self.iteration_behavior == AnimationIterationBehavior::Count
231            && iteration_number >= self.iteration_count as i64
232        {
233            return ExpressionVariant::create(Self::get_key_frame(ctx, &self.key_frames[self.key_frames.len() - 1]));
234        }
235
236        let even_iteration_number = iteration_number % 2 == 0;
237        elapsed %= self.duration;
238
239        let reverse = match self.direction {
240            PlaybackDirection::Alternate => !even_iteration_number,
241            PlaybackDirection::AlternateReverse => even_iteration_number,
242            direction => direction == PlaybackDirection::Reverse,
243        };
244
245        let mut iteration_progress = total_seconds(elapsed) / total_seconds(self.duration);
246        if reverse {
247            iteration_progress = 1.0 - iteration_progress;
248        }
249
250        let starting_frame = ServerKeyFrame { value: self.starting_value.get(), ..ServerKeyFrame::default() };
251        let mut left = &starting_frame;
252        let mut right = &self.key_frames[self.key_frames.len() - 1];
253        for c in 0..self.key_frames.len() {
254            let kf = &self.key_frames[c];
255            if (kf.key as f64) < iteration_progress {
256                // this is the last frame
257                if c == self.key_frames.len() - 1 {
258                    return ExpressionVariant::create(Self::get_key_frame(ctx, kf));
259                }
260
261                left = kf;
262                right = &self.key_frames[c + 1];
263            } else if c == 0 {
264                // The current progress is before the first frame, we implicitly use the starting value
265                // as the first frame in this case
266                right = &self.key_frames[c];
267                break;
268            } else {
269                break;
270            }
271        }
272
273        let key_progress = net_max(
274            0.0,
275            net_min(1.0, (iteration_progress - left.key as f64) / (right.key as f64 - left.key as f64)),
276        );
277
278        let eased = right.easing_function.as_ref().map_or(key_progress, |easing| easing.ease(key_progress));
279        let eased_key_progress = eased as f32;
280        if eased_key_progress.is_nan() || eased_key_progress.is_infinite() {
281            return current_value;
282        }
283
284        ExpressionVariant::create(self.interpolator.interpolate(
285            Self::get_key_frame(ctx, left),
286            Self::get_key_frame(ctx, right),
287            eased_key_progress,
288        ))
289    }
290
291    fn get_key_frame(ctx: &ExpressionEvaluationContext<'_>, f: &ServerKeyFrame<T>) -> T {
292        match &f.expression {
293            Some(expression) => expression.evaluate(ctx).cast_or_default::<T>(),
294            None => f.value,
295        }
296    }
297}
298
299impl<T: ExpressionVariantValue> IServerClockItem for KeyFrameAnimationInstance<T> {
300    fn on_tick(&self) {
301        // A target that is gone (its server object released) never
302        // evaluates the animation again: it leaves the clock and its
303        // batches.
304        if self.base.target().is_none() {
305            if let (Some(compositor), Some(this)) = (self.base.compositor(), self.base.this_clock_item()) {
306                compositor.animations().remove_from_clock(&this);
307            }
308            self.release_completion_now();
309            return;
310        }
311        self.base.on_tick()
312    }
313}
314
315impl<T: ExpressionVariantValue> IAnimationInstance for KeyFrameAnimationInstance<T> {
316    fn target_object(&self) -> ServerObjectId {
317        self.base.target_object()
318    }
319
320    fn resolve(&self, compositor: &Rc<ServerCompositor>) {
321        self.base.resolve(compositor)
322    }
323
324    fn evaluate(&self, now: Duration, current_value: ExpressionVariant) -> ExpressionVariant {
325        self.base.begin_evaluate();
326        self.evaluate_core(now, current_value)
327    }
328
329    fn initialize(&self, started_at: Duration, starting_value: ExpressionVariant, property: &'static CompositionProperty) {
330        let initial_values = std::mem::take(&mut *self.initial_values.borrow_mut());
331        let starting_value = self.base.apply_initial_values(&initial_values, property, starting_value);
332        self.started_at.set(ticks(started_at));
333        self.starting_value.set(starting_value.cast_or_default::<T>());
334        let mut hs = HashSet::new();
335
336        // As upstream, the instance subscribes to the references of all key
337        // frames rather than to those of the current key frame only.
338        for frame in &self.key_frames {
339            if let Some(expression) = &frame.expression {
340                expression.collect_references(&mut hs);
341            }
342        }
343        self.base.initialize(property, &hs);
344    }
345
346    fn activate(&self) {
347        if self.finished.get() {
348            return;
349        }
350        if let (Some(compositor), Some(this)) = (self.base.compositor(), self.base.this_clock_item()) {
351            compositor.animations().add_to_clock(this);
352        }
353        self.base.activate();
354    }
355
356    fn deactivate(&self) {
357        if let (Some(compositor), Some(this)) = (self.base.compositor(), self.base.this_clock_item()) {
358            compositor.animations().remove_from_clock(&this);
359        }
360        self.base.deactivate();
361    }
362
363    fn invalidate(&self) {
364        self.base.invalidate()
365    }
366
367    fn value_on_stop(&self, current_value: ExpressionVariant) -> Option<ExpressionVariant> {
368        match self.stop_behavior {
369            AnimationStopBehavior::LeaveCurrentValue => None,
370            AnimationStopBehavior::SetToInitialValue => Some(ExpressionVariant::create(self.starting_value.get())),
371            AnimationStopBehavior::SetToFinalValue => {
372                Some(ExpressionVariant::create(self.final_key_frame_value(current_value)))
373            }
374        }
375    }
376
377    fn release_completion(&self) {
378        self.release_completion_now()
379    }
380
381    fn attach_controller(&self, controller: ServerObjectId) {
382        self.clock.attach(controller)
383    }
384}