MStatus SelectRingToolCmd2::finalize()
{
	MArgList command;
	command.addArg( commandString() );

	command.addArg( MString(edgeFlag) );
	MSelectionList sel;
	sel.add( selEdgeObject, selEdgeComp );
	MStringArray edges;
	sel.getSelectionStrings( edges );
	command.addArg( edges[0] );

	command.addArg( MString(selEdgesFlag) );
	command.addArg( selEdges );

	command.addArg( MString(selFacesFlag) );
	command.addArg( selFaces );

	command.addArg( MString(selVerticesFlag) );
	command.addArg( selVertices );

	command.addArg( MString(listAdjustFlag) );
	command.addArg( (int)listAdjust );
	
	command.addArg( MString(selTypeFlag) );
	command.addArg( selType );
	
	return MPxToolCommand::doFinalize( command );
}
Ejemplo n.º 2
0
MStatus cvExpand::doIt( const MArgList& args )
{

	MSelectionList list;
	MSelectionList newList;

	// Get the geometry list from what is currently selected in the 
	// model
	//
	MGlobal::getActiveSelectionList( list );

	MDagPath path;
	MObject  component;
 
	// Make expanded Selection List
	//
	for ( MItSelectionList iter( list ); !iter.isDone(); iter.next() ) {
		iter.getDagPath( path, component );
		
		if ( path.hasFn( MFn::kNurbsSurfaceGeom ) && 
			 !component.isNull() ) {
			for ( MItSurfaceCV cvIter( path, component ); 
				  !cvIter.isDone(); cvIter.next() ) {
				newList.add( path, cvIter.cv() );
			}
		} else {
			newList.add( path, component );
		}
	}

	// Return expanded selection list as an array of strings
	//
	MStringArray returnArray;
	newList.getSelectionStrings( returnArray );

	MPxCommand::setResult( returnArray );

	return MS::kSuccess;
}
Ejemplo n.º 3
0
MStatus
cgfxShaderCmd::doCmd(const MArgList& args)
//
//	Description:
//		implements the MEL cgfxShader command.
//
//	Arguments:
//		-fx/fxFile	The CgFX file to load.
//		-e/edit		Edit an existing cgfxShader rather than creating
//					a new one.
//      -q/query    Get specified info
//
//	Return Value:
//		MS::kSuccess - command succeeded
//		MS::kFailure - command failed (returning this value will cause the 
//                     MEL script that is being run to terminate unless the
//                     error is caught using a "catch" statement.
//
{
	// Get the current state of the flag
	// and store it in a temporary variable
	// static int tmpFlag = -1;

#if defined(_WIN32) && defined(CGFX_DEBUG_MEMORY)
	if (tmpFlag == -1)
	{
		tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );

		// Turn On (OR) - call _CrtCheckMemory at every
		// allocation request
		tmpFlag |= _CRTDBG_CHECK_ALWAYS_DF;

		// Turn on (OR) - check for memory leaks at end
		// of program.
		tmpFlag |= _CRTDBG_LEAK_CHECK_DF;

		_CrtSetDbgFlag( tmpFlag );
	}
