Collections

Many of the values you work with in MAXScript are sequenced collections, notably arrays, wild-card pathname selections, and the built-in object sets. In the manner of most operating system shell command languages, MAXScript will automatically apply appropriate operations to each of the members in these target collections.

Examples

hide $lights/key*             -- hide all the 'key' lights in the scene

$box*.position = [0,0,0]      -- recenter all boxes

delete dead_objects           -- delete all the objects in the array

                              -- 'dead_objects'

obj.pos.keys.intangent=#fast  -- set in tangent to fast for all obj's position keys

lights.value *= 0.8           -- turn down all the lights 20%

MAXScript determines whether to perform a mapped operation by looking at the first argument to a function or the target in a property assignment to see if it is a sequenced collection. In the case of a function call, it also looks to see if the function is of the appropriate kind, evaluates all the other argument expressions once only. MAXScript then calls the function on each of the members of the first argument collection passing along the other evaluated arguments. In the case of a property assignment, it evaluates the right-hand-expression once only and assigns the value to the named property of each of the elements in the target value collection.

This means that every element in the collection must be a valid target for the function or have the named property in question. If MAXScript comes across an inappropriate target it will report an error and stop the automatic operation.

Except for certain common properties, MAXScript currently only allows automatic collection mapping of property assignment one level deep. For example, the following will perform collection mapping across the objects ObjectSet:

objects.pos = [0,0,0] -- move all objects to world center

If the last level specified is one of the following component names, the automatic collection mapping is performed on the preceeding property:

.angle

.b

.blue

.axis

.controller

.g

.green

.isAnimated

.keys

.r

.red

.track

.x

.x_rotation

.y

.y_rotation

.z

.z_rotation

For example:

objects.pos.z = 0.0  -- move all objects to world XY plane

This behavior of calling a function on all the elements of a collection is known as mapping in computer science. In the description of each collection type, you will find those kinds of sequenced collections that can be mapped over identified as mappable. In the descriptions of the various functions and operators throughout this document, those that act on each element in a collection are labeled mapped. In general, the functions that are mapped are those that have some side-effect on the value they take, such as moves, and deletes and hides. Others, like the test function isHidden(), are not mapped because it would be pointless.

All mapped functions defined by MAXScript, such as copy(), delete(), or move(), take an optional last argument, #noMap. This causes the functions not to be mapped when called on collection arguments such as arrays. When used with copy() on arrays in particular, this will return a top-level copy of the array itself and not perform an individual copy() on each element of the array.

You can make your own mapped scripted functions by preceding the function definition with the reserved word mapped, for example:

mapped function rand_color x = (x.wireColor = random black white)

This causes such functions to be automatically called repeatedly on the elements of a collection if a collection is given as the first argument to the function. The #noMap argument is not applicable to user-written functions.

As described in For Loop, a for loop can be set up to iterate through a collection set assigning successive elements in the collection to the loop value each time through the loop. For loops can also be set up to return an array collection set as its result.

Examples

for item in table1 do x = x + item.height -- sequence an array

bigones = for obj in $box* -- you can sequence pathnames!

where obj.height > 100 collect obj    -- collect the big boxes

                                      -- into an array

When iterating through a collection set using a for loop, or when passing a collection to a mapped function, care must be taken not to reorder or remove elements in the collection set within the for loop or function. Take for an example a script that is supposed to convert all of the selected splines into NURBS curves. Two scripts that do not execute properly are:

for obj in selection do ( convertToNURBSCurve obj)

convertToNURBSCurve selection

The reason neither of these lines execute properly is that the convertToNURBSCurve() function clears the current selection. So only the first spline is passed to convertToNURBSCurve() and converted. Since convertToNURBSCurve() is a built-in function, there is no way to modify this behavior. To get around this problem, you need to first "snapshot" the selection collection set as an array using the as operator, or use the getCurrentSelection() function, which returns the current selection set as an array. Two scripts that execute properly are:

for obj in (selection as array) do ( convertToNURBSCurve obj)

convertToNURBSCurve (getCurrentSelection())