Accessing Expressions

 
 
 

An expression is a piece of data (a DataSource ) that drives a parameter value, which means it is accessible using any of the following approaches:

You can also get animation information from clips and sources that are based on expressions. For more information, see Expressions and the Mixer.

Python Example: Using the GetSource Command

This example demonstrates how to use the GetSource ommand in Python to get a pointer to the expression. This example continues from Python Example: Setting an Expression with the SetExpr Command, which applied an expression to the null's position:

# Find the expressions on Null4Expr
rtn = Application.GetSource( params, c.siExpressionSource )
for expr in rtn :
	# From the expression we can access its output port
	for oport in expr.OutputPorts :
		target = oport.Target2
		Application.LogMessage( "Expression is writing " + str(target.Value) + " to " + target.FullName );
	

# Expected result:
#INFO : Expression is writing 2.0 to Null4Expr.kine.local.posx
#INFO : Expression is writing 2.0 to Null4Expr.kine.local.posy
#INFO : Expression is writing 2.0 to Null4Expr.kine.local.posz

JScript Example: Using the Source Property

This example demonstrates how to use the Source property in JScript to get a pointer to the expression. This example continues from JScript Example: Setting an Expression with the Parameter.AddExpression Method, which applied an expression to the null's position:

// Find the expressions on Null4Expr
var params2search = n.AnimatedParameters();
for ( var i=0; i<params2search.Count; i++ ) {
	var expr = params2search(i).Source;

	// From the expression we can access its output port
	var oports = new Enumerator( expr.OutputPorts );
	for ( ; !oports.atEnd(); oports.moveNext() ) {
		var target = oports.item().Target2;
		Application.LogMessage( "Expression is writing " + target.Value + " to " + target.FullName );
	}
}

// Expected result:
//INFO : Expression is writing 2 to Null4Expr.kine.local.posx
//INFO : Expression is writing 2 to Null4Expr.kine.local.posy
//INFO : Expression is writing 2 to Null4Expr.kine.local.posz

C++ Example: Using the GetSource Member

This example demonstrates how to use the GetSource member in C++ to get a pointer to the expression. This example continues from C++ API Example: Setting an Expression with the Parameter::AddExpression Member Function, which applied an expression to the null's position:

// Find the expressions on Null4Expr
CRefArray params2search = n.GetAnimatedParameters( siExpressionSource );
for ( LONG i=0; i<params2search.GetCount(); ++i ) 
{
	Parameter posparam( params2search[i] );
	Expression expr( posparam.GetSource() );

	// From the expression we can access its output port
	CRefArray oports( expr.GetOutputPorts() );
	for ( LONG j=0; j<oports.GetCount(); ++j ) 
	{
		OutputPort currport( oports[j] );
		Parameter target( currport.GetTarget() );
		app.LogMessage( L"Expression is writing " + CString(target.GetValue()) + L" to " + target.GetFullName() );
	}
}

// Expected results:
//INFO : Expression is writing 2 to Null4Expr.kine.local.posx
//INFO : Expression is writing 2 to Null4Expr.kine.local.posy
//INFO : Expression is writing 2 to Null4Expr.kine.local.posz

Expressions and the Mixer

Storing Expressions as Actions

The only way to store an action via scripting or the C++ API is by using the StoreAction (or SIStoreAction) command. The StoreAction command stores several different kinds of low-level animation as an ActionSource or ActionSource besides expressions: fcurves, constraints, and static values.

JScript Example: Creating Expression-based Sources and Clips

This example demonstrates how to set up an linked-parameter expression between two nulls and then store it as an action. The example continues in JScript Example: Accessing Expression-based ActionSources and Clips with accessing the source from the model and the clip from the mixer's track.

var root = Application.ActiveSceneRoot;
var jack = root.AddNull("jack");
var diane = root.AddNull("diane");

// Get list of parameters to mark
var params = jack+".kine.local.posx,";
params += jack+".kine.local.posy,";
params += jack+".kine.local.posz";

// Create an Expression (linked parameter)
AddExpr( jack+".kine.local.posx", "l_fcv("+diane+".kine.local.posx)-0.25", true );
AddExpr( jack+".kine.local.posy", "l_fcv("+diane+".kine.local.posy)",      true );
AddExpr( jack+".kine.local.posz", "l_fcv("+diane+".kine.local.posz)+0.25", false );

// Make the Expressions into an Action
StoreAction( root, params, 3, "StoredExprAction", true, 0, 0, false, false, true, 0);

C++ Example: Creating Expression-based Sources and Clips

This example demonstrates how to create an expression source and instantiate it as a clip in the mixer using the C++ API. The example continues in C++ Example: Accessing an Expression-based ActionSource and Clip with accessing the source and clip from the model and the clip from the mixer's track.

Application app = Application();
Model root = app.GetActiveSceneRoot();
Null jack; root.AddNull( L"jack", jack );
Null diane; root.AddNull( L"diane", diane );

// Get list of parameters to mark
CString jname = jack.GetFullName();
CString dname = diane.GetFullName();
CString params = jname + L".kine.local.posx," + jname + L".kine.local.posy," + jname + L".kine.local.posz";

