On the XSI mailing list a few weeks back Dan Yargici posted a question about how to reconfigure a simple non-regular pattern like (1,1,5,5,5,5,8,9,9,9,10,10) into (1,1,2,2,2,2,3,4,4,4,5,5). In the resultant thread Martin Chatterjee came back with a brilliant solution without using repeats. Martin's solution touched on some of the inherent functionality in ICE arrays that's worth expanding upon.
I'm going to try and illustrate some of these with a sample scene that contains an ICE tree that shows several different methods for firstly creating a pattern array and then using that pattern to manipulate an array. If that all sounds abstract it's based on Dan's problem above but with the added wrinkle that the initial pattern is not numerically ascending i.e. it looks something like this: (8,8,2,2,2,14,3,3,1,16,11,11).
The first task is to identify the points at which the pattern changes e.g. when the preceding item in the array is not the same as the current one. There are a couple of ways of doing this illustrated in the scene, but it's the first method that illustrates our first point.You can create an identical array and use the insert in array node to add zero at index zero. Although the arrays are now of different lengths you can subtract the two arrays. Wherever the resultant value is non-zero you can mark the array with a boolean. So, here's the first key point about ICE arrays:
- ICE arrays do not have to be the same size for operations (e.g. subtraction)
You should now have an array consisting of (1,0,1,0,0,1,1,0,1,1,1,0) i.e a series of boolean values. Each 1 represents the starting point of a new digit in the pattern. In this form it's not much use but if you convert the booleans to integers and then multiply by the subindex array you get a list of indices in the original array where the pattern changes - the actual constituent numbers of the pattern. Multiplying subindices by a boolean (converted to integer) is a neat way of isolating the array indices you're interested in.In this case we get (0,0,2,0,0,5,6,0,8,9,10,0).
It's still not entirely usable, though, since it also contains a lot of zero entries. If we were to run this new list through a select in array node we would be picking the zero index entry multiple times. Which brings us to our second point:
- If you feed an index array into the index port of select in array or remove from array you get an equally sized** array as your output*. This output array can contain multiple duplicates of an index if you require it. (* The values output of remove from array). (**Provided all the input indices are valid - more on this below.)
We need to either remove the zero entries or, at least, filter them out in such a way that ICE does not action them. Here's where, I guess, the main trick of this article lies:
- ICE has been optimised to ignore invalid index entries for arrays.
So, in our case, with an array like (0,0,2,0,0,5,6,0,8,9,10,0) where all we're interested in are the non-zero entries we just need to make the zeros into invalid indices (negatives or values that exceed the array size minus one i.e. (-1,-1,2,-1,-1,5,6,-1,8,9,10,-1). This new array can be fed into a select in array node and will only select the following indices (2,5,6,8,9,10). Effectively, you've filtered the array without a repeat loop.The sample scene shows a couple of different ways of doing this.
The final point to make is that two of the array nodes - insert in array and set in array - can each take two array inputs. One for the indices to be affected and one for the values to be utilised at those indices. As above, those arrays can be different lengths - the processing will truncate the length of the longest array to the shortest. What's more, any invalid indices will be ignored and thus any parallel value (ie. one at the same index) in the value array will also be ignored.
It's dry and it's boring but being aware of these kinds of optimisations and behaviours of the ICE array nodes can be very useful as, I think, Martin's solution demonstrated.
Sample scene is here.