AE: Scripting Notes

Just a quick note about a bug and an optimization when scripting After Effects CS4 (and probably earlier versions, too).

addProperty() bug

When adding several effects, each addition invalidates the object variable references to earlier ones. Here’s a code fragment which shows the problem and the solution.

var comp = app.project.activeItem;
var layer = comp.layers.addNull();
layer.name = "null_layer";

var slider1 = layer.Effects.addProperty("Slider Control");
slider1.name = "s1";

var slider2 = layer.Effects.addProperty("Slider Control");
slider2.name = "s2";

// At this point, slider2 is valid, but slider1 is mysteriously not!
// Any action or reference to slider1 will cause an "invalid object" error
//
// What can we do?
//
// Fortunately, they have names by which we can recover them

slider1 = layer.Effects.property("s1");
slider2 = layer.Effects.property("s2");

// Now they're both good to go.

setValueAtTime() Gets very slow!

If you do a whole lot of setValueAtTime() calls to set keyframes, the script will run very slowly. Fortunately, you can just call setValuesAtTimes(), the plural form, to set many at once, which is much more efficient! Makes the minutes seem like seconds, Captain.

// this will be very slow, if myData has more than a few dozen items
for(var i = 0; i < myData.length; i++)
	prop.setValueAtTime(myData[i].t,myData[i].v);

// but if we build up our arrays first...
var timesArray = new Array();
var valuesArray = new Array();
for(var i = 0; i < myData.length; i++)
{
	timesArray.push(myData[i].t);
	valuesArray.push(myData[i].v);
}
// and set them all at once
prop.setValuesAtTime(timesArray,valuesArray);

// it will go lickity-split!

(Thanks creative cow thread!)

AE: A Snowflake

colorfulSnowflake.jpg

download snowflakeSteps.aep

Featuring: Bevel Alpha, omino kaleidoscope, and animated paths.

Sounds like the USA has had a heck of a Christmas weather run! Folks taking their vacations on airport waiting benches, and so forth. In particular, Seattle, home to After Effects. Where I am, it’s 7:33 pm, and I’ve just returned, sweating, from a nice bowl of Mun Eefoo Mee. Total google hits for that dish: 0, but the chef told me it’s his specialty, and it was delicious. Cost, with a cup of iced sweetened barley, 5 Ringitts Malaysian. Current temperature: 80F. Current exchange rate: 3.8 Ringitts to the US Dollar.

It’s my kind of place. “Christmas” is a national holiday, but nobody celebrates beyond a few santas at the mall. On the 25th, the shoe stores and the hardware stores were all open. As were thankfully the restaurants; I’m expated here alone for a few months, no home cooking for this coder.

So, in a fit of boundless atheism, I spent the 25th cobbling together an After Effects project to grow snowflakes!

Step 0: Research

I found this great site, http://snowcrystals.com/ maintained by Kenneth G. Libbrecht, a Caltech physics professor. Fabulous stuff, including movies of lab-grown snow crystals!

After reading his site, and other sources, and general pondering, I jumped in with the scripting. It’s only a 2 day project, so it’s not really going to accurately mimic the physics of snow… but we’ll settle for “suggestive” and “cool-looking”.

(And, here is a short article about snowflake symmetry.)

Step 1: Growth

step1-spikypath.mov

click for
http://omino.com/pixelblog/wp-content/uploads/2008/12/step1-spikypath.mov

This movie shows one branch of a snowflake being “grown”. It’s a layer mask path, animated by ridiculous please-don’t-read-the-code script I wrote… but, truth to tell, you could animate almost anything in there, and, by the end, it’s going to look pretty snow-ish. You can download my script if you like. It’s kinda tweaky; the main trick is that the lengths parameter consists of letters, where a is a very short spike, and z is full length.

Step 2: Hexagonal Symmetry

We all know how to make a paper snowflake, with three or four folds and some scissors. We’ll do the same thing with my plugin, omino kaleidoscope, downloadable here.

(You can also use CC Kaleida, which is included with AE these days, to make an 8-fold snowflake. But use mine for authentic regulation 6-way symmetry.)

