1pub mod errors;
18mod parser;
19
20#[derive(Clone, Debug, PartialEq)]
22pub struct PropertiesAnimation {
23 pub version: (u16, u16, u16),
25
26 pub fps: f32,
28
29 pub animations: Vec<Animation>,
31}
32
33#[derive(Clone, Debug, PartialEq)]
35pub struct Animation {
36 pub fps: f32,
38
39 pub object_name: String,
41
42 pub property_name: String,
44
45 pub frame_start: u32,
47
48 pub frame_end: u32,
50
51 pub frame_values: Vec<f32>,
53}
54
55type Result = std::result::Result<PropertiesAnimation, errors::Error>;
57
58impl PropertiesAnimation {
59 pub fn from_bytes(bytes: &[u8]) -> Result {
61 let props_animation = parser::props_animation(bytes)?;
62 Ok(props_animation.into())
63 }
64
65 pub fn from_file(path: &str) -> Result {
67 let bytes = std::fs::read(path)?;
68 Self::from_bytes(&bytes)
69 }
70}
71
72impl Animation {
73 #[inline]
75 pub fn get_animation_value_at_time(&self, elapsed_time: f32) -> f32 {
76 self.get_interpolated_value_at_frame(self.fps * elapsed_time)
77 }
78
79 pub fn get_value_at_exact_frame(&self, frame: u32) -> f32 {
83 if frame <= self.frame_start {
84 return self.frame_values[0];
85 } else if frame >= self.frame_end {
86 return self.frame_values[self.frame_values.len() - 1];
87 }
88
89 let index = (frame - self.frame_start) as usize;
90 self.frame_values[index]
91 }
92
93 pub fn get_interpolated_value_at_frame(&self, frame: f32) -> f32 {
98 let lower_frame = frame.floor();
99
100 let fraction = frame - lower_frame;
101
102 let lower_frame = lower_frame as u32;
103 let upper_frame = lower_frame + 1;
104
105 let lower_value = self.get_value_at_exact_frame(lower_frame);
106 let upper_value = self.get_value_at_exact_frame(upper_frame);
107
108 lower_value + (upper_value - lower_value) * fraction
109 }
110}
111
112impl<'a> From<parser::PropsAnimation<'a>> for PropertiesAnimation {
113 fn from(props_animation: parser::PropsAnimation<'a>) -> Self {
114 PropertiesAnimation {
115 version: props_animation.semver(),
116 fps: props_animation.fps,
117 animations: props_animation
118 .animations
119 .into_iter()
120 .map(|animation| {
121 let mut anim: Animation = animation.into();
122 anim.fps = props_animation.fps;
123 anim
124 })
125 .collect(),
126 }
127 }
128}
129
130impl<'a> From<parser::Animation<'a>> for Animation {
131 fn from(animation: parser::Animation<'a>) -> Self {
132 Animation {
133 fps: 0.0,
134 object_name: animation.header.object_name.to_string(),
135 property_name: animation.header.property_name.to_string(),
136 frame_start: animation.header.frame_start,
137 frame_end: animation.header.frame_end,
138 frame_values: animation.values.0,
139 }
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use crate::parser::tests::{
146 first_anim, first_anim_header, first_anim_values, single_props_anim,
147 };
148
149 #[test]
150 fn it_works() {
151 let result = crate::PropertiesAnimation::from_file("assets/single_anim.panim").unwrap();
152 assert_eq!(result, single_props_anim!().into());
153
154 let animation = &result.animations[0];
155 assert_eq!(animation.get_value_at_exact_frame(10), 0.0);
156 assert_eq!(animation.get_value_at_exact_frame(200), 1.0);
157 assert_eq!(animation.get_value_at_exact_frame(90), 0.5);
158 assert_eq!(animation.get_interpolated_value_at_frame(85.5), 0.18612504);
159
160 assert_eq!(
161 animation.get_animation_value_at_time(85.4 / 24.0),
162 0.18015012
163 );
164 }
165}