motionloom 0.1.0

MotionLoom DSL parser and renderer for video effects, scene graphs, motion graphics, and world graphs.
Documentation
// =========================================
// crates/motionloom/src/process/adapters/clip/curve.rs
// =========================================

use super::model::AnimF32;

pub fn sample_anim_f32(anim: &AnimF32, frame: u32) -> f32 {
    match anim {
        AnimF32::Const(v) => *v,
        AnimF32::Linear {
            from,
            to,
            start_frame,
            end_frame,
        } => {
            if end_frame <= start_frame {
                return *to;
            }
            let t =
                (frame.saturating_sub(*start_frame)) as f32 / (*end_frame - *start_frame) as f32;
            from + (to - from) * t.clamp(0.0, 1.0)
        }
        AnimF32::Keyframes(points) => {
            if points.is_empty() {
                return 1.0;
            }
            if points.len() == 1 {
                return points[0].1;
            }

            let mut prev = points[0];
            if frame <= prev.0 {
                return prev.1;
            }

            for next in points.iter().copied().skip(1) {
                if frame <= next.0 {
                    if next.0 <= prev.0 {
                        return next.1;
                    }
                    let span = (next.0 - prev.0) as f32;
                    let local = (frame - prev.0) as f32;
                    let t = (local / span).clamp(0.0, 1.0);
                    return prev.1 + (next.1 - prev.1) * t;
                }
                prev = next;
            }

            prev.1
        }
    }
}

#[cfg(test)]
mod tests {
    use super::super::model::AnimF32;
    use super::sample_anim_f32;

    #[test]
    fn linear_sampling_works() {
        let v = sample_anim_f32(
            &AnimF32::Linear {
                from: 1.0,
                to: 2.0,
                start_frame: 0,
                end_frame: 100,
            },
            25,
        );
        assert!((v - 1.25).abs() < 0.001);
    }

    #[test]
    fn keyframe_sampling_interpolates() {
        let v = sample_anim_f32(&AnimF32::Keyframes(vec![(0, 1.0), (10, 2.0)]), 5);
        assert!((v - 1.5).abs() < 0.001);
    }
}