The omino kaleidoscope works by using an AE layer’s Mask Path to represent the shape of the mirrors. Any path will work (though curves are treated as straight segments). For snowflake symmetry, we need a wedge which is one twelfth of the pie. Again, I wrote a script, wedgePath to do this. But you could draw the mask manually, if you’re careful. (Be sure to set its mode to “none”, we don’t want it to clip the image.)

And set up omino kaleidoscope as shown in the screenshot above.

Step 3: Moreness

step3_moreness.mov

click for
http://omino.com/pixelblog/wp-content/uploads/2008/12/step3_moreness.mov

So far so good. Next, let’s add a few more layers of animated paths. We’ll superimpose them to get different gray-levels. Some of them are time-shifted, to evolve at different rates. Different transfer-modes can liven this up a little. Again: you can animate almost anything, and the kaleidoscope will make it snowflake-like.

Step 4: Imperfect

step4_imperfect_frame.jpg

To make it look just a shade less mechanical, we can have the snowflake grow a little bit irregularly. Now, it is supposed to be a crystal, so we’ll go gently with this. Real snowflakes often show subtle imperfections like this, presumably due to environmental variations during formation.

The above frame is from Step 3, with Time Displaclement applied. Here’s the layer used as the Time Displacement Map. It ranges from black to 50% grey, ensuring that only negative time displacements will be used.

slownoise.mov

click for
http://omino.com/pixelblog/wp-content/uploads/2008/12/slownoise.mov

Note. Time Displacement is a render-time hog, since AE needs to render many frames past and present for each final frame. It’s helpful to disable this effect while tinkering, turn it back on for the render.

Step 5: Crystallize

This is the last step, really. We’ll use a few applications of Channel Mapper to convert luma to alpha. And then, this is the exciting part, we use Bevel Alpha with several colored lights to get the colorful crystalline look.

The “light angle” for the bevel effect has been animated with an expression

effect("Bevel Alpha")("Light Angle") + time * 90

just to keep it lively.

Also, Channel Mapper has been used to pull a dark grey in for the snowflake’s color. The different brightnesses earlier were mapped to alpha, for the beveling; the final result looks better with colors darker than white.

And I’m not above adding a bit of Find Edges and Glow, no sir. The secret sauce is ketchup. Whatever it takes!

Here’s the final result:

finalMovie.mov

click for
http://omino.com/pixelblog/wp-content/uploads/2008/12/finalMovie.mov

As ever, here’s the After Effects project. It doesn’t require any assets (the finalMovie comp references the photo and music loops, but pay it no mind).
download snowflakeSteps.aep

And so far, it has not been a cold day in Penang, Malaysia.

AE: Mask Vertices from ExtendScript

We’ll take it as axiomatic that scripting After Effects is pretty keen. But sometimes you can get lost in the nest of properties, property groups, values, attributes, and the subtle differences in nomenclature for properties and attributes used by After Effects and the JavaScript.

It is definitely possible to access and manipulate each vertex on a layer mask. Below is a script which displays some mask points, and then modifies a vertex. It will show this alert:

verticesGot.png

And this is the script which displays the dialog, and then modifies the 4th vertex of the mask. It demonstrates the recipe to navigate the Masks, Mask, Mask Path, and so on.

One thing first. Many sites and blogs show only very, very short scripts. I take a slightly different approach. I think code should tell a story. I try to build things up as simply as possible. Despite its apparent length, I think you’ll find the script easy to follow. Just go 1 line at a time.


// Utility to find a comp by name.
function findComp(name)
{
	for(var i = 1; i <= app.project.numItems; i++)
	{
		var item = app.project.item(i);
		if(item != null && item.name == name)
			return item;
	}
	return null;
}

// Show the vertices of the first mask of comp1/layer1.
function main()
{
	var comp = findComp("comp1");
	var layer = comp.layer("layer1");
	var masks = layer.Masks;

	var firstMask = masks.property(1);

	if(firstMask == null)
		return;

	var maskPathProperty = firstMask.property("Mask Path");
	var maskPath = maskPathProperty.value; // or valueAtTime(t) if  you like

	var sm = "Vertices\n";
	var vertices = maskPath.vertices; // array of [x,y] pairs
	for(var i = 0; i < vertices.length; i++)
	{
		var p = vertices[i];
		var x = p[0];
		var y = p[1];
		sm += "v[" + i + "] = " + x + "," + y + "\n";
	}

	alert(sm); // Show the vertices.

    // Now, we change a point.
	var p = vertices[3];
	p[1] = 300;

	// Must be "put back" bit by bit.
	maskPath.vertices = vertices;
	maskPathProperty.setValue(maskPath);

}

