use std::cmp::Ordering; use super::keyframe::RenderedKeyframe; use serde::{Deserialize, Serialize}; use simple_easing::{ circ_in, circ_in_out, circ_out, cubic_in, cubic_in_out, cubic_out, expo_in, expo_in_out, expo_out, quad_in, quad_in_out, quad_out, quart_in, quart_in_out, quart_out, quint_in, quint_in_out, quint_out, }; #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] pub struct SpringProperties { pub mass: f32, pub damping: f32, pub stiffness: f32, } #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] pub struct SpringState { pub velocity: f32, pub last_val: f32, } #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] #[serde(tag = "easing_function")] pub enum EasingFunction { QuintOut, QuintIn, QuintInOut, CircOut, CircIn, CircInOut, CubicOut, CubicIn, CubicInOut, ExpoOut, ExpoIn, ExpoInOut, QuadOut, QuadIn, QuadInOut, QuartOut, QuartIn, QuartInOut, } impl EasingFunction { fn ease(self: &Self, t: f32) -> f32 { match self { EasingFunction::QuintOut => quint_out(t), EasingFunction::QuintIn => quint_in(t), EasingFunction::QuintInOut => quint_in_out(t), EasingFunction::CircOut => circ_out(t), EasingFunction::CircIn => circ_in(t), EasingFunction::CircInOut => circ_in_out(t), EasingFunction::CubicOut => cubic_out(t), EasingFunction::CubicIn => cubic_in(t), EasingFunction::CubicInOut => cubic_in_out(t), EasingFunction::ExpoOut => expo_out(t), EasingFunction::ExpoIn => expo_in(t), EasingFunction::ExpoInOut => expo_in_out(t), EasingFunction::QuadOut => quad_out(t), EasingFunction::QuadIn => quad_in(t), EasingFunction::QuadInOut => quad_in_out(t), EasingFunction::QuartOut => quart_out(t), EasingFunction::QuartIn => quart_in(t), EasingFunction::QuartInOut => quart_in_out(t), } } } #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] #[serde(tag = "type")] pub enum InterpolationType { Linear, Spring(SpringProperties), EasingFunction(EasingFunction), } pub fn calculate_spring_value( curr_frame: i32, start_value: f32, target_value: f32, start_frame: i32, _end_frame: i32, spring_props: &SpringProperties, ) -> f32 { const PRECISION: f32 = 0.01; const STEP: f32 = 10.0; const REST_VELOCITY: f32 = PRECISION / 10.0; let _is_growing = match start_value.total_cmp(&target_value) { Ordering::Equal => false, Ordering::Less => false, Ordering::Greater => true, }; let mut _is_moving = false; let mut spring_state = SpringState { last_val: start_value, velocity: 0.0, }; let mut position = start_value; let relative_curr_frame = curr_frame - start_frame; // println!("target_value {target_value} start_value {start_value}"); // println!("start_frame {start_frame} end_frame {end_frame}"); for _ in 0..relative_curr_frame { let _is_moving = spring_state.velocity.abs() > REST_VELOCITY; let spring_force = -spring_props.stiffness * 0.000001 * (position - target_value); let damping_force = -spring_props.damping * 0.001 * spring_state.velocity; let acceleration = (spring_force + damping_force) / spring_props.mass; // pt/ms^2 spring_state.velocity = spring_state.velocity + acceleration * STEP; // pt/ms position = position + spring_state.velocity * STEP; // println!("{position}") } position } pub fn interpolate_rendered_keyframes( first_ren_keyframe: &RenderedKeyframe, second_ren_keyframe: &RenderedKeyframe, curr_frame: i32, interpolation_type: InterpolationType, _fps: i16, ) -> f32 { let frame_range = second_ren_keyframe.absolute_frame - first_ren_keyframe.absolute_frame; let position_in_range = curr_frame - first_ren_keyframe.absolute_frame; let progress: f32 = (1.0 / frame_range as f32) * position_in_range as f32; /* println!( "Progress:{0} Frame_Range: {1} Position_In_Range: {2}", progress, frame_range, position_in_range ); */ let value_diff = second_ren_keyframe.keyframe.value - first_ren_keyframe.keyframe.value; match interpolation_type { InterpolationType::Linear => { let interpolated_val = first_ren_keyframe.keyframe.value + (value_diff * progress as f32); return interpolated_val; } InterpolationType::EasingFunction(easing_function) => { let eased_progress = easing_function.ease(progress); let interpolated_val = first_ren_keyframe.keyframe.value + (value_diff * eased_progress as f32); return interpolated_val; } InterpolationType::Spring(spring_properties) => { let interpolated_value = calculate_spring_value( curr_frame, first_ren_keyframe.keyframe.value, second_ren_keyframe.keyframe.value, first_ren_keyframe.absolute_frame, second_ren_keyframe.absolute_frame, &spring_properties, ); return interpolated_value; } }; }