Saturday Snippet: Selection, LAST, and SubComponents

var oCube = ActiveSceneRoot.AddGeometry( "Cube", "MeshSurface" );

var n = oCube.ActivePrimitive.Geometry.Points.Count

//SelectGeometryComponents(oCube.Name + ".pnt[1,3,5,LAST]");
Selection.Add( oCube.ActivePrimitive.Geometry.Points(1) );
Selection.Add( oCube.ActivePrimitive.Geometry.Points(3) );
Selection.Add( oCube.ActivePrimitive.Geometry.Points(5) );
Selection.Add( oCube.ActivePrimitive.Geometry.Points(n-1) );

var sel = XSIFactory.CreateActiveXObject("XSI.Collection");           

LogMessage( sel.Count );
// INFO : 1

LogMessage( ClassName( sel(0) ) );
// INFO : CollectionItem

LogMessage( sel(0).Value );
// INFO : cube.pnt[1,3,5,LAST]

n = sel(0).SubComponent.ComponentCollection.Count
LogMessage( sel(0).SubComponent.ComponentCollection(n-1).Index );
// INFO : 7

LogMessage( VBArray( sel(0).SubComponent.ElementArray ).toArray()[n-1] );
// INFO : 7

//LogMessage( sel(0).SubComponent.ComponentCollection );
LogMessage( VBArray( sel(0).SubComponent.ElementArray ).toArray() );

And don’t do this. You’ll have to restart Softimage to get selection working again.

var sel = XSIFactory.CreateActiveXObject("XSI.Collection");           
sel(0).Value = "cube.pnt[7]";

Saturday Snippet: Selecting a range of polygons by polygon index

Given a selected polygon, add the next 5 polygons to the selection. For example, if polygon 22 is selected, then add polygons 23,24,25,26, and 27 to the selection.

You can do this without using any loops, by using a string expression for the polygon components.

Here’s some JScript that shows two ways to do it. Method 1 uses a string expression. Method 2 is a loop, which can either use the Polygons collection or a string expression.

var o = Selection(0).SubComponent.Parent3DObject
var p = Selection(0).SubComponent.ComponentCollection(0)
var LAST = o.ActivePrimitive.Geometry.Polygons.Count;

// Method 1
// SelectGeometryComponents("grid.poly[22-27]", null, null);

var sPoly = ".poly["+p.Index+"-"+Math.min( p.Index+5,LAST-1 )+"]"
//SelectGeometryComponents( o.FullName + sPoly );
//Selection.SetAsText( o.FullName + sPoly  );

// Method 2
//for ( var i = p.Index+1 ; i < Math.min( p.Index+5,LAST ) ; i++ )
//	ToggleSelection( o.ActivePrimitive.Geometry.Polygons(i) );
// -or-
//	ToggleSelection( o.FullName + ".poly["+i+"]" )

Here’s some Python that does something similar but different:

si = Application
o = si.Selection(0).SubComponent.Parent3DObject
p = si.Selection(0).SubComponent.ComponentCollection(0)
LAST = o.ActivePrimitive.Geometry.Polygons.Count

s = ".poly[%s-%s]" % ( p.Index, min( p.Index+5,LAST-1 ) )
si.SelectGeometryComponents( s )

Scripting – How to get the active objects for component selection

When you’re in a component selection mode (such as Edge, Polygon, or Point), the active objects are highlighted in orange. The “active objects” are the objects that are “active for component selection”.

When Softimage is in a component selection mode, the Selection will either by empty or it will contain CollectionItems (one for each object with selected components).

So, how do you get the active objects? Here’s one way, using the little known, magical “.[obj].”:

# Python
import win32com.client
oActiveObjects = win32com.client.Dispatch( "XSI.Collection" )
oActiveObjects.Items = ".[obj]."
// JScript
var oActiveObjects = new ActiveXObject( "XSI.Collection" );
oActiveObjects.Items = ".[obj].";

About the Selection Change Command

In the Selection preferences, there’s a Selection Change Command text box where you can type in a command to run when the selection changes.