main();

A few notes:

  • To change the vertex, we need to assign it back to the Mask Path and then into the Mask property; changing it "in place" won't alter the actual mask.
  • Chris-g notices that you must assign the vertices before assigning closed to true or false; assigning the vertices "automagically" sets closed to true. Thanks Chris!

Hope that's useful.

Two Illustrator Scripts

Here’s a pair of Illustrator scripts I’ve had up for a while, but this is the first time I’ve posted examples. Also, Javier Enciso of www.formaestudio.com in Uruguay spotted a bug in the triangle script, which is now fixed.

Both available at http://omino.com/sw/ominoAdobeScriptsSuite/.

Circular Gauge (circularGauge.jsx)

circularGauge.jpg

This script draws numbers and tick-marks to resemble a circular gauge. It lands up in its own layer, and the text and ticks are in their own group, ready for tweaking. I use it to make pretty machines in Second Life. A bit of emboss and off you go.

Triangle By Sides (triangleBySides.jsx)

triangleBySides.jpg

I can’t remember what I needed this for, but it was exactly what I needed at the time. Self explanatory!

Handy Scripts

Tasks Beget Subtasks

That’s always how it is, I’m trying to do something, but then a new jig or tool is needed along the way.

Just lately I’m finally “remastering” some ancient (80’s) video of my own. Captured it off VHS video in 2004, been sitting on my hard disk since then. Tragically, the capture introduced some awful drift between audio and video. I probably used iMovie for a 30 minute capture, just like they say not to.

Why am I using AE for this? Because I haven’t upgraded the stupid final cut pro to the stupid expensive intel version, because I stupidly missed the $50 upgrade special window.

And as long as I’m complaining: How come AE doesn’t have some fine-numeric control for time-bumping the layers?? Ok. So it goes.

But meanwhile, I need some fine-control to shift the layers left and right.

I’m thinking, should I write a script for this? But something tickles my memory. A yes, this is just the sort of thing that can be found on Jeff Almasol’s most excellent redefinery blog and scripts collection. Many useful scripts there; solid work-a-day workflow helpers. Also, exquisitely coded. Excellent style, thrilling examples, and so forth.

Happily, redefinery’s rd:scooter was just the tool to nudge things back into place!

After nudging around my 30 minute audio and video layers, I wanted to snip out just the 2 minutes for the particular song-section. But the in- and out-points were far off to the left and right of my dinky 3 minute comp. So I wrote a script panel for this last bit:

I tried to keep it nice and narrow, to fit in easily with the other built-in palettes. It’s ugly, but then it worked so I stopped coding & used it. Click here or on the picture to get it from my scripts collection.

After Effects Scripting Bug

Ah yes, there’s a bug in After Effects CS3’s scripting that I came across. If you set layer.inPoint = t; it also sets the outpoint! My workaround was like so:

	// bug? setting in seems to corrupt out. save and fix.
	var outPoint = layer.outPoint;
	layer.inPoint = myComp.time;
	layer.outPoint = outPoint;

This workaround should be safe even after they fix the scripting bug. (Not sure where to report Adobe bugs…)

So… Then What?

And lastly, some excerpts from the product of this endeavor: Vintage 1986-era animation loops done with Macromind’s Videoworks ! Orchestrated with my own long-forgotten music software, “Synthestra” (MIDI) and P-Drum (sample player), both for the Apple II, distributed by Dan Retzinger’s company, Decillionix.

Check it out. 30 seconds of techno-stalgia.

C5zZ-9Impnk

click for
http://www.youtube.com/v/C5zZ-9Impnk&hl=en&fs=1&rel=0

Omino Dialog Maker for ExtendScript

ExtendScript

My favorite Adobe applications are all scriptable. They use a scripting language called ExtendScript. I think it’s pure and compliant JavaScript (aka EcmaScript)… if it has any deviations, it’s in some subtle fashion that hasn’t affected me.

