Communication between manipulators and nodes
 
 
 

Manipulators communicate with plugs on nodes to set the values of those plugs and also to set manipulator values appropriately with respect to the values of the plugs.

The communication between manipulators and nodes can be done in one of two ways: simple one-to-one associations, or through the use of more complex conversion functions. The following diagram illustrates how the communication between nodes and manipulators takes place.

The converter in the diagram is the mechanism that manages the communication between the plugs on the nodes and manipulator values. The arrows indicate the direction the information flows. Each container manipulator has one converter which is the interface between the container’s children manipulators and the plugs they affect.

There are a number of data items identified by square boxes on the converter and the base manipulators. The items on the converter that are related to children manipulator values are called converterManipValue items, and the items on the converter that are related to the node plug values are called converterPlugValue items.

The items on the base manipulators are called manipValue items. Some of these manipValue items relate directly to an affordance of the manipulator. For example, the MFnDiscManip::angleIndex relates directly to the rotation affordance of the DiscManip.

Other manipValue items do not relate to an affordance of the manipulator, but provide important information on the position or orientation of the manipulator such as MFnDiscManip::centerIndex and MFnDiscManip::axisIndex.

Each converterManipValue item and each converterPlugValue item has an integer index that uniquely identifies that item. Each manipValue item on a base manipulator also has an integer index that uniquely identifies that item.

Note

There is a one-to-one correspondence between a converterManipValue item and a base manipulator’s manipValue item, and these corresponding items share the same integer index. There is also a one-to-one correspondence between a converterPlugValue item and a plug on a node affected by the manipulator.

As seen in the diagram, the one-to-one associations are directly between a converterManipValue item and a converterPlugValue item.

More complex conversions between converterManipValue items and converterPlugValue items are performed through conversion functions. These functions can use any number of converterPlugValue or converterManipValue items to calculate the value of the corresponding converterManipValue or converterPlugValue item.

One-to-one associations

One-to-one assocations between a converterManipValue item and a converterPlugValue item are established through methods on the manipulator classes derived from MFnManip3D. These methods and the data types corresponding to the plugs they connect to are:

These methods should be called from the connectToDependNode method described above. For example, in the footPrintManip:

MStatus footPrintLocatorManip::connectToDependNode
    (const MObject &node)
{
    ...
    MFnDistanceManip distanceManipFn(fDistanceManip);
    MFnDependencyNode nodeFn(node);
    MPlug sizePlug = nodeFn.findPlug("size", &stat);
    if (MStatus::kFailure != stat) {
        distanceManipFn.connectToDistancePlug(sizePlug);
        ...
        finishAddingManips();
        MPxManipContainer::connectToDependNode(node);
    }
    return stat;
} 

Conversion functions

Conversion functions are used to convert between manipulator values and plug values. They are implemented as callback methods. A simple example of a manipulator that uses conversion functions is a container manipulator with a DiscManip connected to a plug (that is associated with an attribute of type MFnUnitAttribute::kAngle) that takes the rotation of the disc manip and multiplies that rotation by 10. The conversion function uses MPxManipContainer:: getConverterManipValue on the MFnDiscManip::angleIndex and then multiplies that angle by 10.

Conversion functions are very useful when the position of a manipulator has to be affected by the position of an object, or to move a group of manipulators together in a specific way. Without conversion functions, manipulators would not be able to move together as a unit, and certain components of the manipulator would remain either at the origin or a fixed position in space.

Note

Conversion functions are not required in situations where a FreePointTriadManip specifies a position or a PointOnCurveManip specifies a parameter along a curve. However, if you have a DiscManip that you want to move along with the PointOnCurveManip, you need a conversion function to give the DiscManip information about its position and normal.

There are two kinds of conversion callback methods: manipToPlug and plugToManip.

plugToManip

A plugToManip conversion callback is used to get the value of a converterManipValue item from various converterPlugValue items. This callback has access to all the converterPlugValue items and returns the value of a converterManipValue item.

manipToPlug

A manipToPlug conversion callback is used to get the value of a converterPlugValue item from various converterManipValue items. This callback has access to all the converterManipValue items and returns the value of a converterPlugValue item.

In general, manipToPlug conversions are less commonly used. In addition to using converterPlugValues and converterManipValues, it is sometimes useful to use class data, such as a DAG path. (See the footPrintManip for an example of how fNodePath is used to calculate the node translation.) For manipulators that operate on components, it may also be useful to store initial component positions (see the componentScaleManip for an example of how this is done).

MManipData

The conversion callback methods return a data type called MManipData. MManipData encapsulates manipulator data which is returned from the manipulator conversion functions. It represents data that is either simple or complex. The simple data methods on MManipData are used to represent bool, short, long, unsigned, float, and double types.

Note

Sometimes attributes associated with the simple data types have higher level meanings such as distance, angle, and time (for example, MFnUnitAttribute::kAngle, MFnUnitAttribute::kDistance, and MFnUnitAttribute::kTime).

MManipData is also used to represent complex data types created by MFnData or classes derived from MFnData, such as matrices, curves, and arrays of data.

The footPrintManip example plug-in has an example of a plugToManip conversion callback called startPointCallback. The startPointCallback returns an MManipData which is set to be an MObject created by MFnNumericData.

class footPrintLocatorManip : public MPxManipContainer
{
    public:
        ...
        MManipData startPointCallback(unsigned index) const;
        MVector nodeTranslation() const;
        MDagPath fDistanceManip;
        ...
};
MManipData footPrintLocatorManip::startPointCallback
    (unsigned index) const
{
    // The index is the startPointIndex that is
    // specified in addPlugToManipConversionCallback,
    // but it is not necessary to use this in the callback.
    MFnNumericData numData;
    MObject numDataObj =
        numData.create(MFnNumericData::k3Double);
    MVector vec = nodeTranslation();
    numData.setData(vec.x, vec.y, vec.z);
    return MManipData(numDataObj);
}
MStatus footPrintLocatorManip::connectToDependNode
    (const MObject &node)
{
    ...
    unsigned startPointIndex =
        distanceManipFn.startPointIndex();
    addPlugToManipConversionCallback(
        startPointIndex, 
        (plugToManipConversionCallback) startPointCallback);
    ...
}