Merge pull request #605 from vbasky/fix/538-work-area
This commit is contained in:
commit
943c42659a
|
|
@ -76,6 +76,8 @@ fn apply_settings(c: &mut Comp, p: &Value) {
|
|||
c.work_area.1 = c.duration;
|
||||
}
|
||||
c.work_area.1 = c.work_area.1.min(c.duration);
|
||||
// A shorter composition must not leave the start past the end.
|
||||
keep_work_area_ordered(c);
|
||||
}
|
||||
if let Some(pa) = f_p(p, "pixelAspect") {
|
||||
c.pixel_aspect = pa.max(0.1);
|
||||
|
|
@ -196,17 +198,40 @@ fn work_area(s: &mut Session, p: &Value) -> Result<Value> {
|
|||
if let Some(e) = end {
|
||||
c.work_area.1 = e.clamp(c.work_area.0 + fd, c.duration);
|
||||
}
|
||||
// A start on its own can land after the current end. Pull it back so the interval
|
||||
// still covers one frame, the same rule as `set: "begin"`.
|
||||
keep_work_area_ordered(c);
|
||||
Ok(())
|
||||
})?;
|
||||
let c = s.project.comp(cid).ok_or(EngineError::NoComp)?;
|
||||
Ok(json!({"start": c.work_area.0.seconds(), "end": c.work_area.1.seconds()}))
|
||||
}
|
||||
|
||||
/// Keep `work_area` inside the composition and at least one frame long.
|
||||
fn keep_work_area_ordered(c: &mut Comp) {
|
||||
let fd = c.frame_duration();
|
||||
c.work_area.1 = c.work_area.1.min(c.duration);
|
||||
if c.work_area.0 < Tick::ZERO {
|
||||
c.work_area.0 = Tick::ZERO;
|
||||
}
|
||||
if c.work_area.0 + fd > c.work_area.1 {
|
||||
c.work_area.0 = (c.work_area.1 - fd).max(Tick::ZERO);
|
||||
}
|
||||
if c.work_area.0 + fd > c.work_area.1 {
|
||||
c.work_area.1 = (c.work_area.0 + fd).min(c.duration);
|
||||
}
|
||||
}
|
||||
|
||||
fn trim_to_wa(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let cid = comp_id(s, p)?;
|
||||
s.edit("Trim Comp to Work Area", None, |proj, _| {
|
||||
let c = proj.comp_mut(cid).ok_or(EngineError::NoComp)?;
|
||||
let (a, b) = c.work_area;
|
||||
let fd = c.frame_duration();
|
||||
// An interval that is already inverted must not become a negative duration.
|
||||
if a < Tick::ZERO || b > c.duration || a + fd > b {
|
||||
return Err(bad("comp.trimToWorkArea", "the work area must cover at least one frame inside the composition"));
|
||||
}
|
||||
for l in &mut c.layers {
|
||||
l.start_time -= a;
|
||||
l.in_point = (l.in_point - a).max(Tick::ZERO);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
use effectcraft_keyframe::Value as KV;
|
||||
use effectcraft_project::{FrameBlend, ItemKind, MatteKind, Quality, Sampling};
|
||||
use effectcraft_time::Tick;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use crate::{EngineError, Event, Session};
|
||||
|
|
@ -253,6 +254,69 @@ fn lift_and_extract_work_area() {
|
|||
assert!((spans[1].0 - 2.0).abs() < 1e-6 && (spans[1].1 - 8.0).abs() < 1e-6, "{spans:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn work_area_stays_ordered_when_the_start_passes_the_end() {
|
||||
let ordered = |s: &Session| {
|
||||
let c = s.active_comp().unwrap();
|
||||
let fd = c.frame_duration();
|
||||
assert!(c.duration >= fd, "duration {}", c.duration.seconds());
|
||||
assert!(c.work_area.0 >= Tick::ZERO);
|
||||
assert!(c.work_area.0 + fd <= c.work_area.1, "start {} end {}", c.work_area.0.seconds(), c.work_area.1.seconds());
|
||||
assert!(c.work_area.1 <= c.duration);
|
||||
(c.duration, c.work_area)
|
||||
};
|
||||
let one_frame_at_two = |s: &Session| {
|
||||
let end = Tick::from_seconds_f64(2.0);
|
||||
(end - s.active_comp().unwrap().frame_duration(), end)
|
||||
};
|
||||
|
||||
let mut s = comp();
|
||||
s.execute("comp.workArea", json!({"end": 2.0})).unwrap();
|
||||
s.execute("comp.workArea", json!({"start": 8.0})).unwrap();
|
||||
assert_eq!(ordered(&s).1, one_frame_at_two(&s));
|
||||
s.execute("edit.undo", json!({})).unwrap();
|
||||
assert_eq!(ordered(&s).1, (Tick::ZERO, Tick::from_seconds_f64(2.0)));
|
||||
|
||||
s.execute("comp.workArea", json!({"start": 0.0, "end": 2.0})).unwrap();
|
||||
s.execute("comp.trimToWorkArea", json!({})).unwrap();
|
||||
assert_eq!(ordered(&s), (Tick::from_seconds_f64(2.0), (Tick::ZERO, Tick::from_seconds_f64(2.0))));
|
||||
s.execute("edit.undo", json!({})).unwrap();
|
||||
assert_eq!(ordered(&s).0, Tick::from_seconds_f64(10.0));
|
||||
|
||||
let mut s = comp();
|
||||
s.execute("comp.workArea", json!({"start": 9.0, "end": 10.0})).unwrap();
|
||||
let before = ordered(&s);
|
||||
s.execute("comp.settings", json!({"duration": 2.0})).unwrap();
|
||||
let (dur, wa) = ordered(&s);
|
||||
assert_eq!(dur, Tick::from_seconds_f64(2.0));
|
||||
assert_eq!(wa, one_frame_at_two(&s));
|
||||
s.execute("edit.undo", json!({})).unwrap();
|
||||
assert_eq!(ordered(&s), before);
|
||||
|
||||
let mut s = comp();
|
||||
s.execute("comp.workArea", json!({"end": 2.0})).unwrap();
|
||||
s.execute("time.set", json!({"time": 8.0})).unwrap();
|
||||
s.execute("comp.workArea", json!({"set": "begin"})).unwrap();
|
||||
assert_eq!(ordered(&s).1, one_frame_at_two(&s));
|
||||
let path = tmp("WorkArea.ecproj");
|
||||
s.execute("file.saveAs", json!({"path": path})).unwrap();
|
||||
s.execute("file.open", json!({"path": path})).unwrap();
|
||||
assert_eq!(ordered(&s).1, one_frame_at_two(&s));
|
||||
|
||||
// A project that already has an inverted interval must not trim into a negative duration.
|
||||
let mut s = comp();
|
||||
let cid = s.state.active_comp.unwrap();
|
||||
{
|
||||
let c = std::sync::Arc::make_mut(&mut s.project).comp_mut(cid).unwrap();
|
||||
c.work_area = (Tick::from_seconds_f64(8.0), Tick::from_seconds_f64(2.0));
|
||||
}
|
||||
let err = s.execute("comp.trimToWorkArea", json!({})).unwrap_err();
|
||||
assert!(err.to_string().contains("work area"), "{err}");
|
||||
let c = s.active_comp().unwrap();
|
||||
assert_eq!(c.duration, Tick::from_seconds_f64(10.0));
|
||||
assert_eq!(c.work_area, (Tick::from_seconds_f64(8.0), Tick::from_seconds_f64(2.0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn label_group_purge_and_edit_original() {
|
||||
crate::tests_roto::hold_roto_cache_test_lock();
|
||||
|
|
|
|||
Loading…
Reference in New Issue