One thing you frequently need in a script is a means to gather input choices, so the script can be flexible. I rolled something up that handles this for many simple situations. I call it the Omino Dialog Maker. It’s a software library that lets you bring up an input dialog with just a couple of lines of code. It is mostly about 2 years old now, works pretty well.

Omino Dialog Maker

Here’s a simple example of a dialog made with the Omino Dialog Maker.

And here’s the code to produce it:

#include "ominoDialogMaker.jsx"

var omd = newOminoDialog("Example Omino Dialog");

omd.number("a number","n",88.21);
omd.string("a string","s","your name here");
var result = omd.run();

if(result == null)
	alert("Cancelled\nYou clicked \"cancel\".");
else
	alert("n is " + result.n + ", and s is \"" + result.s + "\"");

That’s pretty handy, isn’t it? As you can see, omd.run() populates a result variable with the contents of the dialog.

I admit it: tt’s not the best possible dialog and layout. It’s a little clunky looking. But it gets the job done, lets me type in a few numbers and set meat of the script to work.

More UI Elements

Here’s a dialog which shows the various UI elements supported by Omino Dialogs:

And, here’s the code which produced it:

#include "ominoDialogMaker.jsx"
function go()
{
	// setting up a dialog is easy. you can add entries for basic types of
	// string and number, and checkbox and radio buttons, and the
	// dialog is presented reasonably, with very little work.

	var omd = newOminoDialog("Example Omino Dialog");

	omd.boxedText(5,"This is an example of an Omino Dialog. It runs \n"
		+ "with After Effects, Illustrator, and Photoshop, or just in the\n"
		+ "ExtendScript toolkit itself. It can handle simple parameter types."
		+ " (Running in " + app.name + " v" + app.version + ")"
	);
	omd.number("a number","n",15.7);
	omd.string("a string","s","a text value");
	omd.separator();
	omd.sectionLabel("basics");
	omd.checkbox("a checkbox","x",true,"subtitle");
	omd.menu("a menu","menu","cherries",["apples","bananas","cherries","dates","figs"]);
	omd.radioButtons("Choose One","r","red",["red","maroon","scarlet","crimson"]);
	omd.separator();
	omd.sectionLabel("files");
	omd.openFile("a file","jpgFileName","foo.jpg","Choose a JPEG File",".jpg");
	omd.selectFolder("a folder","folderName","/tmp","Choose a folder");
	omd.saveFile("save as","gifFileName","foo.gif","Save as GIF",".gif");s

	// when we "run" the dialog, we get back a result variable.
	// if you hit CANCEL, then the result is null.
	// if you hit OK, then the values are populated into the result.

	// note: you can kill photoshop by running the dialog from ExtendScript toolkit, then halting
	// the script with the dialog still up.

	var result = omd.run();

	if(result == null)
		alert("Cancelled\nYou clicked \"cancel\".");
	else
	{
		var s = "result from dialog:\n";
		for(var f in result)
			s += "   " + f + " := " + result[f] + "\n";
		alert(s);
	}
}
go();

If you’ve read this far, you’re hopefully asking, “Gee, where can I get the actual library?” Here’s the code, shown directly out of my source code repository. Download with the download button.

http://omino.com/src/adobe_scripts/omino_adobe_script_suite/src/shared/ominoDialogMaker.jsx in new window download http://omino.com/src/adobe_scripts/omino_adobe_script_suite/src/shared/ominoDialogMaker.jsx to file hide http://omino.com/src/adobe_scripts/omino_adobe_script_suite/src/shared/ominoDialogMaker.jsx

 

Trivia: UI Differences Across Adobe Applications

Omino Dialog Maker can be used for scripts in After Effects, Photoshop, and Illustrator, at least, and possibly other Adobe applications. I have noticed that the dialogs look slightly different in each of these apps! Not critically… For reference, here are screen shots from several.

Progressive Blur

A Photoshop script was composed and executed to perform Gaussian blur. Over and over. And over. Random characters are stamped from time to time. A slightly cool effect is achieved!

8RrGKYNaxVo

click for
http://www.youtube.com/v/8RrGKYNaxVo&rel=0&color1=0x3a3a3a&color2=0x999999&border=0
Read the rest of this entry »

ExtendScript Modal Whoopsie!

