## ShapeKey

Object Hierarchy | Related C++ Class: ShapeKey

### Inheritance

SIObject

ProjectItem

Property

ClusterProperty

ShapeKey

### Introduced

v4.0

### Description

The ShapeKey object represents a special ClusterProperty which is used to store a specific geometry. ShapeKeys provide information about how the shape of a cluster changes by either storing its absolute value, an offset based on the object referential or values base on the local reference frame of vertices.
You can find out the key's reference mode by getting the value of the KeyType Parameter. To convert from one reference mode to the other, use the ConvertShapeReferenceMode command.
Warning: The KeyType parameter uses a different set of values from the siShapeReferenceMode enum. For more information, see Shape Animation.
Because a ShapeKey modifies scene content when instantiated, it is an AnimationSource and as such can be accessed by using the AnimationSourceItem.Source method.

### Methods

AddCustomOp | AddICEAttribute | AddScriptedOp | AddScriptedOpFromFile
AnimatedParameters2 | BelongsTo | EvaluateAt | GetICEAttributeFromName
IsA | IsAnimated2 | IsClassOf | IsEqualTo
IsKindOf | IsLocked | IsSelected | LockOwners
RemoveICEAttribute | SetAsSelected | SetCapabilityFlag | SetLock
TaggedParameters | UnSetLock |  |

### Properties

Application | Branch | BranchFlag | Capabilities
Categories | Elements | EvaluationID | Families
FullName | Help | HierarchicalEvaluationID | ICEAttributes
LockLevel | LockMasters | LockType | Model
Name | NestedObjects | ObjectID | Origin
OriginPath | Owners | PPGLayout | Parameters
Parent | Parent3DObject | Selected | ShapeGroupName
Singleton | Type |  |

### Examples

