rive_rs/animation/
key_frame.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;
6use std::cell::Cell;
7
8use crate::animation::{
9    CubicInterpolator, KeyFrameColor, KeyFrameDouble, KeyFrameId, KeyedProperty,
10};
11use crate::core::{Core, CoreContext, Object, ObjectRef, OnAdded, Property};
12use crate::importers::{ImportStack, KeyedPropertyImporter};
13use crate::option_cell::OptionCell;
14use crate::status_code::StatusCode;
15
16#[derive(Debug, Default)]
17pub struct KeyFrame {
18    frame: Property<u64>,
19    interpolation_type: Property<u64>,
20    interpolator_id: Property<u64>,
21    cubic_interpolator: OptionCell<Object<CubicInterpolator>>,
22    seconds: Cell<f32>,
23}
24
25impl ObjectRef<'_, KeyFrame> {
26    pub fn frame(&self) -> u64 {
27        self.frame.get()
28    }
29
30    pub fn set_frame(&self, frame: u64) {
31        self.frame.set(frame);
32    }
33
34    pub fn interpolation_type(&self) -> u64 {
35        self.interpolation_type.get()
36    }
37
38    pub fn set_interpolation_type(&self, interpolation_type: u64) {
39        self.interpolation_type.set(interpolation_type);
40    }
41
42    pub fn interpolator_id(&self) -> u64 {
43        self.interpolator_id.get()
44    }
45
46    pub fn set_interpolator_id(&self, interpolator_id: u64) {
47        self.interpolator_id.set(interpolator_id);
48    }
49}
50
51impl ObjectRef<'_, KeyFrame> {
52    pub fn seconds(&self) -> f32 {
53        self.seconds.get()
54    }
55
56    pub fn interpolator(&self) -> Option<Object<CubicInterpolator>> {
57        self.cubic_interpolator.get()
58    }
59
60    pub fn compute_seconds(&self, fps: u64) {
61        self.seconds.set(self.frame() as f32 / fps as f32);
62    }
63
64    pub fn apply(&self, core: Object, property_key: u64, mix: f32) {
65        if let Some(key_frame_color) = self.try_cast::<KeyFrameColor>() {
66            return key_frame_color.apply(core, property_key, mix);
67        }
68
69        if let Some(key_frame_double) = self.try_cast::<KeyFrameDouble>() {
70            return key_frame_double.apply(core, property_key, mix);
71        }
72
73        if let Some(key_frame_id) = self.try_cast::<KeyFrameId>() {
74            return key_frame_id.apply(core, property_key, mix);
75        }
76
77        unreachable!();
78    }
79
80    pub fn apply_interpolation(
81        &self,
82        core: Object,
83        property_key: u64,
84        seconds: f32,
85        next_frame: ObjectRef<'_, KeyFrame>,
86        mix: f32,
87    ) {
88        if let Some(key_frame_color) = self.try_cast::<KeyFrameColor>() {
89            return key_frame_color.apply_interpolation(
90                core,
91                property_key,
92                seconds,
93                next_frame,
94                mix,
95            );
96        }
97
98        if let Some(key_frame_double) = self.try_cast::<KeyFrameDouble>() {
99            return key_frame_double.apply_interpolation(
100                core,
101                property_key,
102                seconds,
103                next_frame,
104                mix,
105            );
106        }
107
108        if let Some(key_frame_id) = self.try_cast::<KeyFrameId>() {
109            return key_frame_id.apply_interpolation(core, property_key, seconds, next_frame, mix);
110        }
111
112        unreachable!();
113    }
114}
115
116impl Core for KeyFrame {
117    properties![
118        (67, frame, set_frame),
119        (68, interpolation_type, set_interpolation_type),
120        (69, interpolator_id, set_interpolator_id)
121    ];
122}
123
124impl OnAdded for ObjectRef<'_, KeyFrame> {
125    on_added!([on_added_clean]);
126
127    fn on_added_dirty(&self, context: &dyn CoreContext) -> StatusCode {
128        if self.interpolator_id() != 0 {
129            if let Some(cubic_interpolator) =
130                context.resolve(self.interpolator_id() as usize).and_then(|core| core.try_cast())
131            {
132                self.cubic_interpolator.set(Some(cubic_interpolator));
133            } else {
134                return StatusCode::MissingObject;
135            }
136        }
137
138        StatusCode::Ok
139    }
140
141    fn import(&self, object: Object, import_stack: &ImportStack) -> StatusCode {
142        if let Some(importer) =
143            import_stack.latest::<KeyedPropertyImporter>(TypeId::of::<KeyedProperty>())
144        {
145            importer.push_key_frame(object.as_ref().cast::<KeyFrame>().as_object());
146            StatusCode::Ok
147        } else {
148            StatusCode::MissingObject
149        }
150    }
151}