ferroui_base/rendering/composition/animations/
key_frame_animation_instance.rs1use 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
15fn ticks(d: Duration) -> i64 {
17 (d.as_nanos() / 100) as i64
18}
19
20fn total_seconds(ticks: i64) -> f64 {
22 ticks as f64 / 10_000_000.0
23}
24
25fn 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
43pub 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 #[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 #[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 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 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 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 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}