mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-30 04:57:54 -06:00
Merge pull request #2539 from nanox333/fix/ease-preset-linear
Ease Presets: restore Linear interpolation and diamond keyframes
This commit is contained in:
@@ -608,3 +608,37 @@ fn presets_saved_by_the_old_core_panel_carry_over() {
|
||||
let win = open_panel(&mut s);
|
||||
assert_eq!(control(&mut s, win, "presets")["items"].as_array().unwrap().len(), 12);
|
||||
}
|
||||
|
||||
/// #2465: the Linear preset sets Linear interpolation on every side it applies to, including the
|
||||
/// unused first-in and last-out sides; editing its speeds makes a Bezier curve.
|
||||
#[test]
|
||||
fn the_linear_preset_sets_linear_interpolation_and_edited_speeds_give_bezier() {
|
||||
let store = Arc::new(MemoryConfig::default());
|
||||
let mut s = session(&store);
|
||||
let l = setup(&mut s);
|
||||
animate(&mut s, l, "transform/opacity", &[(0.0, json!(0)), (1.0, json!(100)), (3.0, json!(20))]);
|
||||
s.execute("prop.select", json!({"layer": l, "path": "transform/opacity"})).unwrap();
|
||||
let win = open_panel(&mut s);
|
||||
// Start from Bezier keys so Linear has something to restore.
|
||||
ok(
|
||||
&mut s,
|
||||
r#"var p = app.project.activeItem.layer(1).transform.opacity, K = KeyframeInterpolationType;
|
||||
for (var i = 1; i <= p.numKeys; i++) p.setInterpolationTypeAtKey(i, K.BEZIER, K.BEZIER);"#,
|
||||
);
|
||||
let op = prop(&s, l, "transform/opacity");
|
||||
for (i, k) in op.keys.iter().enumerate() {
|
||||
assert_eq!((k.in_interp, k.out_interp), (Interp::Bezier, Interp::Bezier), "initial key {i}");
|
||||
}
|
||||
let steps = s.history.undo.len();
|
||||
set(&mut s, win, "presets", json!("Linear"));
|
||||
assert_eq!(s.history.undo.len(), steps + 1, "one undo step");
|
||||
let op = prop(&s, l, "transform/opacity");
|
||||
for (i, k) in op.keys.iter().enumerate() {
|
||||
assert_eq!((k.in_interp, k.out_interp), (Interp::Linear, Interp::Linear), "key {i}");
|
||||
}
|
||||
// Edited speeds make a custom Bezier curve even though Linear is still the selected preset.
|
||||
select_keys(&mut s, l, &[("transform/opacity", 1.0)]);
|
||||
apply_typed(&mut s, win, "In-Out", [33.0, 2.0, 33.0, 2.0]);
|
||||
let k = &prop(&s, l, "transform/opacity").keys[1];
|
||||
assert_eq!((k.in_interp, k.out_interp), (Interp::Bezier, Interp::Bezier));
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
// 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.
|
||||
// The built-in Linear preset uses Linear interpolation, including on unused endpoint sides
|
||||
// so a fully linear key has a diamond icon; the other presets use Bezier.
|
||||
//
|
||||
// User presets are kept with app.settings (section "Ease Presets", key "userPresets") as JSON:
|
||||
// {"version": 1, "presets": [{"name": …, "curve": {"outInfluence", "outSpeed", "inInfluence",
|
||||
@@ -29,6 +31,7 @@
|
||||
var MAX_NAME = 64;
|
||||
var MIN_INFLUENCE = 0.1;
|
||||
var MAX_SPEED = 100;
|
||||
var LINEAR = KeyframeInterpolationType.LINEAR;
|
||||
var BEZIER = KeyframeInterpolationType.BEZIER;
|
||||
var HOLD = KeyframeInterpolationType.HOLD;
|
||||
|
||||
@@ -178,16 +181,16 @@
|
||||
|
||||
// Ease one side of key `k` with `eases`, leaving its other side as it plays (auto-Bezier and
|
||||
// linear sides keep their shape; a continuous key stops being continuous, its sides now differ).
|
||||
function easeSide(p, k, out, eases) {
|
||||
function easeSide(p, k, out, eases, interpolation) {
|
||||
var inType = p.keyInInterpolationType(k), outType = p.keyOutInterpolationType(k);
|
||||
var inEase = p.keyInTemporalEase(k), outEase = p.keyOutTemporalEase(k);
|
||||
if (p.keyTemporalContinuous(k)) p.setTemporalContinuousAtKey(k, false);
|
||||
if (out) {
|
||||
p.setTemporalEaseAtKey(k, inEase, eases);
|
||||
p.setInterpolationTypeAtKey(k, inType, BEZIER);
|
||||
p.setInterpolationTypeAtKey(k, inType, interpolation);
|
||||
} else {
|
||||
p.setTemporalEaseAtKey(k, eases, outEase);
|
||||
p.setInterpolationTypeAtKey(k, BEZIER, outType);
|
||||
p.setInterpolationTypeAtKey(k, interpolation, outType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,7 +204,7 @@
|
||||
// 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
|
||||
// {eased, roving}: the number of keyframes eased, and of selected roving keyframes skipped.
|
||||
function applyCurve(c, mode) {
|
||||
function applyCurve(c, mode, interpolation) {
|
||||
var keyed = selectedKeyed(), sides = [], keys = 0, roving = 0;
|
||||
var doIn = mode !== "Out", doOut = mode !== "In";
|
||||
// Every side and its eases first: the segments are measured before any key changes.
|
||||
@@ -214,16 +217,16 @@
|
||||
continue;
|
||||
}
|
||||
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 || (doIn && interpolation === LINEAR)) sides.push({ prop: p, key: k, out: false, eases: s ? eases(s, c.inSpeed, c.inInfluence) : p.keyInTemporalEase(k) });
|
||||
s = doOut && k < p.numKeys ? slopes(p, k) : null;
|
||||
if (s) sides.push({ prop: p, key: k, out: true, eases: eases(s, c.outSpeed, c.outInfluence) });
|
||||
if (s || (doOut && interpolation === LINEAR)) sides.push({ prop: p, key: k, out: true, eases: s ? eases(s, c.outSpeed, c.outInfluence) : p.keyOutTemporalEase(k) });
|
||||
if (sides.length > before) keys++;
|
||||
}
|
||||
}
|
||||
if (!sides.length) return { eased: 0, roving: roving };
|
||||
app.beginUndoGroup("Apply Ease Preset");
|
||||
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, interpolation);
|
||||
} finally {
|
||||
app.endUndoGroup();
|
||||
}
|
||||
@@ -384,7 +387,10 @@
|
||||
|
||||
function apply(c, what) {
|
||||
var m = mode.selection ? mode.selection.text : "In-Out";
|
||||
var r = applyCurve(c, m), n = r.eased;
|
||||
// Linear is an interpolation type too, so its keys get diamond icons. Edited speeds
|
||||
// make a custom Bezier curve even while Linear remains selected in the list.
|
||||
var interpolation = list.selection && list.selection.text === "Linear" && c.outSpeed === 1 && c.inSpeed === 1 ? LINEAR : BEZIER;
|
||||
var r = applyCurve(c, m, interpolation), n = r.eased;
|
||||
var side = m === "In" ? "before it" : m === "Out" ? "after it" : "next to it";
|
||||
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 : "");
|
||||
|
||||
Reference in New Issue
Block a user