Page 1 of 1

how to make arrayOfValues parameter for setValuesAtTimes?

Posted: March 4th, 2014, 7:36 pm
by danceman
Hello AEnhancersComm.,

i have a few, small scripts that use the setValueAtTime method successfully placing a keyframe on the Layers existing maskPath, but as you could see in the scripts below, using setValueSatTimeS results in errors ie: "Array is not of the correct type", "Value is not an Array" or "Null is not an object", depending on,..

A) which variable is used as Value parameter IE:
.maskPath;
.maskPath.value;
.maskPath.value.vertices

B) which type of array is used as Value parameter as in:

var addTheseKeys = new Array((1,2,3,4); (as Time parameter),..
(with Either)

var keyValues = new Array([10,10],[100,100],[15,50],[100,120]); ...." ]) "
(OR)
var keyValues = [[10,10],[100,100],[15,50],[100,120]]; ...(Array of arrays? RE: " ]] " )


I've just read a "push" of values into an Array may be required?, but i hoped anyone familiar with setValuesAtTimes could please show,..

1) ..how to create the arrayOfValues value parameter for setValuesAtTimes method?

1a): WORKING setValueAtTime script...

// app.project.item(1).layer(1).property(“Masks”).property(“Mask 1”);
var myProperty = app.project.item(1).layer(1).mask(1).maskPath;
myShape = app.project.item(1).layer(1).mask(1).maskPath.value;
myProperty.setValueAtTime(1.0,myShape);


1b): FAILING setValuesAtTimes script...

// var prop1 = app.project.item(1).layer(1).property("ADBE Transform Group").property("ADBE Position");
var fourKeys = 4;
var addTheseKeys = new Array(1,2,3,4,);
var keyValues = new Array([10,10], [100,100], [15,50], [100,120]);

var myProperty = app.project.item(1).layer(1).mask(1).maskPath;
myShape = app.project.item(1).layer(1).mask(1).maskPath.value;
var myVertices = app.project.item(1).layer(1).mask(1).maskPath.vertices;
myProperty.setValuesAtTimes(addTheseKeys, keyValues);

app.beginUndoGroup("Keys");
//~ for(var k=0; k<fourKeys; k++)
//~ {
//~ prop1.setValueAtTime(addTheseKeys[k], keyValues[k]);
//~ }

// myProperty.setValuesAtTimes(addTheseKeys, keyValues);

app.endUndoGroup();



..Really! appreciate any advise\instruction,
J

Re: how to make arrayOfValues parameter for setValuesAtTimes

Posted: March 19th, 2014, 4:34 am
by Paul Tuersley
Setting Mask Path keyframes/values are a bit different from setting other property values.

In your example you only had what appeared to be an array of Position values rather than vertices, so for this example I'm pretending they are supposed to be the 4 vertices for a mask shape and I'm applying them to 4 identical keyframes at the 'addTheseKeys' times. In this example I'm also adding a new mask rather than using an existing one. Even if this isn't exactly what you're after it should point you in the right direction.

Paul

Code: Select all

var activeItem = app.project.activeItem;

if (activeItem != null && activeItem instanceof CompItem) {

	var theMaskGroup = theLayer.property("ADBE Mask Parade").addProperty("ADBE Mask Atom");
	var addTheseKeys = new Array(1,2,3,4,);
	var vertices = new Array([10,10], [15,50], [100,120], [100,100]);
	var myProperty = theMaskGroup.property("ADBE Mask Shape");

	var myShapeArray = new Array();

	var myShape = new Shape();
	myShape.vertices = vertices;

	while (myShapeArray.length < addTheseKeys.length) {
		myShapeArray.push(myShape);
	}

	app.beginUndoGroup("Keys");
	myProperty.setValuesAtTimes(addTheseKeys, myShapeArray);
	app.endUndoGroup();

}

Re: how to make arrayOfValues parameter for setValuesAtTimes

Posted: March 19th, 2014, 2:36 pm
by danceman
Thanks much Paul,
your correction\solution made an immediate difference in both script and my understanding, Awesome!

I hope i'm not asking too much with this next question, and\or if it should be a new topic, please let me know if so:

below i've pasted a script that TransfersMaskKeysToShapePaths. It was created from a functioning script that does the opposite, ie: createMaskKeysFromShapePaths.

