mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-07 02:51:24 +00:00
Scripting: After Effects match names for 14 effects and Casts Shadows (#3122)
* #3113: use After Effects' effect match names in scripting Checked every row of crates/script/src/matchnames.rs against After Effects 26.2 (app.effects, canAddProperty and the layer property trees). Fourteen effect names were wrong: twelve did not exist (Pixel Motion Blur is ADBE OFMotionBlur, Shadow/Highlight is ADBE ShadowHighlight, Ellipse is ADBE ELLIPSE, ...), and Noise and Photo Filter pointed at After Effects' hidden obsolete versions (now ADBE Noise2 and ADBE PhotoFilterPS). A light's Casts Shadows is ADBE Casts Shadows. The old names stay accepted for lookup (addProperty, property(), and parameters such as "ADBE Noise-0001"), so existing scripts keep working; .matchName emits only the After Effects name. A names-only fixture of After Effects' first-party match names backs the new test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Scripting: match names that changed only in case still find effects and parameters property("ADBE Ellipse") and "ADBE Grow Bounds-0001" found nothing after the names became ADBE ELLIPSE / ADBE GROW BOUNDS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9408d70da0
commit
fee50dbfd1
4 files changed
+490
-17
No files matched your search
+108
-17
@@ -149,7 +149,7 @@ const LIGHT: &[(&str, &str)] = &[
|
||||
("falloff", "ADBE Light Falloff Type"),
|
||||
("radius", "ADBE Light Falloff Start"),
|
||||
("falloffDistance", "ADBE Light Falloff Distance"),
|
||||
("castsShadows", "ADBE Light Casts Shadows"),
|
||||
("castsShadows", "ADBE Casts Shadows"),
|
||||
("shadowDarkness", "ADBE Light Shadow Darkness"),
|
||||
("shadowDiffusion", "ADBE Light Shadow Diffusion"),
|
||||
];
|
||||
@@ -309,12 +309,12 @@ pub const EFFECTS: &[(&str, &str)] = &[
|
||||
("ec.color.changetocolor", "ADBE Change To Color"),
|
||||
("ec.color.colorama", "APC Colorama"),
|
||||
("ec.color.leavecolor", "ADBE Leave Color"),
|
||||
("ec.color.photofilter", "ADBE Photo Filter"),
|
||||
("ec.color.photofilter", "ADBE PhotoFilterPS"),
|
||||
("ec.color.vibrance", "ADBE Vibrance"),
|
||||
("ec.color.lumetri", "ADBE Lumetri"),
|
||||
("ec.color.shadowhighlight", "ADBE Shadow/Highlight"),
|
||||
("ec.color.selectivecolor", "ADBE Selective Color"),
|
||||
("ec.color.gammapedestalgain", "ADBE Gamma/Pedestal/Gain"),
|
||||
("ec.color.shadowhighlight", "ADBE ShadowHighlight"),
|
||||
("ec.color.selectivecolor", "ADBE SelectiveColor"),
|
||||
("ec.color.gammapedestalgain", "ADBE Gamma/Pedestal/Gain2"),
|
||||
("ec.color.equalize", "ADBE Equalize"),
|
||||
("ec.color.autocontrast", "ADBE AutoContrast"),
|
||||
("ec.color.autolevels", "ADBE AutoLevels"),
|
||||
@@ -351,15 +351,15 @@ pub const EFFECTS: &[(&str, &str)] = &[
|
||||
("ec.generate.grid", "ADBE Grid"),
|
||||
("ec.generate.checkerboard", "ADBE Checkerboard"),
|
||||
("ec.generate.circle", "ADBE Circle"),
|
||||
("ec.generate.ellipse", "ADBE Ellipse"),
|
||||
("ec.generate.ellipse", "ADBE ELLIPSE"),
|
||||
("ec.generate.cellpattern", "ADBE Cell Pattern"),
|
||||
("ec.generate.lensflare", "ADBE Lens Flare"),
|
||||
("ec.generate.vegas", "ADBE Vegas"),
|
||||
("ec.generate.writeon", "ADBE Write On"),
|
||||
("ec.generate.vegas", "APC Vegas"),
|
||||
("ec.generate.writeon", "ADBE Write-on"),
|
||||
("ec.generate.audiospectrum", "ADBE AudSpect"),
|
||||
("ec.generate.audiowaveform", "ADBE AudWave"),
|
||||
("ec.generate.beam", "ADBE Laser"),
|
||||
("ec.generate.radiowaves", "ADBE Radio Waves"),
|
||||
("ec.generate.radiowaves", "APC Radio Waves"),
|
||||
("ec.generate.scribble", "ADBE Scribble Fill"),
|
||||
("ec.generate.paintbucket", "ADBE Paint Bucket"),
|
||||
("ec.key.colorkey", "ADBE Color Key"),
|
||||
@@ -371,12 +371,12 @@ pub const EFFECTS: &[(&str, &str)] = &[
|
||||
("ec.key.spill", "ADBE Spill2"),
|
||||
("ec.matte.simplechoker", "ADBE Simple Choker"),
|
||||
("ec.matte.mattechoker", "ADBE Matte Choker"),
|
||||
("ec.noise.noise", "ADBE Noise"),
|
||||
("ec.noise.noise", "ADBE Noise2"),
|
||||
("ec.noise.fractal", "ADBE Fractal Noise"),
|
||||
("ec.noise.median", "ADBE Median"),
|
||||
("ec.noise.addgrain", "VISINF Grain Implant"),
|
||||
("ec.noise.dustscratches", "ADBE Dust & Scratches"),
|
||||
("ec.noise.turbulent", "ADBE Turbulent Noise"),
|
||||
("ec.noise.turbulent", "ADBE AIF Perlin Noise 3D"),
|
||||
("ec.perspective.dropshadow", "ADBE Drop Shadow"),
|
||||
("ec.perspective.radialshadow", "ADBE Radial Shadow"),
|
||||
("ec.perspective.bevelalpha", "ADBE Bevel Alpha"),
|
||||
@@ -401,32 +401,73 @@ pub const EFFECTS: &[(&str, &str)] = &[
|
||||
("ec.time.echo", "ADBE Echo"),
|
||||
("ec.time.posterizetime", "ADBE Posterize Time"),
|
||||
("ec.time.timedisplacement", "ADBE Time Displacement"),
|
||||
("ec.time.timedifference", "ADBE Time Difference"),
|
||||
("ec.time.pixelmotionblur", "ADBE Pixel Motion Blur"),
|
||||
("ec.time.timedifference", "ADBE Difference"),
|
||||
("ec.time.pixelmotionblur", "ADBE OFMotionBlur"),
|
||||
("ec.time.timewarp", "ADBE Timewarp"),
|
||||
("ec.transition.linearwipe", "ADBE Linear Wipe"),
|
||||
("ec.transition.radialwipe", "ADBE Radial Wipe"),
|
||||
("ec.transition.venetian", "ADBE Venetian Blinds"),
|
||||
("ec.transition.blockdissolve", "ADBE Block Dissolve"),
|
||||
("ec.transition.gradientwipe", "ADBE Gradient Wipe"),
|
||||
("ec.transition.iriswipe", "ADBE Iris Wipe"),
|
||||
("ec.transition.iriswipe", "ADBE IRIS_WIPE"),
|
||||
("ec.utility.applylut", "ADBE Apply Color LUT2"),
|
||||
("ec.utility.cineon", "ADBE Cineon Converter2"),
|
||||
("ec.utility.growbounds", "ADBE Grow Bounds"),
|
||||
("ec.utility.growbounds", "ADBE GROW BOUNDS"),
|
||||
];
|
||||
|
||||
fn find<'a>(t: &'a [(&str, &'a str)], k: &str) -> Option<&'a str> {
|
||||
t.iter().find(|(a, _)| *a == k).map(|(_, b)| *b)
|
||||
}
|
||||
|
||||
/// Effect match names that resolve to one of our effects but are not the name we emit (#3113):
|
||||
/// After Effects' hidden obsolete versions (`ADBE Noise`, `ADBE Photo Filter`), which its
|
||||
/// `addProperty` still accepts, and the names earlier EffectCraft versions emitted by mistake,
|
||||
/// which scripts written against them may use. Lookup only: `effect()` gives the `EFFECTS` name.
|
||||
const LEGACY_EFFECTS: &[(&str, &str)] = &[
|
||||
("ADBE Noise", "ec.noise.noise"),
|
||||
("ADBE Photo Filter", "ec.color.photofilter"),
|
||||
("ADBE Shadow/Highlight", "ec.color.shadowhighlight"),
|
||||
("ADBE Selective Color", "ec.color.selectivecolor"),
|
||||
("ADBE Gamma/Pedestal/Gain", "ec.color.gammapedestalgain"),
|
||||
("ADBE Vegas", "ec.generate.vegas"),
|
||||
("ADBE Write On", "ec.generate.writeon"),
|
||||
("ADBE Radio Waves", "ec.generate.radiowaves"),
|
||||
("ADBE Turbulent Noise", "ec.noise.turbulent"),
|
||||
("ADBE Time Difference", "ec.time.timedifference"),
|
||||
("ADBE Pixel Motion Blur", "ec.time.pixelmotionblur"),
|
||||
("ADBE Iris Wipe", "ec.transition.iriswipe"),
|
||||
];
|
||||
|
||||
/// AE match name of an effect id (falls back to the id).
|
||||
pub fn effect(id: &str) -> String {
|
||||
find(EFFECTS, id).map(str::to_string).unwrap_or_else(|| id.to_string())
|
||||
}
|
||||
|
||||
/// Our effect id for an AE effect match name.
|
||||
/// Our effect id for an AE effect match name (or a legacy one, see `LEGACY_EFFECTS`).
|
||||
pub fn effect_from_match(m: &str) -> Option<&'static str> {
|
||||
EFFECTS.iter().find(|(_, b)| b.eq_ignore_ascii_case(m)).map(|(a, _)| *a)
|
||||
EFFECTS
|
||||
.iter()
|
||||
.find(|(_, b)| b.eq_ignore_ascii_case(m))
|
||||
.map(|(a, _)| *a)
|
||||
.or_else(|| LEGACY_EFFECTS.iter().find(|(b, _)| b.eq_ignore_ascii_case(m)).map(|(_, a)| *a))
|
||||
}
|
||||
|
||||
/// The current match name for a legacy name a script may still use: a `LEGACY_EFFECTS` effect
|
||||
/// name, one of its parameters (`ADBE Noise-0001` → `ADBE Noise2-0001`), or the light's Casts
|
||||
/// Shadows under its earlier name. `None` when `name` is not a legacy name.
|
||||
pub fn current_name(name: &str) -> Option<String> {
|
||||
if name == "ADBE Light Casts Shadows" {
|
||||
return Some("ADBE Casts Shadows".into());
|
||||
}
|
||||
let legacy = |m: &str| LEGACY_EFFECTS.iter().find(|(b, _)| b.eq_ignore_ascii_case(m)).map(|(_, id)| effect(id));
|
||||
if let Some(m) = legacy(name) {
|
||||
return Some(m);
|
||||
}
|
||||
let (fx, n) = name.rsplit_once('-')?;
|
||||
if n.len() != 4 || !n.bytes().all(|b| b.is_ascii_digit()) {
|
||||
return None;
|
||||
}
|
||||
legacy(fx).map(|m| format!("{m}-{n}"))
|
||||
}
|
||||
|
||||
/// Our shape item kind (`layer.addShapeItem`) for an AE match name, our match id or a display
|
||||
@@ -573,6 +614,56 @@ pub fn node(chain: &[&Node]) -> String {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// After Effects 26.2's `app.effects` (first-party match names and categories), #3113.
|
||||
const AE_EFFECTS: &str = include_str!("../tests/fixtures/ae-effect-match-names.tsv");
|
||||
|
||||
fn ae_effects() -> Vec<(&'static str, &'static str)> {
|
||||
AE_EFFECTS.lines().filter(|l| !l.starts_with('#') && !l.is_empty()).map(|l| l.split_once('\t').unwrap_or((l, ""))).collect()
|
||||
}
|
||||
|
||||
/// #3113: every effect match name we emit is one After Effects lists (case matters: its
|
||||
/// `addProperty("ADBE Ellipse")` fails), and the current version, not a hidden obsolete one,
|
||||
/// unless After Effects only has that one.
|
||||
#[test]
|
||||
fn effect_names_are_after_effects_names() {
|
||||
let ae = ae_effects();
|
||||
// Obsolete in After Effects without a current replacement (Puppet is added by its tool).
|
||||
let only_obsolete = ["ADBE Color Key", "ADBE Luma Key", "ADBE FreePin3"];
|
||||
for (id, m) in EFFECTS {
|
||||
let Some((_, cat)) = ae.iter().find(|(n, _)| n == m) else { panic!("{id}: After Effects has no effect {m:?}") };
|
||||
assert!(!cat.contains("Obsolete") || only_obsolete.contains(m), "{id}: {m:?} is After Effects' obsolete version");
|
||||
}
|
||||
assert_eq!(effect("ec.noise.noise"), "ADBE Noise2");
|
||||
assert_eq!(effect("ec.time.pixelmotionblur"), "ADBE OFMotionBlur");
|
||||
assert_eq!(effect("ec.generate.ellipse"), "ADBE ELLIPSE");
|
||||
assert_eq!(effect("ec.color.photofilter"), "ADBE PhotoFilterPS");
|
||||
}
|
||||
|
||||
/// #3113: the names earlier versions emitted (and After Effects' obsolete ones) still resolve,
|
||||
/// but none shadows a current name.
|
||||
#[test]
|
||||
fn legacy_effect_names_still_resolve() {
|
||||
for (old, id) in LEGACY_EFFECTS {
|
||||
assert_eq!(effect_from_match(old), Some(*id));
|
||||
assert!(!EFFECTS.iter().any(|(_, m)| m.eq_ignore_ascii_case(old)), "{old} is a current name");
|
||||
assert_eq!(current_name(old), Some(effect(id)));
|
||||
}
|
||||
assert_eq!(effect_from_match("ADBE Pixel Motion Blur"), Some("ec.time.pixelmotionblur"));
|
||||
assert_eq!(current_name("ADBE Noise-0001").as_deref(), Some("ADBE Noise2-0001"));
|
||||
assert_eq!(current_name("ADBE Light Casts Shadows").as_deref(), Some("ADBE Casts Shadows"));
|
||||
assert_eq!(current_name("ADBE Noise2-0001"), None);
|
||||
assert_eq!(current_name("ADBE Noise-01"), None);
|
||||
assert_eq!(current_name("ADBE Gaussian Blur 2"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn light_casts_shadows_is_after_effects_name() {
|
||||
let n = |m: &str| Node::from(effectcraft_project::PropGroup::new(1, m, m));
|
||||
let (light, mat, casts) = (n("lightOptions"), n("materialOptions"), n("castsShadows"));
|
||||
assert_eq!(node(&[&light, &casts]), "ADBE Casts Shadows");
|
||||
assert_eq!(node(&[&mat, &casts]), "ADBE Casts Shadows");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn effect_names_round_trip() {
|
||||
for (id, m) in EFFECTS {
|
||||
|
||||
@@ -451,6 +451,9 @@ fn find_child<'a>(l: &'a Layer, g: &'a PropGroup, k: &str, depth: u8) -> Option<
|
||||
g.children
|
||||
.iter()
|
||||
.find(|n| chain_of(n) == k)
|
||||
.or_else(|| matchnames::current_name(k).and_then(|m| g.children.iter().find(|n| chain_of(n) == m)))
|
||||
// Match names that changed only in case (`ADBE Ellipse` is now `ADBE ELLIPSE`, #3113).
|
||||
.or_else(|| g.children.iter().find(|n| chain_of(n).eq_ignore_ascii_case(k)))
|
||||
.or_else(|| g.children.iter().find(|n| n.name() == k))
|
||||
.or_else(|| g.children.iter().find(|n| n.match_id() == k))
|
||||
.or_else(|| g.children.iter().find(|n| n.name().eq_ignore_ascii_case(k)))
|
||||
|
||||
@@ -139,6 +139,42 @@ fn fill_parameters_use_documented_match_names() {
|
||||
assert_eq!(o.result, json!("Color|All Masks|ADBE Fill-0007"));
|
||||
}
|
||||
|
||||
/// #3113: effects carry After Effects' match names (`ADBE Noise2`, `ADBE OFMotionBlur`); the
|
||||
/// names earlier versions emitted still add and find the effect and its parameters.
|
||||
#[test]
|
||||
fn effects_use_after_effects_match_names_and_accept_legacy_ones() {
|
||||
let mut s = session();
|
||||
let o = ok(
|
||||
&mut s,
|
||||
r#"var c = app.project.items.addComp("N", 100, 100, 1, 1, 12); var l = c.layers.addSolid([1,1,1], "S", 100, 100, 1);
|
||||
var fx = l.property("ADBE Effect Parade");
|
||||
var a = fx.addProperty("ADBE Noise2"), b = fx.addProperty("ADBE Noise"), m = fx.addProperty("ADBE OFMotionBlur");
|
||||
var p = fx.addProperty("ADBE Pixel Motion Blur");
|
||||
[a.matchName, b.matchName, m.matchName, p.matchName, a.property("ADBE Noise2-0001").name,
|
||||
fx.property("ADBE Noise").matchName, a.property("ADBE Noise-0001").name, fx.numProperties]"#,
|
||||
);
|
||||
assert_eq!(
|
||||
o.result,
|
||||
json!(["ADBE Noise2", "ADBE Noise2", "ADBE OFMotionBlur", "ADBE OFMotionBlur", "Amount of Noise", "ADBE Noise2", "Amount of Noise", 4])
|
||||
);
|
||||
}
|
||||
|
||||
/// #3113: names that changed only in case (`ADBE Ellipse` → `ADBE ELLIPSE`, `ADBE Grow Bounds` →
|
||||
/// `ADBE GROW BOUNDS`) still find the effect and its parameters.
|
||||
#[test]
|
||||
fn match_names_that_changed_case_still_find_effects_and_parameters() {
|
||||
let mut s = session();
|
||||
let o = ok(
|
||||
&mut s,
|
||||
r#"var c = app.project.items.addComp("E", 100, 100, 1, 1, 12); var l = c.layers.addSolid([1,1,1], "S", 100, 100, 1);
|
||||
var fx = l.property("ADBE Effect Parade");
|
||||
var e = fx.addProperty("ADBE Ellipse"), g = fx.addProperty("ADBE Grow Bounds");
|
||||
[e.matchName, fx.property("ADBE Ellipse").matchName, e.property("ADBE Ellipse-0001").matchName,
|
||||
g.matchName, fx.property("ADBE Grow Bounds").matchName, g.property("ADBE Grow Bounds-0001").matchName]"#,
|
||||
);
|
||||
assert_eq!(o.result, json!(["ADBE ELLIPSE", "ADBE ELLIPSE", "ADBE ELLIPSE-0001", "ADBE GROW BOUNDS", "ADBE GROW BOUNDS", "ADBE GROW BOUNDS-0001"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stroke_parameters_use_after_effects_match_names() {
|
||||
// #1405: `ADBE Stroke-0002` is Stroke's Color in After Effects, not All Masks.
|
||||
|
||||
Reference in new issue
Block a user