// Create an Expression (linked parameter) on posx
CValueArray addExprArgs; CValue outArg;
addExprArgs.Add( jname + L".kine.local.posx" );
addExprArgs.Add( L"l_fcv(" + dname + L".kine.local.posx)-0.25" );
addExprArgs.Add( true );
app.ExecuteCommand( L"AddExpr", addExprArgs, outArg );

// For the second call, just change the first two arguments
addExprArgs[0] = jname + L".kine.local.posy";
addExprArgs[1] = L"l_fcv(" + dname + L".kine.local.posy)";
app.ExecuteCommand( L"AddExpr", addExprArgs, outArg );

// For the third call, change all arguments
addExprArgs[0] = jname + L".kine.local.posz";
addExprArgs[1] = L"l_fcv(" + dname + L".kine.local.posz)+0.25";
addExprArgs[2] = false;
app.ExecuteCommand( L"AddExpr", addExprArgs, outArg );

// Make the Expressions into an Action
CValueArray storeActionArgs; 
storeActionArgs.Add( root.GetFullName() );
storeActionArgs.Add( params );
storeActionArgs.Add( CValue(3.0) );
storeActionArgs.Add( L"StoredExprAction" );
storeActionArgs.Add( true );
storeActionArgs.Add( CValue(0.0) );
storeActionArgs.Add( CValue(0.0) );
storeActionArgs.Add( false );
storeActionArgs.Add( false );
storeActionArgs.Add( true );
storeActionArgs.Add( CValue(0.0) );
app.ExecuteCommand( L"StoreAction", storeActionArgs, outArg );

Accessing Expression-based Sources and Clips

The ActionSource or ActionSource is available from the Model under which it is stored or instantiated via the Model.Sources property (OM) or the Model::GetSources member (C++ API).

The Clip is available from either the mixer or the compound clip on which it is instantiated. It is also available directly off its Track owner. For the object model, the properties to use are ClipContainer.Clips and Track.Clips. For the C++ API you can use ClipContainer::GetClips and Track::GetClips.

JScript Example: Accessing Expression-based ActionSources and Clips

This example demonstrates how to get the ActionSource via the Model.Sources property and how to get the Clip via the Track.Clips property. This example continues from JScript Example: Creating Expression-based Sources and Clips, which applied an expression to the null's position and then stored and instantiated it.

// Get the new source from the model
var sourcelist = new Enumerator( root.Sources );
for ( ; !sourcelist.atEnd(); sourcelist.moveNext() ) {
	var src = sourcelist.item();
	// Make sure we are getting the right source by testing the underlying
	// AnimationSourceItem to verify that it is an Expression
	if ( src.SourceItems(0).Type == siExpressionAnimItem ) {
		Application.LogMessage( "Found " + src.FullName );
	}
}

// Get the clip from the mixer via the Track object
if ( root.HasMixer() ) {
	var alltracks = new Enumerator( root.Mixer.Tracks );
	for ( ; !alltracks.atEnd(); alltracks.moveNext() ) {
		// Make sure to skip audio and shape tracks
		if ( alltracks.item().Type == siTrackAnimationType ) {
			var currtrack = alltracks.item();
			var cliplist = new Enumerator( currtrack.Clips );
			for ( ; !cliplist.atEnd(); cliplist.moveNext() ) {
				var clp = cliplist.item();
				Application.LogMessage( "Found " + clp.FullName );
			}
		}
	}
}

//INFO : Found Sources.Scene_Root.StoredExprAction
//INFO : Found Mixer.Mixer_Anim_Track.StoredExprAction_Clip

C++ Example: Accessing an Expression-based ActionSource and Clip

This example demonstrates how to get the ActionSource via Model::GetSources and how to get the Clip via Track::GetClips. This example continues from C++ Example: Creating Expression-based Sources and Clips, which applied an expression to the null's position and then stored and instantiated it.

// Get the new source from the model
CRefArray sourcelist = root.GetSources();
for ( LONG i=0; i<sourcelist.GetCount(); ++i ) 
{
	ActionSource src( sourcelist[i] );
	// Make sure we are getting the right source by testing the underlying
	// AnimationSourceItem to verify that it is an Expression
	CRefArray underlying = src.GetItems();
	AnimationSourceItem animsrcitm( underlying[0] );
	if ( animsrcitm.GetType() == siExpressionAnimItem ) 
	{
		app.LogMessage( L"Found " + src.GetFullName() );
	} 
}

// Get the clip from the mixer via the Track object
if ( root.HasMixer() ) {
	CRefArray alltracks = root.GetMixer().GetTracks();
	for ( LONG j=0; j<alltracks.GetCount(); ++j ) 
	{
		// Make sure to skip audio and shape tracks
		Track currtrack( alltracks[j] );
		if ( currtrack.GetType() == siTrackAnimationType ) {
			CRefArray cliplist = currtrack.GetClips();
			for ( LONG k=0; k<cliplist.GetCount(); ++k ) 
			{
				Clip clp( cliplist[k] );
				app.LogMessage( L"Found " + clp.GetFullName() );
			}
		}
	}
}

// Expected results:
//INFO : Found Sources.Scene_Root.StoredExprAction
//INFO : Found Mixer.Mixer_Anim_Track.StoredExprAction_Clip

How Do Expressions Interact with Constraints?

Expressions are always evaluated before constraints on the same object. If an expression is reading from a parameter that is driven by a constraint, the data pulled by the expression may be stale. You can work around this problem by applying the constraint to a null and reading the data from there instead.