As far as i've verified its written and iterates thru both mask keys and Shape paths properly, but for some reason i can't see it does not transfer the mask keys to Shape paths. So i hoped that you and AE Enhancers could,..

1) ..please tell me where the error preventing that transfer is occurring in this script?

Appreciate any help or suggestions,
Thx much,
J

copyMaskKeysToShapeLayers();

function copyMaskKeysToShapeLayers()
{
var currentComp = app.project.activeItem;
var shapeLayer = currentComp.selectedLayers[0];
var maskLayer = currentComp.selectedLayers[1];
var vals = [];
var maskPaths = [];
var maskPositions = [];
var maskTimes = [];
var shapePaths = [];

// fill up the shapePaths and shapePositions arrays
getShapePathProperty(shapeLayer, shapePaths, shapePositions

// var shapeLayer = app.project.activeItem.selectedLayers[0];
var maskProperty = findMaskProperty(maskLayer, 0); //1) Get the Mask property
var maskKeyValue = readShowMaskKeys(maskProperty); //2) Find the value at each keyframe for the mask and put these into an array
var shapePathProperty = getShapePathProperty(shapeLayer, vals); //3) Update the Shape values for each group in your shape layer
}


function findMaskProperty(layer, maskNum)
{
try {
var firstMaskPath = layer.property("ADBE Mask Parade").property("ADBE Mask Atom").property("ADBE Mask Shape");
return firstMaskPath;
} catch(e)
{
alert(e);
return -1;
}
}

//2) Find the value at each keyframe for the mask and put these into an array

function readShowMaskKeys(maskProperty)
{
//maskProperty = app.project.activeItem.selectedLayers[1].property("ADBE Mask Parade").property("ADBE Mask Atom").property("ADBE Mask Shape");

var keyLength = maskProperty.numKeys;
var vals = [];
for(var k=1; k<=keyLength; k++)
{
vals[vals.length] = maskProperty.keyValue(k);
}
alert(vals);
}


//3) Update the Shape values for each group in your shape layer

function getShapePathProperty(prop, vals) // prop = shapeLayer = app.project.activeItem.selectedLayers[0];
{
var propDepth = prop.propertyDepth; // propertyDepth:The # of parent levels
try
{
var shapeLayerGroups = prop.property("ADBE Root Vectors Group");
// search through all contents for Groups
for (var g=1;g<=shapeLayerGroups.numProperties;g++) {

var currentGroup = shapeLayerGroups.property(g);
if (currentGroup.matchName == "ADBE Vector Group")
{
// get the shape path and transform for the current group
var shapeTransform = currentGroup.property("ADBE Vector Transform Group").property("ADBE Vector Position");

shapePath.setValue = vals[g];
}
}
} catch(e)
{
alert(e);
}
}

Re: how to make arrayOfValues parameter for setValuesAtTimes

Posted: March 19th, 2014, 3:25 pm
by Paul Tuersley
Well in truth it looks like there's so much wrong with that script it's hard to know where to start. It would be easier to start from the working script that does the opposite, assuming it does work. A few suggestions:

Firstly, do you realise that only shape layers that contain a 'Path' property can be directly converted to the Mask Path property? Many shapes such as a Rectangle aren't stored as vertices, but are instead described by their size and center position. So most shapes will require quite a bit more work to convert the values in the way they're stored into Mask Path vertices and tangents.

Code: Select all

// fill up the shapePaths and shapePositions arrays
getShapePathProperty(shapeLayer, shapePaths, shapePositions
This line isn't terminated with ); as it should be. Also the values that are being sent to this function appear to bear no relation to the variables being assigned in the function, which are:

Code: Select all

function getShapePathProperty(prop, vals)
When looking for a Mask Group you would be better off using the index rather than the "ADBE Mask Atom" property as there could be multiple mask groups. So:

Code: Select all

var firstMaskPath = layer.property("ADBE Mask Parade").property(1).property("ADBE Mask Shape");
Generally you would be better off writing much smaller bits of test code to make sure you get every little step correct rather than writing a lot of code and then trying to figure out where all the bugs are. Use lots of

Code: Select all

$.writeln(myProperty.matchName);
type lines throughout your code to examine every single property you're accessing to ensure you've grabbed it correctly.

Re: how to make arrayOfValues parameter for setValuesAtTimes

Posted: March 19th, 2014, 4:33 pm
by danceman
Thanks Paul,
but to see the code as is without syntax or missing syntax errors i tried to attached it to this reply but this website responded "the extension jsx (and txt) is not allowed.
1) please tell me which format is accepted for upload by this site and i'll upload both the original fully functional "copyShapeLayersToMasks" and the problematic "copyMaskKeysToShapeLayers".

Copy/pasting and editing code in these tiny windows is problematic at best. For example the "getShapePathProperty(shapeLayer, shapePaths, shapePositions" you commented on was commented out in the original so wasn't actually a syntax error. I've therefore removed ALL comments. Hopefully nothing will be lost\misplace etc. again in translation to the supported file format.

"..do you realise that only shape layers that contain a 'Path' property can be directly converted to the Mask Path property?"
= I should probably give you complete details so your fully informed on goals and problems. This is a quoted original question already solved by the functional "copyShapeLayersToMasks.jsx:

"..I have an After Effects Shape layer with a couple hundred Adobe Illustrator paths, re-created by "Create shapes from vector layer".
Each path is in its group vertically stacked in a typical shape layer called: Layer 2 Outlines Masks > Contents > Group 1 > Path 1 > Path.
Then i have a Black solid layer as paste target, with a Mask created on it.

Could you please tell me,..
1) ..how to copy all the Shape paths and paste them as Keyframes! on the Mask path of the Black Solid layer at: Black Solid 1 > Mask 1 > Mask Path?..
..and then return them after editing and scripts to their original Shape Layer paths?.."

