mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-24 01:57:53 -06:00
Ease Presets: skip roving keyframes and say so, rather than reporting them eased (#1329)
A roving key passes speed straight through: the engine keeps both its sides linear whenever the keys are edited (retime_roving), so an ease set on one never plays. The panel still counted it as eased, so a selected roving key showed "Eased 1 keyframe(s)" and stayed linear. The previous pair-only panel did the same to a pair holding a roving key. Selected roving keys are now left out of the work and counted. The status names how many were skipped and that turning off Roving lets them ease; when nothing else was selected, nothing changes and no undo step opens. Fixed keys next to a roving key still ease.
This commit is contained in:
@@ -378,6 +378,41 @@ fn the_side_not_eased_keeps_playing_hold_and_auto_bezier() {
|
|||||||
assert!(!close(eased_rot.value_at(at(0.25)).as_f64(), rot.value_at(at(0.25)).as_f64(), 1e-3), "the in side changed");
|
assert!(!close(eased_rot.value_at(at(0.25)).as_f64(), rot.value_at(at(0.25)).as_f64(), 1e-3), "the in side changed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Roving keys pass speed straight through (the engine keeps their sides linear), so an ease set
|
||||||
|
/// on one wouldn't play: they are skipped and the panel says so, rather than reporting them eased.
|
||||||
|
#[test]
|
||||||
|
fn roving_keyframes_are_skipped_and_reported() {
|
||||||
|
let store = Arc::new(MemoryConfig::default());
|
||||||
|
let mut s = session(&store);
|
||||||
|
let l = setup(&mut s);
|
||||||
|
animate(&mut s, l, "transform/position", &[(0.0, json!([0, 0, 0])), (1.0, json!([100, 0, 0])), (2.0, json!([200, 0, 0]))]);
|
||||||
|
animate(&mut s, l, "transform/opacity", &[(0.0, json!(0)), (1.0, json!(100)), (3.0, json!(20))]);
|
||||||
|
ok(&mut s, "app.project.activeItem.layer(1).transform.position.setRovingAtKey(2, true)");
|
||||||
|
let pos = prop(&s, l, "transform/position");
|
||||||
|
assert!(pos.keys[1].roving);
|
||||||
|
let roving_at = pos.keys[1].time.seconds();
|
||||||
|
let win = open_panel(&mut s);
|
||||||
|
// Only the roving key: nothing changes, no undo step.
|
||||||
|
select_keys(&mut s, l, &[("transform/position", roving_at)]);
|
||||||
|
let steps = s.history.undo.len();
|
||||||
|
set(&mut s, win, "presets", json!("Ease In-Out"));
|
||||||
|
assert_eq!(status(&mut s, win), "Nothing eased: skipped 1 roving keyframe(s); turn off Roving to ease them");
|
||||||
|
assert_eq!(s.history.undo.len(), steps);
|
||||||
|
assert_eq!(prop(&s, l, "transform/position").keys, pos.keys);
|
||||||
|
// With an ordinary key: that one is eased, the roving one is counted as skipped.
|
||||||
|
select_keys(&mut s, l, &[("transform/position", roving_at), ("transform/opacity", 1.0)]);
|
||||||
|
set(&mut s, win, "presets", json!("Linear"));
|
||||||
|
set(&mut s, win, "presets", json!("Ease In-Out"));
|
||||||
|
assert_eq!(
|
||||||
|
status(&mut s, win),
|
||||||
|
"Eased 1 keyframe(s) (In-Out) with \u{201c}Ease In-Out\u{201d}; skipped 1 roving keyframe(s); turn off Roving to ease them"
|
||||||
|
);
|
||||||
|
let k = &prop(&s, l, "transform/position").keys[1];
|
||||||
|
assert!(k.roving && k.in_interp == Interp::Linear && k.out_interp == Interp::Linear, "{k:?}");
|
||||||
|
let op = prop(&s, l, "transform/opacity");
|
||||||
|
assert!(eased(op.keys[1].in_ease[0], 0.0, 0.5) && eased(op.keys[1].out_ease[0], 0.0, 0.5));
|
||||||
|
}
|
||||||
|
|
||||||
/// On one key, each side scales to its own segment: per dimension, and along the motion path.
|
/// On one key, each side scales to its own segment: per dimension, and along the motion path.
|
||||||
#[test]
|
#[test]
|
||||||
fn single_keys_scale_each_side_to_its_dimensions_and_motion_path() {
|
fn single_keys_scale_each_side_to_its_dimensions_and_motion_path() {
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
// its outgoing side with the curve's out side, In-Out both: a side is eased when it has a
|
// its outgoing side with the curve's out side, In-Out both: a side is eased when it has a
|
||||||
// neighbouring key (selected or not; the neighbour isn't changed), its speeds scaled to the
|
// neighbouring key (selected or not; the neighbour isn't changed), its speeds scaled to the
|
||||||
// segment on that side. So a selected pair gets the whole curve, and a single key works too.
|
// segment on that side. So a selected pair gets the whole curve, and a single key works too.
|
||||||
|
// Roving keys are skipped: they pass speed straight through (their sides stay linear), so an ease
|
||||||
|
// set on them wouldn't play.
|
||||||
//
|
//
|
||||||
// User presets are kept with app.settings (section "Ease Presets", key "userPresets") as JSON:
|
// User presets are kept with app.settings (section "Ease Presets", key "userPresets") as JSON:
|
||||||
// {"version": 1, "presets": [{"name": …, "curve": {"outInfluence", "outSpeed", "inInfluence",
|
// {"version": 1, "presets": [{"name": …, "curve": {"outInfluence", "outSpeed", "inInfluence",
|
||||||
@@ -197,16 +199,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ease the sides `mode` names ("In", "Out" or "In-Out") of every selected keyframe with `c`, in
|
// Ease the sides `mode` names ("In", "Out" or "In-Out") of every selected keyframe with `c`, in
|
||||||
// one undo step: a side with a neighbouring key, scaled to the segment on that side. Returns the
|
// one undo step: a side with a neighbouring key, scaled to the segment on that side. Returns
|
||||||
// number of keyframes eased.
|
// {eased, roving}: the number of keyframes eased, and of selected roving keyframes skipped.
|
||||||
function applyCurve(c, mode) {
|
function applyCurve(c, mode) {
|
||||||
var keyed = selectedKeyed(), sides = [], keys = 0;
|
var keyed = selectedKeyed(), sides = [], keys = 0, roving = 0;
|
||||||
var doIn = mode !== "Out", doOut = mode !== "In";
|
var doIn = mode !== "Out", doOut = mode !== "In";
|
||||||
// Every side and its eases first: the segments are measured before any key changes.
|
// Every side and its eases first: the segments are measured before any key changes.
|
||||||
for (var i = 0; i < keyed.length; i++) {
|
for (var i = 0; i < keyed.length; i++) {
|
||||||
var p = keyed[i].prop;
|
var p = keyed[i].prop;
|
||||||
for (var j = 0; j < keyed[i].keys.length; j++) {
|
for (var j = 0; j < keyed[i].keys.length; j++) {
|
||||||
var k = keyed[i].keys[j], before = sides.length;
|
var k = keyed[i].keys[j], before = sides.length;
|
||||||
|
if (p.keyRoving(k)) {
|
||||||
|
roving++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
var s = doIn && k > 1 ? slopes(p, k - 1) : null;
|
var s = doIn && k > 1 ? slopes(p, k - 1) : null;
|
||||||
if (s) sides.push({ prop: p, key: k, out: false, eases: eases(s, c.inSpeed, c.inInfluence) });
|
if (s) sides.push({ prop: p, key: k, out: false, eases: eases(s, c.inSpeed, c.inInfluence) });
|
||||||
s = doOut && k < p.numKeys ? slopes(p, k) : null;
|
s = doOut && k < p.numKeys ? slopes(p, k) : null;
|
||||||
@@ -214,14 +220,14 @@
|
|||||||
if (sides.length > before) keys++;
|
if (sides.length > before) keys++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!sides.length) return 0;
|
if (!sides.length) return { eased: 0, roving: roving };
|
||||||
app.beginUndoGroup("Apply Ease Preset");
|
app.beginUndoGroup("Apply Ease Preset");
|
||||||
try {
|
try {
|
||||||
for (var e = 0; e < sides.length; e++) easeSide(sides[e].prop, sides[e].key, sides[e].out, sides[e].eases);
|
for (var e = 0; e < sides.length; e++) easeSide(sides[e].prop, sides[e].key, sides[e].out, sides[e].eases);
|
||||||
} finally {
|
} finally {
|
||||||
app.endUndoGroup();
|
app.endUndoGroup();
|
||||||
}
|
}
|
||||||
return keys;
|
return { eased: keys, roving: roving };
|
||||||
}
|
}
|
||||||
|
|
||||||
// The curve between the first selected pair of keyframes (measured on the dimension that
|
// The curve between the first selected pair of keyframes (measured on the dimension that
|
||||||
@@ -378,9 +384,11 @@
|
|||||||
|
|
||||||
function apply(c, what) {
|
function apply(c, what) {
|
||||||
var m = mode.selection ? mode.selection.text : "In-Out";
|
var m = mode.selection ? mode.selection.text : "In-Out";
|
||||||
var n = applyCurve(c, m);
|
var r = applyCurve(c, m), n = r.eased;
|
||||||
var side = m === "In" ? "before it" : m === "Out" ? "after it" : "next to it";
|
var side = m === "In" ? "before it" : m === "Out" ? "after it" : "next to it";
|
||||||
status.text = n > 0 ? "Eased " + n + " keyframe(s) (" + m + ") with " + what : "Select keyframes with another keyframe " + side;
|
var skipped = r.roving > 0 ? "skipped " + r.roving + " roving keyframe(s); turn off Roving to ease them" : "";
|
||||||
|
if (n > 0) status.text = "Eased " + n + " keyframe(s) (" + m + ") with " + what + (skipped ? "; " + skipped : "");
|
||||||
|
else status.text = skipped ? "Nothing eased: " + skipped : "Select keyframes with another keyframe " + side;
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user