#endif /* _WIN32 && CGFX_DEBUG_MEMORY */

	MStatus        status;
	MSelectionList selList;
	MObject        oNode;
	MString        sResult;
	MStringArray   saResult;
	MString        sFeedback;
	MString        sTemp;
	MString        sWho = "cgfxShader : ";

	status = parseArgs(args, selList);
	if (!status)
	{
		return status;
	}

	// -pp / -pluginPath 
	//     Returns the directory path where this plug-in's auxiliary
	//     files, such as MEL scripts, are expected to be found.
	//     The path name is in Maya format ('/' delimited) with no
	//     trailing slash.  Result type is string.  (Query only)
	if ( fPluginPath )
	{
		setResult( sPluginPath );
		return MS::kSuccess;
	}

	// -lp / -listProfiles
    //  
	//     Return the names of the profiles supported on the current
	//     platform.
	//
	//     Each item in the result array has the form
	//         "VertexProfileName<,GeometryProfileName,FragmentProfileName"
	//
	//     Result type is string[].  (Query only; set internally)
	if ( fListProfiles )
	{
        setResult( cgfxProfile::getProfileList() );
		return status;
	}

	// -mtc / -maxTexCoords
	//     Returns the maximum number of texcoord inputs that can be
	//     passed to vertex shaders under the currently installed
	//     OpenGL implementation.  Returns 0 if the information is
	//     not available.  Result type is integer.  (Query only)
	//
	// Don't use GL_MAX_TEXTURE_UNITS as this does not provide a proper
	// count when the # of image or texcoord inputs differs
	// from the conventional (older) notion of texture unit. 
	//
	// Instead take the minimum of GL_MAX_TEXTURE_COORDS_ARB and
	// GL_MAX_TEXUTRE_IMAGE_UNITS_ARB according to the 
	// ARB_FRAGMENT_PROGRAM specification.
	if ( fMaxTexCoords )    
	{
		GLint     mtc = 0;
		M3dView vw = M3dView::active3dView( &status );
		if ( status &&
			vw.beginGL() )
		{
			glGetIntegerv( GL_MAX_TEXTURE_COORDS_ARB, &mtc );
			GLint mic = 0;
			glGetIntegerv( GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &mic );
			if (mic < mtc)
				mtc = mic;

			if ( mtc < 1 )
				mtc = 1;
			else if ( mtc > CGFXSHADERNODE_GL_TEXTURE_MAX )
				mtc = CGFXSHADERNODE_GL_TEXTURE_MAX;
			
			vw.endGL();
		}
		setResult( (int)mtc );
		return MS::kSuccess;
	}

	// If edit or query, find the specified cgfxShaderNode.
	MFnDependencyNode fnNode;
	cgfxShaderNode*   pNode = NULL;
	if ( fIsEdit || fIsQuery )
	{
		// We are editing an existing node which must have been
		// provided in the args (or the current selection list).
		// Get the correct node name into fNodeName;
		//
		if (selList.length() != 1)
		{
			status = MS::kNotFound;
			return status;
		}

		// Get the name of the node into fNodeName so that it can
		// be saved for undo/redo
		//
		MStringArray tmpList;
		selList.getSelectionStrings(tmpList);
		fNodeName = tmpList[0];
		if ( fNodeName.length() ) 
		{
			sWho += " \"";
			sWho += fNodeName;
			sWho += "\"";
		}

		status = selList.getDependNode(0, oNode);
		if (!status)
		{
			return status;
		}

		status = fnNode.setObject( oNode );
		if (!status)
		{
			sFeedback = sWho;
			sFeedback += " is not a cgfxShader node.";
			MGlobal::displayError( sFeedback );

			return status;
		}

		if (fnNode.typeId() != cgfxShaderNode::sId)
		{
			status = MS::kInvalidParameter;
			sFeedback = sWho;
			sFeedback += " is not a cgfxShader node.";
			MGlobal::displayError( sFeedback );
			return status;
		}

		pNode = (cgfxShaderNode*)fnNode.userNode();
		if (!pNode)
		{
			status = MS::kInvalidParameter;
			sFeedback = sWho;
			sFeedback += " is not cgfxShader node.";
			MGlobal::displayError( sFeedback );
			return status;
		}
	}

	if ( fIsQuery ) {
        
        // -fx / -fxFile
        //     Returns the shader file name. 
        if ( fFxFile )
        {
            MString path = pNode->shaderFxFile();
            setResult( path );
            return MS::kSuccess;
        }

        // -fxp / -fxPath 
        //     Returns the path of the fx file.  The path name is in Maya
        //     format ('/' delimited).  Result type is string.
        //     (Query only)
        if ( fFxPath )
        {
            MString path = cgfxFindFile(pNode->shaderFxFile());
            setResult( path );
            return MS::kSuccess;
        }

        // -t / -technique
        //     Returns the currently active technique
        if ( fTechnique )
        {
            MString path = pNode->getTechnique();
            setResult( path );
            return MS::kSuccess;
        }

        // -p / -profile
        //     Returns the current profile
        if ( fProfile )
        {
            MString path = pNode->getProfile();
            setResult( path );
            return MS::kSuccess;
        }

        // -lt / -listTechniques
        //     Return the technique names defined by the current effect.
        //
        //     Each item in the result array has the form
        //         "techniqueName<TAB>numPasses"
        //     where 
        //         numPasses is the number of passes defined by the 
        //             technique, or 0 if the technique is not valid.   
        //     (Future versions of the cgfxShader plug-in may append
        //      additional tab-separated fields.)
        //
        //     Result type is string[].  (Query only; set internally)
        if ( fListTechniques )
        {
            setResult( pNode->getTechniqueList() );
            return status;
        }

        // -lp / -listParameters
        //     Return the attribute names corresponding to the
        //     shader's tweakable uniform parameters.
        //     Result type is string[].  (Query only; set internally)
        // -des / -description
        //     If specified, each item in the result array has the form
        //          "attrName<TAB>type<TAB>semantic<TAB>description<TAB>extraAttrSuffix"
        //     (Future versions of the cgfxShader plug-in may provide
        //       additional tab-separated fields after the semantic.)
        //     A missing field is indicated by a single space (" ") 
        //       so the string can be parsed more easily using the MEL
        //       "tokenize" function, which treats a group of consecutive
        //       delimiters the same as a single delimiter.
        if ( fListParameters )
        {
            cgfxRCPtr<cgfxAttrDefList> list = cgfxAttrDef::attrsFromNode( oNode );
            for ( cgfxAttrDefList::iterator it = list; it; ++it )
            {
                cgfxAttrDef* aDef = *it;
                if ( fDescription )
                {
                    sResult = aDef->fName.length() ? aDef->fName : " ";
                    sResult += "\t";
                    sTemp = aDef->typeName();
                    sResult += sTemp.length() ? sTemp : " ";
                    sResult += "\t";               
                    sResult += aDef->fSemantic.length() ? aDef->fSemantic : " ";
                    sResult += "\t";
                    sResult += aDef->fDescription.length() ? aDef->fDescription : " ";
                    sResult += "\t";               
                    const char* suffix = aDef->getExtraAttrSuffix();
                    sResult += suffix ? suffix : " ";
                }
                else
                    sResult = aDef->fName;
                saResult.append( sResult );
            }
            setResult( saResult );
            return status;
        }

        // -p  / -parameter <name>
        //     Return a string describing the data type and usage of 
        //     the attribute whose name is specified.  
        //     Result type is string (with no -description flag), or
        //     string array (if you specify -description).  
        //     (Query only; set internally)
        // -ci / -caseInsensitive
        //     If specified, returns information for the first 
        //     attribute that matches the specified name assuming
        //     no distinction between upper and lower case letters.
        // -des / -description
        //     If specified, the result is a string array containing:
        //          [0] = attribute name
        //          [1] = type
        //          [2] = semantic 
        //          [3] = description from "desc" or "uiname" annotation
        //          [4] = extra attribute suffix for Vector4 ("W") / Color4 ("Alpha")
        //          (Future versions of the cgfxShader plug-in may provide
        //          additional tab-separated fields after the semantic.)
        //     If omitted, only the type is returned (a string).
        if ( fParameterName.length() > 0 )
        {
            cgfxRCPtr<cgfxAttrDefList> list = cgfxAttrDef::attrsFromNode( oNode );
            cgfxAttrDefList::iterator it; 
            if ( fCaseInsensitive )
                it = list->findInsensitive( fParameterName );
            else
                it = list->find( fParameterName );
            if ( fDescription )
            {
                if ( it )
                {
                    cgfxAttrDef* aDef = *it;
                    saResult.append( aDef->fName );
                    saResult.append( aDef->typeName() );
                    saResult.append( aDef->fSemantic );
                    saResult.append( aDef->fDescription );
                    const char* suffix = aDef->getExtraAttrSuffix();
                    saResult.append( suffix ? suffix : "" );
                }
                setResult( saResult );
            }
            else
            {
                if ( it )
                    sResult = (*it)->typeName();
                setResult( sResult );
            }
            return status;
        }

        // -euv / -emptyUV
        //     Returns the names of blacklisted UV sets.  These UV sets
        //     are disabled from being passed to the shader because there
        //     is at least one mesh where the UV set name is defined but 
        //     has no faces mapped.  Due to a bug in Maya (in 5.0 and
        //     possibly some other releases), Maya crashes if an empty
        //     UV set is accessed by a hardware shader.  Blacklisting is
        //     intended to protect the user against accidentally hitting
        //     the bug and crashing Maya.  After the Maya fix has been
        //     verified, this option can continue to be accepted for awhile
        //     for compatibility, returning an empty result array.
        //     Result type is string[].  (Query only; set internally)
        if ( fEmptyUV )
        {
            setResult( pNode->getEmptyUVSets() );
            return MS::kSuccess;
        }

        // -eus / -emptyUVShapes
        //     Returns the names of shape nodes that have empty UV sets 
        //     which are causing the UV set names to be blacklisted.
        //     After the Maya bug fix has been verified, this option
        //     can remain for awhile for compatibility, returning an
        //     empty result array.
        //     Result type is string[].  (Query only; set internally)
        if ( fEmptyUVShapes )
        {
            const MObjectArray& oaShapes = pNode->getEmptyUVSetShapes();
            MFnDagNode          fnDagNode;
            MDagPath            dpShape;
            for ( unsigned iShape = 0; iShape < oaShapes.length(); ++iShape )
            {
                fnDagNode.setObject( oaShapes[ iShape ] );
                fnDagNode.getPath( dpShape );
                saResult.append( dpShape.partialPathName() );
            }
            setResult( saResult );
            return MS::kSuccess;
        }

        // -tcs / -texCoordSource
        //     Returns the value of the texCoordSource attribute, because
        //     the MEL "getAttr" command doesn't work with string arrays.
        //     Result type is string[].  (Query only; set via "setAttr")
        if ( fTexCoordSource )
        {
            setResult( pNode->getTexCoordSource() );
            return MS::kSuccess;
        }

#if MAYA_API_VERSION >= 700

        // -cs / -colorSource
        //     Returns the value of the colorSource attribute, because
        //     the MEL "getAttr" command doesn't work with string arrays.
        //     Result type is string[].  (Query only; set via "setAttr")
        if ( fColorSource )
        {
            setResult( pNode->getColorSource() );
            return MS::kSuccess;
        }

#endif

        // Error if -q with no other query flags.
		return MS::kInvalidParameter;
    }

	// If user didn't specify shader fx file, default to current
	// value of our cgfxShader node's "shader" attribute.
	if (!fFxFile && pNode)
		fNewFxFile = pNode->shaderFxFile();

	// If user didn't specify technique name, default to current
	// value of our cgfxShader node's "technique" attribute.
    //
    // If a new fx file has been specified without a technique, we
    // leave the technique name empty so that the first technique of
    // the effect will be selected.
	if (!fTechnique && pNode)
		fNewTechnique = pNode->getTechnique();

	// If user didn't specify profile name, default to current
	// value of our cgfxShader node's "profile" attribute.
	if (!fProfile && pNode)
		fNewProfile = pNode->getProfile();

	//
	// Load the effect from the .fx file.
	//
	if (fFxFile)
	{
		// Attempt to read the new fEffect from the file
		//
		MString file = cgfxFindFile(fNewFxFile);
        MString projectFile = cgfxFindFile(fNewFxFile, true);

		// Compile and create the effect.
		fNewEffect = cgfxEffect::loadEffect(file, cgfxProfile::getProfile(fNewProfile));

		//// Set the device.
		if (fNewEffect->isValid())
		{           
			// There is no current view in batch mode, just return
			// success then
			const MGlobal::MMayaState mayaState = MGlobal::mayaState(&status);
			if ( !status ) return status;
			if ( mayaState == MGlobal::kBatch ) return MS::kSuccess;

			fNewFxFile = projectFile;

			M3dView view = M3dView::active3dView();

			// The M3dView class doesn't return the correct status if
			// there isn't an active 3D view, so we rely on the
			// success of beginGL() which will make the context
			// current.
			//
			if (!view.beginGL()) 
			{
				MString es = "There is no active view to bind " + sWho + " to.";
				MGlobal::displayWarning( es );
				return MS::kSuccess;
			}

			view.endGL();
		}

		// Tell user if successful.
		if (fNewEffect->isValid())
		{
			sFeedback = sWho;
			sFeedback += " loaded effect \"";
			sFeedback += file;
			sFeedback += "\"";
			MGlobal::displayInfo( sFeedback );
		}
		else
		{
			sFeedback = sWho;
			sFeedback += " unable to load effect \"";
			sFeedback += file.length() ? file : fNewFxFile;
			sFeedback += "\"";
			MGlobal::displayError( sFeedback );
			return MS::kFailure;
		}
	}

	// Create an MDGModifier to hold an agenda of operations to be
	//   performed to update the DG.  We build the agenda here;
	//   then invoke it to do/redo/undo the updates.
	fDagMod = new MDGModifier;

	// Create new cgfxShader node if requested.  
	if ( !fIsEdit )
	{
		// Create node.
		oNode = fDagMod->createNode(cgfxShaderNode::sId, &status);
		M_CHECK( status );

		if ( fNodeName.length() > 0 )
		{
			status = fDagMod->renameNode(oNode, fNodeName);
			M_CHECK( status );
		}

		status = fnNode.setObject( oNode );
		M_CHECK( status && fnNode.typeId() == cgfxShaderNode::sId );

		pNode = (cgfxShaderNode*)fnNode.userNode();
		M_CHECK( pNode );

		// On successful completion, redoCmd() will select the new node.
		// Save old selection for undo.
		status = MGlobal::getActiveSelectionList( fOldSelection );
		M_CHECK( status );
	}

	if (fFxFile) {
        // Save the current state of the node for undo purposes
        fOldFxFile    = pNode->shaderFxFile();
        fOldEffect = pNode->effect();   // save old CGeffect

		cgfxShaderNode::NodeList nodes;
		// getNodesToUpdate will return the list of nodes that will need to be updated :
		// if the new fx file is the same as the old fx file, the action is considered a reload,
		//     we'll gather all the nodes that are using the old effect and reload them all.
		// else the effect file is different and only the current node will be updated.
		getNodesToUpdate(fOldEffect, pNode, nodes);

		cgfxShaderNode::NodeList::const_iterator it = nodes.begin();
		cgfxShaderNode::NodeList::const_iterator itEnd = nodes.end();
		for(; it != itEnd; ++it)
		{
			cgfxShaderNode* node = *it;

			MStringArray &oldAttributeList = fOldAttributeList[node];
			cgfxRCPtr<cgfxAttrDefList> &oldAttrDefList = fOldAttrDefList[node];

			MStringArray &newAttributeList = fNewAttributeList[node];
			cgfxRCPtr<cgfxAttrDefList> &newAttrDefList = fNewAttrDefList[node];


			node->getAttributeList( oldAttributeList );

			oldAttrDefList = node->attrDefList(); // save old cgfxAttrDefList ptr

			// Now figure out what to do with the node.
			//
			// updateNode does a fair amount of work.  First, it gets the
			// cgfxAttrDefList from the effect.  Then it gets the equivalent
			// list from the node itself.  It determines which attributes need
			// to be added and which need to be deleted and fills in all the
			// changes in the MDagModifier fDagMod.  Then it builds a new value
			// for the attributeList attribute.  Finally, it builds a new
			// value for the attrDefList internal value.  All these values are
			// returned here where we can set them into the node.
			//
			cgfxAttrDef::updateNode( fNewEffect,             // IN
									 node,                   // IN
									 fDagMod,                // UPD
									 newAttrDefList,         // OUT
									 newAttributeList );     // OUT
		}
    }

	// Save a reference to the node in a selection list for undo/redo.
	status = fNodeSelection.add( oNode );
	M_CHECK( status );

	// Save the current state of the node for undo purposes
	fOldTechnique = pNode->getTechnique();
	fOldProfile   = pNode->getProfile();

	// I think we have all the information to redoIt().
	//
	// Typically, the doIt() method only collects the infomation required
	// to do/undo the action and then stores it in class members.  The 
	// redo method is then called to do the actuall work.  This prevents
	// code duplication.
	//
	return redoCmd( oNode, fnNode, pNode );
}                                      // cgfxShaderCmd::doCmd
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
MStatus CVstSelectCoincidentFacesCmd::DoSelect()
{
	MSelectionList meshList;
	GetSpecifiedMeshes( meshList );

	MSelectionList coincidentList;

	MDagPath mDagPath;
	MObject cObj;

	MPointArray points;
	MIntArray iIndexes;
	MIntArray jIndexes;

	uint iCount;
	bool addI;
	bool same;
	bool foundVertex;

	double tolerance( MPoint_kTol );
	if ( m_undo.ArgDatabase().isFlagSet( kOptTolerance ) )
	{
		MDistance optTolerance;
		m_undo.ArgDatabase().getFlagArgument( kOptTolerance, 0U, optTolerance );
		tolerance = optTolerance.as( MDistance::internalUnit() );
	}

	for ( MItSelectionList sIt( meshList ); !sIt.isDone(); sIt.next() )
	{
		if ( !sIt.getDagPath( mDagPath, cObj ) )
			continue;

		MFnSingleIndexedComponent sFn;
		MObject sObj( sFn.create( MFn::kMeshPolygonComponent ) );

		MFnMesh meshFn( mDagPath );
		meshFn.getPoints( points );

		if ( !sIt.hasComponents() )
		{
			const uint nFaces( meshFn.numPolygons() );
			for ( uint i( 0U ); i != nFaces; ++i )
			{
				meshFn.getPolygonVertices( i, iIndexes );
				iCount = iIndexes.length();

				addI = false;

				for ( uint j( i + 1 ); j < nFaces; ++j )
				{
					meshFn.getPolygonVertices( j, jIndexes );
					if ( jIndexes.length() == iCount )
					{
						same = true;

						for ( uint k( 0U ); k != iCount; ++k )
						{
							foundVertex = false;

							const MPoint &kPoint( points[ iIndexes[ k ] ] );

							for ( uint l( 0U ); l < iCount; ++l )
							{
								if ( kPoint.isEquivalent( points[ jIndexes[ l ] ], tolerance ) )
								{
									foundVertex = true;
									break;
								}
							}

							if ( !foundVertex )
							{
								same = false;
								break;
							}
						}

						if ( same )
						{
							addI = true;
							sFn.addElement( j );
						}
					}
				}

				if ( addI )
				{
					sFn.addElement( i );
				}
			}
		}
		else
		{
			MFnSingleIndexedComponent cFn( cObj );
			MIntArray cA;
			MFnSingleIndexedComponent( cObj ).getElements( cA );

			const uint nFaces( cA.length() );

			for ( uint i( 0U ); i != nFaces; ++i )
			{
				meshFn.getPolygonVertices( cA[ i ], iIndexes );
				iCount = iIndexes.length();

				addI = false;

				for ( uint j( i + 1U ); j < nFaces; ++j )
				{
					meshFn.getPolygonVertices( cA[ j ], jIndexes );
					if ( jIndexes.length() == iCount )
					{
						same = true;

						for ( uint k( 0U ); k != iCount; ++k )
						{
							foundVertex = false;

							const MPoint &kPoint( points[ iIndexes[ k ] ] );

							for ( uint l( 0U ); l < iCount; ++l )
							{
								if ( kPoint.isEquivalent( points[ jIndexes[ l ] ], tolerance ) )
								{
									foundVertex = true;
									break;
								}
							}

							if ( !foundVertex )
							{
								same = false;
								break;
							}
						}

						if ( same )
						{
							addI = true;
							sFn.addElement( cA[ j ] );
						}
					}
				}

				if ( addI )
				{
					sFn.addElement( cA[ i ] );
				}
			}
		}

		if ( sFn.elementCount() > 0 )
		{
			coincidentList.add( mDagPath, sObj );
		}
		else
		{
			MSelectionList tmpList;
			tmpList.add( mDagPath, cObj );
			MStringArray tmpA;
			tmpList.getSelectionStrings( tmpA );

			minfo << "No coincident faces on:";
			for ( uint i( 0U ); i != tmpA.length(); ++i )
			{
				minfo << " " << tmpA[ i ];
			}
			minfo << std::endl;
		}
	}

	if ( coincidentList.length() )
	{
		MGlobal::setActiveSelectionList( coincidentList );
		MStringArray tmpA;
		coincidentList.getSelectionStrings( tmpA );
		setResult( tmpA );
	}
	else
	{
		if ( meshList.length() > 0U )
		{
			minfo << "No coincident faces found" << std::endl;
		}
	}

	return MS::kSuccess;
}
MStatus convertVerticesToContainedEdgesCommand::redoIt()

