- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Examples

# stringFormatNode.cpp

    //-
    // ==========================================================================
    // Copyright 1995,2006,2008 Autodesk, Inc. All rights reserved.
    //
    // Use of this software is subject to the terms of the Autodesk
    // license agreement provided at the time of installation or download,
    // or which otherwise accompanies this software in either electronic
    // or hard copy form.
    // ==========================================================================
    //+

    // Example Plugin: stringFormatNode.cpp
    //
    // This plug-in is an example of a user-defined dependency graph node.
    // It takes several numbers as input, as well as a format string. It
    // generates a formatted string as an output.
    //
    // All occurences of ^[0-9][dft] are replaced with the corresponding
    // input parameter using the specified format.
    //
    // d means output as an integer.
    // f means output as a floating point value.
    // t means output as a timecode.
    //
    // For example, the string "format ^1f ^2d ^0t end format", using the
    // array {2.3, 3.4, 4.5} as an input will generate the result
    // "format 3.4 4 00:00:02 end format"
    //

    #include <math.h>

    #include <maya/MIOStream.h>
    #include <maya/MPxNode.h>
    #include <maya/MFnNumericAttribute.h>
    #include <maya/MFnTypedAttribute.h>
    #include <maya/MFnStringData.h>
    #include <maya/MFnPlugin.h>

    // The stringFormat class defines the attributes and methods necessary
    // for the stringFormatNode plugin
    //
    class stringFormat : public MPxNode
    {
    public:
                            stringFormat();
        virtual             ~stringFormat();

        virtual MStatus     compute( const MPlug& plug, MDataBlock& data );

        static  void*       creator();
        static  MStatus     initialize();

    public:
        static  MTypeId     id;                 // Unique node Id

        static  MObject     attrFormat;         // Formatting string
        static  MObject     attrValues;         // Input values (array)
        static  MObject     attrOutput;         // Result
    };

    MTypeId     stringFormat::id( 0x81034 );
    MObject     stringFormat::attrFormat;
    MObject     stringFormat::attrValues;
    MObject     stringFormat::attrOutput;

    // The creator method creates an instance of the stringFormat class
    // and is the first method called by Maya when a stringFormat node
    // needs to be created.
    //
    void* stringFormat::creator()
    {
        return new stringFormat();
    }

    // The initialize routine is called after the node has been created.
    // It sets up the input and output attributes and adds them to the
    // node. Finally the dependencies are arranged so that when the
    // inputs change Maya knowns to call compute to recalculate the output
    // value.
    //
    MStatus stringFormat::initialize()
    {
        MFnNumericAttribute numAttr;
        MFnTypedAttribute   typedAttr;
        MFnStringData       stringData;
        MStatus             stat;
        MStatus             stat2;

        // Setup the input attributes
        //
        attrFormat = typedAttr.create("format", "f", MFnData::kString,
                                      stringData.create(&stat2), &stat);
        CHECK_MSTATUS( stat2 );
        CHECK_MSTATUS( stat );
        CHECK_MSTATUS( typedAttr.setStorable( true ) );
        CHECK_MSTATUS( typedAttr.setKeyable( true ) );

        attrValues = numAttr.create("values", "v", MFnNumericData::kDouble,
                                    0, &stat);
        CHECK_MSTATUS( stat );
        CHECK_MSTATUS( numAttr.setArray( true ) );
        CHECK_MSTATUS( numAttr.setReadable( false ) );
        CHECK_MSTATUS( numAttr.setIndexMatters( true ) );
        CHECK_MSTATUS( numAttr.setStorable( true ) );
        CHECK_MSTATUS( numAttr.setKeyable( true ) );

        attrOutput = typedAttr.create( "output", "o", MFnData::kString,
                                       stringData.create(&stat2), &stat);
        CHECK_MSTATUS( stat2 );
        CHECK_MSTATUS( stat );
        CHECK_MSTATUS( typedAttr.setWritable( false ) );
        CHECK_MSTATUS( typedAttr.setStorable( false ) );

        // Add the attributes to the node
        //
        CHECK_MSTATUS( addAttribute( attrFormat ) );
        CHECK_MSTATUS( addAttribute( attrValues ) );
        CHECK_MSTATUS( addAttribute( attrOutput ) );

        // Set the attribute dependencies
        //
        CHECK_MSTATUS( attributeAffects( attrFormat, attrOutput ) );
        CHECK_MSTATUS( attributeAffects( attrValues, attrOutput ) );

        return MS::kSuccess;
    }

    // The constructor does nothing
    //
    stringFormat::stringFormat() {}

    // The destructor does nothing
    //
    stringFormat::~stringFormat() {}

    // The compute method is called by Maya when the input values
    // change and the output values need to be recomputed.
    // The input values are read then using sinf() and cosf()
    // the output values are stored on the output plugs.
    //

    // Look for ^[0-9][a-z] after the given index. Returns the position of
    // the characted just after '^'
    int findNextMatch(MString & string, int indx, int & param, char & letter)
    {
        // Warning this is not I18N compliant
        const char * str = string.asChar();
        while (str[indx]) {
            if ((str[indx] == '^') &&
                (str[indx+1] >= '0') && (str[indx+1] <= '9') &&
                (str[indx+2] >= 'a') && (str[indx+1] <= 'z')) {
                param = str[indx+1] - '0';
                letter = str[indx+2];
                return indx+1;
            }
            indx++;
        }

        return -1;
    }

    MStatus stringFormat::compute (const MPlug& plug, MDataBlock& data)
    {

        MStatus status;

        // Check that the requested recompute is one of the output values
        //
        if (plug == attrOutput) {
            // Read the input values
            //
            MDataHandle inputData = data.inputValue (attrFormat, &status);
            CHECK_MSTATUS( status );
            MString format = inputData.asString();

            // Get input data handle, use outputArrayValue since we do not
            // want to evaluate all inputs, only the ones related to the
            // requested multiIndex. This is for efficiency reasons.
            //
            MArrayDataHandle vals = data.outputArrayValue(attrValues, &status);
            CHECK_MSTATUS( status );

            int indx = 0;
            int param;
            char letter;
            while ((indx = findNextMatch(format, indx, param, letter)) > 0) {
                double val = 0.;
                status = vals.jumpToElement(param);
                if (status == MStatus::kSuccess) {
                    MDataHandle thisVal = vals.inputValue( &status );
                    if (status == MStatus::kSuccess) {
                        val = thisVal.asDouble();
                    }
                }
                MString replace;
                bool valid = false;
                switch (letter) {
                    case 'd':                   // Integer
                    val = floor(val+.5);
                    // No break here

                    case 'f':                   // Float
                    replace.set(val);
                    valid = true;
                    break;

                    case 't':                   // Timecode
                    {
                        const char * sign = "";
                        if (val<0) {
                            sign = "-";
                            val = -val;
                        }
                        int valInt = (int)(val+.5);
                        int sec = valInt / 24;
                        int frame = valInt - sec * 24;
                        int min = sec / 60;
                        sec -= min * 60;
                        int hour = min / 60;
                        min -= hour * 60;
                        char buffer[90];
                        if (hour>0)
                            sprintf(buffer, "%s%d:%02d:%02d.%02d",
                                    sign, hour, min, sec, frame);
                        else
                            sprintf(buffer, "%s%02d:%02d.%02d",
                                    sign, min, sec, frame);
                        replace = buffer;
                    }
                    valid = true;
                    break;
                }

                if (valid) {
                    format = format.substring(0, indx-2) +
                        replace + format.substring(indx+2, format.length()-1);
                    indx += replace.length() - 3;
                }
            }

            // Store the result
            //
            MDataHandle output = data.outputValue(attrOutput, &status );
            CHECK_MSTATUS( status );
            output.set( format );

        } else {
            return MS::kUnknownParameter;
        }

        return MS::kSuccess;
    }

    // The initializePlugin method is called by Maya when the plugin is
    // loaded. It registers the node and the UI.
    //
    MStatus initializePlugin ( MObject obj )
    {
        MStatus   status;
        MFnPlugin plugin( obj, PLUGIN_COMPANY, "8.0", "Any");

        // Register the node
        CHECK_MSTATUS_AND_RETURN_IT(
            plugin.registerNode( "stringFormat", stringFormat::id,
                                 stringFormat::creator, stringFormat::initialize));

        // Create the UI for the plugin.
        // Keep on going, do not fail the load because of this.
        CHECK_MSTATUS(plugin.registerUI("stringFormatCreateUI",
                                        "stringFormatDeleteUI", "", ""));

        return status;
    }

    // The unitializePlugin is called when Maya needs to unload the plugin.
    // It does the opposite of initialize.
    //
    MStatus uninitializePlugin( MObject obj)
    {
        MStatus   status;
        MFnPlugin plugin( obj );

        CHECK_MSTATUS_AND_RETURN_IT(plugin.deregisterNode( stringFormat::id ));

        return status;
    }