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

//
// Lasso selection within a user defined context.
//

#include <maya/MIOStream.h>
#include <math.h>
#include <stdlib.h>

#include <maya/MFnPlugin.h>
#include <maya/MString.h>
#include <maya/MGlobal.h>
#include <maya/M3dView.h>
#include <maya/MDagPath.h>
 
#include <maya/MItSelectionList.h>

#include <maya/MPxContextCommand.h>
#include <maya/MPxContext.h>
#include <maya/MCursor.h>
#include <maya/MEvent.h>

#if defined(OSMac_MachO_)
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#endif

#include <maya/MPoint.h>
#include <maya/MPointArray.h>
#include <maya/MItCurveCV.h>
#include <maya/MItSurfaceCV.h>
#include <maya/MItMeshVertex.h>
#include <maya/MItMeshEdge.h>
#include <maya/MItMeshPolygon.h>

#ifdef _WIN32
LPCSTR lassoToolCursor = "lassoToolCursor.cur";
#else
#include <lassoToolCursor.h>
#include <lassoToolCursorMask.h>

#define lassoToolCursor_x_hot 1
#define lassoToolCursor_y_hot 16
#endif

class coord {
public:
        short h;
        short v;
};

extern "C" int xycompare( coord *p1, coord *p2 );
int xycompare( coord *p1, coord *p2 )
{
        if ( p1->v > p2->v )
                return 1;
        if ( p2->v > p1->v )
                return -1;
        if ( p1->h > p2->h )
                return 1;
        if ( p2->h > p1->h )
                return -1;

        return 0;
}

// The user Context

const int initialSize           = 1024;
const int increment                     =  256;
const char helpString[]         = "drag mouse to select points by encircling";

class lassoTool : public MPxContext
{
public:
                                        lassoTool();
        virtual                 ~lassoTool();
        void*                   creator();

        virtual void    toolOnSetup( MEvent & event );
        virtual MStatus doPress( MEvent & event );
        virtual MStatus doDrag( MEvent & event );
        virtual MStatus doRelease( MEvent & event );

private:
        void                                    append_lasso( short x, short y );
        bool                                    point_in_lasso( coord pt );
        coord                                   min;
        coord                                   max;
        unsigned                                maxSize;
        unsigned                                num_points;
        coord*                                  lasso;
        MGlobal::ListAdjustment listAdjustment;
        M3dView                                 view;
        MCursor                                 lassoCursor;

};

lassoTool::lassoTool()
        : maxSize(0)
        , num_points(0)
        , lasso(NULL)
#ifdef _WIN32
        , lassoCursor(lassoToolCursor)
#else
        , lassoCursor(lassoToolCursor_width,
                          lassoToolCursor_height,
                          lassoToolCursor_x_hot,
                          lassoToolCursor_y_hot,
                          lassoToolCursor_bits,
                          lassoToolCursorMask_bits)
#endif
{
        setTitleString ( "Lasso Pick" );
        
        // set the initial state of the cursor
        setCursor(lassoCursor);

        // Tell the context which XPM to use so the tool can properly
        // be a candidate for the 6th position on the mini-bar.
        setImage("lassoTool.xpm", MPxContext::kImage1 );
}

lassoTool::~lassoTool() {}

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

void lassoTool::toolOnSetup ( MEvent & )
{
        setHelpString( helpString );
}

MStatus lassoTool::doPress( MEvent & event )
// Set up for overlay drawing, and remember our starting point
{
        // Figure out which modifier keys were pressed, and set up the
        // listAdjustment parameter to reflect what to do with the selected points.
        if (event.isModifierShift() || event.isModifierControl() ) {
                if ( event.isModifierShift() ) {
                        if ( event.isModifierControl() ) {
                                // both shift and control pressed, merge new selections
                                listAdjustment = MGlobal::kAddToList;
                        } else {
                                // shift only, xor new selections with previous ones
                                listAdjustment = MGlobal::kXORWithList;
                        }
                } else if ( event.isModifierControl() ) {
                        // control only, remove new selections from the previous list
                        listAdjustment = MGlobal::kRemoveFromList; 
                }
        } else {
                listAdjustment = MGlobal::kReplaceList;
        }

        // Get the active 3D view and set the context
        //
        view = M3dView::active3dView();
        view.beginGL();
        view.beginOverlayDrawing();

        // Create an array to hold out lasso points. Assume no mem failures
        maxSize = initialSize;
        lasso = (coord*) malloc (sizeof(coord) * maxSize);

        coord start;
        event.getPosition( start.h, start.v );
        num_points = 1;
        lasso[0] = min = max = start;

        return MS::kSuccess;
}