{

   MSelectionList finalEdgesSelection;

   MDagPath meshDagPath;

   MObject multiVertexComponent, singleVertexComponent;

   int dummyIndex;



   // ITERATE THROUGH EACH "VERTEX COMPONENT" THAT IS CURRENTLY SELECTED:

   for (MItSelectionList vertexComponentIter(previousSelectionList, MFn::kMeshVertComponent); !vertexComponentIter.isDone(); vertexComponentIter.next())

   {

      // STORE THE DAGPATH, COMPONENT OBJECT AND MESH NAME OF THE CURRENT VERTEX COMPONENT:

      vertexComponentIter.getDagPath(meshDagPath, multiVertexComponent);

      MString meshName = meshDagPath.fullPathName();



      // VERTEX COMPONENT HAS TO CONTAIN AT LEAST ONE VERTEX:

      if (!multiVertexComponent.isNull())

      {

         // ITERATE THROUGH EACH "VERTEX" IN THE CURRENT VERTEX COMPONENT:

         for (MItMeshVertex vertexIter(meshDagPath, multiVertexComponent); !vertexIter.isDone(); vertexIter.next())

         {

            // FOR STORING THE EDGES CONNECTED TO EACH VERTEX:

            MIntArray connectedEdgesIndices;

            vertexIter.getConnectedEdges(connectedEdgesIndices);



            // ITERATE THROUGH EACH EDGE CONNECTED TO THE CURRENT VERTEX:

            MItMeshEdge edgeIter(meshDagPath);

            for (unsigned i=0; i<connectedEdgesIndices.length(); i++)

            {

               // FIND AND STORE THE *FIRST* "END VERTEX" OF THE CURRENT EDGE:

               edgeIter.setIndex(connectedEdgesIndices[i], dummyIndex);

               MSelectionList singleVertexList;

               MString vertexName = meshName;

               vertexName += ".vtx[";

               vertexName += edgeIter.index(0);

               vertexName += "]";

               singleVertexList.add(vertexName);

               singleVertexList.getDagPath(0, meshDagPath, singleVertexComponent);

               // SEE WHETHER THE VERTEX BELONGS TO THE ORIGINAL SELECTION, AND IF IT DOES PROCEED TO CHECK NEXT END VERTEX:

               if (!singleVertexComponent.isNull() && previousSelectionList.hasItem(meshDagPath, singleVertexComponent))

               {

                  // FIND AND STORE THE *SECOND* "END VERTEX" OF THE CURRENT EDGE:

                  singleVertexList.clear();

                  vertexName = meshName;

                  vertexName += ".vtx[";

                  vertexName += edgeIter.index(1);

                  vertexName += "]";

                  singleVertexList.add(vertexName);

                  singleVertexList.getDagPath(0, meshDagPath, singleVertexComponent);

                  // SEE WHETHER THE VERTEX BELONGS TO THE ORIGINAL SELECTION, AND IF IT DOES, ADD THE EDGE TO THE FINAL CONTAINED EDGES LIST:

                  if (!singleVertexComponent.isNull() && previousSelectionList.hasItem(meshDagPath, singleVertexComponent))

                  {

                     MString edgeName = meshName;

                     edgeName += ".e[";

                     edgeName += connectedEdgesIndices[i];

                     edgeName += "]";

                     finalEdgesSelection.add(edgeName);

                  }

               }

            }

         }

      }

   }



   // FINALLY, MAKE THE NEW "CONTAINED EDGES", THE CURRENT SELECTION:

   MGlobal::setActiveSelectionList(finalEdgesSelection, MGlobal::kReplaceList);



   // RETURN NEW CONTAINED EDGES LIST FROM THE MEL COMMAND, AS AN ARRAY OF STRINGS:

   MStringArray containedEdgesArray;

   finalEdgesSelection.getSelectionStrings(containedEdgesArray);

   MPxCommand::setResult(containedEdgesArray);



   return MS::kSuccess;

}