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:
Scripting command:GetSource which takes the name(s) of of one or more parameters and returns an XSICollection of its sources
Object model: Parameter.Source
C++ API: Parameter::GetSource
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
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.