Now, its just the last part ie: "..return them -after editing and scripts- to their original Shape Layer paths..", that the script "copyMaskKeysToShapeLayers" is having problems for which i hope you may help.
So, soon as you may tell me or i can determine a format this site accepts i'll immediately upload both scripts for you. Or i could email, upload to address of your choice, please let me know.
Thanks again,
Jeff

Re: how to make arrayOfValues parameter for setValuesAtTimes

Posted: March 19th, 2014, 4:40 pm
by Paul Tuersley
Have you seen the way I post code?

Code: Select all

like this
Click the 'Code' box at the top of the reply window then paste the code between the tags. Or after pasting, select all the code then click the 'Code' box to do the same thing.

Paul

Re: how to make arrayOfValues parameter for setValuesAtTimes

Posted: March 19th, 2014, 4:53 pm
by danceman
thanks, here you go,

Re: how to make arrayOfValues parameter for setValuesAtTimes

Posted: March 19th, 2014, 5:39 pm
by Paul Tuersley
So what is it that you want to do? Please explain the specifics.
Do you have hundreds of masks that you want to paste into a single shape path as keyframes?
Do you have hundreds of mask keyframes on a single mask that you want to paste into multiple shape paths?
Do you want the script to create the new shape path property or does it already exist?
I'm not sure how soon or if I'll be able to help with this though as I'm pretty busy and this is tricky code to unpick.

Paul

Re: how to make arrayOfValues parameter for setValuesAtTimes

Posted: March 19th, 2014, 6:04 pm
by danceman
Its all good Paul, appreciate your being here and your time. I'm out the door soon and a Script buddy back after i am. Its mainly that the final shapePath.value = vals[g]; failed to actually Transfer the Mask path Keys to their original Shape path. But a msg. i'll finish shows it may be my arrays variable scope: diff. variables assigned from in and outside a function. If not figured i'll write back,
Cheers,
Jeff

Re: how to make arrayOfValues parameter for setValuesAtTimes

Posted: March 19th, 2014, 6:11 pm
by Paul Tuersley
Well yes, in your readShowMaskKeys function you're defining a local instance of the variable vals and you aren't returning it. In fact you're not returning anything to the variable maskKeyValue that is reading the result of that function. But then I don't know if it's supposed to be returning to that or to the local vals variable you're also defining in the copyMasksToShapeLayers function.

It's very difficult to decipher a script when you don't know what it's supposed to be doing.

Paul

Re: how to make arrayOfValues parameter for setValuesAtTimes

Posted: March 19th, 2014, 8:40 pm
by danceman
cool, thx..wondered why maskKeyValue kept coming back "..undefined.", suspected lack of return, but weeks on Extendscript, less learning this tutored code so could'nt tell that readShowMaskKeys needed return when others (removed) did'nt. Also, maskKeyValue was part of past modular test code like kind you suggested, that i'll go back to since you defined problem.