MStatus lassoTool::doDrag( MEvent & event )
// Add to the growing lasso
{
        coord currentPos;
        event.getPosition( currentPos.h, currentPos.v );
        append_lasso( currentPos.h, currentPos.v );

        // clear the overlay planes
        //
        view.clearOverlayPlane();

        // Set up the orthographic projection matix
        //
        glMatrixMode( GL_PROJECTION );
        glLoadIdentity();
        gluOrtho2D(
                                0.0, (GLdouble) view.portWidth(),
                                0.0, (GLdouble) view.portHeight()
        );
        glMatrixMode( GL_MODELVIEW );
        glLoadIdentity();
        glTranslatef(0.375, 0.375, 0.0);

        // Set line values
        //
        glLineStipple( 1, 0x5555 );
        glLineWidth( 1.0 );
        glEnable( GL_LINE_STIPPLE );
        glIndexi( 2 );

        // Draw lasso
        //
        glBegin( GL_LINE_LOOP );
        for ( unsigned i = 0; i < num_points ; i++ ){
                glVertex2i( lasso[i].h, lasso[i].v );
        }
        glEnd();

#ifdef _WIN32
        SwapBuffers( view.deviceContext() );
#elif defined (OSMac_)
        // On 64-bit OSX the GL context is an NSOpenGLContext ptr. To swap
        // buffers using that requires Cocoa code, which we're not yet set
        // up to do. So we currently only do the buffer swap on 32-bit OSX.
# if !defined(__x86_64__)
        ::aglSwapBuffers( view.display());   
# endif
#else
        glXSwapBuffers( view.display(), view.window() );        
#endif // _WIN32        
        glDisable( GL_LINE_STIPPLE );

        return MS::kSuccess;
}

MStatus lassoTool::doRelease( MEvent & /*event*/ )
// Selects objects within the lasso
{
        MStatus                                                 stat;
        MSelectionList                                  incomingList, boundingBoxList, newList;

        // Clear the overlay plane & restore from overlay drawing
        view.clearOverlayPlane();
        view.endOverlayDrawing();
        view.endGL();

        // We have a non-zero sized lasso.  Close the lasso, and sort
        // all the points on it.
        append_lasso(lasso[0].h, lasso[0].v);
        qsort( &(lasso[0]), num_points, sizeof( coord  ),
                (int (*)(const void *, const void *))xycompare);

        // Save the state of the current selections.  The "selectFromSceen"
        // below will alter the active list, and we have to be able to put
        // it back.
        MGlobal::getActiveSelectionList(incomingList);

        // As a first approximation to the lasso, select all components with
        // the bounding box that just contains the lasso.
        MGlobal::selectFromScreen( min.h, min.v, max.h, max.v,
                                                           MGlobal::kReplaceList );

        // Get the list of selected items from within the bounding box
        // and create a iterator for them.
        MGlobal::getActiveSelectionList(boundingBoxList);

        // Restore the active selection list to what it was before we
        // the "selectFromScreen"
        MGlobal::setActiveSelectionList(incomingList, MGlobal::kReplaceList);

        // Iterate over the objects within the bounding box, extract the
        // ones that are within the lasso, and add those to newList.
        MItSelectionList iter(boundingBoxList);
        newList.clear();

        for ( ; !iter.isDone(); iter.next() ) {
                MDagPath        dagPath;
                MObject         component;
                MPoint          point;
                coord           pt;
                MObject     singleComponent;

                iter.getDagPath( dagPath, component );

                if (component.isNull())
                        continue; // not a component

                switch (component.apiType()) {
                case MFn::kCurveCVComponent:
                        {
                                MItCurveCV curveCVIter( dagPath, component, &stat );
                                for ( ; !curveCVIter.isDone(); curveCVIter.next() ) {
                                        point = curveCVIter.position(MSpace::kWorld, &stat );
                                        view.worldToView( point, pt.h, pt.v, &stat );
                                        if (!stat) {
                                                stat.perror("Could not get position");
                                                continue;
                                        }
                                        if ( point_in_lasso( pt ) ) {
                                                singleComponent = curveCVIter.cv();
                                                newList.add (dagPath, singleComponent);
                                        }
                                }
                                break;
                        }

                case MFn::kSurfaceCVComponent:
                        {
                                MItSurfaceCV surfCVIter( dagPath, component, true, &stat );
                                for ( ; !surfCVIter.isDone(); surfCVIter.next() ) {
                                        point = surfCVIter.position(MSpace::kWorld, &stat );
                                        view.worldToView( point, pt.h, pt.v, &stat );
                                        if (!stat) {
                                                stat.perror("Could not get position");
                                                continue;
                                        }
                                        if ( point_in_lasso( pt ) ) {
                                                singleComponent = surfCVIter.cv();
                                                newList.add (dagPath, singleComponent);
                                        }
                                }
                                break;
                        }

                case MFn::kMeshVertComponent:
                        {
                                MItMeshVertex vertexIter( dagPath, component, &stat );
                                for ( ; !vertexIter.isDone(); vertexIter.next() ) {
                                        point = vertexIter.position(MSpace::kWorld, &stat );
                                        view.worldToView( point, pt.h, pt.v, &stat );
                                        if (!stat) {
                                                stat.perror("Could not get position");
                                                continue;
                                        }
                                        if ( point_in_lasso( pt ) ) {
                                                singleComponent = vertexIter.vertex();
                                                newList.add (dagPath, singleComponent);
                                        }
                                }
                                break;
                        }

                case MFn::kMeshEdgeComponent:
                        {
                                MItMeshEdge edgeIter( dagPath, component, &stat );
                                for ( ; !edgeIter.isDone(); edgeIter.next() ) {
                                        point = edgeIter.center(MSpace::kWorld, &stat );
                                        view.worldToView( point, pt.h, pt.v, &stat );
                                        if (!stat) {
                                                stat.perror("Could not get position");
                                                continue;
                                        }
                                        if ( point_in_lasso( pt ) ) {
                                                singleComponent = edgeIter.edge();
                                                newList.add (dagPath, singleComponent);
                                        }
                                }
                                break;
                        }

                case MFn::kMeshPolygonComponent:
                        {
                                MItMeshPolygon polygonIter( dagPath, component, &stat );
                                for ( ; !polygonIter.isDone(); polygonIter.next() ) {
                                        point = polygonIter.center(MSpace::kWorld, &stat );
                                        view.worldToView( point, pt.h, pt.v, &stat );
                                        if (!stat) {
                                                stat.perror("Could not get position");
                                                continue;
                                        }
                                        if ( point_in_lasso( pt ) ) {
                                                singleComponent = polygonIter.polygon();
                                                newList.add (dagPath, singleComponent);
                                        }
                                }
                                break;
                        }

                default:
#ifdef DEBUG
                        cerr << "Selected unsupported type: (" << component.apiType()
                                 << "): " << component.apiTypeStr() << endl;
#endif /* DEBUG */
                        continue;
                }
        }

        // Update the selection list as indicated by the modifier keys.
        MGlobal::selectCommand(newList, listAdjustment);

        // Free the memory that held our lasso points.
        free(lasso);
        lasso = (coord*) 0;
        maxSize = 0;
        num_points = 0;
 
        return MS::kSuccess;
}

