mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-01 05:58:19 -06:00
Engine: a typed prop.set can move follow components by each layer's own change (#2485)
This commit is contained in:
@@ -300,7 +300,9 @@ pub(crate) fn proportional_share(index: usize, count: usize) -> f64 {
|
||||
/// `prop.set`; with `selected`, also on the other selected layers: the same value, or with
|
||||
/// `offset` (a scrub) the same change from each layer's own value. A typed value with `dims`
|
||||
/// (the components edited, e.g. `[2]` for Z) sets only those components on the other layers,
|
||||
/// which keep the rest of their own value (#1434). With `offset` and
|
||||
/// which keep the rest of their own value (#1434); `follow` components move on each layer by the
|
||||
/// change its first typed component takes (linked Audio Levels keep each layer's balance, #2485).
|
||||
/// With `offset` and
|
||||
/// `proportional` (Proportional Scrubbing) each layer takes [`proportional_share`] of the change
|
||||
/// by its place in the selection order, the scrubbed layer too. One undo step (with `merge`,
|
||||
/// one that a continuing scrub keeps folding into).
|
||||
@@ -325,10 +327,18 @@ fn set_selected(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
(None, Some(a), Some(_)) if !scalar => Some(a.iter().filter_map(Value::as_u64).filter_map(|d| usize::try_from(d).ok()).collect()),
|
||||
_ => None,
|
||||
};
|
||||
let follow: Vec<usize> =
|
||||
p.get("follow").and_then(Value::as_array).into_iter().flatten().filter_map(Value::as_u64).filter_map(|d| usize::try_from(d).ok()).collect();
|
||||
let typed = |s: &Session, q: &mut Value, olid: LayerId, ouid: Uid| {
|
||||
if let (Some(dims), Some(n)) = (&dims, &new)
|
||||
&& let Some(mut c) = current_components(s, cid, olid, ouid)
|
||||
{
|
||||
let change = dims.first().and_then(|&d| Some(n.get(d)? - c.get(d)?)).unwrap_or(0.0);
|
||||
for &d in &follow {
|
||||
if let Some(slot) = c.get_mut(d) {
|
||||
*slot += change;
|
||||
}
|
||||
}
|
||||
for &d in dims {
|
||||
if let (Some(slot), Some(v)) = (c.get_mut(d), n.get(d)) {
|
||||
*slot = *v;
|
||||
@@ -1310,7 +1320,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Set Property Value",
|
||||
[],
|
||||
None,
|
||||
"{layer?, path|prop, value, time? (layer s by default), timeBase?: layer|comp, merge?, selected? (also the other selected layers), offset? (them by the same change), proportional? (with offset: Proportional Scrubbing, each layer by its share in selection order, the first none, the last all), dims? (with selected, a typed value: the components edited, e.g. [2] for Z; the other layers keep the rest of their own value)}",
|
||||
"{layer?, path|prop, value, time? (layer s by default), timeBase?: layer|comp, merge?, selected? (also the other selected layers), offset? (them by the same change), proportional? (with offset: Proportional Scrubbing, each layer by its share in selection order, the first none, the last all), dims? (with selected, a typed value: the components edited, e.g. [2] for Z; the other layers keep the rest of their own value), follow? (with dims: components that move on each other layer by the change its first typed component takes)}",
|
||||
has_layers,
|
||||
set_selected
|
||||
),
|
||||
|
||||
@@ -1430,3 +1430,23 @@ fn a_typed_component_applies_only_that_component_to_the_selected_layers() {
|
||||
s.execute("prop.set", json!({"layer": a, "prop": uid, "value": [1.0, 2.0, 3.0], "selected": true})).unwrap();
|
||||
assert_eq!(pos(&s, c), vec![1.0, 2.0, 3.0]);
|
||||
}
|
||||
|
||||
/// #2485: linked Audio Levels typed with several layers selected: each takes the typed Left, and
|
||||
/// its Right moves by its own change, so every layer keeps its own stereo balance.
|
||||
#[test]
|
||||
fn a_typed_component_moves_its_follow_components_by_each_layers_own_change() {
|
||||
let mut s = Session::default();
|
||||
s.execute("comp.new", json!({"name": "Sound", "width": 64, "height": 36, "duration": 4})).unwrap();
|
||||
s.execute("comp.new", json!({"name": "Main", "width": 320, "height": 180, "duration": 4})).unwrap();
|
||||
let a = s.execute("layer.addItem", json!({"item": "Sound"})).unwrap()["layer"].as_u64().unwrap();
|
||||
let b = s.execute("layer.addItem", json!({"item": "Sound"})).unwrap()["layer"].as_u64().unwrap();
|
||||
s.execute("prop.set", json!({"layer": a, "path": "audio/levels", "value": [-6.0, -12.0]})).unwrap();
|
||||
s.execute("prop.set", json!({"layer": b, "path": "audio/levels", "value": [-20.0, -30.0]})).unwrap();
|
||||
s.execute("layer.select", json!({"layers": [a, b]})).unwrap();
|
||||
let uid = prop(&s, a, "audio/levels").uid;
|
||||
let undo = s.history.undo.len();
|
||||
s.execute_checked("prop.set", json!({"layer": a, "prop": uid, "value": [-3.0, -9.0], "selected": true, "dims": [0], "follow": [1]})).unwrap();
|
||||
assert_eq!(prop(&s, a, "audio/levels").value.components(), vec![-3.0, -9.0]);
|
||||
assert_eq!(prop(&s, b, "audio/levels").value.components(), vec![-3.0, -13.0], "B rises 17 dB on both channels");
|
||||
assert_eq!(s.history.undo.len(), undo + 1, "one undo step");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user