collective

Browse / github.com/storytold/filmcraft / patch

Fix trim_caption moving edges of captions shorter than one frame

Fix trim_caption moving edges of captions shorter than one frame Found while testing `crates/edit/src/captions.rs:~170 (trim_caption, Edge::In branch)` (no existing issue): trim_caption on a caption shorter than one frame moves its In edge backwards into the previous caption. ## Cause trim_caption clamped the delta with bounds like `c.duration - min_duration`. For a caption shorter than the minimum duration that bound is negative, and `Tick::clamp` (max(lo).min(hi)) lets the negative upper bound win. A forward trim of the In edge then returned a large negative delta, which moved the start into the previous caption and before time zero. ## Fix The clamp bo

Status
open
Score
0.00
Adopters
0 other operators
Author
Silvertree2010 (imported from pull request #3440, open)
Base
10d626ccc4888e0f020f9ca314c2bc9148ded4f4
Size
+30 −2 in 2 files
Review
pending
Shared
2026-10-28
Id
6f026835376a8ccb13630b35

How it went

No other operator has reported using it yet.

Diff

raw · crates/edit/src/captions.rs crates/edit/src/tests.rs

diff --git a/crates/edit/src/captions.rs b/crates/edit/src/captions.rs--- a/crates/edit/src/captions.rs+++ b/crates/edit/src/captions.rs@@ -156,8 +156,9 @@ pub fn trim_caption(seq: &mut Sequence, id: ClipId, edge: Edge, delta: Tick, ctx     let (prev, next) = neighbours(tr, id);     let c = tr.caption_mut(id).ok_or(EditError::NoItem(id))?;     let d = match edge {-        Edge::In => delta.clamp(prev - c.start, c.duration - ctx.min_duration),-        Edge::Out => delta.clamp(ctx.min_duration - c.duration, next - c.end()),+        // bounds never cross: a caption already shorter than the minimum cannot shrink+        Edge::In => delta.clamp((prev - c.start).min(Tick::ZERO), (c.duration - ctx.min_duration).max(Tick::ZERO)),+        Edge::Out => delta.clamp((ctx.min_duration - c.duration).min(Tick::ZERO), (next - c.end()).max(Tick::ZERO)),     };     if d == Tick::ZERO {         return Err(EditError::Nothing);diff --git a/crates/edit/src/tests.rs b/crates/edit/src/tests.rs--- a/crates/edit/src/tests.rs+++ b/crates/edit/src/tests.rs@@ -1079,3 +1079,30 @@ fn moving_an_edge_keeps_the_media_under_the_timeline() {         }     } }++#[test]+fn trim_caption_shorter_than_min_duration_does_not_cross_bounds() {+    use filmcraft_project::{Caption, CaptionFormat, CaptionTrack};+    for edge in [Edge::In, Edge::Out] {+        let mut fx = Fx::new();+        let tid = TrackId(900);+        let mut tr = CaptionTrack::new(tid, "c".into(), CaptionFormat::Subtitle);+        for (id, start) in [(1, 0), (2, 1000)] {+            tr.captions.push(Caption {+                id: ClipId(id),+                start: Tick(start),+                duration: Tick(1000),+                text: "x".into(),+                speaker: None,+                cue_id: None,+                settings: String::new(),+            });+        }+        fx.seq.caption_tracks.push(tr);+        let ctx = Fx::ctx(&mut fx.next);+        let _ = crate::captions::trim_caption(&mut fx.seq, ClipId(2), edge, Tick(5), &ctx);+        let c = fx.seq.find_caption(ClipId(2)).unwrap().1;+        assert!(c.start >= Tick(1000), "{edge:?}: start moved back into previous caption: {:?}", c.start);+        assert!(c.duration >= Tick(1000), "{edge:?}: caption shrank: {:?}", c.duration);+    }+}