Seeking Help with AE Script: Merging Step 2 & Step 2.5 for Keyframe & Separated Dimensions Handling
Hey!
I'm working on an After Effects script that involves two main functions, Step 2 and Step 2.5, which I'm trying to merge. Individually, they work as intended, but integration has hit a snag, especially concerning properties with separated dimensions.
Step 2 (Universal Keyframe Detection & Expression Application): This part duplicates a layer and aims to apply a placeholder expression to any property with keyframes. However, it struggles specifically with properties that have separated dimensions (like "Position" into "X Position" and "Y Position"), not applying expressions as needed.
Step 2.5 (Handling Separated Dimensions and Orientation): Focused on properties with separated dimensions, this script part effectively duplicates a layer, clears keyframes, and applies expressions for separated dimensions and the "Orientation" property on 3D layers.
Challenge: The main issue arises when trying to integrate Step 2's broad keyframe handling with Step 2.5's specialized approach for separated dimensions and "Orientation." The expected seamless application of placeholder expressions to properties with separated dimensions (XYZ properties) in Step 2 does not occur, leading to errors like "property or a parent property is hidden."
Request for Advice: I need to create a unified script that can:
- Duplicate a layer,
- Identify and remove keyframes from all properties, focusing on ensuring that properties with separated dimensions and "Orientation" are included,
- Apply a universal placeholder expression to these properties without encountering errors.
I'd greatly appreciate any insights on how to address the issue with separated dimensions in Step 2 or any tips for successfully integrating these functionalities.
Thanks for your support and guidance!
The below is script Step 2.0
#target aftereffects
(function createComprehensiveDelaySetupUI(thisObj) {
var scriptName = "Complete Delay Setup with Keyframe Removal";
function createUI(thisObj) {
var panel = (thisObj instanceof Panel) ? thisObj : new Window("palette", scriptName, undefined, {resizeable:true});
if (!panel) return null;
panel.add("button", undefined, "Apply Keyframe Removal", {name: "applyBtn"}).onClick = function() {
applyKeyframeRemoval();
};
panel.onResizing = panel.onResize = function() { this.layout.resize(); };
panel.layout.layout(true);
return panel;
}
function applyKeyframeRemoval() {
var comp = app.project.activeItem;
if (!comp || !(comp instanceof CompItem)) {
alert("Please select a composition.");
return;
}
var selectedLayers = comp.selectedLayers;
if (selectedLayers.length !== 1) {
alert("Please select exactly one layer.");
return;
}
app.beginUndoGroup(scriptName);
var baseLayer = selectedLayers[0];
var delayLayer = baseLayer.duplicate();
delayLayer.name = baseLayer.name + " Delay 01";
removeAllKeyframesAndApplyExpression(delayLayer);
app.endUndoGroup();
}
function removeAllKeyframesAndApplyExpression(layer) {
traverseProperties(layer, function(prop) {
if (prop.numKeys > 0) {
for (var k = prop.numKeys; k >= 1; k--) {
prop.removeKey(k);
}
prop.expression = "\"Placeholder expression for removed keyframes\"";
}
});
}
function traverseProperties(group, callback) {
for (var i = 1; i <= group.numProperties; i++) {
var prop = group.property(i);
if (!prop) continue;
if (prop.propertyType === PropertyType.PROPERTY) {
callback(prop);
} else if (prop.propertyType === PropertyType.INDEXED_GROUP || prop.propertyType === PropertyType.NAMED_GROUP) {
traverseProperties(prop, callback); // Recurse into groups
}
}
}
var myScriptPal = createUI(thisObj);
if (myScriptPal instanceof Window) {
myScriptPal.center();
myScriptPal.show();
} else {
myScriptPal.layout.layout(true);
}
})();
The script below is Step 2.5
#target aftereffects
(function createDelaySetupUI(thisObj) {
var scriptName = "Comprehensive Delay Setup for Separated Dimensions and Orientation";
function createUI(thisObj) {
var panel = (thisObj instanceof Panel) ? thisObj : new Window("palette", scriptName, undefined, {resizeable: true});
if (!panel) return null;
panel.add("button", undefined, "Apply Delay Setup", {name: "applyBtn"}).onClick = function() {
applyExpressionsToSeparatedPropertiesAndOrientation();
};
panel.onResizing = panel.onResize = function() { this.layout.resize(); };
panel.layout.layout(true);
return panel;
}
function applyExpressionsToSeparatedPropertiesAndOrientation() {
var comp = app.project.activeItem;
if (!comp || !(comp instanceof CompItem)) {
alert("Please select a composition.");
return;
}
var selectedLayers = comp.selectedLayers;
if (selectedLayers.length !== 1) {
alert("Please select exactly one layer.");
return;
}
app.beginUndoGroup(scriptName);
var baseLayer = selectedLayers[0];
var delayLayer = baseLayer.duplicate();
delayLayer.name = baseLayer.name + " Delay 01";
clearAndApplyExpressionIfKeyframed(delayLayer, "position");
if (delayLayer.threeDLayer) {
clearAndApplyExpressionIfKeyframed(delayLayer, "rotation");
handleOrientationIfKeyframed(delayLayer); // Handle Orientation for 3D layers
}
app.endUndoGroup();
}
function clearAndApplyExpressionIfKeyframed(layer, propName) {
// This checks for separated dimensions in position and rotation and applies expressions if they had keyframes
var dimensions = propName === "position" ? ['X Position', 'Y Position', (layer.threeDLayer ? 'Z Position' : null)] :
['X Rotation', 'Y Rotation', 'Z Rotation'];
dimensions.forEach(function(dimName) {
if (dimName && layer.transform[dimName] && layer.transform[dimName].numKeys > 0) {
var dimProp = layer.transform[dimName];
while (dimProp.numKeys > 0) {
dimProp.removeKey(1);
}
dimProp.expression = "\"Delayed expression for " + dimName + "\"";
}
});
}
function handleOrientationIfKeyframed(layer) {
// This function specifically handles the Orientation property for 3D layers
var orientationProp = layer.transform.orientation;
if (orientationProp.numKeys > 0) {
while (orientationProp.numKeys > 0) {
orientationProp.removeKey(1);
}
orientationProp.expression = "\"Delayed expression for Orientation\"";
}
}
var myScriptPal = createUI(thisObj);
if (myScriptPal instanceof Window) {
myScriptPal.center();
myScriptPal.show();
} else {
myScriptPal.layout.layout(true);
}
})();
