RSS FEED

Unique random wirecolor

As I've indicated in my previous retrospective that there's a skeleton in my closet, a bulk of code waiting to be commented. Let's now have a look at a piece of code from almost a year ago (original question at Stack Overflow: Give each object in scene a unique wirecolor?).

The common solution used to the automate wirecolor randomization is assigning a random color to each object in scene by calling random function for every node. While this might seem to work in many cases it actually generates repeating colors and might lead to unexpected result.

What we can do instead is prepare a unique set of elements to build the colors of and iterate over them as we traverse the node collection:

fn shuffle arr =
    for counter = arr.count to 2 by -1 collect
    (
        local swapIndex = random 1 counter
        swap arr[random 1 counter] arr[counter]
    )

fn incrementCounters &r &g &b step =
    if (b += step) > 256 do
    (
        b = 1
        if (g += step) > 256 do
        (
            g = 1
            if (r += step) > 256 do r = 1
        )
    )

fn assignRandomWirecolor objs shuffleObjs:false =(
    local stepCount = objs.count^(double 1/3) + 1
    local step = 255./stepCount
    local redArr = shuffle (#(0) + #{1..255})
    local greenArr = shuffle (#(0) + #{1..255})
    local blueArr = shuffle (#(0) + #{1..255})
    local r = 1, g = 1, b = 1

    if shuffleObjs do
      objs = shuffle objs

    for obj in objs do
    (
        obj.wirecolor = [redArr[int(r)], greenArr[int(g)], blueArr[int(b)]]
        incrementCounters &r &g &b step
    )
)

The process involved in getting unique random colors does not add much complexity and is faster than many naive implementations, especially with large number of objects. As we are working in the RGB color space with red, green and blue values in the range 0 to 255, there are 255 × 255 × 255, i.e. 16 777 216 different colors to make use of.

This means that for the most complex case we could make three lists of numbers from 0 to 255 and assign color 0 0 0 to the first object, then color 0 0 1 to the second one and so on all the way up to 0 0 255. After this step we would zero out the last counter and increase the middle one so we get 0 1 0, and continue up to 0 255 255, increasing the middle counter each time the last counter is zeroed out as it gets bigger than 255. Finally the first counter would increase every time the other two reach 255.

Instead of directly assigning the counter values we can use them to get an item at that index on a list of values from 0 to 255, so redHues[1] gives us 0 and redHues[256] gives us 255. When we shuffle the three lists those values are no longer predictable and we get the randomness we wanted while still keeping the uniqueness.

In a general case we rarely need 255 × 255 × 255 colors, e.g. for 1 000 objects 10 × 10 × 10 colors is enough – 10 being the cube root of the number of objects here. The easiest solution to get only that many values from the complete 256 item long lists is to increase the counter not by one as in the previous case but by a bigger number, 25.6 in this case. As we only want to access indexed members of the list, the resulting counter values have to rounded off and 25.6 becomes 26, 51.2 becomes 51 and so on.

DISCLAIMER: All scripts and snippets are provided as is under Creative Commons Zero (public domain, no restrictions) license. The author and this blog cannot be held liable for any loss caused as a result of inaccuracy or error within these web pages. Use at your own risk.

This Post needs Your Comment!

Eric Hance

I could see this being pretty useful in conjunction with VRay Wirecolor, etc for generating lots of random colors for mattes

(Very necessary from time to time at our shop).

Thanks for putting this up!

Return to top