Skip to main content
updated algorithm to use only one threshold (color difference) instead of component wise
Source Link
bummzack
  • 22.6k
  • 5
  • 63
  • 87
// background color, currently equals RGB(0, 0, 255)
bg = HSV(240, 1.0, 1.0)

// number of colors to create is equal to the amount of balls
numColors = balls.length

// set thresholdsthreshold to compare colors. you can tweak thesethis to your liking
// colors should at least have 20 degrees hue offset
hueThreshold = 20
// colors should be at least 20 percent different in saturation
satThreshold = 0.2
// colors should be at least 20 percent different in brightness
valThresholdthreshold = 0.215

for(i = 0; i < numColors; i++){
    do {
        // assuming rnd returns a random float 0-1 inclusive
        color = HSV(rnd() * 360, rnd(), rnd())
    } while(
        // if thecalculate colordelta isvalues, toonormalize closehue to the background (below the 0-1
        // thresholds for all components), pick another one
      dH = (180 - abs(abs(bg.h - color.h) - 180)) </ hueThreshold360
        &&dS abs(= bg.s - color.s) < satThreshold
        &&dV abs(= bg.v - color.v) 
 < valThreshold      // "distance" between bg and color
        difference = sqrt(dH * dH + dS * dS + dV * dV)
    } while(difference < threshold) 

    ball[i].color = color
}
// background color, currently equals RGB(0, 0, 255)
bg = HSV(240, 1.0, 1.0)

// number of colors to create is equal to the amount of balls
numColors = balls.length

// set thresholds to compare colors. you can tweak these to your liking
// colors should at least have 20 degrees hue offset
hueThreshold = 20
// colors should be at least 20 percent different in saturation
satThreshold = 0.2
// colors should be at least 20 percent different in brightness
valThreshold = 0.2

for(i = 0; i < numColors; i++){
    do {
        // assuming rnd returns a random float 0-1 inclusive
        color = HSV(rnd() * 360, rnd(), rnd())
    } while(
        // if the color is too close to the background (below the 
        // thresholds for all components), pick another one
        (180 - abs(abs(bg.h - color.h) - 180)) < hueThreshold
        && abs(bg.s - color.s) < satThreshold
        && abs(bg.v - color.v) < valThreshold
    )
    ball[i].color = color
}
// background color, currently equals RGB(0, 0, 255)
bg = HSV(240, 1.0, 1.0)

// number of colors to create is equal to the amount of balls
numColors = balls.length

// set threshold to compare colors. you can tweak this to your liking
threshold = 0.15

for(i = 0; i < numColors; i++){
    do {
        // assuming rnd returns a random float 0-1 inclusive
        color = HSV(rnd() * 360, rnd(), rnd())
        // calculate delta values, normalize hue to 0-1
        dH = (180 - abs(abs(bg.h - color.h) - 180)) / 360
        dS = bg.s - color.s
        dV = bg.v - color.v 
        // "distance" between bg and color
        difference = sqrt(dH * dH + dS * dS + dV * dV)
    } while(difference < threshold) 

    ball[i].color = color
}
added another algorithm
Source Link
bummzack
  • 22.6k
  • 5
  • 63
  • 87

UPDATE:

Since I didn't really answer your question but rather provided another approach, here's how an algorithm for an arbitrary background and completely random colored balls could look like (this is a brute-force approach but should work fine for your use-case):

// background color, currently equals RGB(0, 0, 255)
bg = HSV(240, 1.0, 1.0)

// number of colors to create is equal to the amount of balls
numColors = balls.length

// set thresholds to compare colors. you can tweak these to your liking
// colors should at least have 20 degrees hue offset
hueThreshold = 20
// colors should be at least 20 percent different in saturation
satThreshold = 0.2
// colors should be at least 20 percent different in brightness
valThreshold = 0.2

for(i = 0; i < numColors; i++){
    do {
        // assuming rnd returns a random float 0-1 inclusive
        color = HSV(rnd() * 360, rnd(), rnd())
    } while(
        // if the color is too close to the background (below the 
        // thresholds for all components), pick another one
        (180 - abs(abs(bg.h - color.h) - 180)) < hueThreshold
        && abs(bg.s - color.s) < satThreshold
        && abs(bg.v - color.v) < valThreshold
    )
    ball[i].color = color
}

UPDATE:

Since I didn't really answer your question but rather provided another approach, here's how an algorithm for an arbitrary background and completely random colored balls could look like (this is a brute-force approach but should work fine for your use-case):

// background color, currently equals RGB(0, 0, 255)
bg = HSV(240, 1.0, 1.0)

// number of colors to create is equal to the amount of balls
numColors = balls.length

// set thresholds to compare colors. you can tweak these to your liking
// colors should at least have 20 degrees hue offset
hueThreshold = 20
// colors should be at least 20 percent different in saturation
satThreshold = 0.2
// colors should be at least 20 percent different in brightness
valThreshold = 0.2

for(i = 0; i < numColors; i++){
    do {
        // assuming rnd returns a random float 0-1 inclusive
        color = HSV(rnd() * 360, rnd(), rnd())
    } while(
        // if the color is too close to the background (below the 
        // thresholds for all components), pick another one
        (180 - abs(abs(bg.h - color.h) - 180)) < hueThreshold
        && abs(bg.s - color.s) < satThreshold
        && abs(bg.v - color.v) < valThreshold
    )
    ball[i].color = color
}
Source Link
bummzack
  • 22.6k
  • 5
  • 63
  • 87

Using RGB values by generating a random 0-255 value for each component might not only create colors that are similar to the background, you can also end up with very similar colors for the balls.

I'd probably choose a less random approach and create colors that are guaranteed to be different instead. You could create a color for each ~32 degrees in the hue range and alternate the brightness or saturation (use HSV color-model instead of RGB).

So something like this:

numColors = 20;
stepSize = 360 / (numColors / 2 + 1); // hue step size
for(i = 0; i < numColors; i++){
    color = HSV(i / 2 * stepSize, 0.5 + (i % 2) * 0.5, 1.0) 
}

which will create 20 colors, nicely distributed. This assumes you're using integer math, so i = 0 and i = 1 will create the same hue value.

Of course this doesn't scale too well.. if you have 100 color values to create, the hue spacing might not be enough and you'll end up with similar colors again. In that case you could also vary the V (brightness) value.

I don't know what your background color is like, but HSV makes it also easier to compare colors (just compare the 3 components and don't forget that HUE is circular (0 == 360)).