ICE: Removing duplicates from arrays


Here’s an ICE tree that removes all duplicate elements from an array. It uses Generate Sample Set, so there’s no repeat nodes. But it relies on the fact that you can feed in an array of indices into Remove from Array, and Remove from Array doesn’t care if that array of indices itself contains duplicate. So, if you plug the array [1,1,1,2,2,2,3,3,3] in the Index port, Remove from Array will nicely remove elements 1, 2, and 3 with no complaints.

GenerateSampleSet_Remove_Duplicates

Unlike some other methods, this works with scalars too:

GenerateSampleSet_Remove_Duplicates_Scalar

Collection methods and the documentation


The SDK reference page for PluginCollection says that it implements the inherited methods Find, Filter, and GetAsText.
plugincollectiondoc
But it doesn’t. And according to the object hierarchy, it doesn’t have a base class (so where do those inherited methods come from 🙂

I can’t really remember anymore, but I think all the collection objects derive from a now-undocumented base class; or at least the documentation pretends they do (that made it simpler to auto-generate the pages). That’s why they all have inherited methods and properties, and they all have the same scripting examples.

If we use the OLE-COM Object Viewer that comes with the Windows SDK 7.1, we can check out the object model type library and see what methods PluginCollection really does support:
ITypeLib Viewer

Note: On a reference page, the yellow highlighting marks inherited methods and properties.

Saturday snippet: Python classes, __getattr_, lambda, and-or trick, and getattr


Here’s a snippet of some python posted on the Softimage mailing list this week.

from win32com.client import constants
class Softimage:
    __getattr__ = lambda x, a: getattr(Application, a, False) or getattr(constants, a)
softimage = Softimage()

Let’s break this down…

First, this snippet defines a class named Softimage (lines 1 and 2) and then instantiates an instance of that class (line 4).

Once you have an instance of a class, you can access the attributes of the class. Now this Softimage class doesn’t define any attributes, but it does provide a __getattr__ method. For example, if you ask for softimage.GetValue, then that __getattr__ method gives you back the function object Application.GetValue. If you ask for softimage.ActiveSceneRoot, you get back the value of the Application.ActiveSceneRoot property. And if you ask for softimage.siX3DObjectID, you get back constants.siX3DObjectID.

So, how does that work?

__getattr__ is a method that is called when an attribute isn’t found on a class instance.
If you define __getattr__ without using lambda or the and-or trick, it could look something like this:

from win32com.client import constants
class Softimage:
#    __getattr__ = lambda x, a: getattr(Application, a, False) or getattr(constants, a)
    def __getattr__( self, name ):
        a = getattr(Application, name, False) 
        if not a:
            a = getattr(constants, name)
        return a

getattr is a built-in Python function that gets an attribute on a specified object. Here, we’re getting an attribute of the Softimage Application object. For example:

# Get the scene root
a = getattr(Application, "ActiveSceneRoot", False)
print a
print a.Parameters.Count

# Get the selection with the GetValue command
a = getattr(Application, "GetValue", False)
print a
print a( 'SelectionList' )

Friday Flashback #109


Softimage started with $350,000 in venture capital funding. Here’s some comments from Loudon Owen, who with John Eckert helped finance and advise Softimage in its growth.

Proceedings of the Standing Senate Committee on Banking, Trade and Commerce
Issue 51 – Evidence
TORONTO, Thursday, April 29, 1999
The Standing Senate Committee on Banking, Trade and Commerce met this day at 9:00 a.m. to consider the present state of the financial system in Canada (equity financing).

From the opening comments by Mr. Loudon F. Owen, Managing Partner, McLean Watson Capital Inc.:

When we started, we were trying to raise money for a company in Montreal called Softimage. We were carrying around our little flip books but nobody wanted to give us money. We were quite astonished because we thought it was an exciting opportunity. We spoke to American venture capital firms, we spoke to Canadian venture capital firms and we decided there was an opportunity for a highly specialized venture group, so that is what we set up. We invest exclusively in software companies. We were highly focused, driven by what we perceived to be a market need. That was quite a few years ago and I think the market has changed dramatically in the last five years. However, that was what gave us the impetus to go forward.

I do not know if you have heard about Softimage. It is an animation software company. If you have seen Titanic, Jurassic Park, Death Becomes Her or most of the commercials on the television, you will have seen Softimage’s technology. The company was funded with $350,000. The shares which we receive from Microsoft are today worth $2.2 billion. It has 400 employees in Montreal and it was instrumental in building the animation industry in Montreal. There have been a variety of spinoff companies such as Discreet Logic and other companies in Montreal, so the company grew pretty dramatically. The only venture capital that went in was $350,000. After that it went public on the NASDAQ.

Our role was to invest. John Eckert and I share the duties of chief operating officer, and took it public on the NASDAQ. It was the first Quebec company to make its initial public offering on the NASDAQ. We considered the Canadian markets and elected not to go public here. We then sold it to Microsoft. We took the company from the initial point of investment, with its four employees, including the founder, Daniel Langois, to over 200 when we sold it to Microsoft.

On the question of whether Quebec was a hot of entrepreneurship [at that point in time, 1999] due to a more favourable regulatory and tax climate or just because people are more into the culture of entrepreneurship:

Do hotbeds of technology or clusters grow naturally because they are sponsored and supported? Again, it is a combination. I think Montreal’s animation, post-production and special effects community grew without any government support. For example, neither Softimage nor Discreet Logic had any significant government support or tax breaks. In fact, we tried to sell our first product to the CBC, and they would not buy it. They bought a French product, so we had to go to France and sell our first product there.

These companies grew up indigenously through their own creative efforts. What is happening now to sustain those industries and help them grow with their larger working capital requirements is assisted by government efforts.

Loudon Owen is co-founder of McLean Watson Capital. Prior to establishing McLean Watson Capital, Loudon and John Eckert financed and advised Softimage, a world leader in high-end 3D animation, in its growth from 4 to 250 employees, its IPO on Nasdaq in 1992 and the sale to Microsoft in 1994. Loudon and John served as the Joint COO for Softimage from 1993 to its sale.

Getting all visibility.viewvis parameters


From a discussion on the mailing list today, a few of the different ways to get every instance of a specific parameter, and some [crude] timing of the different methods.

I was also glad to see confirmation that using wildcards like “*.visibility” pick up everything (because I’ve never got any variation of “*#3dobject” to pick up all objects).

si = Application
import time
from win32com.client import Dispatch as disp
from win32com.client import constants as c


t = time.clock()
oObj = disp("XSI.Collection")
oObj.Items = "*.visibility"
for o in oObj:
	v = o.viewvis.Value
print 'XSICollection.Items  : count=%s, time=%s' % ( oObj.Count, time.clock() - t )


t = time.clock()
oObj.Items = "*.visibility.viewvis"
for o in oObj:
	v = o.Value
print 'XSICollection.Items  : count=%s, time=%s' % ( oObj.Count, time.clock() - t )



t = time.clock()
items = si.ActiveSceneRoot.FindChildren2()
for obj in items:
    vis = obj.GetPropertyFromName2("Visibility")
    v = vis.viewvis.value
print 'GetPropertyFromName2 : count=%s, time=%s' % ( items.Count, time.clock() - t )


t = time.clock()
items = si.ActiveSceneRoot.FindChildren2()
for obj in items:
    v = obj.Properties('visibility').Parameters('viewvis').Value   
print 'Properties.Parameters: count=%s, time=%s' % ( items.Count, time.clock() - t )


t = time.clock()
items = si.ActiveSceneRoot.FindChildren2()
for item in items:
    si.Tag(item.fullname + '.visibility.viewvis', c.siTag1)
print 'Tag()                : count=%s, time=%s' % ( items.Count, time.clock() - t )

t = time.clock()
items = si.ActiveSceneRoot.FindChildren2()
for item in items:
	p = item.Properties( "Visibility" ).Parameters( "viewvis" )
	p.Tags = c.siTag1
print 'Parameter.Tag        : count=%s, time=%s' % ( items.Count, time.clock() - t )


t = time.clock()
val = si.ActiveSceneRoot.TaggedParameters(c.siTag1, False)
for v in val:
    v = v.value
print 'TaggedParameters     : count=%s, time=%s' % ( items.Count, time.clock() - t )

Notice how turning off command logging speeds up those two commands:

# XSICollection.Items  : count=4367, time=1.18428964264
# XSICollection.Items  : count=4367, time=1.00415073153
# GetPropertyFromName2 : count=4367, time=2.07334397855
# Properties.Parameters: count=4367, time=4.38064481075
# Tag()                : count=4367, time=20.6681847681
# Parameter.Tag        : count=4367, time=5.11316244631
# TaggedParameters     : count=4367, time=0.705648810261
Application.SetValue("preferences.scripting.cmdlog", False, "")
# XSICollection.Items  : count=4367, time=1.16849869148
# XSICollection.Items  : count=4367, time=0.988541493731
# GetPropertyFromName2 : count=4367, time=2.03344763365
# Properties.Parameters: count=4367, time=4.33773781962
# Tag()                : count=4367, time=7.47015675041
# Parameter.Tag        : count=4367, time=4.99573667863
# TaggedParameters     : count=4367, time=0.687065751859

Use Global Coordinates for Display


Sometimes it can be useful to turn on Use Global Coordinates for Display. Because otherwise you’re going to be looking at points in local space, and that can mess up your thinking.

Here’s a simple example to show the difference. Purple is global, light green is local. As you can see, the purple points match up with the actual coordinates.
UseGlobalCoordsForDisplay

Now here’s a better example of the usefulness of Use Global Coordinates for Display. Red is local (and misleading). Yellow is global. Imagine you’re doing all kinds of coordinate system conversions in a complicated tree, and then you decide to show values as points. If you’re not careful, like me sometimes, you end up doubting everything you’ve done and pulling it all apart.
UseGlobalCoordsForDisplay1

Getting selected text from the script history


Here’s how to get the selection from the script history. This is how the Repeat command works.

def GetScriptHistoryView():
	oLayout = Application.Desktop.ActiveLayout

	# See if the script history is a view of it's own
	oViews = oLayout.Views.Filter( "Script History" )
	if oViews.Count == 0:
		oViews = oLayout.Views.Filter( "Script Editor" )

	return oViews(0)

oLog = GetScriptHistoryView()
text = oLog.GetAttributeValue( "historyline" ) if oLog else "<Nothing>"
print text