Sometimes you’re just toodling along trying things out, and you commit a blunder like this:

I was working on a color picker… and brought up a modal dialog with no Ok or Cancel buttons, inside the ExtendScript editor. All menus greyed out, can’t click the stop button. Oops!

How would it end? Would I have to kill the process? Maybe unplug the computer and reset the P-RAM?

Read the rest of this entry »

Binary Files in ExtendScript

Adobe’s done a real whiz-bang job with their scripting support, known as ExtendScript. They’ve filled in some important missing pieces to make JavaScript into a full-featured programming environment, include network communications, XML, and graphic UI elements. These are uniformly available across all their scriptable applications (with, alas, minor variations with the UI support).

This post shows a little example of how to use the humble File object.

Like all good library writers, they’ve designed File to shield us from the absolute blazing stupidity of different linefeed characters. It detects CR, LF, LFCR, and CRLF and still chugs along reading your files, line by line, correctly. No, really, it does work!

So the exciting case of course is, how to defeat that and read or write binary files? This example demonstrates the recipe.


// adobe extendscript writing binary data
var f = new File("/Users/poly/junk/jsbinTest.bin");
f.encoding = "BINARY";

f.open ("w");

for(i = 0; i < 256; i++)
	f.write(String.fromCharCode (i));

f.write("this file is: " + f.fsName + "\n");

f.close();

They've got this highly-developed notion of "encoding", but, in truth, you can usually either ignore it, for text, or set it to the magic value BINARY. Easy peasy!

Note also the standard JavaScript String.fromCharCode(integer).

And here's the file created by that ExtendScript code snippet.

poly@Omino-Lux: hexdump -C ~/junk/jsbinTest.bin
00000000  00 01 02 03 04 05 06 07  08 09 0a 0b 0c 0d 0e 0f  |................|
00000010  10 11 12 13 14 15 16 17  18 19 1a 1b 1c 1d 1e 1f  |................|
00000020  20 21 22 23 24 25 26 27  28 29 2a 2b 2c 2d 2e 2f  | !"#$%&'()*+,-./|
00000030  30 31 32 33 34 35 36 37  38 39 3a 3b 3c 3d 3e 3f  |0123456789:;<=>?|
00000040  40 41 42 43 44 45 46 47  48 49 4a 4b 4c 4d 4e 4f  |@ABCDEFGHIJKLMNO|
00000050  50 51 52 53 54 55 56 57  58 59 5a 5b 5c 5d 5e 5f  |PQRSTUVWXYZ[\]^_|
00000060  60 61 62 63 64 65 66 67  68 69 6a 6b 6c 6d 6e 6f  |`abcdefghijklmno|
00000070  70 71 72 73 74 75 76 77  78 79 7a 7b 7c 7d 7e 7f  |pqrstuvwxyz{|}~.|
00000080  80 81 82 83 84 85 86 87  88 89 8a 8b 8c 8d 8e 8f  |................|
00000090  90 91 92 93 94 95 96 97  98 99 9a 9b 9c 9d 9e 9f  |................|
000000a0  a0 a1 a2 a3 a4 a5 a6 a7  a8 a9 aa ab ac ad ae af  |................|
000000b0  b0 b1 b2 b3 b4 b5 b6 b7  b8 b9 ba bb bc bd be bf  |................|
000000c0  c0 c1 c2 c3 c4 c5 c6 c7  c8 c9 ca cb cc cd ce cf  |................|
000000d0  d0 d1 d2 d3 d4 d5 d6 d7  d8 d9 da db dc dd de df  |................|
000000e0  e0 e1 e2 e3 e4 e5 e6 e7  e8 e9 ea eb ec ed ee ef  |................|
000000f0  f0 f1 f2 f3 f4 f5 f6 f7  f8 f9 fa fb fc fd fe ff  |................|
00000100  74 68 69 73 20 66 69 6c  65 20 69 73 3a 20 2f 55  |this file is: /U|
00000110  73 65 72 73 2f 70 6f 6c  79 2f 6a 75 6e 6b 2f 6a  |sers/poly/junk/j|
00000120  73 62 69 6e 54 65 73 74  2e 62 69 6e 0a           |sbinTest.bin.|

So why interact with binary files and, oh, say, After Effects? I have some ideas...