Optimization of a Color Cycler

One of my projects in progress depends on the generation of different colored objects (Trust me, once I actually get a release pushed out, it will make sense (actually, I also need to push out a new iteration of my LaTeX editor. Note that I’m putting the name up for reconsideration)). First, I’ll define HSV space for y’all: assuming that you are familiar with RGB space… or, RGB space is the definition of colors by red, green, and blue components (hence the acronym RGB). Usually, computers will use 8 bits to define each channel, for a total of 24 bits to define a pixel. But I digress. HSV uses hue, saturation, and value for it’s definition, hue being the color (say, orange versus blue), saturation the presence of that color (ex. 0 saturation is white, total saturation a bloody red), and value the brightness (ex. 0 value is black, high value something not-black). In this particular case, I’m using HSV since I can set the saturation low, value high, and change the hue for a rainbow of easy-on-the-eye colors. However, it is problematic if one can’t distinguish between two objects that are supposed to be different.

One strategy that comes to mind is using some sort of binary division (for illustrative purposes, I’ll take hue 360 = hue 0): Take the first value to be 0, then 180, then 270, then 90, etc. However, it isn’t dead easy to code, and another programming scheme that could work is merely adding a constant to each iteration and modulating it by 360 (ex. 0+105->105+105->210+105->(315+105)%360->60 (where % is the modulus, otherwise known as the remainder)), which is dead easy to code. However, adding a constant could result in trouble: if you choose 90, you’ll start getting duplicate colors after 4 steps. 91 isn’t much better, because while the colors are not exactly the same, they are pretty close, perhaps nigh indistinguishable. How do you choose the constant that will put off disaster for the longest time?

Guess and check is a time-honored method, and it yields okay reults: I made up a constant and used to to almost no ill-effect. However, one could do better. Checking each constant by hand is tedious, but only by hand: if you brew up a script, then its turns out to be a bit simpler.

For a naive script, we do iterations until we get a duplicate of a previous iteration: the number of iterations before termination is a measure of quality, where higher numbers are better. Note, as a curiosity, that if the bound were some prime number, rather than 360, then many constants will turn out to be the maximum number of iterations with this method. However, we still need to take into account how close the subsequent hues are (for instance, 1,2,…365 works, but it really doesn’t).

To work around this, I used a sort of logistic function to decrease the amount of influence later numbers have, since the application doesn’t expect someone to be using 50+ objects at once. To do this, I weighted the hue-distance between the nearest colors (for each color added: I didn’t go back and recalculate all the distances for each iterations, although that *might* give us a different answer) by the reverse logistic function translated so that the first 20 colors had a pretty big say relative to everything else. Ah, well, here, have some code.

[python]
#colorlen.py
import math

n = 360

def modlen(a,b,limit):
nominal = abs(b-a)
h = (limit-b)+a
L = (limit-a)+b
return min([nominal,h,L])

def fitness(i,prev):
return sum([1.0/(1.0*modlen(x,i,n)**1.0) for x in prev])

def total_fitness(m):
i=m
f=0.0
steps=0
prev = [0]
while not(i in prev) and steps<50:
f += fitness(i,prev)*1.0/(1+math.exp(0.01*(steps-10)))
prev.append(i)
i = (i+m)%n
steps +=1
return f-steps

def main():
lst = [total_fitness(m+1) for m in range(n-2)]
best = lst.index(min(lst))
return best+1

print main()
[/python]

While writing this up, I’ve realized that I could do some more work on this and have some pretty graphics to show to everyone. Hmm, I’ll have to fit this in somewhere…

Oh, and hope this was helpful to someone. Dunno, go and write code fruitfully?