1) would i use the same type as function findMaskProperty?, IE:

Code: Select all

return maskKeyValue;
          } catch(e)
            {
     		return -1;
and good to know:

Code: Select all

When looking for a Mask Group you would be better off using the index rather than the "ADBE Mask Atom" property as there could be multiple mask groups. So:
Code:
var firstMaskPath = layer.property("ADBE Mask Parade").property(1).property("ADBE Mask Shape");
...i wondered for while when to use either. So the index Replaces the property!, thats why my following statement failed:

Code: Select all

//  app.project.activeItem.selectedLayers[1].property("ADBE Mask Parade").property("ADBE Mask Atom").property(g).property("ADBE Mask Shape");   
..and should have been,..

Code: Select all

//  app.project.activeItem.selectedLayers[1].property("ADBE Mask Parade").property(g).property("ADBE Mask Shape");
..which did'nt error,

..and now i can cleanup this expansion,..

Code: Select all

  //  app.project.activeItem.selectedLayers[1].property("ADBE Mask Parade").property("ADBE Mask Atom").property("ADBE Mask Shape");					
A)//  app.project.activeItem.selectedLayers[1].property("ADBE Mask Parade").property(1).property("ADBE Mask Shape"); 
  //  app.project.item(1).layer("Black Solid 1").property("Masks")         .property(g).property("Mask Path");
  //  app.project.item(1).layer("Black Solid 1").property("Masks").property(1).property("Mask Path");
B)//  app.project.item(1).layer("Black Solid 1").property("Masks").property(g).property("Mask Path");
2) whens the best time to use property path A) (above) or B),..if using functions, values, iterating etc.?

Appreciate it Paul,
Jeff

Re: how to make arrayOfValues parameter for setValuesAtTimes

Posted: March 19th, 2014, 9:13 pm
by Paul Tuersley
danceman wrote:1) would i use the same type as function findMaskProperty?, IE:

Code: Select all

return maskKeyValue;
          } catch(e)
            {
     		return -1;
I'd advise you to remove all the try/catches. They should only be used when you have no other option than to try something that could fail. There's nothing here that should need it so I just consider it horrible coding! (there are arguments for wrapping large blocks of code in try/catch if you want end users to be able to report errors without it bringing up the debugger though). Plus it makes it harder to tell where the errors are. As long as you have 'Enable Javascript Debugger' checked in Preferences > General it will jump to exactly where an error is happening and give you the same information. Then add some $.writeln()'s to figure out what's going wrong if necessary.

Your script does things like ask you to select the two layers in the correct order. It's never a great idea to rely on the user always doing things as expected so you should always include checks. So for example you could check if the Shape layer is really "instanceof ShapeLayer". And before trying to access a mask by its index, make sure the ADBE Mask Parade has at least that number of properties.
danceman wrote:

Code: Select all

  //  app.project.activeItem.selectedLayers[1].property("ADBE Mask Parade").property("ADBE Mask Atom").property("ADBE Mask Shape");					
A)//  app.project.activeItem.selectedLayers[1].property("ADBE Mask Parade").property(1).property("ADBE Mask Shape"); 
  //  app.project.item(1).layer("Black Solid 1").property("Masks")         .property(g).property("Mask Path");
  //  app.project.item(1).layer("Black Solid 1").property("Masks").property(1).property("Mask Path");
B)//  app.project.item(1).layer("Black Solid 1").property("Masks").property(g).property("Mask Path");
2) whens the best time to use property path A) (above) or B),..if using functions, values,
Well, avoid using app.project.item(1). Who knows what the first thing in the project panel might be in various projects! app.project.activeItem will return either the currently active comp, or if the project panel is active it will return the selected item if only one is selected or return null if multiple items are selected. Thats why you also need "if (activeItem != null && activeItem instanceof CompItem) {"

About the only time you'd use ("ADBE Mask Atom") is when adding a mask group (which includes a single Mask Shape, Feather,etc) to the root ADBE Mask Parade property (which always exists on suitable layer types). The Mask Atom group is both named and indexed, so you could access with either .property(1) or .property("Mask 1"). You can find property("ADBE Mask Parade").numProperties then loop through them by index if you like.

