Skip to main content

ferroui_base/rendering/composition/animations/
subchannel_animation_instance.rs

1//! Animations of components of a property: WinUI's sub-channel targets of
2//! `StartAnimation` and `StopAnimation` (`Offset.X`, `Scale.XY`,
3//! `Translation.Z`).
4//!
5//! The animation of a sub-channel animates a value of the sub-channel's type
6//! (a scalar for one component, a `Vector2` for two, a `Vector3` for three, a
7//! `Vector4` for four). It is started as an animation of the whole property:
8//! the UI thread wraps the instance of the animation in a
9//! `SubchannelAnimation`, which the generated start path of the property
10//! sends like any animation, and the server runs a
11//! [`SubchannelAnimationInstance`] that evaluates the inner animation and
12//! writes its result into the components of the current value of the
13//! property. Animations of other components of the same property started
14//! later are merged into the running instance on the server, so `Offset.X`
15//! and `Offset.Y` run together; an animation of the whole property replaces
16//! them all.
17
18use super::{AnimationInstanceFactory, IAnimationInstance, ICompositionAnimation, ICompositionAnimationBase};
19use crate::numerics::{Vector2, Vector3, Vector4};
20use crate::rendering::composition::expressions::ExpressionVariant;
21use crate::rendering::composition::server::{CompositionProperty, IServerClockItem, ServerCompositor, ServerObjectId};
22use crate::{Vector, Vector3D};
23use std::cell::{Cell, RefCell};
24use std::rc::Rc;
25use std::time::Duration;
26
27/// Splits a sub-channel target (`Offset.X`, `scale.xy`) into the property
28/// name and the indices of the components (X = 0, Y = 1, Z = 2, W = 3).
29/// Returns `None` for a name without a `.`, and for one whose part after
30/// the `.` is not made of the letters X, Y, Z and W: a dotted property name
31/// of the class itself (the animatable properties of an effect brush,
32/// `Blur.BlurAmount`), which the class resolves as a whole. Panics when the
33/// part after the `.` is more than four or repeated components (WinUI
34/// throws).
35///
36/// The components are case-insensitive; the property name is matched as
37/// given.
38pub(crate) fn parse_subchannel(property_name: &str) -> Option<(&str, Vec<usize>)> {
39    let (property, channel) = property_name.split_once('.')?;
40    if channel.is_empty() || !channel.chars().all(|c| matches!(c.to_ascii_uppercase(), 'X' | 'Y' | 'Z' | 'W')) {
41        return None;
42    }
43    let mut components = Vec::with_capacity(channel.len());
44    for c in channel.chars() {
45        let index = match c.to_ascii_uppercase() {
46            'X' => 0,
47            'Y' => 1,
48            'Z' => 2,
49            'W' => 3,
50            _ => panic!("Unknown property {property_name}"),
51        };
52        if components.contains(&index) {
53            panic!("Unknown property {property_name}");
54        }
55        components.push(index);
56    }
57    if components.is_empty() || components.len() > 4 || property.is_empty() {
58        panic!("Unknown property {property_name}");
59    }
60    Some((property, components))
61}
62
63/// The components of a vector value, with their number.
64fn vector_components(value: &ExpressionVariant) -> Option<([f64; 4], usize)> {
65    match *value {
66        ExpressionVariant::Vector2(v) => Some(([v.x as f64, v.y as f64, 0.0, 0.0], 2)),
67        ExpressionVariant::Vector(v) => Some(([v.x, v.y, 0.0, 0.0], 2)),
68        ExpressionVariant::Vector3(v) => Some(([v.x as f64, v.y as f64, v.z as f64, 0.0], 3)),
69        ExpressionVariant::Vector3D(v) => Some(([v.x, v.y, v.z, 0.0], 3)),
70        ExpressionVariant::Vector4(v) => Some(([v.x as f64, v.y as f64, v.z as f64, v.w as f64], 4)),
71        _ => None,
72    }
73}
74
75/// The components of the value of a sub-channel: a scalar or a vector.
76fn channel_components(value: &ExpressionVariant) -> Option<([f64; 4], usize)> {
77    match *value {
78        ExpressionVariant::Double(v) => Some(([v, 0.0, 0.0, 0.0], 1)),
79        _ => vector_components(value),
80    }
81}
82
83/// `value` with its components replaced, of the same type.
84fn with_components(value: ExpressionVariant, c: [f64; 4]) -> ExpressionVariant {
85    match value {
86        ExpressionVariant::Vector2(_) => Vector2::new(c[0] as f32, c[1] as f32).into(),
87        ExpressionVariant::Vector(_) => Vector::new(c[0], c[1]).into(),
88        ExpressionVariant::Vector3(_) => Vector3::new(c[0] as f32, c[1] as f32, c[2] as f32).into(),
89        ExpressionVariant::Vector3D(_) => Vector3D::new(c[0], c[1], c[2]).into(),
90        ExpressionVariant::Vector4(_) => Vector4::new(c[0] as f32, c[1] as f32, c[2] as f32, c[3] as f32).into(),
91        other => other,
92    }
93}
94
95/// The value of the sub-channel `components` of `value`: a scalar for one
96/// component, a `Vector2`, `Vector3` or `Vector4` for more. `Invalid` when
97/// `value` is not a vector with these components.
98pub(crate) fn extract_subchannel(value: ExpressionVariant, components: &[usize]) -> ExpressionVariant {
99    let Some((c, n)) = vector_components(&value) else { return ExpressionVariant::Invalid };
100    if components.iter().any(|&i| i >= n) {
101        return ExpressionVariant::Invalid;
102    }
103    let at = |k: usize| c[components[k]];
104    match components.len() {
105        1 => ExpressionVariant::Double(at(0)),
106        2 => Vector2::new(at(0) as f32, at(1) as f32).into(),
107        3 => Vector3::new(at(0) as f32, at(1) as f32, at(2) as f32).into(),
108        4 => Vector4::new(at(0) as f32, at(1) as f32, at(2) as f32, at(3) as f32).into(),
109        _ => ExpressionVariant::Invalid,
110    }
111}
112
113/// `value` with the sub-channel `components` set from `channel_value`.
114/// Components the value does not have, and a channel value that is not a
115/// scalar or a vector, leave it unchanged.
116pub(crate) fn write_subchannel(
117    value: ExpressionVariant,
118    components: &[usize],
119    channel_value: ExpressionVariant,
120) -> ExpressionVariant {
121    let Some((mut c, n)) = vector_components(&value) else { return value };
122    let Some((o, on)) = channel_components(&channel_value) else { return value };
123    for (k, &i) in components.iter().enumerate() {
124        if k < on && i < n {
125            c[i] = o[k];
126        }
127    }
128    with_components(value, c)
129}
130
131/// The animation of a sub-channel on its way to the start path of the
132/// property: what the generated `start_animation` of the property is given
133/// in place of the animation. It holds the factory of the inner instance,
134/// created when the animation was started, and wraps it in a
135/// [`SubchannelAnimationInstance`].
136///
137/// `previous` is the animation of the property that is waiting for the same
138/// batch (started earlier in the frame), which the start path replaces: when
139/// it animates other sub-channels, the new sub-channel joins it on the
140/// server, as for an animation that runs already.
141pub(crate) struct SubchannelAnimation {
142    components: Vec<usize>,
143    inner: RefCell<Option<AnimationInstanceFactory>>,
144    previous: RefCell<Option<AnimationInstanceFactory>>,
145}
146
147impl SubchannelAnimation {
148    pub(crate) fn new(
149        components: Vec<usize>,
150        inner: AnimationInstanceFactory,
151        previous: Option<AnimationInstanceFactory>,
152    ) -> SubchannelAnimation {
153        SubchannelAnimation { components, inner: RefCell::new(Some(inner)), previous: RefCell::new(previous) }
154    }
155}
156
157impl ICompositionAnimationBase for SubchannelAnimation {
158    fn as_composition_animation(&self) -> Option<&dyn ICompositionAnimation> {
159        Some(self)
160    }
161}
162
163impl ICompositionAnimation for SubchannelAnimation {
164    fn target(&self) -> Option<String> {
165        None
166    }
167
168    /// Wraps the inner instance; `final_value` was applied to the inner
169    /// instance when it was created. Called once by the start path.
170    fn create_instance(&self, target_object: ServerObjectId, _final_value: Option<ExpressionVariant>) -> AnimationInstanceFactory {
171        let Some(inner) = self.inner.borrow_mut().take() else {
172            panic!("the instance of a sub-channel animation is created once");
173        };
174        let previous = self.previous.borrow_mut().take();
175        let components = self.components.clone();
176        AnimationInstanceFactory::new(move || {
177            let instance = SubchannelAnimationInstance::new(target_object, components, inner.create());
178            if let Some(previous) = previous {
179                // A previous animation of the whole property is replaced
180                // (dropped); one of sub-channels takes the new sub-channel.
181                let previous = previous.create();
182                let merged = match previous.as_subchannel() {
183                    Some(running) => {
184                        running.absorb(&instance);
185                        true
186                    }
187                    None => false,
188                };
189                if merged {
190                    return previous;
191                }
192            }
193            let instance: Rc<dyn IAnimationInstance> = instance;
194            instance
195        })
196    }
197}
198
199/// One running animation of a sub-channel.
200struct Subchannel {
201    components: Vec<usize>,
202    instance: Rc<dyn IAnimationInstance>,
203}
204
205/// The server instance of the animations of sub-channels of one property:
206/// it evaluates the animation of each sub-channel and writes the result into
207/// the components of the current value of the property.
208///
209/// The inner instances are initialized with the property itself, so their
210/// invalidation marks this instance (the animation of the property) dirty.
211pub struct SubchannelAnimationInstance {
212    target: ServerObjectId,
213    channels: RefCell<Vec<Subchannel>>,
214    property: Cell<Option<&'static CompositionProperty>>,
215    starting_value: Cell<ExpressionVariant>,
216    active: Cell<bool>,
217}
218
219impl SubchannelAnimationInstance {
220    /// An instance with one sub-channel, `components` of the property,
221    /// animated by `instance`.
222    pub fn new(
223        target: ServerObjectId,
224        components: Vec<usize>,
225        instance: Rc<dyn IAnimationInstance>,
226    ) -> Rc<SubchannelAnimationInstance> {
227        Rc::new(SubchannelAnimationInstance {
228            target,
229            channels: RefCell::new(vec![Subchannel { components, instance }]),
230            property: Cell::new(None),
231            starting_value: Cell::new(ExpressionVariant::Invalid),
232            active: Cell::new(false),
233        })
234    }
235
236    /// The component sets of the running sub-channels.
237    pub fn components(&self) -> Vec<Vec<usize>> {
238        self.channels.borrow().iter().map(|c| c.components.clone()).collect()
239    }
240
241    /// Stops and drops the channels that share a component with
242    /// `components`, without applying their stop behavior.
243    // Deviation (winui-composition.md): a running sub-channel that shares
244    // only some components with a new one (`Offset.XY`, then `Offset.X`) is
245    // stopped as a whole.
246    fn drop_overlapping(&self, components: &[usize]) {
247        let removed: Vec<Subchannel> = {
248            let mut channels = self.channels.borrow_mut();
249            let (removed, kept) = std::mem::take(&mut *channels)
250                .into_iter()
251                .partition(|c| c.components.iter().any(|i| components.contains(i)));
252            *channels = kept;
253            removed
254        };
255        for channel in removed {
256            if self.active.get() {
257                channel.instance.deactivate();
258            }
259            channel.instance.release_completion();
260        }
261    }
262
263    /// Takes over the sub-channels of `other` before either is initialized:
264    /// they replace the channels that share a component with them.
265    pub(crate) fn absorb(&self, other: &SubchannelAnimationInstance) {
266        let incoming = std::mem::take(&mut *other.channels.borrow_mut());
267        for channel in incoming {
268            self.drop_overlapping(&channel.components);
269            self.channels.borrow_mut().push(channel);
270        }
271    }
272
273    /// Takes over the sub-channels of `other`, an instance started later on
274    /// the same property and resolved already: they replace the channels
275    /// that share a component with them, and start from `current_value`.
276    pub(crate) fn merge(&self, other: &SubchannelAnimationInstance, committed_at: Duration, current_value: ExpressionVariant) {
277        let incoming = std::mem::take(&mut *other.channels.borrow_mut());
278        for channel in incoming {
279            self.drop_overlapping(&channel.components);
280            if let Some(property) = self.property.get() {
281                channel.instance.initialize(
282                    committed_at,
283                    extract_subchannel(current_value, &channel.components),
284                    property,
285                );
286            }
287            if self.active.get() {
288                channel.instance.activate();
289            }
290            self.channels.borrow_mut().push(channel);
291        }
292    }
293
294    /// Stops the channels that share a component with `components`
295    /// (`StopAnimation("Offset.X")`), applying their stop behavior to
296    /// `current_value`. Returns the value to set, if a stop behavior changes
297    /// it, and whether no channel is left.
298    pub(crate) fn stop_components(
299        &self,
300        components: &[usize],
301        current_value: ExpressionVariant,
302    ) -> (Option<ExpressionVariant>, bool) {
303        let removed: Vec<Subchannel> = {
304            let mut channels = self.channels.borrow_mut();
305            let (removed, kept) = std::mem::take(&mut *channels)
306                .into_iter()
307                .partition(|c| c.components.iter().any(|i| components.contains(i)));
308            *channels = kept;
309            removed
310        };
311        let mut value = current_value;
312        let mut changed = false;
313        for channel in removed {
314            let channel_value = extract_subchannel(value, &channel.components);
315            if let Some(stop) = channel.instance.value_on_stop(channel_value) {
316                value = write_subchannel(value, &channel.components, stop);
317                changed = true;
318            }
319            if self.active.get() {
320                channel.instance.deactivate();
321            }
322            channel.instance.release_completion();
323        }
324        (changed.then_some(value), self.channels.borrow().is_empty())
325    }
326}
327
328impl IServerClockItem for SubchannelAnimationInstance {
329    /// The inner instances are on the clock themselves.
330    fn on_tick(&self) {}
331}
332
333impl IAnimationInstance for SubchannelAnimationInstance {
334    fn target_object(&self) -> ServerObjectId {
335        self.target
336    }
337
338    fn resolve(&self, compositor: &Rc<ServerCompositor>) {
339        for channel in self.channels.borrow().iter() {
340            channel.instance.resolve(compositor);
341        }
342    }
343
344    fn evaluate(&self, now: Duration, current_value: ExpressionVariant) -> ExpressionVariant {
345        // The first evaluation has no current value yet: the value of the
346        // property when the animation started stands in for it.
347        let mut value = if current_value == ExpressionVariant::Invalid { self.starting_value.get() } else { current_value };
348        let channels: Vec<(Vec<usize>, Rc<dyn IAnimationInstance>)> =
349            self.channels.borrow().iter().map(|c| (c.components.clone(), c.instance.clone())).collect();
350        for (components, instance) in channels {
351            let channel_value = instance.evaluate(now, extract_subchannel(value, &components));
352            value = write_subchannel(value, &components, channel_value);
353        }
354        value
355    }
356
357    fn initialize(&self, started_at: Duration, starting_value: ExpressionVariant, property: &'static CompositionProperty) {
358        self.property.set(Some(property));
359        self.starting_value.set(starting_value);
360        for channel in self.channels.borrow().iter() {
361            channel.instance.initialize(started_at, extract_subchannel(starting_value, &channel.components), property);
362        }
363    }
364
365    fn activate(&self) {
366        self.active.set(true);
367        let instances: Vec<_> = self.channels.borrow().iter().map(|c| c.instance.clone()).collect();
368        for instance in instances {
369            instance.activate();
370        }
371    }
372
373    fn deactivate(&self) {
374        self.active.set(false);
375        let instances: Vec<_> = self.channels.borrow().iter().map(|c| c.instance.clone()).collect();
376        for instance in instances {
377            instance.deactivate();
378        }
379    }
380
381    fn invalidate(&self) {
382        let instances: Vec<_> = self.channels.borrow().iter().map(|c| c.instance.clone()).collect();
383        for instance in instances {
384            instance.invalidate();
385        }
386    }
387
388    fn value_on_stop(&self, current_value: ExpressionVariant) -> Option<ExpressionVariant> {
389        let mut value = current_value;
390        let mut changed = false;
391        for channel in self.channels.borrow().iter() {
392            let channel_value = extract_subchannel(value, &channel.components);
393            if let Some(stop) = channel.instance.value_on_stop(channel_value) {
394                value = write_subchannel(value, &channel.components, stop);
395                changed = true;
396            }
397        }
398        changed.then_some(value)
399    }
400
401    fn release_completion(&self) {
402        let instances: Vec<_> = self.channels.borrow().iter().map(|c| c.instance.clone()).collect();
403        for instance in instances {
404            instance.release_completion();
405        }
406    }
407
408    /// Every running sub-channel is put under the controller.
409    fn attach_controller(&self, controller: ServerObjectId) {
410        let instances: Vec<_> = self.channels.borrow().iter().map(|c| c.instance.clone()).collect();
411        for instance in instances {
412            instance.attach_controller(controller);
413        }
414    }
415
416    fn as_subchannel(&self) -> Option<&SubchannelAnimationInstance> {
417        Some(self)
418    }
419}