#### 1. JScript Example

    /*
            The following example illustrates how to access the position
            information stored in a shape key. Adding a shape key using
            local reference mode will store the positions as deltas to
            the local referential of each point. To calculate the
            absolute position for each point you need the local
            referential for the point and the original position before
            the shape was applied.
    */
    // Create the scene with an object with a shape clip
    NewScene( null, false );
    var root = Application.ActiveSceneRoot;
    var c1 = root.AddGeometry( "Cube", "MeshSurface" );
    var c2 = Duplicate( c1, 1, 2, 1, 1, 0, 0, 1, 0, 1 )(0);
    Translate( c2, 10, 0, -2, siRelative, siView, siObj, siXYZ );
    Translate( c2 + ".pnt[2,3,6,LAST]", -6, -4, 4, siRelative, siView, siObj, siXYZ );
    // Add a shapeclip where the position data is stored as deltas to
    // the point's local referential. You can also store the shape in
    // absolute mode in which case the cluster property contains
    // for the shape the actual point data or you can store it in
    // object mode in which case deltas are stored in the object
    // referential (i.e. the pose of the object).
    var key = SelectShapeKey( c1, c2, siShapeLocalReferenceMode );
    var mix = ( root.HasMixer() ) ? root.Mixer : root.AddMixer();
    var shapetrack = AddTrack( root, mix, 1 );
    AddClip( root, key, "", shapetrack, 1, null, "", "", false );
    //
    // Get the shapeclip
    //
    var clip;
    var c = new Enumerator( mix.Clips );
    for ( ; !c.atEnd(); c.moveNext() ) {
            if ( c.item().Type == siClipShapeType ) {
                    clip = c.item();
            }
    }
    // We need to mute the shape combiner and all the operators above it
    // in order to get the local reference frame and position of each
    // point before the shape animation was applied to the geometry.
    // (This gives the position values before the shape that was stored
    // and does not include the result of deformations applied afterwards.)
    var geom = null;
    for ( var idx=0; idx<clip.MappedItems.Count; idx++ ) {
            var mi = clip.MappedItems(idx);
            Application.LogMessage( Application.ClassName(mi) );
            Application.LogMessage( Application.ClassName(mi.Source2) );
            // Get the cluster property/shape key stored in the shapeclip
            var clsprop = mi.Source2;
            // Since the geometry for each mappeditem will be
            // the same we'll only need to get it once.
            if ( !geom ) {
                    // Get the primitive and geometry from the cluster property
                    geom = clsprop.Parent.Parent;
                    var prim = geom.Parent;
                    // Deactivate all operators above and including the shape combiner
                    DeactivateAbove( prim.FullName + ".clustershapecombiner", true );
                    // Get the position array from the geometry
                    var aPosition = geom.Points.PositionArray;
                    // Get the old Geometry0D object to access
                    // the local reference frame for each point
                    var geomv1 = geom.Parent.Obj;
                    var o0DGeometry = geomv1.Geometry0D;
            }
            // Assume that the number of cluster property elements
            // matches the geometry and that they are same order i.e.
            // point0 == clusterelement0
            // Get the delta position array from the clusterproperty
            var vbaDelta = clsprop.Elements.Array;
            // Figure out the absolute position value using the deltas,
            // the original point position and the local referential
            // of each point
            for ( var i=vbaDelta.lbound(2); i<=vbaDelta.ubound(2); i++ ) {
                    // Set a vector3 with the delta for the position
                    var oDelta = XSIMath.CreateVector3(
                            vbaDelta.getItem(0,i),
                            vbaDelta.getItem(1,i),
                            vbaDelta.getItem(2,i)
                    );
                    // Get the local reference frame for the point
                    var oXAxis = XSIMath.CreateVector3();
                    var oYAxis = XSIMath.CreateVector3();
                    var oZAxis = XSIMath.CreateVector3();
                    var oXAxisValid, oYAxisValid, oZAxisValid;
                    var rtn = o0DGeometry.LocalReferenceFrame( i, oXAxis, oXAxisValid, oYAxis, oYAxisValid, oZAxis, oZAxisValid );
                    // Get the transformation matrix to go from point
                    // local reference frame to object referential
                    var oMatrix33 = XSIMath.CreateMatrix3(
                            oXAxis.X , oXAxis.Y, oXAxis.Z,
                            oYAxis.X , oYAxis.Y, oYAxis.Z,
                            oZAxis.X , oZAxis.Y, oZAxis.Z
                    );
                    oDelta.MulByMatrix3( oDelta, oMatrix33 );
                    // Set a vector3 with the point position
                    var oPosition = XSIMath.CreateVector3(
                            aPosition.getItem(0,i),
                            aPosition.getItem(1,i),
                            aPosition.getItem(2,i)
                    );
                    // Compute the absolute position by adding the delta
                    oPosition.AddInPlace( oDelta );
                    // Log the absolute position values for the point
                    Application.LogMessage( oPosition.X + "," + oPosition.Y + "," + oPosition.Z     );
            }
    }
    // Reactivate shapecombiner and all operators above it
    if ( !prim ) {
            DeactivateAbove( prim.FullName + ".clustershapecombiner", false );
    }
    // Enumerate the shapeclips under the mixer shape track.
    var t = new Enumerator( ActiveSceneRoot.Mixer.Tracks );
    for ( ; !t.atEnd(); t.moveNext() ) {
            if ( t.item().Type == siTrackShapeType ) {
                    var shapetrack = t.item();
                    break;
            }
    }
    for ( var s=0; s<shapetrack.Clips.Count; s++ ) {
            var shapeclip = shapetrack.Clips(s);
            Application.LogMessage( shapeclip );
    }
    // Expected results:
    // INFO : MappedItem
    // INFO : ShapeKey
    // INFO : -4,-4,-4
    // INFO : 4,-4,-4
    // INFO : -10,-8.881784197001252e-16,8.881784197001252e-16
    // INFO : -2.000000000000001,-8.881784197001252e-16,8.881784197001252e-16
    // INFO : -4,-4,4
    // INFO : 4,-4,4
    // INFO : -10.000000000000001,-8.881784197001252e-16,8.000000000000001
    // INFO : -2.0000000000000017,-1.7763568394002505e-15,8
    // INFO : Mixer.Mixer_Shape_Track.cube1_ShapeKey_Clip

#### 2. Python Example

    #
    # Three cones are created with identical animation although
    # they use different Reference Modes to record the shape
    #
    from win32com.client import constants as c
    app = Application
    app.NewScene( "", 0 )
    root = app.ActiveSceneRoot
    # Local mode
    obj1 = root.AddGeometry( "Cone", "MeshSurface" )
    app.ApplyOp( "bend", obj1 )
    obj1.posz.Value = -3
    geom1 = obj1.ActivePrimitive.Geometry
    clip1 = geom1.SaveShapeKey( 1, 1, c.siShapeLocalReferenceMode, c.siShapeInstanceOnlyMode, "MyShapeKey1", [1], [1,1,0] )
    # Absolute mode
    obj2 = root.AddGeometry( "Cone", "MeshSurface" )
    app.ApplyOp( "bend", obj2 )
    geom2 = obj2.ActivePrimitive.Geometry
    clip2 = geom2.SaveShapeKey( 1, 1, c.siShapeAbsoluteReferenceMode, c.siShapeInstanceOnlyMode, "MyShapeKey2", [1], [1,1,0] )
    # Object mode
    obj3 = root.AddGeometry( "Cone", "MeshSurface" )
    app.ApplyOp( "bend", obj3 )
    obj3.posz.Value = 3
    geom3 = obj3.ActivePrimitive.Geometry
    clip3 = geom3.SaveShapeKey( 1, 1, c.siShapeObjectReferenceMode, c.siShapeInstanceOnlyMode, "MyShapeKey3", [1], [1,1,0] )

