rive_rs/animation/
keyed_property.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::any::TypeId;
6
7use crate::animation::{KeyFrame, KeyedObject};
8use crate::core::{Core, CoreContext, Object, ObjectRef, OnAdded, Property};
9use crate::dyn_vec::DynVec;
10use crate::importers::{ImportStack, KeyedObjectImporter};
11use crate::status_code::StatusCode;
12
13#[derive(Debug, Default)]
14pub struct KeyedProperty {
15    property_key: Property<u64>,
16    key_frames: DynVec<Object<KeyFrame>>,
17}
18
19impl ObjectRef<'_, KeyedProperty> {
20    pub fn property_key(&self) -> u64 {
21        self.property_key.get()
22    }
23
24    pub fn set_property_key(&self, property_key: u64) {
25        self.property_key.set(property_key)
26    }
27}
28
29impl ObjectRef<'_, KeyedProperty> {
30    pub fn push_key_frame(&self, key_frame: Object<KeyFrame>) {
31        self.key_frames.push(key_frame);
32    }
33
34    pub fn apply(&self, core: Object, seconds: f32, mix: f32) {
35        assert!(!self.key_frames.is_empty());
36
37        let mut i = 0;
38        let mut mid;
39        let mut closest_seconds;
40        let mut start = 0;
41        let mut end = self.key_frames.len() - 1;
42
43        while start <= end {
44            mid = (start + end) / 2;
45            closest_seconds = self.key_frames.index(mid).as_ref().seconds();
46
47            if closest_seconds < seconds {
48                start = mid + 1;
49            } else if closest_seconds > seconds {
50                if let Some(new_end) = mid.checked_sub(1) {
51                    end = new_end;
52                } else {
53                    break;
54                }
55            } else {
56                i = mid;
57                break;
58            }
59
60            i = start;
61        }
62
63        let property_key = self.property_key();
64
65        if i == 0 {
66            self.key_frames.index(0).as_ref().apply(core, property_key, mix);
67            return;
68        }
69
70        if i == self.key_frames.len() {
71            self.key_frames.index(i - 1).as_ref().apply(core, property_key, mix);
72            return;
73        }
74
75        let from_frame = self.key_frames.index(i - 1);
76        let from_frame = from_frame.as_ref();
77        let to_frame = self.key_frames.index(i);
78        let to_frame = to_frame.as_ref();
79
80        if seconds == to_frame.seconds() {
81            to_frame.apply(core, property_key, mix);
82        } else if from_frame.interpolation_type() == 0 {
83            from_frame.apply(core, property_key, mix);
84        } else {
85            from_frame.apply_interpolation(core, property_key, seconds, to_frame, mix);
86        }
87    }
88}
89
90impl Core for KeyedProperty {
91    properties![(53, property_key, set_property_key)];
92}
93
94impl OnAdded for ObjectRef<'_, KeyedProperty> {
95    fn on_added_dirty(&self, context: &dyn CoreContext) -> StatusCode {
96        for key_frame in self.key_frames.iter() {
97            let code = key_frame.as_ref().on_added_dirty(context);
98            if code != StatusCode::Ok {
99                return code;
100            }
101        }
102
103        StatusCode::Ok
104    }
105
106    fn on_added_clean(&self, context: &dyn CoreContext) -> StatusCode {
107        for key_frame in self.key_frames.iter() {
108            let code = key_frame.as_ref().on_added_clean(context);
109            if code != StatusCode::Ok {
110                return code;
111            }
112        }
113
114        StatusCode::Ok
115    }
116
117    fn import(&self, object: Object, import_stack: &ImportStack) -> StatusCode {
118        if let Some(importer) =
119            import_stack.latest::<KeyedObjectImporter>(TypeId::of::<KeyedObject>())
120        {
121            importer.push_keyed_property(object.as_ref().cast::<KeyedProperty>().as_object());
122            StatusCode::Ok
123        } else {
124            StatusCode::MissingObject
125        }
126    }
127}