blindComplexDataCmd.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.
// ==========================================================================
//+


#include <maya/MIOStream.h>
#include <maya/MFnPlugin.h>
#include <maya/MString.h>
#include <maya/MArgList.h>

#include <maya/MPxCommand.h>

#include <maya/MGlobal.h>
#include <maya/MItSelectionList.h>

#include <maya/MFnDependencyNode.h>
#include <maya/MFnTypedAttribute.h>

#include <maya/MPxData.h>
#include <maya/MTypeId.h>
#include <maya/MPlug.h>  
#include <maya/MFnPluginData.h>
#include <maya/MFnNurbsCurve.h>

#include <assert.h>

struct CVData {

    MStatus readASCII( const MArgList&, unsigned& );
    MStatus writeASCII( ostream& );

    MStatus readBinary( istream& in );
    MStatus writeBinary( ostream& out );

    double  _doubleData;
    int     _intData;

    static CVData empty;
};

CVData CVData::empty;

//
// Proxy data class declaration
//

class blindComplexData : public MPxData
{
public:
    blindComplexData();
    blindComplexData( const unsigned int );
    virtual ~blindComplexData();

    //
    // Override methods in MPxData.
    virtual MStatus         readASCII( const MArgList&, unsigned& lastElement );
    virtual MStatus         readBinary( istream& in, unsigned length );
    virtual MStatus         writeASCII( ostream& out );
    virtual MStatus         writeBinary( ostream& out );

    //
    // Custom methods.
    virtual void            copy( const MPxData& ); 

    MTypeId                 typeId() const; 
    MString                 name() const;

    bool                setLength( const unsigned int numCVs,
                                       const bool copyOldData = false );
    
    const CVData &operator [] ( const unsigned int index ) const;
    CVData &operator [] ( const unsigned int index );

    unsigned int                    length() const;

    //
    // static methods and data.
    static const MString    typeName;
    static const MTypeId    id;
    static void*            creator();

private:

    bool indexOk( const unsigned int ) const;

    // 
    // any customized data can be declared here, in this case, an array of 
    // CVData that is associated with the CVs. 
    
    CVData*                 _CVDataArrayPtr;    
    unsigned int                    _length;
};

const MTypeId blindComplexData::id( 0x80002 );
const MString blindComplexData::typeName( "blindComplexData" );

class blindComplexDataCmd : public MPxCommand
{
public:
                        blindComplexDataCmd();
    virtual             ~blindComplexDataCmd(); 

    MStatus             doIt( const MArgList& args );
    MStatus             redoIt();
    MStatus             undoIt();
    bool                isUndoable() const;
    
public:
    static void*        creator();
    MItSelectionList*   iter;
};


//
// Proxy data class implementation
//

void* blindComplexData::creator()
{
    return new blindComplexData;
}

blindComplexData::blindComplexData()
    :_CVDataArrayPtr( NULL ),
     _length(0)
{
}

blindComplexData::~blindComplexData()
{
    delete [] _CVDataArrayPtr;
    _length = 0;
}

blindComplexData::blindComplexData( const unsigned int cvNum )
    : _length(0),
      _CVDataArrayPtr( NULL )
{
    setLength( cvNum );
}

//
// Set the length of the array  
bool blindComplexData::setLength ( const unsigned int cvNum, 
                                         const bool copyOldData ) 
{
    bool ret = true;
    if ( _length != cvNum || _CVDataArrayPtr == NULL ) {
        CVData* ptr;
        if ( _length != 0 && ! copyOldData ) {
            cerr << "warning: might be erasing previous data." << endl;
        }
        ptr = new CVData[cvNum];
        if ( ptr == NULL ) {
            cerr << "out of memory, setLength() failed" << endl;
            ret = false;
        } else {
            if ( copyOldData ) {
                if ( cvNum < _length ) {
                    cerr << "warning: new size not big enough for old data." 
                         << endl;
                }
                for ( unsigned int i = 0; i < cvNum; i++ ) {
                    ptr[i] = _CVDataArrayPtr[i];  
                }
            }
            delete [] _CVDataArrayPtr;
            _CVDataArrayPtr = ptr;
            _length = cvNum;
        }
    } else {
        cerr << "warning: setLength() o.k., same length, no change." << endl;
    }
    return ret;
}

unsigned int blindComplexData::length ( ) const 
{
    return _length;
}

inline 
bool blindComplexData::indexOk( const unsigned int index ) const {
    return ( index < _length );
}

const CVData& blindComplexData::operator [] ( const unsigned int index ) const 
{
    if ( indexOk( index  ) ) {
        return _CVDataArrayPtr[index];
    } else {
        cerr << "indexing error.  operator [] failed, returning invalid object"
             << endl;
        return CVData::empty;
    }
}

