selectedProperties on Effects

Find out why the . goes before the /

Moderator: Paul Tuersley

Post Reply
jordanwade33
Posts: 16
Joined: December 8th, 2014, 11:11 pm

December 10th, 2014, 1:06 am

I'm really new to scripting and need some help with what I've been working on lately. I've been using javascript to make buttons for ft-toolbar to try and speed up my workflow on things. I've been mostly successful until now.

My goal - Create a script that takes a selected property on a layer, add two sliders to the layer, and link a wiggle expression for the selected property to the two sliders.

What I came up with works just fine for any of the native transform properties, however I've found that it doesn't work on properties that belong to effects. Does anyone have any ideas? Does AE see selectedProperties on effects as actual properties or something else?

Here's the script as it currently stands:
var comp = app.project.activeItem;
app.beginUndoGroup("Add Wiggle");
{
var propertyTest = comp.selectedProperties[0];
if(propertyTest instanceof Property){
for(var j = 0 ; j < comp.selectedLayers.length ; j++){
    var layer = comp.selectedLayers[j];
for(var i = 0 ; i < layer.selectedProperties.length ; i++){
    var property = layer.selectedProperties;
    var fx = layer.Effects.addProperty('ADBE Slider Control');
    fx.name = 'Wiggle Frequency';
    fx.property(String('ADBE Slider Control-0001')).setValue([24]);
    var fx2 = layer.Effects.addProperty('ADBE Slider Control');
    fx2.name = 'Wiggle Amount';
    fx2.property(String('ADBE Slider Control-0001')).setValue([5]);

    var wiggleExpression ="wiggle(effect(\"Wiggle Frequency\")(\"Slider\"),effect(\"Wiggle Amount\")(\"Slider\"))";
    property.expression = wiggleExpression;
    }
}
}
else {
    alert("Please select a property to wiggle.");
    }
}
beginUndoGroup
Posts: 81
Joined: November 27th, 2012, 6:41 am

December 10th, 2014, 1:40 am

There are two things:
(1) selecting properties most of the time also selects their renamable parents.
(2) After Effects always sorts the array comp.selectedProperties (and also layer.selectedProperties) and the order is (apparently) the lexicographic order on propertyIndex.

So if you select one property inside an effect, AE also selects the effect itself (because it is renamable) and that effect will appear before the initially selected property (because of the auto ordering).

For that reason the property that the user originally selected is most probably comp.selectedProperties[comp.selectedProperties.length-1] (ie the last one) and not comp.selectedProperties[0].
You won't have that kind of problem for transform properties (or also properties in "Layer Styles", "Camera Options", etc) because there are "unique" (only one instance per layer, no renamable parent).

Xavier
jordanwade33
Posts: 16
Joined: December 8th, 2014, 11:11 pm

December 10th, 2014, 3:00 pm

Thanks for the help. That's very good to know!

I've stripped the script down to the bare bones to troubleshoot and I'm learning a lot. I can now get the script to read the property from the effect and apply an expression to that property if I omit the part that adds the two sliders. Is it possible that after the script adds the two sliders, the selectedProperties array is now changed because there's more properties on the layer? If I shift the part that adds the two sliders after setting the expression, it will set the expression (the problem here is that I want to link the expression to the sliders). Is there any way around this?


Bare bones script:
var myComp = app.project.activeItem;
var myLayer = comp.selectedLayers[0];
for(var i = 0 ; i < myLayer.selectedProperties.length ; i++){
    var myProperty = myLayer.selectedProperties;
   var fx = myLayer.Effects.addProperty('ADBE Slider Control'); //If I omit this line or move it after the bottom line, it will successfully set an expression for an effect, but I need to link the slider and the expression
    var wiggleExpression ="wiggle(10,20)";
    myProperty.expression = wiggleExpression;
 }
beginUndoGroup
Posts: 81
Joined: November 27th, 2012, 6:41 am

December 11th, 2014, 2:45 am

Hi,

yes adding a slider to the layer's effects will invalidate any existing reference to properties inside "effect" (but not to anything else), plus it destroyes the layer's selectedProperties state.
It's a pain actually.
You have to save some data to be able to recover those selected properties and apply the expression to them.
Here's a way to do it, not sure whether it is the most efficient way but it works.

Xavier

Code: Select all

var myComp = app.project.activeItem;
if (myComp instanceof CompItem && myComp.selectedProperties.length>0){
	app.beginUndoGroup("add wiggle");
	for (var i=0; i<myComp.selectedLayers.length; i++) processLayer(myComp.selectedLayers[i]);
	app.endUndoGroup();
	};

function processLayer(layer){
	
	var selProps = layer.selectedProperties, N=selProps.length, n, prop, saves=[];
	var slider, sliderName = "Super Controller", wiggleExpression = "wiggle(effect(\""+sliderName+"\").slider, 100);";
	
	// run through all selected properties that accept expressions
	for (n=0; n<N; n++){
		prop = selProps[n];
		// if the property does not accept expressions, skip it
		if (prop.canSetExpression){
			if (prop.propertyDepth>2 && prop.propertyGroup(prop.propertyDepth-2).isEffect){
				// if prop is a property inside an effect, adding a slider to the layer will invalidate that property
				// so: save the sequence of propertyIndices that lead to the property, to be able to find it back
				saves.push(getPropIndexPath(prop));
				}
			else{
				// the property won't be invalidated
				saves.push(prop);
				};
			};
		};
	// redefine N
	N=saves.length;
	if (N>0){
		// add a slider and set the expressions
		
		slider = layer.effect.addProperty("ADBE Slider Control");
		slider.name = sliderName;
		slider.property(1).setValue(1337);
		
		for (n=0; n<N; n++){
			prop = saves[n] instanceof Property ? saves[n] : getPropFromIndexPath(layer, saves[n]);
			try{prop.expression = wiggleExpression;}catch(e){alert(e);};
			};
		};
	return;
	};
	
function getPropIndexPath(prop){
	// returns an array of indices of length 1+prop.propertyDepth
	// entry j (j>0) corresponds to a property of propertyDepth j
	// entry 0 is filled in with the containing layer index but not used
	var indices=  [];
	while(prop.propertyDepth>0) {indices.unshift(prop.propertyIndex); prop = prop.parentProperty;};
	indices.unshift(prop.index);
	return indices;
	};
function getPropFromIndexPath(layer, indexPath){
	// returns a prop or null
	var prop = layer, n=0, N=indexPath.length;
	while (++n<N && (prop = prop.property(indexPath[n])));	
	return prop;
	};

jordanwade33
Posts: 16
Joined: December 8th, 2014, 11:11 pm

December 12th, 2014, 12:19 am

Hi Xavier,

Thanks for your help!

It is a bit of a pain how that works. I thought I was saving some data by just storing the array as a variable, but I guess it's way more complicated than that. Thanks for the comments on the script, it helps me understand what's going on. Thank you so much!

-Jordan
Post Reply