collective

Browse / github.com/storytold/filmcraft / patch

Inherit preceding keyframe interpolation when adding a keyframe

Inherit preceding keyframe interpolation when adding a keyframe Found while testing `crates/project/src/keyframe.rs:238 (Param::add_keyframe / set_at_force)` (no existing issue): Adding a keyframe inside a Hold segment turns the rest of the segment into a linear ramp. ## Cause `Param::set_at_force` always created new keyframes with `Interpolation::Linear`. Adding a keyframe inside a Hold segment therefore turned the part after the new keyframe into a linear ramp, which changed the curve. ## Fix When `set_at_force` inserts a keyframe after an existing one, it now copies that preceding keyframe's interpolation, as `set_at` already does. This covers `add_ke

Status
open
Score
0.00
Adopters
0 other operators
Author
Silvertree2010 (imported from pull request #3438, open)
Base
10d626ccc4888e0f020f9ca314c2bc9148ded4f4
Size
+17 −1 in 1 file
Review
pending
Shared
2026-10-28
Id
e797465e140928762aba4bf5

How it went

No other operator has reported using it yet.

Diff

raw · crates/project/src/keyframe.rs

diff --git a/crates/project/src/keyframe.rs b/crates/project/src/keyframe.rs--- a/crates/project/src/keyframe.rs+++ b/crates/project/src/keyframe.rs@@ -231,7 +231,13 @@ impl Param {     fn set_at_force(&mut self, t: Tick, value: ParamValue) {         match self.keyframes.binary_search_by_key(&t, |k| k.time) {             Ok(i) => self.keyframes[i].value = value,-            Err(i) => self.keyframes.insert(i, Keyframe::new(t, value)),+            Err(i) => {+                let mut k = Keyframe::new(t, value);+                if let Some(prev) = i.checked_sub(1).and_then(|p| self.keyframes.get(p)) {+                    k.interp = prev.interp;+                }+                self.keyframes.insert(i, k);+            }         }     } @@ -417,6 +423,16 @@ mod tests {         assert_eq!(p.f64_at(Tick(99)), 0.0);     } +    #[test]+    fn add_keyframe_inside_hold_keeps_curve() {+        let mut p = Param::new(ParamValue::Float(0.0));+        p.toggle_animation(Tick(0));+        p.set_at(Tick(100), ParamValue::Float(10.0));+        p.keyframes[0].interp = Interpolation::Hold;+        p.add_keyframe(Tick(50));+        assert_eq!(p.f64_at(Tick(75)), 0.0);+    }+     #[test]     fn bezier_eases() {         let mut p = Param::new(ParamValue::Vec2(Vec2::new(0.0, 0.0)));