CVData &blindComplexData::operator [] ( const unsigned int index ) 
{
    if ( indexOk( index  ) ) {
        return _CVDataArrayPtr[index];
    } else {
        cerr << "indexing error.  operator [] failed, returning invalid object"
             << endl;
        // 
        // to get away a compiler warning.
        return CVData::empty; 
    }
}

void blindComplexData::copy( const MPxData& other )
//
//  Deescription:
//      Perform a copy or a conversion
//
{ 
    if( other.typeId() == blindComplexData::id ) {
        const blindComplexData* otherData =
                                        (const blindComplexData*)&other;
        if ( _length != otherData->_length ) {
            if ( ! setLength( otherData->_length ) ) {
                return;
            }
        } 
        for ( unsigned int i = 0; i < _length; i++ ) {
            _CVDataArrayPtr[i] = (*otherData)[i];
        }

    } else {
        //  we need to convert to the other type based on its iff Tag
        cerr << "wrong data format!" << endl;
    }
    return;
}

MTypeId blindComplexData::typeId() const
{
    return blindComplexData::id;
}

MString blindComplexData::name() const
{ 
    return blindComplexData::typeName; 
}

MStatus blindComplexData::readASCII(  const MArgList& args,
                                        unsigned& lastParsedElement )
{
    MStatus status;
    int argLength = args.length();
    if( argLength > 0 ) {
        int numDataRecord = (argLength - lastParsedElement); 
        //
        // Note: a better solution to determine the number of records is to
        // write out the number of records in the writeASCII() routine.
        //
        if ( numDataRecord % 2 != 0 ) {
            cerr << "warning: corrupted data for blindComplexData" << endl;
        }
        // 
        // 2 numbers per record.
        numDataRecord /= 2;
        setLength(numDataRecord); 
        for ( unsigned int i=0; i < _length; i++ ) {
            status = _CVDataArrayPtr[i].readASCII(args, lastParsedElement);
            if ( status != MS::kSuccess ) {
                return status;
            }
        }
        return MS::kSuccess;
    } 
    return MS::kFailure;
}

MStatus CVData::readASCII ( const MArgList& args,
                            unsigned& lastParsedElement ) 
{
    MStatus status;
    _intData = args.asInt( lastParsedElement++, &status );
    if ( status == MS::kSuccess ) {
        _doubleData = args.asDouble( lastParsedElement++, &status );
    } 
    return status;
}
                
MStatus CVData::writeASCII ( ostream& out )  
{
    out << _intData << " " << _doubleData << " ";
    return out.fail() ? MS::kFailure : MS::kSuccess;
}

MStatus blindComplexData::writeASCII( ostream& out )
{
    MStatus status;
    for ( unsigned int i=0; i < _length; i++ ) {
        status = _CVDataArrayPtr[i].writeASCII(out);
        if ( status != MS::kSuccess ) {
            return MS::kFailure;
        }
    }
    return MS::kSuccess;
}

MStatus blindComplexData::readBinary( istream& in, unsigned length )
{
    MStatus status;
    if ( length > 0 ) {
        unsigned int recNum;
        in.read( (char*) &recNum, sizeof(recNum) );
        if ( ! in.fail() && recNum > 0 ) {
            setLength(recNum); 
            for ( unsigned int i=0; i < _length; i++ ) {
                status = _CVDataArrayPtr[i].readBinary(in);
                if ( status != MS::kSuccess ) {
                    return status;
                }
            }
        }
    } else {
        return MS::kFailure;
    }
    return MS::kSuccess;
}

MStatus blindComplexData::writeBinary( ostream& out )
{
    MStatus status;
    out.write( (char*) &_length, sizeof(_length) );
    if ( ! out.fail() ) {
        for ( unsigned int i=0; i < _length; i++ ) {
            status = _CVDataArrayPtr[i].writeBinary(out);
            if ( status != MS::kSuccess ) {
                return status; 
            }
        }
    } else {
        return MS::kFailure;
    }
    return MS::kSuccess;
}

MStatus CVData::readBinary ( istream& in )
{
    in.read( (char*) &_intData, sizeof(_intData) );
    if ( !in.fail() ) {
        in.read( (char*) &_doubleData, sizeof(_doubleData) );
    } else {
        return MS::kFailure;
    }
    return in.fail() ? MS::kFailure : MS::kSuccess;
}
                
MStatus CVData::writeBinary ( ostream& out )  
{
    out.write( (char*) &_intData, sizeof(_intData) );
    if ( !out.fail() ) {
        out.write( (char*) &_doubleData, sizeof(_doubleData) );
    } else {
        return MS::kFailure;
    }
    return out.fail() ? MS::kFailure : MS::kSuccess;
}


//
// Command class implementation
//

