[Closed] selecting an array of objects
Hi,
I was quickly making a script to select all instances of the objects selected, and I realized that it was veeery slow, even with undo off, disableSceneRedraw and suspendEditing.
I thought a bit and discovered that I could select an array of objects like in this code:
before:
for o in objects do selectmore o
after:
a=#()
for o in objects do append a o
selectmore a
the difference in speed is really huge (like 3000%+ faster).
But There is absolutely no reference to the possibility of selecting an array of objects in maxscript’s help, even in the “how to make it faster” section.
Is there anything I should know about that way of selecting objects?
Why isn’t that documented at all as it looks very effective?
thanks.
This doesn’t have to do with ‘select’ in particular (though it is a very obvious example), it is a more general issue with mapped functions (which are in MXS help). This is covered both in the reference, and beginner tutorials.
thanks,
indeed it is in the help that you can use mapped functions on arrays. I was looking for select and not for mapped functions, its in the “how to make it faster” too, but there isn’t any example in the section on mapped functions, so I didn’t catch that it was that.
It has been a long time since I last did some of maxscripting, I should have realized it was a mapped function thing and not a select thing.
So I’m happy there’s no trap in that, my little script will now execute in less than a sec for 10000 objects, instead of more than a minute!
Let me also add: I’ve run into a couple times where trying to loop through an array or collection (geometry, objects, selection), and ‘if x then delete’ and it has crashed max. I have had to end up putting the objects into a new array, and doing ‘delete theNewArray’. Don’t know why, but just an FYI.
The truth is people tend to code differently. None of the above expressions fit into what I would do when selecting objects, and I see what you mean when you say the Help does not help in this particular case. All the info is there, but it does not really tell you “Don’t do this” because frankly the authors cannot anticipate every possible expression people might write. Doing things one at a time instead of once for the whole collection sounds like it would be slow, and it is.
The first example loops through an array and sets the isSelected property of the node to true for each one, which is very very slow.
The second example does indeed call selectMore() in a mapped fashion, but the array building is a bit inefficient, as it uses append() which has to grow the array on each call by duplicating the previous array in memory. With few objects it would be negligeble though, with millions of objects it might start showing slowdowns, but who works with millions of objects?
If you really have to do a FOR loop, use the COLLECT form instead of DO. It builds the array and gives you back the result without using append() explicitly or any intermediate variables to store the array. You could assign to a vatiable if you want to keep the selection snapshot around though.
What I would have said would have been something like:
selectmore (for o in objects collect o)
This would allow you to perform where tests, for example to select all visible objects,
select (for o in objects where not o.isHiddenInVpt collect o)
or, if this really is what you are trying to do, selecting ALL scene object, simply
select objects
There is no reason to do selectmore() if you are selecting all scene objects anyway, and you can operate on the live collection of scene objects without converting it to array. Note that select() will make sure the previous selection is removed, while selectMore() will obviously add to the existing one.
In fact, the “How To Make It Faster” chapter suggests that you use mapped functions where possible, because the mapping is a C++ loop instead of a MAXScript loop and has a lower overhead. I will make sure these examples are added to the next version.
I did some benchmarking myself. With 100 boxes in a grid, ‘select objects’ took 31 ms in Max 2008. With 1000 boxes, it took around 350 ms, so it scales in a nearly linear fashion. This time is measured around the select() method and does NOT include the time it takes the viewport(s) to actually redraw, just the time to set the selected flags of the nodes.
Here some more results:
select (for o in objects where not o.isHiddenInVpt collect o)
--took between 402 and 469 ms in multiple attempts
for o in objects do selectmore o
--Took only 171 ms if all objects were already selected,
--but 674172 ms (yes, over 11 minutes!) if none of the 1000 were selected.
--STAY AWAY FROM IT!
a=#()
for o in objects do append a o
selectmore a
--This was very comparable to my other code, 375 ms.
--Growing an array 1000 times does not really make any measurable difference to
selectmore (for o in objects collect o)
--or
select (for o in objects collect o)
--but I prefer the inline form below since it is a single line and does not require a variable
--so I consider it more elegant
thank you bobo!
I had forgotten that “collect” keyword. It is indeed way more elegant. I didn’t do any maxsripting in more than a year, so I’m a bit rusty.
And btw no I dont want to just select all objects ;). It was just for the example, but I could have done one having more sense.
Using a selectmore in a loop is definitely something to avoid, and your times do reflect what I had observed. I’ll just select once at the end the entire array of objects (if I cant use collect for any reason).
I have a scene with thousands of objects and the difference is huge!