And the reason I always use what are called matchNames (i.e. "ADBE Mask Parade" not "Masks") is because property matchNames are language independent. Might not be an issue for you, but others will appreciate the script not breaking when running on different languages if you share it, so it's good practice in general.

Paul

Re: how to make arrayOfValues parameter for setValuesAtTimes

Posted: March 20th, 2014, 12:24 pm
by danceman
Thanks Paul,
you should be proud of your diagnostic skills: with barely a glance you nailed my exact AmateurHour fault in the script, IE: vals array Not passed to readShowMaskKeys and so was empty when passed to getShapePathProperty,..

(Please always feel free to correct\improve etc. the following and any statement):

..,Then there was my Variable Scope errors: vals declared Globally AND within a function that changes vals = NO Global changes to vals,.

also, my mistaking a Function, setValue for a property and attempting to assign a value rather than Pass a Parameter, IE: shapePath.setValue = vals[g]; should be, shapePath.setValue(vals[g]; ,..

and finally, the incompatible Indices: variable g iterates from 1 -> numProperties, but my vals array begins with index of " 0 ", so shapePath.setValue(vals[g]; should be shapePath.setValue(vals[g-1];


Really! appreciate these Gems of experience:
..remove all the try/catches...used when you have no other option than to try something that could fail...As long as you have 'Enable Javascript Debugger' checked in Preferences > General it will jump to exactly where an error is happening and give you the same information. Then add some $.writeln()'s to figure out what's going wrong if necessary.

..should always include checks. So for example you could check if the Shape layer is really "instanceof ShapeLayer". And before trying to access a mask by its index, make sure the ADBE Mask Parade has at least that number of properties.

Well, avoid using app.project.item(1)....app.project.activeItem will return either the currently active comp, ..the selected item..or return null if multiple items are selected. ..you also need "if (activeItem != null && activeItem instanceof CompItem) {"

About the only time you'd use ("ADBE Mask Atom") is when adding a mask group (which includes a single Mask Shape, Feather,etc) to the root ADBE Mask Parade property (which always exists on suitable layer types). The Mask Atom group is both named and indexed, so you could access with either .property(1) or .property("Mask 1"). You can find property("ADBE Mask Parade").numProperties then loop through them by index if you like.

..always use what are called matchNames (i.e. "ADBE Mask Parade" not "Masks") is because property matchNames are language independent..

All help compensate for my lack of formal training and learning Extendscript backwards, from Advanced codes to basics.

lastly,
3) are there any doc's, vids, manuals of any kind that describe the following ADBE groups and detail their functions and diff's?,. and\or detail Shape, Path and Keyframe Scripting please?:
"ADBE Root Vectors Group","ADBE Vectors Group","ADBE Vector Shape - Group","ADBE Vector Shape",
"ADBE Mask Parade","ADBE Mask Atom","ADBE Mask Shape",..

I owe ya,
Jeff

Re: how to make arrayOfValues parameter for setValuesAtTimes

Posted: March 20th, 2014, 8:24 pm
by Paul Tuersley
danceman wrote:Then there was my Variable Scope errors: vals declared Globally AND within a function that changes vals = NO Global changes to vals,.

3) are there any doc's, vids, manuals of any kind that describe the following ADBE groups and detail their functions and diff's?,. and\or detail Shape, Path and Keyframe Scripting please?:
"ADBE Root Vectors Group","ADBE Vectors Group","ADBE Vector Shape - Group","ADBE Vector Shape",
"ADBE Mask Parade","ADBE Mask Atom","ADBE Mask Shape",..
You didn't actually have any global variables in your script. They were all defined within functions and therefore only had scope within that specific function. If you wanted a variable to be accessible throughout the functions you could have just defined it outside all functions with a simple "var vals;".

But variables defined outside all functions are global across any scripts run in that session, which can produce unpredictable results when bug testing (i.e. a variable has been defined in an earlier version of your script you were testing, but then not in a later version, but this might not produce an error until you retest the later script after restarting AE), so better to have a function which contains all other functions and define your global variables in the main function, then variables are accessible to all functions but the global scope is only for that instance of the script while running.

There's the After Effects CS6 Scripting Guide which covers a lot of stuff. Just google it and you'll find a download link. I also highly recommend Jeff Almasol's GimmePropPaths script which you can use to check the matchNames and hierarchy of any selected property:
http://www.redefinery.com/xae/scripts/r ... h_v3.0.zip