mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-11 16:33:01 -06:00
Merge remote-tracking branch 'origin/main' into HEAD
This commit is contained in:
@@ -58,7 +58,8 @@ fn slip_moves_the_source_under_fixed_in_and_out() {
|
||||
let before = get(&s);
|
||||
assert_eq!((before.start_time, before.in_point.seconds(), before.out_point.seconds()), (Tick::ZERO, 1.0, 3.0));
|
||||
// Slip the source 0.5 s later: in/out stay, the source (start) moves.
|
||||
s.execute("layer.slip", json!({"layers": [id], "delta": 0.5})).unwrap();
|
||||
let r = s.execute("layer.slip", json!({"layers": [id], "delta": 0.5})).unwrap();
|
||||
assert_eq!(r["slipped"][0]["clamped"], json!(false));
|
||||
let l = get(&s);
|
||||
assert_eq!((l.start_time.seconds(), l.in_point, l.out_point), (0.5, before.in_point, before.out_point));
|
||||
// The source time at comp 2 s is now 1.5 s.
|
||||
@@ -67,6 +68,8 @@ fn slip_moves_the_source_under_fixed_in_and_out() {
|
||||
let r = s.execute("layer.slip", json!({"layers": [id], "delta": 5.0})).unwrap();
|
||||
assert_eq!(get(&s).start_time.seconds(), 1.0);
|
||||
assert_eq!(r["slipped"][0]["delta"], json!(0.5));
|
||||
// The response says the 5 s asked for was cut short (#2371).
|
||||
assert_eq!((r["requested"].clone(), r["slipped"][0]["clamped"].clone()), (json!(5.0), json!(true)));
|
||||
// Slip by a frame.
|
||||
s.execute("layer.slipBack", json!({"layers": [id]})).unwrap();
|
||||
assert!((get(&s).start_time.seconds() - (1.0 - 1.0 / 30.0)).abs() < 1e-9);
|
||||
|
||||
@@ -320,6 +320,53 @@ fn toolbar_fill_and_stroke_paint_selected_shape_layers() {
|
||||
let _ = s.render(cid, s.time(), Default::default());
|
||||
}
|
||||
|
||||
/// The toolbar/query width follows the selected solid or gradient stroke, including animation.
|
||||
#[test]
|
||||
fn shape_toolbar_reads_solid_and_gradient_stroke_widths_at_the_current_time() {
|
||||
for kind in ["solid", "linear", "radial"] {
|
||||
for (stretch, expected) in [(100.0, 17.0), (200.0, 13.0)] {
|
||||
let mut s = Session::default();
|
||||
s.execute("comp.new", json!({"width": 400, "height": 300, "duration": 2})).unwrap();
|
||||
let layer = s.execute("layer.newShape", json!({"kind": "rect"})).unwrap()["layer"].as_u64().unwrap();
|
||||
s.execute("layer.timeStretch", json!({"layer": layer, "percent": stretch})).unwrap();
|
||||
let configured = s.execute("shape.fillStroke", json!({"fill": false, "strokeType": kind, "strokeWidth": 9})).unwrap();
|
||||
assert_eq!(configured["strokeWidth"].as_f64(), Some(9.0), "configured {kind} stroke");
|
||||
let group = if kind == "solid" { "stroke" } else { "gstroke" };
|
||||
let path = format!("contents/group/contents/{group}/width");
|
||||
for (time, width) in [(0, 9), (1, 17)] {
|
||||
s.execute("prop.addKey", json!({"layer": layer, "path": path, "time": time, "timeBase": "layer", "value": width})).unwrap();
|
||||
}
|
||||
s.set_time(effectcraft_time::Tick::from_seconds_f64(1.0));
|
||||
let displayed = s.execute("shape.fillStroke", json!({})).unwrap();
|
||||
assert_eq!(displayed["strokeWidth"].as_f64(), Some(expected), "animated {kind} stroke at {stretch}% stretch");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A later solid stroke supplies color without overwriting the first gradient stroke's width.
|
||||
#[test]
|
||||
fn shape_toolbar_keeps_the_first_gradient_width_with_a_later_solid_stroke() {
|
||||
for kind in ["linear", "radial"] {
|
||||
let mut s = Session::default();
|
||||
s.execute("comp.new", json!({"width": 400, "height": 300, "duration": 2})).unwrap();
|
||||
let layer = s.execute("layer.newShape", json!({"kind": "rect"})).unwrap()["layer"].as_u64().unwrap();
|
||||
s.execute("shape.fillStroke", json!({"strokeType": kind, "strokeWidth": 9})).unwrap();
|
||||
s.execute("layer.addShapeItem", json!({"layer": layer, "group": "contents/group", "kind": "stroke"})).unwrap();
|
||||
for (field, value) in [("color", json!([0.0, 1.0, 0.0, 1.0])), ("width", json!(99))] {
|
||||
s.execute("prop.set", json!({"layer": layer, "path": format!("contents/group/contents/stroke/{field}"), "value": value})).unwrap();
|
||||
}
|
||||
for (time, width) in [(0, 9), (1, 17)] {
|
||||
s.execute("prop.addKey", json!({"layer": layer, "path": "contents/group/contents/gstroke/width", "time": time, "value": width})).unwrap();
|
||||
}
|
||||
for (time, width) in [(0.0, 9.0), (1.0, 17.0)] {
|
||||
s.set_time(effectcraft_time::Tick::from_seconds_f64(time));
|
||||
let displayed = s.execute("shape.fillStroke", json!({})).unwrap();
|
||||
assert_eq!(displayed["strokeWidth"].as_f64(), Some(width));
|
||||
assert_eq!(displayed["stroke"], json!([0.0, 1.0, 0.0, 1.0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A Wiggle Paths saved without Spatial Phase gets the property, at 0°, after Temporal Phase when
|
||||
/// the project opens.
|
||||
#[test]
|
||||
|
||||
@@ -52,6 +52,14 @@ fn export_and_reimport_timeline_commands() {
|
||||
let r = s.execute_checked("file.exportTimeline", json!({"comp": main, "path": tmp("NoExt"), "format": "otio", "prerender": "none"})).unwrap();
|
||||
assert!(r["path"].as_str().unwrap().ends_with("NoExt.otio"));
|
||||
assert!(s.execute_checked("file.exportTimeline", json!({"path": tmp("x.xml"), "prerender": "sometimes"})).is_err());
|
||||
// In the background (the export dialog, #2269): a job, then the same file and result.
|
||||
let path = tmp("Background.xml");
|
||||
let r = s.execute_checked("file.exportTimeline", json!({"comp": main, "path": path, "background": true})).unwrap();
|
||||
assert!(r["job"].is_string(), "{r}");
|
||||
s.wait_jobs();
|
||||
let done = s.job_log.last().unwrap();
|
||||
assert_eq!((done.status.as_str(), done.result["path"].as_str()), ("done", Some(path.as_str())));
|
||||
assert!(std::fs::read_to_string(&path).unwrap().contains("<xmeml"));
|
||||
assert!(s.execute_checked("file.importTimeline", json!({"path": "/nope/a.prproj"})).is_err());
|
||||
let formats = s.execute("file.timelineFormats", json!({})).unwrap();
|
||||
assert_eq!(formats.as_array().unwrap().len(), 6);
|
||||
|
||||
Reference in New Issue
Block a user