mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-11 13:04:59 +00:00
Time: keep a composition's final partial frame reachable
A frame-rate change can leave the duration off the frame grid. The current-time clamp used duration minus one frame, which put the last reachable frame before a final partial frame, so a layer fitted to a work area inside it never got the playhead. Clamp to the last frame that starts before the end instead; on-grid durations are unchanged.
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
use super::{Comp, CompSelection, Event, ItemId, LayerId, Result, Session, Tick, Value, commands};
|
||||
|
||||
/// `t` as a current time of `comp`: a frame within its duration.
|
||||
/// `t` as a current time of `comp`: a frame within its duration. The last frame is the last one
|
||||
/// that starts before the end, so a final partial frame (a duration off the frame grid after a
|
||||
/// frame-rate change) stays reachable; on the grid it is `duration - frame_duration`.
|
||||
fn comp_frame_at(comp: &Comp, t: Tick) -> Tick {
|
||||
comp.frame_rate.snap_nearest(t.min(comp.duration - comp.frame_duration()).max(Tick::ZERO))
|
||||
let last = comp.frame_rate.snap(comp.duration - Tick(1)).max(Tick::ZERO);
|
||||
comp.frame_rate.snap_nearest(t.max(Tick::ZERO)).min(last)
|
||||
}
|
||||
|
||||
impl Session {
|
||||
|
||||
@@ -470,6 +470,26 @@ fn fitted_layers_off_the_frame_grid_move_the_playhead_inside_them() {
|
||||
}
|
||||
}
|
||||
|
||||
/// A duration off the frame grid (2.01 s set at 100 fps, then 24 fps) keeps its final partial
|
||||
/// frame reachable: a layer fitted to a work area inside it gets the playhead on that frame.
|
||||
#[test]
|
||||
fn fitted_layers_in_the_final_partial_frame_get_the_playhead() {
|
||||
let mut s = Session::default();
|
||||
s.execute("comp.new", json!({"name": "Tail", "duration": 2.01, "frameRate": 100})).unwrap();
|
||||
s.execute("prefs.set", json!({"key": "general.fitNewLayersToWorkArea", "value": true})).unwrap();
|
||||
s.execute("comp.workArea", json!({"start": 2.0, "end": 2.01})).unwrap();
|
||||
s.execute("comp.settings", json!({"frameRate": 24})).unwrap();
|
||||
s.set_time(effectcraft_time::Tick::ZERO);
|
||||
s.execute("shape.newPath", json!({"vertices": [[0, 0], [10, 0], [10, 10]]})).unwrap();
|
||||
let l = &s.active_comp().unwrap().layers[0];
|
||||
let t = s.time();
|
||||
assert!(l.in_point <= t && t < l.out_point, "{t:?} in {:?}..{:?}", l.in_point, l.out_point);
|
||||
assert_eq!(t, effectcraft_time::Tick::from_seconds_f64(2.0), "frame 48 of 24 fps");
|
||||
// The playhead can't go past that last frame.
|
||||
s.set_time(effectcraft_time::Tick::from_seconds_f64(5.0));
|
||||
assert_eq!(s.time(), effectcraft_time::Tick::from_seconds_f64(2.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_spatial_interpolation_linear() {
|
||||
let mut s = session_with_comp();
|
||||
|
||||
Reference in New Issue
Block a user