mirror of
https://github.com/storytold/filmcraft.git
synced 2026-10-30 21:17:36 -06:00
Ripple trim: a head trim grabbed by its early sound uses the picture's cut
Sync-locked tracks rippled from the grabbed member's start, so grabbing a J cut's early sound checked and shifted from before the picture's cut and was refused. For a head trim the cut is the latest member's start. Adds the test only the new stretch catches: a clip starting at the cut on a sync-locked track, which the old stretch let fall out of sync. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
27124080bf
commit
7f77bdfd3a
@@ -652,7 +652,11 @@ pub fn ripple_trim_group(seq: &mut Sequence, clips: &[ClipId], edge: Edge, delta
|
||||
origins.push((tid, from));
|
||||
}
|
||||
}
|
||||
let main_from = {
|
||||
// a shorter or longer head: the cut is the latest member's start (a sound that leads its picture
|
||||
// starts earlier), whichever member was grabbed
|
||||
let main_from = if edge == Edge::In {
|
||||
origins.iter().map(|o| o.1).max().unwrap_or_default()
|
||||
} else {
|
||||
let (tid, _) = seq.find_item(first).ok_or(EditError::NoItem(first))?;
|
||||
origins.iter().find(|(t, _)| *t == tid).map(|o| o.1).unwrap_or_default()
|
||||
};
|
||||
|
||||
@@ -600,4 +600,22 @@ fn shorter_head_closes_the_stretch_after_the_cut_on_sync_locked_tracks() {
|
||||
};
|
||||
assert_eq!(r, Err(EditError::SyncLockConflict), "group {group}");
|
||||
}
|
||||
// a clip on a sync-locked track that starts at the cut stays put while the pictures after it move
|
||||
// up: only the stretch after the cut sees it (the old stretch, before the cut, let it through)
|
||||
for group in [false, true] {
|
||||
let mut fx = Fx::new();
|
||||
let (v1, a1) = (fx.v(0), fx.a(0));
|
||||
fx.put(v1, 0, 10, 0);
|
||||
let b = fx.put(v1, 10, 10, 0);
|
||||
fx.put(v1, 20, 10, 0);
|
||||
fx.put(a1, 10, 5, 0);
|
||||
let mut n = fx.next;
|
||||
let r = if group {
|
||||
ripple_trim_group(&mut fx.seq, &[b], Edge::In, f(3), &mut Fx::ctx(&mut n))
|
||||
} else {
|
||||
trim(&mut fx.seq, b, Edge::In, TrimMode::Ripple, f(3), &mut Fx::ctx(&mut n))
|
||||
};
|
||||
assert_eq!(r, Err(EditError::SyncLockConflict), "group {group}");
|
||||
assert_eq!(fx.spans(v1), vec![(0, 10), (10, 10), (20, 10)], "unchanged on failure");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +92,16 @@ fn ripple_trim_is_refused_when_the_early_sound_has_no_room() {
|
||||
/// the shorter head takes away.
|
||||
#[test]
|
||||
fn shorter_head_on_a_j_cut_take_shortens_picture_and_sound_together() {
|
||||
shorter_head_on_a_j_cut_take(false);
|
||||
}
|
||||
|
||||
/// The same trim grabbed by the early sound instead of the picture gives the same result.
|
||||
#[test]
|
||||
fn shorter_head_grabbed_by_the_early_sound_gives_the_same_result() {
|
||||
shorter_head_on_a_j_cut_take(true);
|
||||
}
|
||||
|
||||
fn shorter_head_on_a_j_cut_take(grab_sound: bool) {
|
||||
let mut s = Session::default();
|
||||
let (first, second, sound) = j_cut(&mut s);
|
||||
let rate = s.sequence_rate();
|
||||
@@ -100,7 +110,7 @@ fn shorter_head_on_a_j_cut_take_shortens_picture_and_sound_together() {
|
||||
let ((pic_len, pic_in), (snd_len, snd_in)) = (len(&s, second), len(&s, sound));
|
||||
let first_before = len(&s, first);
|
||||
let before = s.project.clone();
|
||||
let r = s.execute("timeline.trim", json!({"clip": second.0, "edge": "in", "mode": "ripple", "deltaFrames": 8})).unwrap();
|
||||
let r = s.execute("timeline.trim", json!({"clip": if grab_sound { sound.0 } else { second.0 }, "edge": "in", "mode": "ripple", "deltaFrames": 8})).unwrap();
|
||||
let by = rate.tick_of(8);
|
||||
assert_eq!(r["delta"], json!(by.0));
|
||||
assert_eq!((start(&s, second), start(&s, sound)), (picture_at, sound_at), "both stay where they start");
|
||||
|
||||
Reference in New Issue
Block a user