void lassoTool::append_lasso( short x, short y )
{
        int             cy, iy, ix, ydif, yinc, i;
        float   fx, xinc;

        iy = (int)lasso[num_points-1].v;
        ix = (int)lasso[num_points-1].h;
        ydif = abs( y - iy ); 
        if ( ydif == 0 )
                return;

        // Keep track of smallest rectangular area of the screen that
        // completely contains the lasso.
        if ( min.h > x )
                min.h = x;
        if ( max.h < x )
                max.h = x;
        if ( min.v > y )
                min.v = y;
        if ( max.v < y )
                max.v = y;

        if ( ((int)y - iy ) < 0 )
                yinc = -1;
        else
                yinc = 1;

        xinc = (float)((int)x - ix)/(float)ydif;
        fx = (float)ix + xinc;
        cy = iy + yinc;
        for ( i = 0; i < ydif; i++ ) {

                if ( num_points >= maxSize ) {
                        // Make the array of lasso points bigger
                        maxSize += increment;
                        lasso = (coord*) realloc (lasso, sizeof(coord) * maxSize);
                        // Assume no mem failures
                }

                lasso[num_points].h = (short) fx;
                lasso[num_points].v = (short) cy;
                fx += xinc;
                cy += yinc;
                num_points++;
        }

        return;
}

bool lassoTool::point_in_lasso( coord pt )
{
        unsigned i, sides;

        for ( i = 0; i < num_points; i++ ) {
                if ( lasso[i].v == pt.v ) {
                        while ( (lasso[i].v == pt.v ) && (lasso[i].h < pt.h) )
                                i++;
                        if ( lasso[i].v != pt.v )
                                return( false );
                        sides = 0;
                        i++;
                        while ( lasso[i].v == pt.v  ) {
                                i++;
                                sides++;
                        }
                        if ( sides % 2 )
                                return( false );
                        else
                                return( true );
                }
        }
        return( false );
}

// Command to create contexts

class lassoContextCmd : public MPxContextCommand
{
public: 
                                                lassoContextCmd() {};
        virtual MPxContext* makeObj();
        static void*            creator();
};

MPxContext* lassoContextCmd::makeObj()
{
        return new lassoTool;
}

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

// plugin initialization
MStatus initializePlugin( MObject obj )
{
        MStatus         status;
        MFnPlugin       plugin( obj, PLUGIN_COMPANY, "3.0", "Any");

        status = plugin.registerContextCommand( "lassoToolContext",
                                                                                    lassoContextCmd::creator );

        if (!status) {
                status.perror("registerContextCommand");
                return status;
        }

        // set the mel scripts to be run when the plugin is loaded / unloaded
        status = plugin.registerUI("lassoToolCreateUI", "lassoToolDeleteUI");
        if (!status) {
                status.perror("registerUIScripts");
                return status;
        }

        return status;
}

MStatus uninitializePlugin( MObject obj )
{
        MStatus         status;
        MFnPlugin       plugin( obj );

        status = plugin.deregisterContextCommand( "lassoToolContext" );

        return status;
}

Autodesk® Maya® 2011 © 1997-2010 Autodesk, Inc. All rights reserved. Generated with doxygen 1.5.6