#### 3. C# Example

    /*
            This example demonstrates how you access the shape information for a particular shape key by looking
            at the cluster properties which store this data
    */
    using System;
    using Softimage.XSIOM; // Softimage object model
    using Softimage.XSIMath;
    using Softimage.XSIUtil;
    using System.Text.RegularExpressions;   // for Regex class instanced in ShowShapeInformation
    public class XSIPlugin
    {
            public bool Load( PluginRegistrar in_reg )
            {
                    in_reg.Author = "ShapeKeyDemo";
                    in_reg.Name = "ShapeKeyCSexamplePlugin";
                    in_reg.Major = 1;
                    in_reg.Minor = 0;
                    in_reg.RegisterCommand("ShapeKeyCSexample", "ShapeKeyCSexample");
                    return true;
            }
    }
    public class ShapeKeyCSexample : XSIPlugin
    {
            public bool Init( Context in_ctxt )
            {
                    Command oCmd = null;
                    oCmd = (Command)in_ctxt.Source;
                    oCmd.Description = "ShapeKey object example written in CSharp";
                    oCmd.Tooltip = "CSharp ShapeKey example";
                    oCmd.ReturnValue = false;
                    return true;
            }
            // --------------------------------------------------------------------------
            // Gets the shape clusters on the specified object, accesses their shape keys
            // and reports on their reference mode and position
            public bool ShowShapeInformation( X3DObject in_obj )
            {
                    CXSIApplicationClass app = new CXSIApplicationClass();
                    try {
                            ClusterCollection oClusters = in_obj.ShapeAnimatedClusters();
                            app.LogMessage( "Dump of shape key data on object " + in_obj.FullName, siSeverity.siInfo );
                            long cntFoundShapes = 0;
                            // Go through the various clusters on the object
                            // (in practice only the Point clusters will have shape keys)
                            foreach ( Cluster oCluster in oClusters ) {
                                    // Cluster indices are not the same as the indices on the
                                    // geometry, but we can easily determine the relationship with
                                    // this array:
                                    ClusterElementCollection oClusterElementsCollection = oCluster.Elements;
                                    Array aElements = (Array)oClusterElementsCollection.Array;
                                    // Only search for the shape keys, which have type "clskey"
                                    PropertyCollection oLocalProps = oCluster.LocalProperties;
                                    PropertyCollection oClsKeyProps = oLocalProps.Filter("clskey", null, null);
                                    foreach ( ShapeKey oProperty in oClsKeyProps ) {
                                            // Found a shape key
                                            cntFoundShapes++;
                                            Regex rx = new Regex(@"ResultClusterKey$", RegexOptions.IgnoreCase);
                                            if ( rx.IsMatch(oProperty.Name) ) {
                                                    // There may also be an internal cluster property
                                                    // called "ResultClusterKey" which stores the
                                                    // result of blending the various shapes at the
                                                    // current time
                                                    app.LogMessage( "Blended shape at current time: " + oProperty.FullName, siSeverity.siInfo );
                                            } else {
                                                    app.LogMessage( "Shape key: " + oProperty.FullName, siSeverity.siInfo );
                                            }
                                            // The reference mode is available from the KeyType parameter
                                            Parameter oClsKeyTypeParam = oProperty.Parameters["KeyType"];
                                            Object ReferenceType =  oClsKeyTypeParam.get_Value(null);
                                            switch ( ReferenceType.ToString() ) {
                                                    case "0" :
                                                    case "6" :
                                                            app.LogMessage( "\tUses: Absolute Reference Mode", siSeverity.siInfo );
                                                            break;
                                                    case "1" :
                                                    case "5" :
                                                            app.LogMessage( "\tUses: Local Reference Mode", siSeverity.siInfo );
                                                            break;
                                                    case "2" :
                                                    case "3" :
                                                            app.LogMessage( "\tUses: Object Reference Mode", siSeverity.siInfo );
                                                            break;
                                                    default :
                                                            app.LogMessage( "\tReference Mode = " + ReferenceType.ToString(), siSeverity.siInfo );
                                                            break;
                                            }
                                            // The contents of the cluster can be found in this safearray
                                            // (which we can read via the VBArray object)
                                            ClusterElementCollection oShapeKeyElements = oProperty.get_Elements();
                                            Array aXYZArray = (Array)oShapeKeyElements.Array;
                                            for ( int i=0; i<aXYZArray.GetLength(aXYZArray.Rank-1); i++ ) {
                                                    // Print out the x,y,z values
                                                    app.LogMessage( "\tpnt["+ aElements.GetValue(i).ToString() + "] has position ("
                                                            + Math.Round( (double)aXYZArray.GetValue(0,i), 3 ).ToString() + ","
                                                            + Math.Round( (double)aXYZArray.GetValue(1,i), 3 ).ToString() + ","
                                                            + Math.Round( (double)aXYZArray.GetValue(2,i), 3 ).ToString() + ")",
                                                            siSeverity.siInfo
                                                    );
                                            }
                                    }
                            }
                            if ( cntFoundShapes == 0 ) {
                                    app.LogMessage( "There are no shapes on this object", siSeverity.siInfo );
                            }
                    } catch {
                            app.LogMessage( "Please select a 3D Object", siSeverity.siInfo );
                            return false;
                    }
                    return true;
            }
            public bool Execute( Context in_ctxt )
            {
                    // Set up a little scene
                    CXSIApplicationClass app = new CXSIApplicationClass();
                    Object[] args = new Object[2] { null, false };
                    Object rtn = app.ExecuteCommand( "NewScene", args );
                    Model root = app.ActiveSceneRoot;
                    X3DObject oCircle = root.AddGeometry( "Sphere", "MeshSurface", null );
                    // Move the top and bottom point of the sphere outwards
                    Primitive oPrim = oCircle.ActivePrimitive;
                    Geometry oGeom = oPrim.get_Geometry( null );
                    Object[] aIndexArray = new Object[2] { 1, 0  };
                    Object[] aPositionArray = new Object[6] { 0,6,0, 0,-6,0 };
                    oGeom.SaveShapeKey( 0.5, 3.0, siShapeReferenceMode.siShapeAbsoluteReferenceMode,
                            siShapeInstanceMode.siShapeInstanceOnlyMode, "ShapeKey1", aIndexArray, aPositionArray, null );
                    // Move two different points - this creates a new, independent cluster
                    // We can use a different reference mode for this, but we still specify the
                    // point positions in Absolute terms.
                    aIndexArray = new Object[2] { 5, 33  };
                    aPositionArray = new Object[6] { -2,0,0, 2,0,0 };
                    oGeom.SaveShapeKey( 0.8, 1.0, siShapeReferenceMode.siShapeLocalReferenceMode,
                            siShapeInstanceMode.siShapeInstanceOnlyMode, "ShapeKey2", aIndexArray, aPositionArray, null );
                    if ( ShowShapeInformation(oCircle) ) {
                            return true;
                    } else {
                            app.LogMessage("ShowShapeInformation failed.", siSeverity.siInfo);
                            return false;
                    }
            }
    }
    // --------------------------------------------------------------------------
    // Expected output:
    // INFO : Dump of shape key data on object sphere
    // INFO : Blended shape at current time: sphere.polymsh.cls.Shape.ResultClusterKey
    // INFO :       Uses: Absolute Reference Mode
    // INFO :       pnt[1] has position (0,0,0)
    // INFO :       pnt[0] has position (0,0,0)
    // INFO : Shape key: sphere.polymsh.cls.Shape.ShapeKey
    // INFO :       Uses: Absolute Reference Mode
    // INFO :       pnt[1] has position (0,6,0)
    // INFO :       pnt[0] has position (0,-6,0)
    // INFO : Blended shape at current time: sphere.polymsh.cls.Shape1.ResultClusterKey
    // INFO :       Uses: Local Reference Mode
    // INFO :       pnt[5] has position (0,0,0)
    // INFO :       pnt[33] has position (0,0,0)
    // INFO : Shape key: sphere.polymsh.cls.Shape1.ShapeKey2
    // INFO :       Uses: Local Reference Mode
    // INFO :       pnt[5] has position (0,-2,0)
    // INFO :       pnt[33] has position (0,-2,0)

### See Also

ShapeClip ActionSource Clip.Source StoreShapeKey SelectShapeKey