void* blindComplexDataCmd::creator()
{
    return new blindComplexDataCmd;
}

blindComplexDataCmd::~blindComplexDataCmd()
{
}

blindComplexDataCmd::blindComplexDataCmd()
    : MPxCommand()
{
}

MStatus blindComplexDataCmd::doIt( const MArgList& )
{
    MStatus stat;

    // Create a selection list iterator
    //
    MSelectionList list;
    MGlobal::getActiveSelectionList( list );
    iter = new MItSelectionList( list,  MFn::kInvalid, &stat    );
    if ( MS::kSuccess == stat )
        stat = redoIt();

    return stat;
}

MStatus blindComplexDataCmd::redoIt()
{
    MStatus     stat;               // Status code
    MObject     dependNode;     // Selected dependency node

    // Iterate over all selected dependency nodes
    //
    for ( ; !iter->isDone(); iter->next() ) 
    {
        // Get the selected dependency node and create
        // a function set for it
        //
        if ( MS::kSuccess != iter->getDependNode( dependNode ) ) {
            cerr << "Error getting the dependency node" << endl;
            continue;
        }
        
        MFnDependencyNode fnDN( dependNode, &stat );
        if ( MS::kSuccess != stat ) {
            cerr << "Error creating MFnDependencyNode" << endl;
            continue;
        }

        // Create a new attribute for our blind data
        //
        // cout << "Creating attr..." << endl;
        
        MFnTypedAttribute fnAttr;
        const MString fullName( "blindComplexData" );
        const MString briefName( "BCD" );
        MObject newAttr = fnAttr.create( fullName, briefName,
                                         blindComplexData::id );

        // Now add the new attribute to the current dependency node
        //
        // cout << "Adding attr..." << endl;
        fnDN.addAttribute( newAttr, MFnDependencyNode::kLocalDynamicAttr );
        
        //
        // now we will demonstrate setting the value by using a plug.
        MPlug plug( dependNode, newAttr );  

        //
        // create an instance of the blind data with an initial array size of
        // 5.
        blindComplexData * newData = new blindComplexData( 5 );

        //
        // initialized 
        // cout << "setting data values..." << endl;
        unsigned int i;
        for ( i= 0; i < newData->length(); i++ ) {
            (*newData)[i]._intData = 10 + i;
            (*newData)[i]._doubleData = 20.02 + i;
        }

        // 
        // setting the value for the plug.
        stat = plug.setValue( newData );

        //
        // The following code demonstrates the retrieving of data from the 
        // plug.
        MObject sData;
        stat = plug.getValue( sData );

        if ( stat != MS::kSuccess ) {
            cerr << "error getting value off plug" << endl;
            continue;
        }

        // 
        // Convert the data from an MObject back to a pointer to MPxData, then
        // cast it back to a pointer to blindComplexData.
        MFnPluginData pdFn( sData ); 
        blindComplexData* data =
                            ( blindComplexData* ) pdFn.constData( &stat );
        //
        // read the data, and set the result to the values set.
        
        clearResult();

        if ( NULL != data ) {
            // cout << "retrieving data values..." << endl;
            for ( i = 0; i < data->length(); i++ ) {
                // cout << "rec #" << i << ": " << (*data)[i]._intData << ", "; 
                // cout << (*data)[i]._doubleData << endl;
                appendToResult((double) ((*data)[i]._intData));
                appendToResult((*data)[i]._doubleData);
            }
        } else {
            // cout << "Null data" << endl;
        }

    }
    return MS::kSuccess;
}

MStatus blindComplexDataCmd::undoIt()
{
    return MS::kSuccess;
}

bool blindComplexDataCmd::isUndoable() const
{
    return true;
}


//
// The following routines are used to register/unregister
// the command we are creating within Maya
//
MStatus initializePlugin( MObject obj )
{ 
    MStatus   status;
    MFnPlugin plugin( obj, PLUGIN_COMPANY, "3.0", "Any" );

    status = plugin.registerData( "blindComplexData",
                          blindComplexData::id,
                          blindComplexData::creator );
    if (!status) {
        status.perror("registerData");
        return status;
    }

    status = plugin.registerCommand(  "blindComplexData",
                             blindComplexDataCmd::creator );
    if (!status) {
        plugin.deregisterData( blindComplexData::id );
        status.perror("registerCommand");
        return status;
    }

    return status;
}

MStatus uninitializePlugin( MObject obj)
{
    MStatus   status1, status2;
    MFnPlugin plugin( obj );

    status1 = plugin.deregisterCommand( "blindComplexData" );
    if (!status1) {
        status1.perror("deregisterCommand");
    }
    status2 = plugin.deregisterData( blindComplexData::id );
    if (!status2) {
        status2.perror("deregisterData");
    }

    if ( !status1 )
        return status1;
    return status2;
}