add: animation curves support#4164
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces an AnimationCurve implementation, featuring keyframe-based interpolation (Linear, Constant, and Bezier) and integration into the node graph system via new nodes and registry updates. Review feedback suggests refining the evaluation logic to clamp out-of-bounds values to the nearest keyframe for better consistency and optimizing keyframe insertion using binary search to improve performance and data validation.
| if index == 0 { | ||
| return 0.0; | ||
| } else if index == self.keyframes.len() { | ||
| // unwrap is safe because of the non-empty guard at the top | ||
| return self.keyframes.last().unwrap().knot.y; | ||
| } |
There was a problem hiding this comment.
The evaluation logic for time values before the first keyframe returns 0.0, while evaluation after the last keyframe returns the last keyframe's value. For animation curves, it is more consistent to clamp to the first keyframe's value when evaluated before the start of the curve, ensuring that properties (like scale or opacity) don't unexpectedly jump to zero.
| if index == 0 { | |
| return 0.0; | |
| } else if index == self.keyframes.len() { | |
| // unwrap is safe because of the non-empty guard at the top | |
| return self.keyframes.last().unwrap().knot.y; | |
| } | |
| if index == 0 { | |
| return self.keyframes[0].knot.y; | |
| } else if index == self.keyframes.len() { | |
| // unwrap is safe because of the non-empty guard at the top | |
| return self.keyframes.last().unwrap().knot.y; | |
| } |
| pub fn push_keyframe(&mut self, keyframe: Keyframe) { | ||
| self.keyframes.push(keyframe); | ||
| self.keyframes.sort_by(|lhs, rhs| lhs.knot.x.partial_cmp(&rhs.knot.x).unwrap_or(std::cmp::Ordering::Equal)); | ||
| } |
There was a problem hiding this comment.
Sorting the entire keyframes vector after every push is inefficient (O(N log N)). Since the vector is already sorted, you can use binary_search_by to find the correct insertion point and use insert or replace the existing keyframe, which is O(N). Additionally, validating that knot.x is finite prevents issues with sorting and searching logic caused by NaN values.
| pub fn push_keyframe(&mut self, keyframe: Keyframe) { | |
| self.keyframes.push(keyframe); | |
| self.keyframes.sort_by(|lhs, rhs| lhs.knot.x.partial_cmp(&rhs.knot.x).unwrap_or(std::cmp::Ordering::Equal)); | |
| } | |
| pub fn push_keyframe(&mut self, keyframe: Keyframe) { | |
| if !keyframe.knot.x.is_finite() { | |
| return; | |
| } | |
| let index = self.keyframes.binary_search_by(|kf| kf.knot.x.partial_cmp(&keyframe.knot.x).unwrap_or(std::cmp::Ordering::Equal)); | |
| match index { | |
| Ok(idx) => self.keyframes[idx] = keyframe, | |
| Err(idx) => self.keyframes.insert(idx, keyframe), | |
| } | |
| } |
| knot: DVec2::new(1.0, 10.0), | ||
| interp_behavior: InterpolationBehavior::Constant, | ||
| }); | ||
| assert_eq!(single_kf.evaluate(0.0), 0.0); |
There was a problem hiding this comment.
0105b40 to
3ddcda6
Compare
|
Note: I rebased your branch. |
A work-in-progress PR adding support for f-curves and a timeline
NodeInputvariant.TODO:
AnimationCurve(f-curves implementation based on Blender's system)NodeInputvariant.In the future, the timeline inputs can be hooked up to the timeline curve editor.