Here are two important points about the Selection Change Command:

  1. Use the VBScript syntax to enter the command. It doesn’t matter what the current scripting language is, the preference expects VBScript (so something like Application.LogMessage( “Hello %s” % Application.Selection(0) ) will end up logging an error when you select something).
  2. The Selection Change Command is triggered when you select something in a 3D viewport. The command isn’t triggered when you select an object in the explorer.

New in Softimage 2013: Getting the selected materials in the Material Manager

In 2013, you can use the selection view attribute to get the materials that are selected in the Material Manager.

Here’s a python custom menu item that shows how to do it. In summary, you do this:

  • Add a custom menu in the Manager Manager
  • Use a callback item, so you can get the view from the context
  • Use selection view attribute to get the names of the selected materials
import win32com.client
from win32com.client import constants

null = None
false = 0
true = 1

def XSILoadPlugin( in_reg ):
	in_reg.Author = "blairs"
	in_reg.Name = "MaterialsManagerPlugin"
	in_reg.Major = 1
	in_reg.Minor = 0

	#RegistrationInsertionPoint - do not remove this line

	return true

def XSIUnloadPlugin( in_reg ):
	strPluginName = in_reg.Name
	Application.LogMessage(str(strPluginName) + str(" has been unloaded."),constants.siVerbose)
	return true

def Custom_Tools_Init( in_ctxt ):
	oMenu = in_ctxt.Source
	oMenu.AddCallbackItem("Get Selected Materials","OnGetSelected")
	return true

def OnGetSelected( c ):
	view = c.GetAttribute( "Target" )
	Application.LogMessage( view )
	Application.LogMessage( view.GetAttributeValue( "selection" ) )
	for mat in view.GetAttributeValue( "selection" ).split(","):
		Application.LogMessage(  mat )

	return true

Selection filter for objects with ICE trees

Here’s a custom filter for selecting objects with ICE trees: psCustomFilters.xsiaddon. The filter appears as Obj_w_ICETree in the filter list of the Select panel in the MCP:

You can use this filter in the MCP Select panel (choose the filter and then press Ctrl+A to select all objects with an ICE tree).

Or you could make a toolbar button with these snippets:

// JScript
SelectAllUsingFilter("Obj_w_ICETree", siCheckComponentVisibility, null, null);
from siutils import C		# win32com.client.constants
Application.SelectAllUsingFilter("Obj_w_ICETree", C.siCheckComponentVisibility, "", "")

Filtering object selection by volume

Here’s an addon that uses the ICE Volume attribute to filter object selections. The Volume attribute is always defined (so you don’t need to Show Values or anything for this filter to work).

Note that you have to freeze scaling to get the right volume for an object.

For simplicity, I’m using a custom property (on the scene root) to hold the preferences for the filter.

The filter itself is simple. It just gets the Volume attribute value and compares it to the range of values specified in the PPG.

# Match callback for the psCustomFilters custom filter.
def ObjbyVolume_Match( in_ctxt ):
	Application.LogMessage("ObjbyVolume_Match called",constants.siVerbose)

	in_object = in_ctxt.GetAttribute( "Input" );

	obj = Get3DObject( in_object );
	if ( Application.ClassName(obj) != "X3DObject" ):
		return false;
	v = obj.ActivePrimitive.ICEAttributes("Volume").DataArray[0]

	filterProp = GetFilterProp( )
	if filterProp.Filter_by_range.Value == True:
		bMatch = filterProp.Volume_Min.Value <= v <= filterProp.Volume_Max.Value
	else: # Filter by value with an epsilon
		min = filterProp.Value.Value - filterProp.Epsilon.Value
		max = filterProp.Value.Value + filterProp.Epsilon.Value
		bMatch = min <= v <= max
	return bMatch

Tip: Selecting objects from inside an ICE Tree view

ICE tree views are typically locked, so that they don’t constantly update while you’re working with a scene and selecting different objects. But what do you do if you want to select an object referenced in the ICE tree? Do you have to hunt down the object in the explorer?

Nope. As of 2011.5, you can right-click a Get Data node to select the referenced object.

This works with the self, this_model and this_parent tokens too.

It also works on an Get Data inside a compound, if the Get Data In Name is connected to a valid reference. So, you don’t need to exit a compound just to select an input object.

hat tip: Guillaume Laforge