Softimage SDK Programmer's Guide > Customizing Softimage using the SDK > Custom Filters >

# Writing SubComponent Filters

To filter selections of subcomponents (edges, points, or polygons), Softimage prefers the Subset callback to the Match callback. That is, Softimage calls Subset instead of the Match callback when a user selects some subcomponents. You should always provide a Subset callback for a subcomponent filter.

## Subset

When Softimage calls Subset, it puts an XSICollection in the Input attribute of the callback context. This collection contains one CollectionItem for each 3D object with selected subcomponents. In the callback function, you get the selected subcomponents from the CollectionItem.SubComponent.ComponentCollection property.

The following example shows the typical structure of a Subset callback for subcomponent filter. First, the callback creates a new XSICollection, which will be used to hold the output of the callback. Then for each 3D object, Subset finds the matching polygons and creates a new SubComponent object with them. The new SubComponent objects are added to the output collection, and the collection is put in the Output attribute of the callback context.

    function MyPolygonFilter_Subset( oContext )
    {
    	var oFilter = oContext.Source;
    	logmessage( oFilter.Name + "_Subset");

    	// Create a new XSICollection to hold the output
    	var cloSubset = new ActiveXObject( "XSI.Collection" );

    	// Get the XSI.Collection from the context
    	// The collection contains one CollectionItem for each object with selected polygons
    	var cloObjects = oContext.GetAttribute( "Input" );

    	for (var objEnumerator = new Enumerator(cloObjects) ; !objEnumerator.atEnd(); objEnumerator.moveNext())
    	{
    		// Get the CollectionItem
    		var oItem = objEnumerator.item();

    		// Get the SubComponent object
    		var oPolySubComponent = oItem.SubComponent;

    		if ( !( (classname(oPolySubComponent) == "SubComponent") && ( oPolySubComponent.type == "polySubComponent" ) ) )
    			continue;

    		// This array will hold the indices of the polygons that match the filter conditions
    		var aIndices = new Array();

    		// Enumerate the selected polygons; if a polygon matches, put its index in the array
    		for (var polyEnumerator = new Enumerator(oPolySubComponent.ComponentCollection) ; !polyEnumerator.atEnd(); polyEnumerator.moveNext())
    		{
    			var oPoly = polyEnumerator.item();
    			if ( polygon_isamatch( oPoly ) )
    			{
    				aIndices.push( oPoly.index );
    			}
    		}

    		// Create a SubComponent from the subset of polygons that match, and add the SubComponent to the output
    		if ( aIndices.length > 0 )
    		{
    			var oSubComponent = oPolySubComponent.Parent3DObject.ActivePrimitive.Geometry.CreateSubComponent(siPolygonCluster, aIndices );
    			cloSubset.Add( oSubComponent );
    		}
    	}

    	// You always set the Output attribute, even if the subset is empty
    	oContext.SetAttribute( "Output", cloSubset );

    	// Return True if the subset is non-empty
    	return (cloSubset.Count > 0);
    }

    // Select only polygons with more than 3 points
    function polygon_isamatch( oPoly )
    {
    	return ( oPoly.NbPoints != 3 )
    }

Another way to implement a Subset callback is to work directly with the SubComponent objects passed in by Softimage. In this approach, you use SubComponent.RemoveElement to remove the polygons that don't match, and put the modified SubComponent in the output collection.

    function MyPolygonFilter_Subset( oContext )
    {
    	var cloSubset = new ActiveXObject( "XSI.Collection" );

    	var cloObjects = oContext.GetAttribute( "Input" );

    	for (var objEnumerator = new Enumerator(cloObjects) ; !objEnumerator.atEnd(); objEnumerator.moveNext())
    	{
    		var oItem = objEnumerator.item();

    		var oPolySubComponent = oItem.SubComponent;

    		if ( !( (classname(oPolySubComponent) == "SubComponent") && ( oPolySubComponent.type == "polySubComponent" ) ) )
    			continue;

    		// Get a copy of the PolygonFaceCollection;
    		// We'll enumerate cloTmpCopy, and modify oPolySubComponent.ComponentCollection
    		var cloTmpCopy = oPolySubComponent.ComponentCollection;

    		// Enumerate the selected polygons; if a polygon doesn't match, remove it from oPolySubComponent
    		for (var polyEnumerator = new Enumerator(cloTmpCopy) ; !polyEnumerator.atEnd(); polyEnumerator.moveNext())
    		{
    			var oPoly = polyEnumerator.item();
    			if ( !polygon_isamatch( oPoly ) )
    			{
    				oPolySubComponent.RemoveElement( oPoly.index );
    			}
    		}

    		// Add the modified SubComponent the output collection
    		if ( aIndices.length > 0 )
    		{
    			cloSubset.Add( oPolySubComponent );
    		}
    	}

    	oContext.SetAttribute( "Output", cloSubset );

    	return (cloSubset.Count > 0);
    }

## Match

For subcomponent filters, Subset is the most important callback. Softimage won't call Match unless a filter doesn't have a Subset callback. However, Match is required, so you must provide an implementation. Typically, Match returns false as soon as it finds a subcomponent that doesn't match the filter conditions.

When Softimage calls Match for a subcomponent filter, it puts a SubComponent object in the callback context. The following example shows a typical structure for a Match callback.

    function MyPolygonFilter_Match( oContext )
    {
    	// Get the SubComponent object from the context
    	var oPolySubComponent = oContext.GetAttribute( "Input" );

    	// Return false if we don't have a SubComponent, or we don't have polygons
    	if ( !( (classname(oPolySubComponent) == "SubComponent") && ( oPolySubComponent.type == "polySubComponent" ) ) )
    	{
    		return false;
    	}

    	// Enumerate the selected polygons; Return false as soon as we find a polygon that doesn't match
    	for (var polyEnumerator = new Enumerator(oPolySubComponent.ComponentCollection) ; !polyEnumerator.atEnd(); polyEnumerator.moveNext())
    	{
    		var oPoly = polyEnumerator.item();
    		if ( !polygon_isamatch( oPoly ) )
    		{
    			return false;
    		}
    	}

    	// If we make it here, all the polygons match
    	return true;
    }